今日完成

1. 技术栈确认

  • Embedding:BAAI/bge-m3 int8 ONNX(544MB),本地 CPU 运行,1024 维,L2 归一化,中/日/英跨语言对齐验证通过。
  • 向量库:Chroma(本地持久化,余弦距离)。
  • LLM:One API → NVIDIA 远程渠道,deepseek-ai/deepseek-v4.1-flash。
  • 前端/会话管理:WordPress(待对接)。
  • API:FastAPI,端口 8093,systemd 常驻。

2. 环境隔离

golforfun 旧项目和新项目分两个 venv,避免依赖冲突:

  • /opt/golf-rag/venv:旧项目,transformers 5.20.2。
  • /opt/rag-new/venv:新项目,optimum + onnxruntime,不装 sentence-transformers。

3. 文档解析器

parse_pdf.py / parse_pdf_v2.py(PDF → chunk):

  • 按页拆分,识别段落和表格行。
  • 表格行拼表头:Net Sales (FY2025=440, FY2026=481)。
  • 清洗:去页眉页脚、合并字距问题(K O K O P E L L I → KOKOPELLI)、合并断行段落。
  • 过滤纯数字图表行。
  • KOKOPELLI 财报:178 chunk(149 段落 + 29 表格行),跳过 8 个标题页。

parse_excel.py(Excel → chunk):

  • 规整表:表头 + 逐行数据,每行拼成 Date: ... | Time: ... | Activity: ...。
  • 合并单元格继承:只对 Date/Time 维度列生效,Details 空就空着。
  • 行程表:30 chunk,无跳过。

4. 入库管道

ingest.py:

  • 接入 parse_pdf_v2 和 parse_excel。
  • 中文辅助映射:财务科目(Net Sales→销售额)、KPI、景点名。
  • documents 存原文,embeddings 用「原文 + 中文辅助」算。
  • 分批 embedding(BATCH=10),避免 OOM。
  • 按 source_id 删旧再写,避免重复。
  • 本次入库 185 条(PDF 155 + Excel 30),collection 总数 188。

5. 检索与生成

rag_core.py:

  • ask(query, top_k, where) 对外接口。
  • build_prompt 带 source_type/page/row/table_type,模型能区分 [actual] 和 [forecast]。
  • SYSTEM_PROMPT 明确规则:优先用 actual、不编造、标引用。
  • max_tokens=2000 + 空内容容错(finish_reason=’length’ 时返回明确提示)。
  • config.py:集中配置,.env 读令牌,chmod 600。

6. 黄金测试集

golden_set.json + eval_golden.py:

  • 4 条测试:营业利润、销售额、归母净利润、operating profit。
  • 4/4 通过,跨语言检索验证。

7. FastAPI 服务

api.py:

  • POST /api/ask:接收 {text, top_k, where},返回 {answer, sources}。
  • GET /api/health:健康检查。
  • CORS 开放(验证期)。
  • systemd 服务 rag-api,端口 8093,Restart=on-failure,TimeoutStartSec=120。

8. 验证结果

问题 结果
Net Sales 是多少 ✅ 481(FY2026 Q1 实际值),正确排除 p36 预测值
EBITDA 是多少 ✅ 58 百万日元(+574.3%),actual/forecast 区分正确
第2天下午去哪 ✅ 祇园 + 伏见稻荷大社,中文辅助生效
营业利润是多少 ✅ 100 亿日元,跨语言命中日文 営業利益

当前架构

WordPress(待接)
  ↓ POST /api/ask
FastAPI(127.0.0.1:8093)
  ↓
rag_core.ask()
  ├─ bge-m3 int8 embedding(本地 CPU)
  ├─ Chroma 检索(PDF 155 + Excel 30)
  └─ One API → deepseek-v4.1-flash → NVIDIA 远程

已知问题(不紧急)

  • PDF 表格列名部分错位:p27 的 FY2025=+41, FY2026=+9.3% 应为 Change=+41, YoY=+9.3%,但数字对了,模型靠量级和 [actual] 标签绕过。
  • p36 表头不完整:col1=1,758, col2=2,000 缺 FY2025/FY2026 列名,但 [forecast] 标签让模型知道是预测值。
  • 不同 PDF 需调规则:guess_columns、is_table_header 是针对 KOKOPELLI 写的,新 PDF 格式不同需重新调,或引入 agent 做语义映射。
  • 内存紧张:golforfun + rag-api 同时跑,available 1.4Gi,swap 1.7Gi。验证期建议停 golforfun。

接下来的 Task

高优先级

  • ☐ nginx 反代 + HTTPS:子域(如 rag.golforfun.net)→ 127.0.0.1:8093,Let’s Encrypt DNS-01 证书。参考 golforfun 项目的踩坑清单(DNS 传播、certbot manual、tailscaled 占 443)。
  • ☐ WordPress 对接:Codex 侧写 POST /api/ask 调用 + 会话保存(父 post 存会话、子 post 存每轮消息)。
  • ☐ 权限过滤接入:build_access_filter(user_context) 策略函数,当前 public/registered 两档,where 参数已透传。
  • ☐ 会话入库:一问一答作为一个 chunk,异步批量写 Chroma,metadata 带 session_id、user_id、turn_index。

中优先级

  • ☐ Word/PPT 解析:用 MarkItDown 或 python-docx/python-pptx,输出统一 {text, aux, meta},接入 ingest.py。
  • ☐ 表格列名映射修复:parse_pdf_v2.py 的占位逻辑,让 col1/col2 变成 FY2025/FY2026。
  • ☐ 内存优化:停 golforfun 或迁到另一台;或换更小的 embedding 模型(multilingual-e5-small,约 500MB)。

低优先级(验证期后)

  • ☐ agent 表格映射:规则解析失败时调 LLM 做语义映射,处理不同公司财报。
  • ☐ 视频/音频入库:faster-whisper CPU 转录,按时间窗口切 chunk。
  • ☐ 图片入库:视觉模型生成描述,描述文本入库。
  • ☐ URL 入库:ChromeRAG 抽正文,按 heading 拆。
  • ☐ K 线图:前端展示 + 代码算指标 + 文本 RAG 补新闻,不塞进文本 RAG。

参考资料

  • 旧项目复盘:https://helloai.jp/archives/2984
  • 踩坑清单:拆条按子条款、关键词硬过滤、with_structured_output 不可用、评分死循环、timeout=300、proxy_read_timeout 300s、Restart=on-failure。
  • 文件路径:/opt/rag-new/(bge_m3.py、rag_core.py、api.py、parse_pdf_v2.py、parse_excel.py、ingest.py、eval_golden.py、config.py、.env)、/opt/rag-nvidia/data/(koko.pdf、VIP Trip to Japan.xlsx)。

一句话总结:PDF + Excel 入库、跨语言检索、actual/forecast 区分、FastAPI 服务全部跑通,Net Sales 和 EBITDA 答对,systemd 常驻。下一步是 nginx + WordPress 对接 + 权限过滤 + 会话入库。