跳至主要内容

Table Tennis RuleBot (RAG)

我們希望能夠建立一個 桌球規則查詢系統,透過 RAG(Retrieval-Augmented Generation)技術,讓使用者能用自然語言快速查詢桌球比賽規則,並自動生成精確、可追溯的答案。


系統成果

整個系統流程如下:

  1. 使用者提問:以自然語言輸入問題(例如「桌球的桌子高度應該要多高?」)。
  2. Embedding 檢索
    • bge-m3 將問題轉換成向量
    • 在 PostgreSQL + pgvector 進行 Top-N 語意檢索
  3. Rerankxitao/bge-reranker-v2-m3 對檢索結果重新排序,挑出最相關的 Top-K 條文。
  4. 生成回答qwen3:1.7b 閱讀相關規則段落,輸出 繁體中文、條列式、帶規則編號 的答案。

result


目的

本專案的目標,是將 113 年度桌球規則 文件數位化並向量化,結合 pgvectorLLM,實現一個能夠:

  • 以自然語言提問
  • 自動檢索相關規則條文
  • 由大語言模型整理回答並附規則編號

這樣可以取代傳統人工翻閱 PDF 的方式,提升查詢效率與正確性。


資料蒐集與前處理

資料來源

資料來源是中華民國桌球規則的《113 桌球規則》,內容完整且是有權威機構認定。

資料結構分析

由於 PDF 內容包含「章 → 節 → 條/子條」層級結構,還夾雜頁碼與不規則換行,需要做前處理。

我們使用 tta_rules_chunker.py 進行以下步驟:

  1. 使用 PyMuPDF (fitz) 逐頁讀取 PDF
  2. 正則表達式解析章節條文結構
  3. 輸出為 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

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 寫入資料庫。

流程:

  1. 讀取 JSONL:解析條文結構
  2. Embedding:呼叫 Ollama /v1/embeddings,使用 bge-m3 產生向量
  3. Upsert:利用 SQLAlchemy + pgvector 寫入 rules

程式提供 embed_batchupsert_rules,支援批次處理。

ingest-data.png


測試與查詢

完成資料寫入後,透過 answer.py 進入查詢階段。

查詢流程

  1. 使用者輸入問題,例如「發球規則有哪些?」
  2. 系統將問題轉向量
  3. 在資料庫檢索 Top-N 條文(cosine similarity)
  4. 使用 reranker 模型(如 xitao/bge-reranker-v2-m3)排序
  5. 將 Top-K 條文送入 LLM(Qwen3:1.7b)生成回答