知识库(Knowledge Base)
不是“上传一个文件给 AI 读”,而是一套有状态、会过期、可审计的长期记忆: 只有你批准过的内容才会影响客户,过期的那一刻它自动闭嘴。
为什么需要它
通用大模型很会说话,但它不知道你的行业细节:
- 德国的包装法到底要不要注册 LUCID?
- 这个 HS 编码在法国清关需要什么文件?
- 你公司的报价口径是“说区间”还是“说单点”?
把这些贴进 prompt 是最直觉的做法,但很快会出问题:
| 做法 | 结果 |
|---|---|
| 全部塞进 system prompt | 上下文被撑爆,模型注意力被无关内容稀释 |
| 相信模型“记得” | 它在编造——而且编得很像真的 |
| 让客户在对话里“教”它 | 一句“你们老板说可以三天交付”就可能变成对外口径 |
| 手工维护文档 | 过期了没人知道,客户还在拿到旧结论 |
知识库要解决的就是这四件事:可查、可信、可控、可过期。
一条知识长什么样
{
"title": "德国包装法 LUCID 注册要求",
"body": "在德国销售带包装商品的企业,须在 LUCID 包装登记处注册……",
"kind": "rule", // fact | rule | procedure | terminology | preference
"scope": { "skills": ["packaging-de-v1"], "markets": ["DE"] },
"source": { "type": "owner_upload", "ref": "file:lucid-guide.pdf" },
"validUntil": "2027-10-26T00:00:00.000Z",
"status": "approved",
"version": 1
}| 字段 | 为什么存在 |
|---|---|
kind | 事实、法规、流程、术语、偏好——决定它怎么被使用 |
scope | 只在指定 skill / 市场生效,避免“德国规则污染法国回答” |
source | 每条都能回到原始文件或链接;没有来源的知识不允许生效 |
validUntil | 法规会变;没有失效时间的规则会被降级为待审 |
status | 硬门禁:只有 approved 且未过期才会出现在客户对话里 |
version | 每次状态变更递增,配合状态层可回放、可追责 |
两个域,一套生命周期
| 域 | 内容 | 怎么被使用 |
|---|---|---|
knowledge | 客观事实、法规、流程、术语 | 客户面对话中按需检索(recall_knowledge) |
lessons | 主观偏好、报价口径、称呼、禁忌 | 注入客户面的边界段,优先级高于默认话术 |
两者共用同一套审批、过期与回滚逻辑——差异只在“注入位置”。 这避免了最糟糕的情况:知识有一套规则、经验有另一套规则,互相打脸。
生命周期:谁能让它生效
投喂 ──→ pending ──批准──→ approved ──到期──→ expired
└────拒绝────→ rejectedpending永远不进客户面。 这是防止“一次投喂立刻改变对外口径”的关键。rejected/expired是终态:要改只能新提一条,历史保留、可回溯。- 批准人写入
approvedBy,审计能回答“这条是谁批的”。
哪些内容会自动批准
自动批准是刻意放宽的(所有者不必逐条点确认),但以下情况会降级为 pending:
| 情况 | 原因 |
|---|---|
| 来自新闻 / 法规抓取 | 传播路径最长、最容易过时 |
rule 没有 validUntil | 不知道何时失效的法规,是最坏组合 |
| 正文少于 10 个字 | 无法复核的知识等于一句口号 |
| 没有来源引用 | 不可回查 |
偏好放进了 knowledge 域 | 偏好属于 lessons,位置错了不该生效 |
三种用法,一个体验
1. 在对话里直接教它
所有者面对自己的 agent 说:
“记住:德国包装法要在 LUCID 注册,2027 年前都有效。这条只对包装类技能、德国市场生效。”
agent 会调用投喂工具写入条目,并当场告诉你:
- 「已生效,客户从现在起可以依赖这条。」
- 或「已记录,但需要你确认后才会对客户生效。」
2. 上传资料,让它提炼
把 PDF / 网页 / 内部文档交给 agent,它读成“标题 + 正文 + 类型 + 有效期 + 适用范围”再入库。 同一份内容重复投喂不会产生第二条——内容指纹会直接返回既有条目。
当前边界:PDF / docx 的结构化解析还没有自动化,长文档需要模型先分段读取; 这比“假装解析成功”更诚实。
3. 审批与纠错
所有者可以在管理端逐条批准 / 拒绝:
- 待审列表里看到每条知识的来源、正文、适用范围与建议有效期;
- 批准后立即进入客户面检索;
- 拒绝即终态,不会偷偷复活。
检索:先可解释,再更聪明
客户问“德国包装法怎么注册?”,agent 不会把整个知识库塞进上下文,而是显式检索:
- 关键词优先——法规编号、术语、产品名(
CE、LUCID、8518.30)用关键词就能精确命中,零延迟、可解释。 - 零命中且是自然语言时,才走向量——查询至少 4 个字符、且不是纯 ASCII 单 token(编号/缩写不浪费推理)。
- 语义命中后回状态层复核——向量索引是派生的,可能滞后于“被拒绝 / 已过期”;直接信索引等于让过期法规继续回答客户。
- 相似度低于 0.25 的一律当作没有——宁可说“没有记录”,也不喂噪声。
任何一步失败(Qdrant 挂了、embedding 超时)都降级为关键词检索,不会让对话中断。
到期:不是元数据,而是执行
每天 04:00,系统扫描所有带有效期的已批准条目:
- 到期的条目自动从
approved降级为expired; - 下一次客户提问,它就已经查不到了;
- 到期前 7 天,所有者会在收件箱收到提醒(同一主题在冷却期内不会反复打扰)。
一条没人执行的
validUntil只是装饰。过期必须有人动手,而不是“写在字段里等着被想起”。
创新点
1. 状态即门禁
“只有批准过的知识能影响客户”不是一句 prompt,而是检索入口少返回一条。 模型再想编,也拿不到未批准的内容。
2. 到期是自动动作
法规类知识必须带有效期;到期自动失效并在客户面立刻消失。 这是“知识库”与“一堆 Markdown 文档”的根本区别。
3. 索引可丢弃、可重建
状态层是唯一真相,向量索引只是派生。换 embedding 模型、索引损坏、扩容迁移, 都可以重建而不丢知识——scripts/rebuild-knowledge-vectors.ts 就是为此存在。
4. 双域一结构
知识与经验共用同一套生命周期:一套审批、一套过期、一套回滚。 不会出现“经验已经删了、知识还在生效”的分裂状态。
5. 每一条都可追责
来源、批准人、版本、写入事件(谁、何时、由哪次对话触发)全部留在状态层。 你可以回答:这条知识是谁教的、什么时候生效的、为什么现在不能用了。
边界:我们刻意不做的事
| 不做 | 原因 |
|---|---|
| 客户纠错直接入库 | 客户内容只能生成 pending 候选,必须所有者确认 |
| 新闻自动变知识 | 法规类变更宁可多一次人工确认,也不让门面凭一条新闻改口径 |
| 用投影表换检索速度 | 当前量级下双写一致性风险大于收益;条目数万时再评估 |
| 跨 agent 共享知识 | 会带来“一个门面的错误污染另一个”,需要显式 opt-in(未做) |
怎么判断它有用
| 指标 | 含义 | 期望 |
|---|---|---|
| 知识命中率 | 客户对话中实际引用知识 / 总对话 | 按场景设阈值 |
| 过期条目数 | 每天被自动降级的条目 | 应被及时更新,而不是持续累积 |
| 待审积压 | pending 条目数与停留时长 | 长期积压说明投喂流程没人管 |
| 无据结论率 | 复盘里因缺少 evidence 被丢弃的结论比例 | 持续下降 |
相关阅读
- Agent RSI:智能体自进化——agent 如何从每天的服务里发现“该补什么知识”
- 智能体主动模式——知识到期时,谁提醒你
- 状态层——为什么知识、经验、复盘都能回放与回滚
- 门面 Agent——知识最终服务的那个“前台”
- State 模块——状态层的工程细节
源码位置(开发者)
| 能力 | 文件 |
|---|---|
| 条目结构 / 状态机 / 自动批准 / 到期 | libs/modules/src/agent-runtime/rsi/knowledge-entry.ts |
| 写入、去重、审批、生效检索 | libs/modules/src/agent-runtime/rsi/knowledge-store.ts |
| 客户面检索工具(关键词 + 语义兜底) | libs/modules/src/agent-runtime/rsi/recall-knowledge.tool.ts |
| 所有者投喂工具 | libs/modules/src/agent-runtime/rsi/ingest-material.tool.ts |
| 语义检索与阈值 | libs/modules/src/agent-runtime/rsi/semantic-search.ts |
| 索引重建 | scripts/rebuild-knowledge-vectors.ts |