编译范式在能力栈里的位置
「从零开始搭建知识库」系列第六期,范式篇开篇。EP01 论证建库,EP02 跑通存与找,EP03 验证数据准备,EP04 拧完检索参数面,EP05 把库挂上智能体应用。五期走的都是同一条路:知识在库里躺着,每次提问现场检索。本期换一条路,先让模型把整个库通读一遍编译成结构化知识文档,之后提问不检索,直接读这份产物。
对照组沿用污染库 zj0knmrbye(8 文档 27 切片,与 EP04/EP05 留痕逐位一致,本期一个字没改),实验组走 bl text chat --messages-file 把全库 8,811 字一次喂进上下文。十次付费调用共 1.27 元。

理论出处是 Karpathy 讲 LLM Wiki 的那份 gist:RAG 是解释器,每次提问现场解释一遍;LLM Wiki 是编译器,提前编译一次之后直接执行。三层结构 raw/(原始文档只读)、wiki/(模型读写的知识页,[[双括号]] 互链)、schema/(结构约定),三个操作 Ingest、Query、Lint。gist 里有一句自我限定:「This document is intentionally abstract. It describes the idea, not a specific implementation.」写的是想法不是实现,公网上流传的「Karpathy 研究发现文档超过一定规模准确率断崖式下跌」在原文里没有对应实验。
通道分工表:本期新增两行
| 需求 | 正确通道 | 说明 |
|---|---|---|
| 知识库建库 | 百炼控制台(网页) | 含排序模型与 rewrite 开关,建库时定 |
| 知识库检索(服务化) | bl knowledge search --query <text> --agent-id <id> |
走检索服务,与 retrieve 内核一致 |
| 智能体应用调用 | bl app call --app-id <id> --prompt <text> |
库挂到应用上,Agent 自主检索 |
| 整库编译 / 全上下文提问 | bl text chat --messages-file <file> |
本期新增:不经过任何检索链路,语料直接进上下文 |
| 模型单价与窗口查询 | bl model list --model qwen3.8-max |
本期新增:不需要鉴权,输入 12 元、输出 36 元每百万,窗口 1,000,000 |
--messages-file 这个 flag 在本期是必需项,不是可选项。中文 system prompt 走命令行参数会被 PowerShell 弄坏编码,写进 JSON 文件用 UTF-8 存盘才稳。同理,脚本里 print 中文到 Windows 控制台会撞 GBK 编码页,一个 emoji 就能让 print 抛 UnicodeEncodeError,本期所有结果都是写进 UTF-8 文件再读的。
编译命令:
bl text chat --model qwen3.8-max --messages-file messages.json --max-tokens 8000 --enable-thinking --thinking-budget 3000 --output json
开思考是刻意对齐 EP05 那个应用的配置,不然回头说不清是范式的差别还是配置的差别。烂 Excel 那份用 openpyxl 全表转文本,合并单元格造成的空列、错位表头、跑到另一个工作表的补充说明全部原样保留,不做人工清理。
单变量对照:两组 system prompt 只差一条
Karpathy 给 Ingest 列的要求里有一条「摄入过程中,标注新旧内容的矛盾之处」。照抄这条跑出来的冲突标注没法用,说不清是「全局视野」起作用还是「你直接叫它找矛盾」起作用。EP05 那个应用的提示词里从来没有这句。
- A1a 中性版:按主题建知识页、合并同主题内容、建索引页、
[[页面名]]互链、每条事实标注来源文档名、文档没写的不要补。全文不含「矛盾」「冲突」「59」「99」任何一个词 - A1b Karpathy 版:A1a 后面加一条「5. 摄入过程中,标注新旧内容的矛盾之处。」

