AI 智能体的知识库一定要上向量数据库吗?一个近千万字 SQLite 库的治理实战
起因:一次聊天捅破了四层窗户纸
10 月 11 号晚上,我和军师(我的 AI 总架构师,基于 Kimi Code)本来只是闲聊。聊着聊着,话题收敛到一个判断:配置体系、知识库、记忆体系,表面是三件事,实际上是同一件事——AI 的记忆问题。
当时我们的家底是这样的【实测 2026-10-11】:
| 指标 | 数值 |
|---|---|
| 条目数 | 1795 条 |
| 总字数 | 近 995 万字 |
| 库文件 | 87.9 MB |
| 检索引擎 | SQLite FTS5(trigram 分词) |
| 向量检索 | 没有 |
1795 条文档量级,FTS5 全文检索毫秒级返回,离 SQLite 的极限差着两个数量级。所以"要不要上向量库"这个问题,我们前后讨论了三遍,结论都是维持原状。理由不是省钱,是一个更底层的判断:
战略层的知识,根本不需要大数据库。
一个董事长做一个值钱的决策,依据的不是海量语料,是几张纸。一个人一辈子沉淀下来的经典判断,数据量大不到哪去。大企业老板案头的战略决策文件,就是几个小文档。我自己这套系统就是实证:全部战略级内容 = 一页态势纸(64 行)+ 一份章程(127 行),就这么多。
真正大的、会膨胀的是工具文献类的固有知识——那类东西的作用是"查询",要求是信息精准、检索准确,而不是语义联想。所以我们的路线是:分级分类存储,分场景调用,而不是堆一个更大的库。
但"不上向量库"不等于"没问题"。这一晚连查出四个真问题。
问题一:治理字段建了没人填,0/1784
entries 表早就预留了五个治理字段——document_class(文档分类)、confidence(可信度)、valid_until(有效期)、source_url(来源)、fetched_at(采集时间)。我打开 Navicat 一看,填充数:
document_class 已填: 0 / 1784
confidence 已填: 0 / 1784
valid_until 已填: 0 / 1784
source_url 已填: 0 / 1784
fetched_at 已填: 0 / 1784
根因很快查到:索引器 sync() 只从 front matter 里取 title/category/tags,根本没读这五个字段。字段是死字段,闸门从来没运转过。
修法不是"提醒大家记得填",是机器闸——不填就不许入库:
def ingest_gate_check(path: str, meta: dict) -> str | None:
"""入库闸门:返回拒收原因,放行返回 None。"""
dclass = str(meta.get("document_class") or "").strip()
vu = str(meta.get("valid_until") or "").strip()
if dclass and dclass not in VALID_DOCUMENT_CLASSES:
return f"document_class 非法值 {dclass!r}(可选: decision/canon/timely)"
if dclass == "timely" and not vu:
return "timely 条目必须填 valid_until(YYYY-MM-DD)"
if path.startswith(CANON_DIR_PREFIX) and dclass != "canon":
return "常识库分区条目必须标 document_class: canon"
return None
分类只设三档,多了没人记得住:
| document_class | 内容 | valid_until |
|---|---|---|
| decision | 战略决策、裁定(常青) | 不填 |
| canon | 常识经典、基础概念(常青) | 不填 |
| timely | 会过时的时效信息 | 必填 |
上闸当天实测:故意丢进去三个违规文件(缺分类、timely 缺有效期、分类拼错),三条全部被拒收、原因逐条正确;合规文件正常入库。闸门不是写在文档里的,是跑在代码里的。
问题二:聊出来的共识,根本没进库
这是我个人觉得最典型的一个坑。10 月 9 号我们开会定过一条规矩:任务卡发出前,AI 必须先复述三行——干什么、验收标准、动哪些文件——我过目了才发。当场拍板"从建议升级为硬闸"。
10 月 11 号我抽查:开发团队的章程文件里 grep "复述/三行",零命中。这条闸只活在会议记录里,而执行干活的 AI 实例读的是章程文件——对它来说,这条规矩从来没存在过。
这暴露的是入库时机问题。定下来的触发器:
- 共识达成——聊天中确认"以后就这么干",当场落成规则文档入索引;
- 拍板/冻结——态势纸一行状态 + 台账详情 + kb 可检索,三处落点缺一不可;
- 外部采集——入库时当场填齐治理字段,填不齐不入库。
一句话:只活在会话里的共识,等于没共识。
问题三:新鲜度治理,90 天一刀切被我自己否了
第一版方案里,时效类条目有效期默认 90 天。我当场把这个设计否了——不同内容衰减速度根本不一样:模型价格 30 天就过期,Python 语法十年不变,项目计划不按日期过期而是被新决定取代。
更关键的是维护成本:定时批量给 1800 条存量做回源核验,90% 的条目一年没人查一次,为它们做核验是纯浪费。
改成触发式核验:谁检索命中,谁看这条的采集时间和有效期;可疑才回源核验;验完必须回填——有效就刷新有效期,失效就作废。作废操作很轻:把 front matter 里 valid_until 改成过去日期,检索结果立刻标 [已过期]。
定时任务只保留零成本的曝光:kb stats 报过期条数,SQL 查询,不占任何模型调用。
问题四:Navicat 三连抓包——时间字段的教训
我直接用 Navicat 连上库文件抽查(SQLite 是文件库,没有账号密码,直连 .db 文件),连抓三个:
① 日期格式不统一。 1775 条是 2026-10-11,混着 20 条早期导入的紧凑格式 20260424。
② created_at 只写不更新。 sync 的 UPDATE 语句漏了 created_at——就算把源文件改了,库里也不跟着改。"文件即真相源"的原则破了个洞。
③ 时间精度只有天。 我干了三十多年开发,不能接受这个。同一天上午下午晚上三轮修改,天粒度连先后顺序都排不出来;出了问题要排查,没有时间线怎么查?
军师的回应是当晚全部修掉:
- 时间精度升到秒级 ISO(
2026-10-11T22:04:28); - 时间来源有优先级:front matter 带时分就采用;只有日期就拼接文件系统真实时刻(创建时间/修改时间——文件不会撒谎);文件日期与 front matter 日期对不上时,整条信文件;
- 存量 1795 条全部回填,非标格式清零;
- 回归测试 13 个用例全过。
还有个小插曲:查询日志它第一版写成了 jsonl 文件,我问了一句"都上数据库了为什么还用文件记日志",改成了库里的 query_log 表。遥测数据的消费者是库内查询,就该进库——顺手用文件是肌肉记忆,不是设计。
记忆隔离的真相:不是更大的上下文,是分级按需调用
很多人做 Agent 记忆,第一反应是把更多东西塞进上下文。我们的做法相反——记忆隔离靠的是分类检索,按需调用:
| 层 | 载体 | 加载方式 | 上下文占用 |
|---|---|---|---|
| L0 章程层 | AGENTS.md | 每会话自动注入 | 恒占,<200 行硬预算 |
| L1 岗位层 | 岗位 agent.md + 技能 | 岗位激活时加载 | 按需 |
| L2 文档层 | docs/(SOP/台账/登记册) | L0 一行指针,按需读 | 按需 |
| L3 检索层 | kb.db(FTS5) | 查询制,不自动加载 | 零 |
| L4 冷归档 | _archive | 不检索不加载 | 零 |
战略层住 L0/L2,就那一屏纸;知识层住 L3,查的时候才进门;垃圾住 L4,永不见面。每个 Agent 实例只看到它岗位该看的,这就是隔离——不需要向量相似度,需要的是分类纪律。
配套的配置治理也一样:配置文件分 P0/P1/P2 三级管控,登记册在册 146 件;AI 写配置的三大毛病(记流水账、滥用绝对禁词、小毛病就加枷锁)用 lint 脚本机器闸卡死,违规不落盘。此前一轮治理把 108 条负向禁令改写成正面模板——禁令只留给有故障台账证据的红线,其余一律写"应该怎么做"。
调用侧也补了埋点:每次检索落 query_log 表(时间/查询词/命中数),一个月后"哪些场景在查、哪些词查不到"用数据说话,不靠感觉。
可抄走的清单
- 先审计再建设:打开你的库,跑一句
SELECT COUNT(*) FROM entries WHERE document_class IS NULL,治理字段零填充的,先修索引器再谈别的; - 入库闸门代码化:非法分类、缺有效期的文件,拒收比提醒管用一万倍;
- 共识三处落点:章程一行 + 台账详情 + 可检索库,只活在会话里的共识等于没共识;
- 新鲜度触发式核验:谁用谁验、验完回填,定时只曝光不普查;
- 时间用秒级 ISO,来源优先级:front matter > 文件系统时刻;
- SQLite 文件库直连 Navicat 抽查——这是人对 AI 最便宜有效的审计通道,AI 报一百遍"已验证",不如自己进去看一眼。
收尾
这一晚的全部改动落在 7 个 git commit 里(21:55 到 22:51,不到一小时):入库闸门、触发式核验、调用埋点、时间精度升级、存量迁移。 pytest 从 9 个用例涨到 13 个,全绿。
回到标题的问题:AI 知识库一定要上向量库吗?我们的答案是——检索精度的问题用分类和闸门解决,语义联想的需求等常识库建起来再评估。sqlite-vec 混合检索方案在候排里躺着,但它的启动条件是"第一批常识条目入库",不是"感觉库大了"。
战略层几页纸,知识层按需查,垃圾层不见面。小而准,比大而全值钱。
素材点
- 治理字段 0/1784 的审计命令(一句话查出"建了没人填");
- ingest_gate_check 闸门函数全文(可直接抄);
- 四级记忆表(L0-L4 与上下文占用对照);
- "复述三行"硬闸零命中案例:会议裁定不落配置=没裁定;
- Navicat 直连 SQLite 做 AI 审计的玩法;
- 108 条禁令改正面模板的配置治理口径。
