Files
tps-dashboard/logs/phase3_log_task1.md
2026-05-26 18:59:39 +08:00

6.8 KiB
Raw Permalink Blame History

Phase 3 开发日志

本文件记录 Phase 3(知识库基础设施)各 Task 的已完成事实。 续接快照(待办/决策)见单独文件,不混入本 log。


Task 1 — embedding 最小链路验证 2026-05-26 完成)

目标

用最小代价验证 Phase 3 的命脉:文本能否转成正确维度的向量、整条「解析→embedding→入库→语义检索」链路能否跑通。不碰 schema、不碰前端、不碰 docx。

关键结论(地基事实,后续 Task 依赖)

  • embedding 服务确定为 MiniMax embo-01。前期查证已排除 DeepSeek(官方无 embedding 接口)。
  • embo-01 输出维度 = 1536,与 001_init.sqlknowledge_embeddings.embedding vector(1536) 完全匹配,无需改维度、无需改宪法。
  • embo-01 真实接口结构(实测确认,非凭印象):
    • 请求:{"model":"embo-01", "texts":[...], "type":"db"|"query"} —— 注意是 texts 数组,不是 OpenAI 的 input
    • 返回:向量在 vectors 字段(data["vectors"][0]),不是 OpenAI 的 data[0].embedding
    • 存文档用 type="db",查询用 type="query",不可混。
    • 调用需 Authorization: Bearer {API_KEY} + GroupId 两个凭证(MiniMax 特有 GroupId)。
  • 检索方式:使用 pgvector 数据库原生余弦距离算子 <=>,不在 Python 侧计算。
  • 凭证管理API Key + GroupId 存于 backend/.envMINIMAX_EMBED_API_KEY / MINIMAX_GROUP_ID),代码从 .env 读取,未进任何代码、未进 git.env 已在 .gitignore)。
  • 注意:embedding 接口地址 https://api.minimax.chat/v1/embeddings,与 Cline 写代码所用的 M2.7 chat 接口是两套独立配置,互不影响。

新增/改动文件(已 commit & pushcommit 38873ac

  • backend/requirements.txt+pgvector==0.2.5
  • backend/app/core/config.py+引导凭证字段)
  • backend/app/models/knowledge.py(新增,embedding 字段用 pgvector.Vector
  • backend/app/services/embedding_service.py(新增,embo-01 调用封装)
  • backend/app/services/knowledge_service.py(新增,写库 + SQL 向量检索)
  • backend/scripts/test_embo01_api.py(探路脚本)
  • backend/scripts/verify_embedding.py(全链路验证脚本)
  • backend/sample_md/(空目录,测试文件已清)

收尾

  • 验证用测试数据已全部清除:knowledge_items = 0 行,knowledge_embeddings = 0 行,sample_md/ 空。
  • 知识库当前为干净空库,等待真实笔记导入。

顺手核实到的现状(未处理,留待办)

  • episodes 表当前仅 7 行。
  • Phase 2 的「批量导入」前端有入口但从未真实导入过收视数据(制片人确认:是流程上没提醒导入,加上收视表里有测试数据不会删)。属 Phase 2 遗留债,记入 backlog,另行处理,不并入 Phase 3。

Task 2 — 知识库管理后台(最小可用版)2026-05-26 完成)

目标

做一条端到端跑通的链路:上传 .md 笔记 → 解析 yaml frontmatter → 入库(含语义向量)→ 列表展示 → 删除。不改 schema、不碰认证、不引向量索引以外的新依赖。

新增/改动文件(4 次独立 commit)

文件 动作 说明
backend/requirements.txt +python-frontmatter==1.1.0
backend/app/services/knowledge_service.py 扩展 +parse_md_file() + delete_item() + list_items() + get_distinct_sources();复用 EmbeddingService
backend/app/api/knowledge.py 新增 4 个接口:POST /upload、GET /items、DELETE /items/{id}、GET /sources
backend/app/main.py +app.include_router(knowledge_router)
frontend/src/services/knowledgeService.js 新增 封装 4 个 API 调用
frontend/src/pages/KnowledgeBase/KnowledgeBase.jsx 重写 上传区(Dragger) + 筛选栏(Select) + 列表(Table) + 删除(Popconfirm)

yaml frontmatter 解析映射(按真实样本,写死)

# 类型 → source_type(硬映射,不猜测)
杂志文章/军报  military_report
节目文稿  manuscript
报题单  baoti

# 标题
名称  标题  title

# 作者
作者  编导  author

# 出处详情(存 JSONB 的 source_detail
期刊 + 期号  拼接"航空知识 2026年第1期"

# 播出日期
容错"待补充"等非日期文本  null

# 权重
原样存 JSONB不展示不排序Phase 4 排序用

# 双链 [[...]]
_extract_double_brackets() 原样存入 JSONB 预留给 Phase 4

关键设计决策

  1. source_detail 存 JSONB 不 ALTERTask Brief 说"不要新增表字段"tags JSONB 列已用于存权重,故 source_detail 也放进来,不 ALTER TABLE。查询时从 tags->>'source_detail' 解压。
  2. 来源筛选动态从 DB 取get_distinct_sources() 查库里所有 tags->>'source_detail' DISTINCT 值,下拉选项不写死,新增一本杂志自动出现。
  3. embedding 复用:直接调用 EmbeddingService.embed_single(content_md, embed_type="db")1536 维向量已验证,不重写。
  4. 删除 CASCADE:数据库层已有 ON DELETE CASCADEPython 侧只删 KnowledgeItem 即可。

测试结果(已验证)

验收项 结果
两篇 md 均成功入库 id=27(光辉之路)、id=28(超级战舰)
杂志篇显示作者"钱峰",无播出时间 author=钱峰, publish_date=null
节目篇显示编导"左鑫",播出日期"待补充"不显示 author=左鑫, publish_date=null
source_detail 正确存下("航空知识 2026年第1期" tags 中可见
来源筛选下拉可据此筛 /api/knowledge/sources 返回 航空知识 2026年第1期
knowledge_embeddings 有对应行(向量已生成) 两行,19207/19174 字符(1536 维)
删除有二次确认,确认后条目+向量一并消失 删除 id=27 验证,embeddings 级联消失

commit 历史

  • 779429a feat: 添加 python-frontmatter 依赖用于解析 md yaml frontmatter
  • 7b6ae24 feat: 知识库管理后台上传/列表/删除API,含frontmatter解析
  • 7155a18 feat: 知识库管理前端页面,含上传/列表/筛选/删除

潜在风险

  1. windows CRLF 警告:每次 commit 都有 CRLF→LF 警告,不影响功能但难看,属 git 全局配置问题(core.autocrlf),下次提醒用户设一下。
  2. "待补充"播出日期:节目文稿的播出日期"待补充"正确存为 null,列表"播出/发表时间"格无值时不显示(render: val || null)。
  3. 删除后 sources 下拉残留:删除后 get_distinct_sources() 重新查会去掉已删条目的 source_detail,正常。
  4. embo-01 调用失败时整条上传失败:当前是 try-except 包装,失败时返回 errors 数组,不污染已入库数据(因为单条原子),但前端需要展示 errors 反馈(已实现)。