| 组 | 输入 tokens | 输出 tokens | 其中思考 | 耗时 | 折算 |
|---|---|---|---|---|---|
| A1a 中性 | 5,905 | 9,594 | 1,725 | 179.7s | 0.4162 元 |
| A1b Karpathy | 5,918 | 8,757 | 2,100 | 175.8s | 0.3863 元 |
两组 finish_reason 都是 stop,无截断,所以后面看到的覆盖差异是模型自己的取舍。
判定结果:A1a 中性组自己开了一节「包邮门槛与运费」,首行是 ⚠️ 文档冲突提示,紧跟一张三列表把满 59 和满 99 两套口径并排摆出来,还从烂表角落里提上来一句「以下单页面为准」作为处置建议。对照 EP05:同样这两份文档同场检索,01 分数 0.6557 还比好版的 0.5755 高,Agent 按主题各取所需,对 59 还是 99 只字不提。按提前定好的判定门,功劳归全局视野。
A1b 用 🔴 矛盾 逐条列三处,其中一处不在我的脏数据清单上:偏远地区定义,01 写的是新疆西藏内蒙青海宁夏加海南,13-好版 写的是新疆西藏甘肃青海内蒙宁夏。一份含海南不含甘肃,一份含甘肃不含海南。回源核对过,它报的是真的。这个库我自己造的,用了五期,这处口径打架躺在里面五期没人发现。学术上这类问题叫 inter-context conflict,EMNLP 2024《Knowledge Conflicts for LLMs: A Survey》(arXiv 2403.08319)分的三类之一。
产物质量:编造 0、断链 0、遗漏命中
反方论点里有三条能靠脚本验。做法是把产物里所有数字 token 抽出来逐个回原始语料匹配,再检查每个 [[link]] 有没有对应标题。
| 检查项 | A1a 中性 | A1b Karpathy | 对应质疑 |
|---|---|---|---|
| 产物体量 | 12,089 字 / 578 行 | 10,089 字 / 433 行 | — |
| 数字回源 | 73 个去重数字,查不到 0 个 | 71 个,0 个 | 「编造参数」没出现 |
[[link]] 断链 |
42 次链接 11 个目标,断链 0 | 7 次 7 个目标,断链 0 | 「孤岛知识」没出现 |
| 18 项关键要点抽查 | 人工复核 18/18 | 人工复核 17/18 | 「遗漏」部分出现 |
最有杀伤力的那条质疑叫「矛盾覆盖」,编译时新版把旧版盖掉,旧值使用者查不到自己那个值。我们库里两份报销制度和这个例子同构,实测两组都没覆盖:索引页两版并列,旧版条目写「已废止的 2023 版制度,仅供历史参照」,两个独立页面各留全条款,旧版页首挂废止告警,两页互相 [[link]]。边界要说清,这两份文档正文里本来就写着生效日期和废止关系,元信息是齐的。
遗漏那条兑现了,丢的东西很要命:两组都丢了烂版 Excel 里「以上如有变动以客服最新答复为准(2024.6 更新)」这个时间戳。冲突里的另一方 01 本身没有时间戳,所以「谁更新」这个问题在产物里彻底没了判据。编译丢的恰恰是仲裁所需要的元信息。
A1b 额外多丢三条,还比 A1a 短 2,000 字,[[link]] 从 42 次掉到 7 次。输出长度有限时注意力是零和的,多花力气标冲突就少铺细节。照抄那条指令不是免费加分项,是一次交换。
同题对跑:编译解决了发现,没解决披露
编译产物是给模型用的,所以真正的测法是把产物当上下文,问和 EP05 一模一样的题。口径钉死:system prompt 只有一句「你是暖屋家居的客服助手。」,与 EP05 应用对等;产物用 A1a 中性版(挑标注更显式的 A1b 是放水);检索侧仍是 bl app call 那个应用,零改动。
包邮门槛这道题,编译侧答满 99 包邮、不满收 8 元,检索侧答一样的内容。两边都只报一套,都没提 59。编译侧还多犯一样:产物里明写的「文档冲突」,答题时改口成「近期规则有调整」,而库里没有任何一处说过规则调整过。

编译解决了发现冲突,没解决把冲突讲给用户。 发现和披露是两道工序,那份 gist 里的范式只覆盖前一道。版本题(有元信息那种)两边都答对,换范式零增益。
真正拉开差距的是跨文档综合题:「新疆的客户买沙发,能发货吗?能货到付款吗?运费怎么算?」这道题逼两条冲突规则同时进一个答案。检索侧把「偏远地区满 129 包邮」和「偏远地区不参与满减包邮需补 15 元」并列成两个 bullet,读起来像互补条款,客服照这个答复客户就是当场自相矛盾。编译侧按来源分列成表,加冲突提示。机制能解释清楚:编译侧在编译那一刻把两条并排看过一次、判定过冲突,判定留在产物里;检索侧从头到尾没同时见过全局。
编译范式的价值在两条冲突规则同时进入答案的时候。 简单单点问题上,它和检索侧一样会静默挑一套。
隐式前缀缓存:这一节是本期最实用的账
编译侧四问连着跑,第二问开始 prompt_tokens_details.cached_tokens 就有值了:
| 调用 | 输入 tokens | 其中命中缓存 | 输出 tokens | 成本 |
|---|---|---|---|---|
| 第一问 | 7,929 | 0 | 370 | 0.1085 元 |
| 第二问 | 7,935 | 7,168 | 388 | 0.0339 元 |
| 第三问 | 7,932 | 7,168 | 287 | 0.0303 元 |
| 第四问 | 7,942 | 7,168 | 665 | 0.0440 元 |
编译产物这段前缀走缓存命中价 1.5 元每百万,标准输入价的 12.5%,只有变动的那几百 token 按全价算。输入侧单问成本从 0.0951 元掉到 0.0200 元。没做任何缓存配置,服务端隐式命中。

