Files
mcn-short-video/极致事件卡片_文档型容器可行性实测_V1.0_20260923.md
maogeigei 80072ff542 chore(repo): 归集 09-14~09-29 工作产出(技能/知识库/工作台/报告)并收敛临时产物
- skill: 素材库分块定义(00~11)、05_极致事件知识库 4 篇、08_对话风格总纲、mcn-video-prompt 提示词质量门禁与外部语料检索流程

- workbench: mcn-work-shop 新增 cli-backend.js(本地 CLI 接入)、工作台视觉交互规范;移除旧 UI 规范

- docs: 根目录极致事件/素材卡/审计方案报告与热门短视频清单入库

- memory: 补 09-14~09-29 日志与自动化任务记忆

- chore: .gitignore 排除 tools/、.tmp-chrome-*/、_k_test.cjs
2026-09-29 18:56:24 +08:00

10 KiB
Raw Permalink Blame History

极致事件卡片 · 文档型容器可行性实测 V1.0

日期:2026-09-23 | 环境:本机 WeKnora 0.8.0(localhost:31607,容器 WeKnora-app) 起因:用户追问「为什么不用文档型知识库呢?不分块可以吗」 方法:全部为真实实测,非推演。实验 1 走零写入的 POST /chunker/preview;实验 2 建临时库(跑完 DELETE,有 HTTP 200 回执),未触碰正式库 MCN极致事件素材库 与 mcnvideoprompt。 脚本与原始输出:tools/weknora-fit/7_doc_noblock.py、8_doc_vs_faq.py 及 out/7_doc_noblock.txt、out/8_doc_vs_faq.txt


〇、结论先行(三句话)

  1. "不分块"能做到 —— 但只有一条路:一卡一文档 + chunk_size ≥ 单卡长度。实测 11 篇文档全部 = 1 块,字段零丢失。
  2. 分块不是选 FAQ 的理由。即使把卡片做得完整无损,文档型的口语问法召回仍低于 FAQ(阈值 0.5 时 9/11 vs 11/11)。
  3. 真正的分水岭在**「一个问题有几个语义入口」**:FAQ 能给一个答案挂上「标准问 + N 条相似问」多条问法;文档型只有整卡正文一条。结论:分块问题可绕,问法杠杆绕不过。

一、分块问题:能解决,但前提很硬

1.1 机制(源码依据)

事实 出处
文本 ≤ chunkSize 时直接整段返回,完全不切 internal/infrastructure/chunker/splitter.go:225-227(splitBySeparators 提前 return)
分隔符按优先级递归切:先按第一个切,切出的片段仍超长才用下一个 splitter.go:214-216(注释明示 parity with Python splitter)
absoluteMaxSize = 7500 硬上限:任何配置下,单块超 7500 字强制切开 splitter.go:400、splitter.go:412-427、splitter.go:499
无"不分块 / 单块"开关 ChunkingConfig 结构仅含 chunk_size / chunk_overlap / separators / strategy / token_limit / …(internal/types/knowledgebase.go:254-292)

1.2 实测结果(3 张卡共 2793 字 / 11 张卡共 8605 字)

# 配置 输入 输出 判定
① 单卡 + chunk_size 1200 1150 字 1 块,10/10 字段齐 ✅ 不切
② 单卡 + chunk_size 100000 1150 字 1 块,10/10 字段齐 ✅ 绝对上限对单卡无影响
③ 整篇 11 卡 + 1200 + 卡片标题 ### 提为第一优先级 8605 字 10 块 = 6 完整 + 3 块多卡混装 + 2 块半卡 ❌
④ 整篇 11 卡 + chunk_size 100000 8605 字 2 块(7298 + 1307),块 1 硬塞 9 张卡 ❌ 7500 上限被证实
⑤ 整篇 11 卡 + 1200 + 普通分隔符(对照组) 8605 字 与 ③ 逐块完全一致 —

三条硬结论:

  1. "按卡片标题切"这个思路无效 —— ③ 与 ⑤ 输出逐字相同,说明把 ### 提到第一优先级没有改变任何边界。卡片内 \n\n 密度远高于卡间,递归切分照样在卡内下刀。
  2. "多卡一文档"必碎 —— 8605 字 > 7500 硬上限,必然被切;且切点是字符位置,不是卡片边界 → 块内混装、块间劈卡。
  3. "一卡一文档"可做到零切割 —— ①② 证明:单卡 1150 字 < chunk_size 即整段返回。

1.3 代价:容器语义被反过来用

要让文档型不切碎,必须把"一篇文档"降到"一张卡"的粒度。这实际上是在用手工方式模拟"一条目 = 一分块"——正是 FAQ 型的原生行为。


二、检索问题:这才是真正的分水岭

前提:文档型走 ①的姿势(一卡一文档),实测 11 篇全部 = 1 块([4] 分块核对 输出 11 行「块数=1」)。即排除分块干扰后再比检索。

2.1 同口径三组探针(A 标准问 / B 相似问 / C 未登录口语问法,各 11 条)

文档型容器(/hybrid-search,vector + keyword 双开,一卡一文档):

组 0.7 0.6 0.5 0.45 0.4 0.3
A 标准问 11/11 8/11 7/11 7/11 7/11 7/11
B 相似问 9/11 10/11 10/11 10/11 10/11 10/11
C 新问法 7/11 8/11 9/11 8/11 8/11 8/11

FAQ 型容器(/faq/search,纯向量)— 基线数据来自 out/6_threshold.txt:

组 0.7 0.5
A 标准问 11/11 11/11
B 相似问 8/11 11/11
C 新问法 1/11 11/11

判定口径:top1 是否为期望卡。文档型判 knowledge_title,FAQ 判返回条目的答案首行事件名。

