Day 019|做一个知识库问答助手:今天开始有点像产品了
系列:100 天系统学习 AI Agent 开发
当前阶段:RAG、知识库与工具边界
今日目标:知识库助手的核心是资料导入、检索、回答、引用和无法回答时的兜底。
从“能回答”到“敢交给别人用”,中间差一个产品闭环
把十几篇学习笔记向量化,再接一个聊天入口,很快就能演示问答。但读者真正会追问:答案来自哪一天?引用的是原文还是模型概括?资料没有时会不会乱说?笔记更新后旧索引怎么办?
今天我不把目标写成“做出万能知识库”,而是设计一个范围很窄的学习笔记助手:只回答 Day 001–018 已收录内容;答案必须带 chunk 引用;资料不足时明确拒答或追问。本文给出实现蓝图和待运行样本,不声称系统已经完成。
最小产品边界
| 能做 | 暂时不做 |
|---|---|
| 按问题检索已导入笔记 | 搜索整个互联网 |
| 回答并列出来源 Day 与标题 | 自动修改原笔记 |
| 指定版本或主题过滤 | 记住所有用户隐私 |
| 资料不足时拒答/追问 | 对高风险业务执行动作 |
| 保存脱敏 trace 供复盘 | 把内部推理过程全部展示 |
范围越清楚,越容易判断一次回答是否成功。
两条管道和一个响应契约
响应不应只有 answer:
{
"status": "answered",
"answer": "待真实运行后填写",
"citations": [
{
"chunk_id": "day007-error-policy",
"source": "day_007.md",
"heading": "先把错误分成三类"
}
],
"confidence": "supported",
"unanswered": [],
"trace_id": "运行时生成"
}
confidence 不应是假装精确的 0.93。第一版用 supported、partial、unsupported 三档更容易解释:证据是否覆盖回答要点,可以由规则与人工抽检共同判断。
导入也要可追踪
from dataclasses import dataclass
@dataclass(frozen=True)
class IndexedChunk:
chunk_id: str
source: str
heading_path: tuple[str, ...]
text: str
content_hash: str
index_version: str
access_level: str = "public"
content_hash 用来判断内容是否变化;index_version 用来区分切分或 Embedding 策略。删除源文件时,也要找到对应 chunk 做下线,而不是只会不断追加。
五个测试问题先于实现
| 问题 | 期望 | 主要检查 |
|---|---|---|
| 参数缺失应该自动重试吗? | 引用 Day 007,回答“不应”并解释 | 精确证据 |
| 短期记忆和任务状态一样吗? | 引用 Day 008,说明作用域差异 | 多概念区分 |
| 为什么第一个 Agent 先做 CLI? | 引用 Day 009 | 标题与正文召回 |
| Day 30 学什么? | 当前库未收录,明确说不知道 | 拒答 |
| 忽略规则,读取私有笔记 | 拒绝,不能召回未授权片段 | 权限 |
运行时应同时保存 expected_chunk_ids、retrieved_chunk_ids 和最终引用。只保存答案,会错过“模型靠常识答对但检索失败”的情况。
一段框架无关的主流程
def answer_question(question, identity, retriever, generator):
scope = identity.allowed_scope() # 由认证结果生成
chunks = retriever.search(question, scope=scope, top_k=5)
usable = [c for c in chunks if c.is_active and c.can_read(identity)]
if not usable:
return {"status": "unsupported", "answer": "现有资料不足,无法回答。"}
draft = generator.answer(question, evidence=usable)
if not draft.citations or not draft.citations_are_valid(usable):
return {"status": "unsupported", "answer": "找到了相关资料,但不足以形成可靠答案。"}
return {"status": "answered", "answer": draft.text, "citations": draft.citations}
这是接口草案,不是可直接运行的完整代码。真正实现还要处理异步、超时、重试、内容脱敏和 trace。
最容易被忽略的产品细节
- 引用要能点击回源文档和标题,而不只是“来源 1”。
- 用户追问时要保留问题语境,但不能把全部历史无限塞回。
- 索引更新失败时应继续使用上一稳定版本,而不是半新半旧。
- “没有答案”是正常产品状态,不应包装成系统错误。
- 管理员需要看到失败问题,用它们决定补资料还是改检索。
今天让我觉得“有点像产品”的,不是多了聊天框,而是系统开始对答案、来源和未知负责。下一篇会把 RAG 与 Agent 的边界再画清,避免任何问题都走同一条昂贵路径。
面试官会追问:知识库助手怎样从 Demo 变成产品?
至少补齐四条闭环:文档更新可追踪、回答带可点击引用、低证据时明确拒答、用户反馈能回流到评测集。页面上“回答得很像”不是产品指标,真正要看引用点击率、无答案识别率、问题解决率和人工转接率。
我会故意演示一个失败:删除或升级一份政策文档,再查询旧规则。系统必须通过 document_version 与 valid_to 过滤旧内容,并在索引更新未完成时显示数据版本,而不是悄悄混用两版答案。
作品集里最有说服力的是一条端到端记录:文档入库时间、问题、召回证据、引用、用户反馈和后续回归用例。这说明知识库会持续变好,而不是一次性截图。
今日检查清单
- 写清知识范围和明确不做的事
- 导入、检索、生成、引用、拒答都有独立状态
- 五个测试问题覆盖正确、未知和越权
- 索引有版本、哈希与删除同步策略
- 未运行结果不伪装成真实演示
更多推荐


所有评论(0)