| 同一道题 | 编译侧(缓存命中) | 检索侧 | 差 |
|---|---|---|---|
| 包邮门槛 | 0.0339 元 | 0.0753 元 | 编译省 55% |
| 新疆沙发(跨文档) | 0.0440 元 | 0.1431 元 | 编译省 69% |
两个原因:检索侧那 4,739 个输入 token 一分钱折扣没有,全按 12 元算;另一个是输出,检索侧要吐工具调用、要复述检索到的内容,跨文档那题输出 1,510 对 665,而输出单价 36 元是最贵的一项。
编译一次投入 0.4162 元,稳态每问省 0.0703 元,0.4162 ÷ 0.0703 ≈ 6,大约第 6 问回本。但这个 6 完全押在缓存上:第一问没命中缓存时编译侧 0.1085 元,比检索侧的 0.0753 元还贵 44%。

还有一条工程上直接能用的:把人工仲裁记录追加到产物末尾之后,cached_tokens 仍然是 7,168,前缀没变,缓存没失效。追加在尾部而不是插在中间,编译产物的增量维护就是便宜的。那一轮的答案里,59 这个数字第一次进了给客户的回复,还带了正确处置;故意留着没裁定的那处冲突,它没硬答,改成请客户提供地址后人工确认。
边界与不适用场景
窗口不是约束项。bl model list --model qwen3.8-max 拉出来的数:上下文窗口 1,000,000,最大可输入 991,808。全库 8,811 字实测编成 5,905 个输入 token,991,808 ÷ 5,905 ≈ 168,这个库还能再长 168 倍。中小企业库该担心的是下面这几项。
有一处口径不对称得摊开:编译侧读的是烂 Excel 全表转出的文本,检索侧读的是它被切成的 11 片碎片,编译侧看到的表格确实更完整。这是两种范式的定义差异(编译吃全文,检索吃切片),但跨文档那道题上编译侧的优势有一部分确实来自看得更全。
改一份文档的代价两边差得很远。编译侧要重跑全库编译,0.4162 元加大约三分钟,重编译之后前缀失效,下一问按全价重付一次;检索侧只需重传那一份文档并重新索引,其余文档不受影响。库越活、改得越勤,编译范式越吃亏。
明确不适用的场景,按优先级排:
- 需要按人分权的库。编译产物把跨部门知识揉进同一页,天然打破权限边界。检索链路能在检索阶段钉
tenant_id做前置过滤,编译产物是一份揉好的成品,很难再按人分权 - 高频变更的库。见上面的更新代价
- 低频零散提问。不命中缓存时编译侧每问都更贵
- 临时查一个问题。Karpathy 自己划的界
- 个人知识管理。企业库是给别人查的,自动化没损失;自己的学习笔记全自动化掉,学习就不发生了
与生态其他组件的衔接
- EP05 的钩子在本期换范式重跑了。上期结论是「决策层救得了有证据链的冲突,救不了没有元信息的冲突」,编译范式把没有元信息的那处冲突变可见了,但没把它讲给用户
- EP03 清单里那条「冲突内容要有优先级标记」在本期又添一条实测依据。编译丢的东西不随机,它倾向于丢看起来像元数据碎屑的东西,而元数据正好是仲裁的依据
- 检索链路不需要改动。本期检索侧全程零配置改动,两条范式可以并存,判断标准是问题类型而不是二选一
- 本期靠的是服务端隐式前缀缓存,什么时候失效完全不可控。百炼还有显式缓存这条路,创建价 15 元、命中价 1 元,规则和隐式完全不同,EP07 把两种缓存对着跑一遍
复现路径
npm install -g bailian-cli
bl auth login --api-key sk-xxxxx
bl model list --model qwen3.8-max
bl text chat --model qwen3.8-max --messages-file messages.json --max-tokens 8000 --enable-thinking --thinking-budget 3000 --output json
审计重点:prompt_tokens_details.cached_tokens 有没有值、产物里的数字能不能逐个回源、[[link]] 有没有断、原文里的时间戳和废止关系有没有被丢掉。
本文实测基于百炼 CLI(bl),10 次调用的完整 usage、思考链与产物留痕于项目仓库。命令格式可能随版本更新调整,以官方文档为准。API Key 在控制台密钥管理页创建(新用户有免费额度)。