Table Tennis RuleBot (RAG)
我們希望能夠建立一個 桌球規則查詢系統,透過 RAG(Retrieval-Augmented Generation)技術,讓使用者能用自然語言快速查詢桌球比賽規則,並自動生成精確、可追溯的答案。
系統成果
整個系統流程如下:
- 使用者提問:以自然語言輸入問題(例 如「桌球的桌子高度應該要多高?」)。
- Embedding 檢索:
bge-m3
將問題轉換成向量- 在 PostgreSQL + pgvector 進行 Top-N 語意檢索
- Rerank:
xitao/bge-reranker-v2-m3
對檢索結果重新排序,挑出最相關的 Top-K 條文。 - 生成回答:
qwen3:1.7b
閱讀相關規則段落,輸出 繁體中文、條列式、帶規則編號 的答案。
目的
本專案的目標,是將 113 年度桌球規則 文件數位化並向量化,結合 pgvector 與 LLM,實現一個能夠:
- 以自然語言提問
- 自動檢索相關規則條文
- 由大語言模型整理回答並附規則編號
這樣可以取代傳統人工翻閱 PDF 的方式,提升查詢效率與正確性。
資料蒐集與前處理
資料來源
資料來源是中華民國桌球規則的《113 桌球規則》,內容完整且是有權威機構認定。
資料結構分析
由於 PDF 內容包含「章 → 節 → 條/子條」層級結構,還夾雜頁碼與不規則換行,需要做前處理。
我們使用 tta_rules_chunker.py
進行以下步驟:
- 使用 PyMuPDF (fitz) 逐頁讀取 PDF
- 正則表達式解析章節條文結構
- 輸出為 JSONL,每一筆為一個 chunk,包含:
rule_id
(如 3.2.1.1)hier_path
(章節路徑)- 條文全文
text
- 頁碼
執行流程
conda create -n tt-rag python=3.11
conda activate tt-rag
pip install pymupdf
python tta_rules_chunker.py -i "113桌球規則-5-40.pdf" -o "tta_rules_chunks.jsonl"
輸出檔案:tta_rules_chunks.jsonl
。
環境建立
我們透過 Docker Compose 部署以下服務:
- PostgreSQL + pgvector:儲存規則與向量索引
- Ollama:產生向量 embedding 與 LLM 回答
- pgAdmin:資料庫管理
docker-compose.yaml
version: "3.9"
services:
db:
image: pgvector/pgvector:pg16
container_name: pgvec
environment:
POSTGRES_USER: ******
POSTGRES_PASSWORD: ******
POSTGRES_DB: ttrules
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4:latest
container_name: pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: ******
PGADMIN_DEFAULT_PASSWORD: ******
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: "True"
ports:
- "127.0.0.1:5050:80"
volumes:
- pgadmin:/var/lib/pgadmin
depends_on:
- db
ollama:
image: ollama/ollama
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama:/root/.ollama
restart: unless-stopped
volumes:
pgdata:
pgadmin:
ollama:
資料表結構
我們在 pgvector.sql
中定義了 rules
資料表,用來儲存規則條文、結構化資訊與向量嵌入,並加上索引提升檢索效率。
pgvector.sql
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS rules (
rule_id text PRIMARY KEY,
doc_id text NOT NULL,
version_date date,
jurisdiction text,
source text,
language text,
chapter text,
chapter_title text,
section_id text,
section_title text,
hier_path text[] NOT NULL,
page_start int,
page_end int,
chunk_type text DEFAULT 'rule',
text text NOT NULL,
embedding vector(1024),
meta jsonb
);
CREATE INDEX IF NOT EXISTS idx_rules_doc ON rules (doc_id);
CREATE INDEX IF NOT EXISTS idx_rules_section ON rules (chapter, section_id);
模型拉取
在完成環境建立後,需要先透過 Ollama 拉取所需的模型,包含 embedding 模型、reranker 模型 與 LLM,才能支援後續的向量化與問答流程。
docker exec -it ollama ollama pull bge-m3
docker exec -it ollama ollama pull xitao/bge-reranker-v2-m3
docker exec -it ollama ollama pull qwen3:1.7b
寫入資料
透過 ingest.py
,將前處理的 JSONL 寫入資料庫。
流程:
- 讀取 JSONL:解析條文結構
- Embedding:呼叫 Ollama
/v1/embeddings
,使用bge-m3
產生向量 - Upsert:利用 SQLAlchemy + pgvector 寫入
rules
表
程式提供 embed_batch
與 upsert_rules
,支援批次處理。
測試與查詢
完成資料寫入後,透過 answer.py
進入查詢階段。
查詢流程
- 使用者輸入問題,例如「發球規則有哪些?」
- 系統將問題轉向量
- 在資料庫檢索 Top-N 條文(cosine similarity)
- 使用 reranker 模型(如
xitao/bge-reranker-v2-m3
)排序 - 將 Top-K 條文送入 LLM(Qwen3:1.7b)生成回答