QMD ่ฎพ่ฎกๆๆณๆป็ป
ๆดไฝๆถๆ่ฎพ่ฎกๅฒๅญฆ
1. ๅๅฑๆถๆ
QMD ้็จๆธ ๆฐ็ๅๅฑๆถๆ๏ผๆฏไธๅฑ้ฝๆๆ็กฎ็่่ดฃ๏ผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๆฅๅฃๅฑ (CLI / MCP / HTTP) โ
โ - ๅฝไปค่ก็้ข โ
โ - Model Context Protocol ๆๅกๅจ โ
โ - HTTP API โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅบ็จๅฑ (qmd.ts) โ
โ - ๅฝไปค่ทฏ็ฑๅๅๆฐ่งฃๆ โ
โ - ็จๆทไบคไบๅ่พๅบๆ ผๅผๅ โ
โ - ็ๅฝๅจๆ็ฎก็ โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ไธๅก้ป่พๅฑ (store.ts) โ
โ - ๆ็ดข็ผๆ (BM25 + Vector + Rerank) โ
โ - ๆๆกฃๅๅๅ็ดขๅผ โ
โ - Context ็ฎก็ๅ่ๆ่ทฏๅพ โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๆฐๆฎ่ฎฟ้ฎๅฑ (db.ts / collections.ts) โ
โ - SQLite ๆฐๆฎๅบๆไฝ โ
โ - YAML ้
็ฝฎ็ฎก็ โ
โ - ๆไปถ็ณป็ปๆฝ่ฑก โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅบ็ก่ฎพๆฝๅฑ (llm.ts) โ
โ - node-llama-cpp ๅฐ่ฃ
โ
โ - ๆจกๅๅ ่ฝฝๅ่ตๆบ็ฎก็ โ
โ - GPU/CPU ๅนถ่กไผๅ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
่ฎพ่ฎกๅๅ๏ผ
- ไพ่ตๅ็ฝฎ๏ผไธๅฑไพ่ตไธๅฑๆฅๅฃ๏ผไธไพ่ตๅ ทไฝๅฎ็ฐ
- ๅไธ่่ดฃ๏ผๆฏไธชๆจกๅๅชๅไธไปถไบ๏ผๅๅฅฝไธไปถไบ
- ๅฏๆต่ฏๆง๏ผๆฏๅฑ้ฝๅฏไปฅ็ฌ็ซๆต่ฏ๏ผ้่ฟๆฅๅฃๆณจๅ ฅ mock
2. ๆฐๆฎๆต่ฎพ่ฎก
QMD ้็จๅฝๆฐๅผๆฐๆฎๆต๏ผ้ฟๅ ๅฏไฝ็จ๏ผ
// ๅฅฝ็่ฎพ่ฎก๏ผ็บฏๅฝๆฐ๏ผ่พๅ
ฅ -> ่พๅบ
function reciprocalRankFusion(resultLists: RankedResult[][]): RankedResult[] {
// ไธไฟฎๆน่พๅ
ฅ๏ผ่ฟๅๆฐ็ปๆ
return sortedResults;
}
// ้ฟๅ
๏ผๅฏไฝ็จ๏ผไฟฎๆนๅ
จๅฑ็ถๆ
let globalResults: RankedResult[] = [];
function badFusion(lists: RankedResult[][]) {
globalResults = ...; // ๅฏไฝ็จ๏ผ
}ไผๅฟ๏ผ
- ๅฏ้ขๆตๆง๏ผ็ธๅ่พๅ ฅๆปๆฏไบง็็ธๅ่พๅบ
- ๅฏๆต่ฏๆง๏ผๅฎนๆ็ผๅๅๅ ๆต่ฏ
- ๅฏๅนถ่กๆง๏ผๆ ๅฏไฝ็จ็ๅฝๆฐๅฏไปฅๅฎๅ จๅนถ่กๆง่ก
3. ้ ็ฝฎไธๆฐๆฎๅ็ฆป
QMD ๅฐ้ ็ฝฎ๏ผYAML๏ผๅๆฐๆฎ๏ผSQLite๏ผๅ็ฆป๏ผ
# ~/.config/qmd/index.yml (้
็ฝฎ)
collections:
notes:
path: ~/notes
pattern: "**/*.md"
context:
"/": "Personal notes and ideas"-- ~/.cache/qmd/index.sqlite (ๆฐๆฎ)
-- documents, content, vectors ็ญ่กจไธบไปไน่ฟๆ ท่ฎพ่ฎก๏ผ
- ้ ็ฝฎๅฏ็ๆฌๆงๅถ๏ผYAML ๅฏไปฅๆพๅ ฅ git๏ผSQLite ไธๅบ่ฏฅ
- ๆฐๆฎๅฏ้ๅปบ๏ผๅ ้ค SQLite ๅฏไปฅ้ๆฐ็ดขๅผ๏ผ้ ็ฝฎไธไผไธขๅคฑ
- ๅค่ฎพๅคๅๆญฅ๏ผ้ ็ฝฎๅฎนๆๅๆญฅ๏ผๆฐๆฎ้ๅธธไธ้่ฆๅๆญฅ
ๅ ณ้ฎ่ฎพ่ฎกๅณ็ญๅๆ
1. ไธบไปไนไฝฟ็จ SQLite + FTS5 + sqlite-vec๏ผ
้ๆฉ็็ฑ๏ผ
| ็นๆง | SQLite | ๅ ถไป้ๆฉ (Elasticsearch, Pinecone) |
|---|---|---|
| ๆฌๅฐ่ฟ่ก | โ ๅฎๅ จๆฌๅฐ | โ ้่ฆๆๅกๅจ |
| ๅๆไปถ | โ ไธไธช .sqlite ๆไปถ | โ ๅคๆไปถ/ๆๅก |
| ้ถ้ ็ฝฎ | โ ๅผ็ฎฑๅณ็จ | โ ้่ฆ้ ็ฝฎ |
| FTS5 | โ ๅ ็ฝฎๅ จๆๆ็ดข | โ ๏ธ ้่ฆๆไปถ |
| ๅ้ๆ็ดข | โ sqlite-vec ๆฉๅฑ | โ ๅ็ๆฏๆ |
| ACID | โ ไบๅกๆฏๆ | โ ๆฏๆ |
| ่ตๆบๅ ็จ | โ ไฝ | โ ้ซ |
ๆ่กก๏ผ
- ๆง่ฝ๏ผSQLite ๅๆบๆง่ฝไธๅฆไธ็จๆ็ดขๅผๆ
- ๆฉๅฑๆง๏ผไธ้ๅๅๅธๅผ้จ็ฝฒ
- ้็จๅบๆฏ๏ผไธชไบบ็ฅ่ฏ็ฎก็๏ผ< 100K ๆๆกฃ๏ผๅฎๅ จ่ถณๅค
2. ไธบไปไนไฝฟ็จ node-llama-cpp๏ผ
ๆฌๅฐๆจ็็ไผๅฟ๏ผ
ไบ็ซฏ API (OpenAI, Claude)
โ
โโโ ้่ฆ็ฝ็ป่ฟๆฅ
โโโ ๆฐๆฎ็ฆปๅผๆฌๅฐ๏ผ้็ง้ฎ้ข๏ผ
โโโ ๆ token ่ฎก่ดน๏ผๆๆฌ้ฎ้ข๏ผ
โโโ ๅปถ่ฟ่พ้ซ๏ผRTT๏ผ
ๆฌๅฐๆจ็ (node-llama-cpp)
โ
โโโ ๅฎๅ
จ็ฆป็บฟ
โโโ ๆฐๆฎไธๅบๅข
โโโ ไธๆฌกๆงๆๆฌ๏ผ็กฌไปถ๏ผ
โโโ ๅปถ่ฟไฝ๏ผๆฌๅฐ่ฎก็ฎ๏ผ
ๆๆฏ้ๆฉ๏ผ
- GGUF ๆ ผๅผ๏ผ้ๅๆจกๅ๏ผๅๅฐๅ ๅญๅ ็จ
- Metal/CUDA/Vulkan๏ผGPU ๅ ้๏ผๆๅๆจ็้ๅบฆ
- ๅคไธไธๆๅนถ่ก๏ผๅ ๅๅฉ็จ GPU ่ฎก็ฎ่ตๆบ
3. ไธบไปไน่ฎพ่ฎก Context ็ณป็ป๏ผ
้ฎ้ข๏ผ็บฏๅบไบๅ ๅฎน็ๆ็ดขๆ ๆณ็่งฃๆๆกฃ็ไธไธๆ
็คบไพ๏ผ
# Meeting Notes
## Action Items
- Fix bug #123
- Update documentationๆฒกๆ Context๏ผ
- ๆ็ดข โbugโ ่ฟๅ่ฟไธชๆๆกฃ
- ไธ็ฅ้่ฟๆฏไปไน็ฑปๅ็ bug
ๆ Context๏ผ
qmd context add qmd://meetings "Meeting notes and action items"ๆ็ดข โbugโ ่ฟๅ๏ผ
meetings/2024-01-15.md #a1b2c3
Title: Meeting Notes
Context: Meeting notes and action items
Score: 85%
Action Items
- Fix bug #123
...
่ฎพ่ฎกๆๆณ๏ผ
- ๅ ๆฐๆฎๅขๅผบ๏ผไธบๆบๅจๆไพไบบ็ฑป็่งฃ็ไธไธๆ
- ๅฑๆฌกๅ็ปงๆฟ๏ผๅ จๅฑ โ ้ๅ โ ่ทฏๅพ๏ผๅฑๅฑ็ปๅ
- Agent ๅๅฅฝ๏ผLLM ๅฏไปฅๆ นๆฎ Context ๅๅบๆดๅฅฝ็้ๆฉ
4. ไธบไปไนไฝฟ็จ่ๆ่ทฏๅพ (qmd://)๏ผ
็ฉ็่ทฏๅพ็้ฎ้ข๏ผ
/Users/tobi/notes/work/project-a/README.md
/home/tobi/notes/work/project-a/README.md # Linux
C:\Users\tobi\notes\work\project-a\README.md # Windows
- ๅนณๅฐๅทฎๅผ
- ่ทฏๅพๅๅๅฏผ่ด้พๆฅๅคฑๆ
- ๆด้ฒๆไปถ็ณป็ป็ปๆ
่ๆ่ทฏๅพ็ไผๅฟ๏ผ
qmd://notes/work/project-a/README.md
- ๅนณๅฐๆ ๅ ณ๏ผ็ปไธๆ ผๅผ
- ็จณๅฎ๏ผ็ฉ็่ทฏๅพๅๅไธๅฝฑๅ่ๆ่ทฏๅพ
- ๅฎๅ จ๏ผไธๆด้ฒ็ๅฎๆไปถ็ณป็ป
- ็ฎๆด๏ผๆไบ้ ่ฏปๅ่ฎฐๅฟ
5. ไธบไปไนไฝฟ็จ RRF ่ไธๆฏๆบๅจๅญฆไน ่ๅ๏ผ
RRF (Reciprocal Rank Fusion)๏ผ
ไผ็น๏ผ
- ๆ ้่ฎญ็ปๆฐๆฎ
- ๆ ้่ฐๅ
- ่ฎก็ฎ็ฎๅ O(n)
- ๅฏ่งฃ้ๆงๅผบ
็ผบ็น๏ผ
- ไธๆฏๆไผ็๏ผ็ธๆฏ่ฎญ็ปๅฅฝ็ๆจกๅ๏ผ
- ไธ่ฝๅญฆไน ็นๅพไบคไบ
ๆบๅจๅญฆไน ่ๅ๏ผ
ไผ็น๏ผ
- ๅฏไปฅๅญฆไน ๆไผๆ้
- ๅฏไปฅๆๆ็นๅพไบคไบ
็ผบ็น๏ผ
- ้่ฆๅคง้ๆ ๆณจๆฐๆฎ
- ้่ฆ่ฎญ็ปๅ่ฐๅ
- ่ฟๆๅ้ฃ้ฉ
- ้พไปฅ่งฃ้
QMD ็้ๆฉ๏ผRRF + ไฝ็ฝฎๆ็ฅๆททๅ
- ็ฎๅๆๆ๏ผRRF ๅจไฟกๆฏๆฃ็ดข้ขๅ้ช่ฏๅคๅนด
- ๆ ้ๆฐๆฎ๏ผไธ้่ฆๆถ้่ฎญ็ปๆฐๆฎ
- ๅฏ่ฐๆด๏ผ้่ฟไฝ็ฝฎๆ็ฅๆททๅๅพฎ่ฐ
- ๅฏ่งฃ้๏ผ็จๆทๅฏไปฅ็่งฃ็่ๅ้ป่พ
ไปฃ็ ็ป็ปๅๆฝ่ฑกๅฑๆฌก
1. ็ฑปๅ้ฉฑๅจๅผๅ
QMD ๅคง้ไฝฟ็จ TypeScript ็ฑปๅ็ณป็ป๏ผ
// ๆ็กฎ็็ฑปๅๅฎไน
export type SearchResult = DocumentResult & {
score: number;
source: "fts" | "vec";
chunkPos?: number;
};
export type HybridQueryResult = {
file: string;
displayPath: string;
title: string;
body: string;
bestChunk: string;
bestChunkPos: number;
score: number;
context: string | null;
docid: string;
};ๅฅฝๅค๏ผ
- ็ผ่ฏๆถๆฃๆฅ๏ผๅๅฐ่ฟ่กๆถ้่ฏฏ
- ่ชๆๆกฃๅ๏ผ็ฑปๅๅณๆๆกฃ
- IDE ๆฏๆ๏ผ่ชๅจ่กฅๅ จๅ้ๆ
2. ๅฝๆฐ็ปๅ
ๅคๆๅ่ฝ้่ฟ็ฎๅๅฝๆฐ็ปๅๅฎ็ฐ๏ผ
// ้ซๅฑๅฝๆฐ๏ผๆททๅๆ็ดข
async function hybridQuery(query: string): Promise<HybridQueryResult[]> {
const expanded = await expandQuery(query);
const results = await Promise.all([
searchFTS(query),
searchVec(query),
...expanded.map(q => search(q))
]);
const fused = reciprocalRankFusion(results);
const reranked = await rerank(query, fused);
return blendScores(fused, reranked);
}
// ๆฏไธชๅบๅฑๅฝๆฐ้ฝๆฏๅฏๆต่ฏ็ๅๅ
function reciprocalRankFusion(lists: RankedResult[][]): RankedResult[] { ... }
function blendScores(fts: RankedResult[], reranked: RerankResult[]): HybridQueryResult[] { ... }่ฎพ่ฎกๆๆณ๏ผ
- ๅไธ่่ดฃ๏ผๆฏไธชๅฝๆฐๅชๅไธไปถไบ
- ๅฏ็ปๅๆง๏ผๅฝๆฐๅฏไปฅๅ็งฏๆจไธๆ ท็ปๅ
- ๅฏๆต่ฏๆง๏ผๆฏไธชๅฝๆฐ็ฌ็ซๆต่ฏ
3. ้่ฏฏๅค็็ญ็ฅ
QMD ้็จไผ้ ้็บง็ญ็ฅ๏ผ
async function searchVec(query: string): Promise<SearchResult[]> {
try {
const embedding = await getEmbedding(query);
if (!embedding) return []; // ้็บง๏ผ่ฟๅ็ฉบ็ปๆ
const results = await db.query(...);
return results;
} catch (error) {
console.error("Vector search failed:", error);
return []; // ้็บง๏ผ่ฟๅ็ฉบ็ปๆ๏ผไธไธญๆญๆต็จ
}
}ๅๅ๏ผ
- ้จๅๅคฑ่ดฅไธๅฝฑๅๆดไฝๅ่ฝ
- ๅ้ๆ็ดขๅคฑ่ดฅ๏ผFTS ไป็ถๅฏไปฅๅทฅไฝ
- ่ฎฐๅฝ้่ฏฏ๏ผไฝไธๆๅบๅผๅธธไธญๆญ็จๆทๆไฝ
ๅฏๆฉๅฑๆง่ฎพ่ฎก
1. ๆไปถๅๆถๆ
่ฝ็ถ QMD ็ฎๅๆฒกๆๆญฃๅผๆไปถ็ณป็ป๏ผไฝไปฃ็ ็ปๆๆฏๆๆฉๅฑ๏ผ
// LLM ๆฅๅฃๅฏไปฅๆไธๅ็ๅฎ็ฐ
interface LLM {
embed(text: string): Promise<EmbeddingResult>;
rerank(query: string, docs: Document[]): Promise<RerankResult>;
}
// ๅฝๅๅฎ็ฐ
class LlamaCpp implements LLM { ... }
// ๆชๆฅๅฏ่ฝ็ๅฎ็ฐ
class OpenAILLM implements LLM { ... }
class OllamaLLM implements LLM { ... }2. ้ ็ฝฎ้ฉฑๅจ
ๆฐๅ่ฝ้่ฟ้ ็ฝฎๅฏ็จ๏ผๆ ้ไฟฎๆนไปฃ็ ๏ผ
# ๆชๆฅๅฏ่ฝ็ๆฉๅฑ
collections:
notes:
path: ~/notes
pattern: "**/*.md"
# ๆฐ็้
็ฝฎ้้กน
embed_model: "custom-model" # ่ชๅฎไนๅตๅ
ฅๆจกๅ
chunk_size: 1000 # ่ชๅฎไนๅๅๅคงๅฐ
preprocessors: # ่ชๅฎไน้ขๅค็ๅจ
- strip_frontmatter
- normalize_links3. ๆจกๅๅ่ฎพ่ฎก
ๆฏไธชๆจกๅๅฏไปฅ็ฌ็ซๆผ่ฟ๏ผ
src/
โโโ qmd.ts # CLI ๅ
ฅๅฃ๏ผๅฏไปฅๆฟๆขไธบ Web UI๏ผ
โโโ store.ts # ไธๅก้ป่พ๏ผๅฏไปฅ็ฌ็ซๅๅธไธบๅบ๏ผ
โโโ db.ts # ๆฐๆฎๅบๅฑ๏ผๅฏไปฅๆฟๆขไธบๅ
ถไปๆฐๆฎๅบ๏ผ
โโโ llm.ts # LLM ๅฑ๏ผๅฏไปฅๆฏๆๅ
ถไปๆจ็ๅผๆ๏ผ
โโโ collections.ts # ้
็ฝฎ็ฎก็
โโโ formatter.ts # ่พๅบๆ ผๅผๅ๏ผๅฏไปฅๆทปๅ ๆฐๆ ผๅผ๏ผ
ๆง่ฝไผๅ็ญ็ฅ
1. ๅปถ่ฟๅ ่ฝฝ (Lazy Loading)
private async ensureEmbedModel(): Promise<LlamaModel> {
if (this.embedModel) return this.embedModel; // ๅทฒๅ ่ฝฝ๏ผ็ดๆฅ่ฟๅ
// ๅฆๅๅ ่ฝฝๆจกๅ
}ไผๅฟ๏ผ
- ๅฏๅจ้ๅบฆๅฟซ๏ผไธๅ ่ฝฝๆชไฝฟ็จ็ๆจกๅ๏ผ
- ๅ ๅญๅ ็จไฝ๏ผๅชๅ ่ฝฝ้่ฆ็ๆจกๅ๏ผ
- ๆ้ไป่ดน๏ผๅชๅจ้่ฆๆถๆถ่่ตๆบ๏ผ
2. ๆน้ๅค็
// ไฝๆ๏ผ้กบๅบๅตๅ
ฅ
for (const text of texts) {
await embed(text); // N ๆฌก่ฐ็จ
}
// ้ซๆ๏ผๆน้ๅตๅ
ฅ
await embedBatch(texts); // 1 ๆฌก่ฐ็จ๏ผๅนถ่กๅค็ๆถ็๏ผ2-3x ๆง่ฝๆๅ
3. ๆบ่ฝ็ผๅญ
// LLM ็ปๆ็ผๅญ
const cacheKey = getCacheKey("expandQuery", { query, model });
const cached = getCachedResult(db, cacheKey);
if (cached) return JSON.parse(cached);
// ่ฎก็ฎๅนถ็ผๅญ
const result = await llm.expandQuery(query);
setCachedResult(db, cacheKey, JSON.stringify(result));็ญ็ฅ๏ผ
- ็ผๅญๆฅ่ฏขๆฉๅฑ็ปๆ๏ผ็ธๅๆฅ่ฏขๆปๆฏไบง็็ธๅๆฉๅฑ๏ผ
- ็ผๅญ้ๆๅบ็ปๆ๏ผๆๆกฃๅๆฅ่ฏขไธๅ๏ผ็ปๆไธๅ๏ผ
- LRU ๆธ ็๏ผ้ๆบๆฆ็ๆธ ็๏ผ้ฟๅ ้ไธญๅผๆธ ็็ๆง่ฝๅฐๅณฐ
4. ๅผบไฟกๅท็ญ่ทฏ
const hasStrongSignal = topScore >= 0.85 && (topScore - secondScore) >= 0.15;
if (hasStrongSignal) {
// ่ทณ่ฟๆ่ดต็ LLM ๆฉๅฑ
return initialResults;
}ๆถ็๏ผๅธธ่งๆ ๅตไธ่็ 1-2 ็ง
ๆต่ฏ็ญ็ฅ
1. ๅๅฑๆต่ฏ
test/
โโโ unit/ # ๅๅ
ๆต่ฏ๏ผ็บฏๅฝๆฐ๏ผ
โ โโโ chunk.test.ts
โ โโโ rrf.test.ts
โ โโโ fts.test.ts
โโโ integration/ # ้ๆๆต่ฏ๏ผๆฐๆฎๅบ + ้ป่พ๏ผ
โ โโโ search.test.ts
โ โโโ index.test.ts
โโโ e2e/ # ็ซฏๅฐ็ซฏๆต่ฏ๏ผๅฎๆดๆต็จ๏ผ
โโโ cli.test.ts
2. ๆต่ฏๆฐๆฎ็ฎก็
// ไฝฟ็จๅ
ๅญๆฐๆฎๅบ่ฟ่กๆต่ฏ
const db = openDatabase(":memory:");
// ๆต่ฏๅๆธ
็
afterEach(() => {
db.close();
});3. ๅ ณ้ฎ่ทฏๅพ่ฆ็
- ๅๅ็ฎๆณ๏ผๅ็ง่พน็ๆ ๅต๏ผ็ฉบๆๆกฃใไปฃ็ ๅใๆ ้ข๏ผ
- RRF ่ๅ๏ผๅคๅ่กจใ้ๅคๆๆกฃใๆ้่ฎก็ฎ
- ๆ็ดขๆต็จ๏ผFTSใๅ้ใๆททๅใ้ๆๅบ
ๆป็ป
QMD ็่ฎพ่ฎกไฝ็ฐไบไปฅไธๆ ธๅฟๆๆณ๏ผ
- ็ฎๅไผๅ ๏ผไฝฟ็จๆ็ๆๆฏ๏ผSQLiteใRRF๏ผ๏ผ้ฟๅ ่ฟๅบฆๅทฅ็จ
- ๆฌๅฐไผๅ ๏ผๆฐๆฎไธๅบๅข๏ผไฟๆค้็ง
- ๆง่ฝๆ่ฏ๏ผๅปถ่ฟๅ ่ฝฝใๆน้ๅค็ใๆบ่ฝ็ผๅญ
- ๅฏๆฉๅฑๆง๏ผๆจกๅๅ่ฎพ่ฎก๏ผๆฅๅฃๆฝ่ฑก
- ็จๆทไฝ้ช๏ผๅฟซ้ใๅ็กฎใๅฏ่งฃ้
่ฟไบ่ฎพ่ฎกๅณ็ญไฝฟๅพ QMD ๆไธบไธไธชๅฟซ้ใๅฏ้ ใๆ็จ็ไธชไบบ็ฅ่ฏ็ฎก็ๆ็ดขๅทฅๅ ทใ