2.2 三个可读出来的事实

事实 1:文档型没有"可用工作点"。 A 组在 0.7 最好(11/11),C 组在 0.5 最好(9/11)——两者互斥。文档型升阈值救标准问、降阈值救口语问,不存在单点让三组同时达标。FAQ 则在 0.5 单点即三组全 11/11。

事实 2:文档型的召回"卡上限",降阈值不解决。 B 组 0.7→0.3 是 9→10→10→10→10→10;C 组是 7→8→9→8→8→8。到某个上限就不再涨 —— 说明瓶颈不是阈值(召回不足),而是召回不到(语义入口不够)。

事实 3:文档型排序不稳,且降阈值会反噬。 A 组从 0.7 的 11/11 掉到 0.5 的 7/11,是降阈值引入误召回、把正确答案挤出 top1。典型如 C 组「找一条家常菜做到能开店水准的素材」→ 全阈值 ✗(错配到别的菜);B 组「逞强之后立刻被打脸」→ 全阈值 ✗。

2.3 差异的机制(为什么 FAQ 更准)

FAQ 型 文档型
入索引的文本 标准问 + 相似问(question_index_mode=combined) 整卡正文(600–978 字)
语义单元 短问句(一问一答) 整卡平均语义(含画面内容/镜头语言/复用场景…)
匹配方式 问 ↔ 问 问 ↔ 整卡

文档型把"画面内容 + 镜头语言 + 画面风格 + 复用场景"等大量非关键信息一并向量化,需求句的权重被稀释。举实测原例:

  • 探针:「找一条家常菜做到能开店水准的素材」
  • 文档型 → 全阈值都错配(辣鸡爪卡片正文里"家常菜/开店水准"这类抽象需求占比太小)
  • FAQ → 命中「蒜香辣鸡爪」score 0.6902,靠的正是相似问「有没有难度高、有技术卡点的下饭菜素材」

这就是"问法杠杆":FAQ 允许你为同一个答案挂上多条真实检索口径(含反例边界),而这些问法自己就成了索引。


三、文档型并非全无优势(如实说明)

能力 FAQ 型 文档型
条目级多标签 ❌ 只有 tag_name 单值 ✅ tag_ids 数组
文件夹树 ❌ ✅ 有
草稿 / 发布 状态机 ❌ ✅ draft/publish
条目正文可编辑(人工改卡面) ❌ 需重导 ✅ PUT /knowledge/manual/:id
一次批量导入 ✅ faq/entries 数组(含 dry_run 预校验) ❌ 逐篇创建
一问多入口(口语问法召回) ✅ 11/11 ❌ 9/11

若将来卡片的标签改成多值一级分类、或需要人工直接在库里改卡面,文档型的这四项是真实优势,届时可重估。


四、落地与维护差异(工程视角)

项 FAQ 型 文档型(一卡一文档)
灌 11 张卡 1 次 API(POST .../faq/entries,11 条一次提交) 11 次 API(逐篇 POST .../knowledge/manual)
预校验 ✅ dry_run=true(实测 3/3 通过) ❌ 无
索引进度 单任务轮询(/faq/import/progress/{task}) 逐篇轮询(实测 11 篇约 125s)
改一张卡 整条 upsert(幂等删旧保新) 改文档 + 重新索引
检索接口 /faq/search(MCP 无此工具) /hybrid-search(MCP 有)

注意最后一行是文档型的唯一独有便利:MCP 通道能直接调 /hybrid-search,而 FAQ 只能走 HTTP。但 MCP 的 hybrid_search 同样没有 tag_ids,标签圈定两边都得上 HTTP。


五、未做的对照(明确声明)

  1. 「把相似问塞进文档正文开头」的对照组 —— 理论上能让文档型模拟 FAQ 的多入口,但那本身就是"在文档型里手写 FAQ 格式",反而印证 FAQ 结构有效。未做。
  2. 文档型纯向量(关 keyword)对照组 —— 本轮文档型是 vector+keyword 双开(比 FAQ 的单通道更宽),结果仍低于 FAQ,说明瓶颈不在通道。未做反向对照。
  3. rerank 组 —— 当前实例未在检索路径启用 reranker(模型已配 bge-reranker-v2-m3 但未挂到检索)。这是两边都还有的提升空间,会同时抬高 FAQ 与文档型。

六、最终答复口径

可以不分块,但要改成"一卡一文档"才能做到;而且即便做到了,检索也不如 FAQ。 换容器不是为了"能不能存进去",而是为了一张卡能被多少种问法找到。

决策建议:维持 FAQ 型容器(已上线的 MCN极致事件素材库),把工程力气投在补相似问的真实口径上——本轮实测证明,相似问就是这个库唯一的召回杠杆(DisableKeywordsMatch: true,纯向量)。


附:证据索引

结论 证据位置
文本 ≤ chunkSize 不切 splitter.go:225-227
分隔符按优先级递归 splitter.go:214-216
7500 绝对上限 splitter.go:400 / 412-427 / 499
无"不分块"开关 internal/types/knowledgebase.go:254-292
单卡不切、多卡一文档必碎 out/7_doc_noblock.txt(①②③④⑤)
一卡一文档 = 11 篇各 1 块 out/8_doc_vs_faq.txt [4] 分块核对
文档型三组命中率 out/8_doc_vs_faq.txt [5][6]
FAQ 基线命中率 out/6_threshold.txt [一][二]
manual 知识路由 internal/router/routes_knowledge.go:75(/knowledge-bases/:id/knowledge/manual)
文档型检索路由 routes_knowledge.go:232(/knowledge-bases/:id/hybrid-search)
请求参数结构 internal/types/search.go(SearchParams)