A24第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

知识库应用实战

知识库应用实战 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能不碰界面、直接用站内 /api/documents 接口走完资料库的一生:创建资料集 → 上传文档 → 自动索引 → 检索问答 → 删除清理,并用一次真实测试验证分类检索和引用有效性。

问题场景

A19 讲了切分、A21 讲了检索、A22 讲了引用,但都是单点。真到用时你会发现断点都在衔接处:上传后索引到底完成没有?删除文档后引用为什么还能搜到?资料混在一个集合里,检索结果全是无关文档的噪声。这课把整条链路跑通,重点看状态流转和数据一致性。

核心解释

本站资料库的完整数据流(真实实现,app/api/documents/route.ts + lib/documents/parse.ts):

上传(PUT multipart) → 解析提取文本 → 切分成片段 → 写 doc_chunk + fts_doc → status='indexed'
                                              ↘ 失败 → status='failed' + error 原因可查
检索(search_documents 工具) → FTS5 MATCH → 空则 LIKE 兜底 → 仍空则「没有命中」提示
删除(DELETE ?id=) → 删 document(级联清 chunk/job)→ 删 fts_doc → 删磁盘文件

对应三张表一条链:document(文档元数据与状态)→ doc_chunk(片段,检索兜底与展示用)→ fts_doc(FTS5 索引虚表)。index_job 记录每次索引任务的成败与块数。

关键流程:上传用 PUT + multipart 表单(字段 file 和 collection),支持 .txt/.md/.markdown/.pdf,单文件上限 10MB;collection 传 default 时会懒创建默认资料集。删除走 DELETE /api/documents?id=<文档id>,级联清理三处数据加磁盘文件——少删任何一处,就会出现「搜得到、打不开」的幽灵引用。

用 curl 走一遍全流程:

# 1. 创建资料集
curl -X POST http://localhost:3000/api/documents \
  -H "Content-Type: application/json" -d '{"name":"课程笔记"}'

# 2. 上传文档(collection 用第 1 步返回的 slug)
curl -X PUT http://localhost:3000/api/documents \
  -F "file=@./notes.md" -F "collection=<上一步的slug>"

# 3. 查看列表:确认 status=indexed、chunks 数量
curl http://localhost:3000/api/documents

# 4. 删除文档(级联清理索引与文件)
curl -X "DELETE http://localhost:3000/api/documents?id=<文档id>"

资料集(collection)是检索的范围开关:search_documents 工具有可选的 collection 参数,传了就只在该资料集内查(SQL 里加 AND collection_slug = ?)。分类的价值在检索质量:把「接口规范」和「学习笔记」分开,问接口问题时就不会被笔记里的同词段落干扰。

示例拆解

验证「引用有效性」的固定测试(每导入一批资料都跑一遍):

{
  "case": "删除接口幂等要求",
  "expect_document": "团队接口规范.md",
  "expect_heading": "删除接口",
  "steps": [
    "问:删除接口要求幂等吗?",
    "检查 1:回答是否基于命中片段而非编造",
    "检查 2:引用能否定位到 expect_document + expect_heading",
    "检查 3:点开片段原文,结论是否被原文支持(A22 三问)"
  ]
}

关键点:

  • 索引是同步的但状态是显式的:本站上传后立即解析索引(本地小文件),成功 indexed、失败 failed 且 error 存人话原因(如「PDF 没有文字层」)。判断「能不能搜」看状态,不看接口返回 200。
  • 删除的一致性靠「同事务清理四处」:document 行、doc_chunk、fts_doc、磁盘文件。测试方法:删完后把「被删文档里的原句」拿去检索,必须零命中。
  • 分类是检索质量手段,不是文件夹审美:分多少个集合,取决于「你会不会按它限定检索范围」,而不是「它们像不像一类」。
  • fail 的资料要可诊断:document.error 和 index_job.error 都存原因——「为什么我传的文件搜不到」这个问题,答案永远在状态和错误字段里。

动手任务

  1. 用上面的 curl 命令走完「创建 → 上传 → 查列表」三步,记录返回的 slug、文档 id 和 chunks 数。
  2. 建两个资料集各传一份文档,问一个两份资料里都有同义词的问题,分别用 collection 限定检索,对比命中差异。
  3. 删除其中一份文档,再用它原文里的句子检索,确认零命中;查列表确认状态——验证「幽灵引用」没有发生。

常见错误

  • 上传成功当索引成功:HTTP 201 只代表文件收下了,索引成败看 status 字段——failed 的文档照样躺在列表里。
  • 删除只删文件不删索引:磁盘上文件没了,doc_chunk/fts_doc 还在,检索照样命中、引用点开是空的。
  • 所有资料堆一个 default 集合:短期方便,长期检索全是跨主题噪声,限定范围的价值没兑现。
  • 重复上传不检查旧版本:同一文档传三次出三份片段,检索结果重复,引用顺序错乱。先删旧的再传新的。

检查与复盘

自查清单:

  • 能画出「上传 → 索引 → 检索 → 删除」的状态流转图,标出每个环节落在哪张表
  • 删除后能用「原文检索零命中」验证清理彻底
  • 能说出 collection 分类如何影响检索质量,并举一个自己资料的分类方案

参考资料

检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1故障定位较难

bug 现场:知识库项目里同一份文档导入两次后,检索结果出现重复片段、引用指向错乱。查库发现 doc_chunk 里同一文档的片段存了两份。导入代码如下。根因与修法?

// 导入处理(节选)
const text = await extractText(file);
const chunks = chunkText(text);
for (const c of chunks) {
  db.prepare(
    "INSERT INTO doc_chunk (document_id, collection_slug, order_index, heading, text) VALUES (?, ?, ?, ?, ?)"
  ).run(documentId, collection, c.orderIndex, c.heading, c.text);
}

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...