跳到主要内容

从写死的链到会变通的代理 — 书自称的落点,以及它为什么没落住

这一章讲三件事: 链的那个根本毛病到底是什么; 「让模型自己决定要不要再查一次」这件事,拆开来是哪几步; 以及这本书自称的落点,为什么一个字都没落住。 它在全书链条里的位置: 这是原书的最后一章,也是它自己给全书安排的高潮。 而全书对这件事最完整的定义,不在这一章,在第 7 章的结语里。

1. 顶层全景

链(第 10 章):

问题 ──► 检索 ──► 拼提示 ──► 生成 ──► 答案

└─ 这一步什么都没找到?**它照样往下走。**


想要的样子:

问题 ──► 检索 ──► 先给个初步答案


┌─► 手上这些证据够不够、对不对题?
│ │
│ 够 ──┴──► 交出答案,结束
│ 不够
│ │
└──── 改写问题/换检索条件,**再查一遍**

每转一圈都问一次:该停了吗?

图说:两张图的差别只有一样东西 —— 那个回头的箭头。 这一章的全部内容,就是这个箭头存在还是不存在。

一句话链条: 链是写死的顺序 → 第一次没查到它也不会重来 → 出路是让模型自己判断证据够不够、不够就再查一次 → 而只要有循环就必须有停下来的条件 → 书把这一整套讲成了「用了某个框架」,于是一步都没做。

2. 链的根本毛病:第一次没找到,它照样往下走

现象

用户问: 我们公司的年假是怎么算的?
库里: 压根没有这份制度文件

链的走法:
检索 → 取前 3 条(**永远凑够 3 条**,第 05 章讲过)
→ 这 3 条讲的是报销流程、考勤打卡、差旅标准
拼提示 → 把这 3 条原样贴进去
生成 → 「年假按工龄计算,满一年 5 天……」 ← **编的**

图说:整条链没有一个环节报错。它**不知道自己没找到**,
因为链里没有任何一步在问这个问题。

为什么链修不了这件事

因为链的形状就是「一步接一步,走完为止」。 它没有回头的路 —— 要回头,就得有人决定「回不回」,而链里没有这个人。

第 09 章说过,不确定的时候有三条路:报出来、拒答、再查一次。 链能做前两条(在最后一步加个判断),做不了第三条 —— 再查一次意味着回到前面的步骤,而链只能往前。

3. 代理式 RAG 的真定义,藏在第 7 章的结语里

先说这个名字

「让程序自己决定下一步做什么」这件事,这一行叫代理,英文就叫 agent。

把它用在 RAG 上,就是「让模型自己控制检索」—— 这套做法叫代理式 RAG。

定义在哪儿

书给这件事下过一次完整的定义,而那段话出现在第 7 章的结语里 —— 第 7 章讲的是怎么生成答案,下一章讲提示写法,这段话和前后都接不上1

它说的是四步加一个条件:

做什么
先生成一个初步答案
模型评估:检索回来的文档,证据够不够、对不对题
不够就改写查询,或者发出更有针对性的子问题,重新检索一遍(可以换检索器、换过滤条件、换时间范围)
回到 ①,再来一轮
停止条件重复到满足停止条件为止 —— 够有把握了、转够几轮了、或者花的钱到顶了

书还给了它的适用场景:问题问得含糊、或者库里文档质量参差不齐时,这套做法能减少胡编。

「停止条件」为什么必须单独拿出来说

只要有循环,就必须有停下来的理由 —— 否则它会一直转。

书给的三种,各管一件事:

停止条件它拦的是什么定不好会怎样
够有把握了拦「已经够好还在瞎折腾」定太高就永远停不下来
转够几轮了拦「怎么改都找不到」定太小等于没循环
花的钱到顶了拦「一个问题烧掉一天的预算」不设就会有失控的账单

实际系统里三种一般同时设,任何一条满足就停。

判断(我们的,不是书里的): 我们认为这三条里**「转够几轮」是唯一不可省的一条**。 另外两条都依赖一个数值判断 —— 而第 09 章刚讲过,那种数值本身可能是假的; 只有轮数是绝对可靠的刹车。 如果错,会错在: 如果每一轮的成本很低而问题很值钱, 那么把轮数上限设得很大、主要靠把握度停下来,是更划算的。 判据是「多转一轮的收益 vs 多转一轮的成本」—— 这需要评测,而评测是这本书最大的洞(第 12 章)。

4. 主走查:拿这把尺子,量一量书自己的最后一个配方

这一节是本章的主走查。上面那张定义表的每一步,在这条线上都占一格。

量的对象是原书的第 121 个配方,也就是全书最后一个 —— 它的标题叫「查询扩展代理」2

它的库只有五条文档:

doc1 Alice wrote about intermittent fasting and health.
doc2 The 2008 financial crisis impacted global markets.
doc3 The French Revolution began in 1789.
doc4 Research shows fasting improves insulin sensitivity.
doc5 Global financial markets collapsed in 2008.

逐格对照

定义要求的这个配方实际做了什么判定
① 先生成一个初步答案它一个字的答案都没生成 —— 整个程序只返回检索结果
② 评估证据够不够没有任何判断语句在问这件事
③ 不够就改写查询重检索有改写,但不是「不够才改」 —— 它一上来就无条件把查询扩成 8 个变体,各查一次半个
④ 回到 ① 再来一轮没有循环。 扩展 → 各查一次 → 合并 → 返回,一趟走完
⑤ 停止条件不需要,因为它只跑一趟

五格里四格是空的。而这是全书的最后一个配方,书名给它的定位是「代理」。

第 ③ 格展开:它是怎么改写查询的

它用的是一本英文同义词词典 —— 一份把英语单词按意思分组编好的现成词库, 查一个词,能得到和它同组的其他词3

做法是:把问句拆成词,每个词都去查同义词,然后拿查到的词替换原词,得到一批新问句。

书印出来的实际结果是4:

原问句:Tell me about health (跟我讲讲健康)

扩展出的 8 个变体:
Tell me about health ← 原句,被保留了下来
Tell me about wellness ← health → wellness,**有用**
Tell me almost health ← about → almost(几乎)
Tell me approximately health ← about → approximately(大约)
Tell me around health ← about → around(周围)
Tell me astir health ← about → astir(苏格兰方言:活动着的)
Tell Maine about health ← **me → Maine(缅因州)**
Tell Pine Tree State about health ← **me → 松树之州(缅因州的别称)**

图说:「me」变成了「缅因州」,因为在那本词典里,
ME 是缅因州的邮政缩写、「松树之州」是它的别名,三者在同一组。
8 个变体里,**只有 1 个是有用的改写,1 个是原句,其余 6 个是噪声。**

另一个查询更离谱4:

原问句:History before 1900 (1900 年以前的历史)

变体: History earlier 1900
History **ahead** 1900 ← before → ahead(在前面)
History **in front** 1900 ← before → in front(在前方)

图说:before 的时间含义被换成了空间含义。
**「1900 年之前」变成了「1900 的前面」——方向整个飞了。**

而它给出的结果是对的

「Tell me about health」 → doc1 1.2788 (Alice 写过间歇性禁食与健康)
doc4 1.5752 (研究显示禁食能改善胰岛素敏感性)

「History before 1900」 → doc3 1.3599 (法国大革命始于 1789)
doc2 1.7255 (2008 金融危机)

图说:这些数是 L2 距离,**越小越近**(第 04 章讲过)。
两个查询的第一名都对。

它凭什么还能对?两个兜底设计

这才是这个配方真正值钱的地方,而书一个字没说。

兜底一:**原查询被留在了变体集合里。**
代码建那个集合时,第一个放进去的就是原句。
→ 所以最坏情况下,结果不会比「不做扩展」更差。

兜底二:**同一篇文档在所有变体里,只保留它拿到过的最好分数。**
「Tell Maine about health」当然也会返回一堆文档,
但它给的分数很差,**顶不掉原句给出的好分数**。

图说:这两条合起来的效果是——
**一个坏变体只能往候选池里多扔几条,不能把好结果挤下去。**
它把「扩展」从一个有风险的操作,变成了一个只增不减的操作。

判断(我们的,不是书里的): 我们认为这个配方最该被学走的不是同义词扩展, 而是这两条兜底。 扩展本身在这里的表现是净负的(8 个变体里 6 个是噪声), 而它之所以没有害处,完全靠这两条设计。 推广开来:凡是「多试几种问法」的做法,都应当把原问法留在候选里、并且按最优分合并。 如果错,会错在: 如果换一个语料,某个坏变体恰好在某篇不相关的文档上拿到了极高的分, 那么「只增不减」就不成立了 —— 坏变体确实会把一条垃圾送进前几名。 判据是拿一个含有噪声文档的库重跑 —— 书里只有 5 条干净文档,做不了这个对照,我们也没有跑。

5. 十个「代理」,逐条对照那张定义表

这一章一共十个配方。拿第 3 节那五格去量,结果是这样的:

配方书给它的名字代码实际做的事有循环吗
112自查询代理让模型把问题转成一份带过滤条件的数据 —— 失败了,走了兜底分支,过滤条件是空的5
113任务导向工具代理句子里有数字就走计算器,否则走检索
114上下文感知的对话代理维护了一个对话历史列表,但从来不读它6
115动态重排代理重排 = 数问句和文档有几个共同的词,连「is」「the」都算7
116自适应摘要代理库里只有一篇文档,而分支条件是「只有一篇就直接返回」—— 摘要那条路是死代码8
117思维链检索代理提示里写了「一步一步想」,模型把提示里的 Context: 标签一起抄了回来9
118混合检索代理两路结果直接拼起来去重,没有打分也没有融合,按意思找的那一路永远占前排
119时间感知检索代理取一条结果,然后对这一条按日期排序10
120流式检索代理先把整段答案生成完,再逐词打印 —— 代码注释自己写着「模拟」11
121查询扩展代理第 4 节那条走查

十个配方,零个循环。没有一次自我评估,没有一次重试,没有一个停止条件。 分支最多的那个,是一句「有没有数字」的判断。

三处值得单独看一眼

第 114 个配方最能说明问题。 它叫「上下文感知」, 代码确实把每一轮问答都存进了一个列表 —— 而这个列表从未进入任何一次提示。

后果就印在输出里: 问「法国的首都是什么」,答案是 France —— 一个词,而且是问题里的词6

而同样的库、同样的问题,第 115 个配方答对了:The capital of France is Paris.7 差别在于第 114 个配方调了两次模型:先让模型把文档「答」一遍, 再把这个答案当资料喂进第二次。两道小模型串起来,信息被磨没了。

第 119 个配方是全书最短的一处空转: 它取回 1 条结果,然后对这 1 条按日期排序10对一个元素排序,等于什么都没做。

6. 还有一层技术原因:这一章的模型只有 6000 万参数

第 01 章说过,这本书的生成模型逐章缩水。这一章缩到了最小。

第 1–6 章 : 约 30 亿参数
第 7–10 章: 约 2.5 亿参数
**第 11 章 : 约 6000 万参数** ← 比第一档小了 50 倍

图说:而环境清单里写的**仍然是第一档那个模型**——**书从没交代过换模型这件事。**

更要紧的是另一件事,而它和大小无关。

补充(不在书里,来自通用知识):下面这几段讲模型是怎么训出来的,书里一个字都没有 —— 书只印了配方的输出,没解释它为什么长这样。

先说这个模型是怎么造出来的。 第一遍训练是让它读海量文字、学会把话接下去, 这一遍叫预训练 —— 第 01 章说的那个「照着眼前的文字往下接」,就是这一遍练出来的本事。

练完之后通常还有第二遍:拿少量专门准备的例子继续训一小段,把它掰向某种用途。 这一遍叫微调。

而这一章那个模型,缺的正是第二遍里最要紧的那一种: 拿大量「一句指令 + 一份理想回答」去教它「用户写的是要求,你要照着做」 —— 这一种叫指令微调。

没经过这道训练的模型,不认识「请一步一步想」这类话。 它只认识自己第一遍训练时见过的那几种固定开头(「把英语翻译成德语:」之类)。

给它一段自由格式的指令,它最常见的反应就是把输入抄回来。

证据就在第 117 个配方的输出里:答案的第一个词是 Context: —— 那是提示里的标签,它连标签一起抄了出来9

但这一层解释不了全部

现象模型太小能解释吗
输出是原文回声
「自查询」没吐出结构化数据
对话历史只写不读不能 —— 那是代码没写
「重排」是数共同词不能 —— 那是代码就这么写的
对一条结果排序不能
没有一个循环不能 —— 循环是代码结构,和模型大小无关

六条里只有两条能算在模型头上。 剩下四条,包括这一章最要命的那一条, 是设计问题,不是模型大小的问题。

7. 作者的判断与证据

书里给了证据的:

说法证据
代理式 RAG 是「评估证据 → 不够就改写重检索 → 直到满足停止条件」的循环第 7 章结语那段定义 —— 这是全书对这件事最完整的一次表述
查询扩展能多召回一些文档第 121 个配方两个查询的第一名都对
同义词扩展会产生离谱的变体「Tell Maine about health」「History ahead 1900」—— 书原样印了出来

作者只是断言、没有给证据的:

说法缺什么
「检索系统从静态流水线演进成了能做决策的代理」(本章结语)12十个配方没有一个做决策
「这些代理不只是取信息,还能智能地判断取什么、何时取、怎么取」判断只有一句「有没有数字」
「上下文感知」历史列表只写不读
「动态重排」数共同词个数,连停用词都算
「自适应摘要」摘要那条路是死代码
「流式」先生成完再逐词打印,注释自己写着「模拟」
「稀疏检索用 BM25」代码用的是另一种更简单的词频加权法,而 BM25 比它多做三件事(第 06 章讲过)

判断(我们的,不是书里的): 我们认为这一章的问题不是「做得不够好」, 而是书自己给的定义和书自己的代码不在同一个层面上 —— 定义讲的是一个控制结构(循环 + 评估 + 停止),而十个配方演示的全是检索的花样。 这不是实现得糙,是根本没有开始实现。 如果错,会错在: 如果作者的定位是「代理式 RAG 需要的各种零件」, 而把「怎么把零件装成循环」留给读者,那这一章是完整的、只是缺一节总装说明。 但本章结语写的是「已经演进成了能做决策的代理」 —— 那是结论,不是零件清单, 所以我们按结论追究。

8. 边界与局限

  • 书自己对代理式 RAG 最完整的定义,不在这一章。 它在第 7 章的结语里, 前后都接不上,读者极容易错过。
  • 没有一处演示循环。 而循环是这套做法的定义本身。
  • 没有一处演示「评估证据够不够」。 第 09 章那个真置信度本来现成可用,书没有接上。
  • 没有一处提到停止条件。 只在第 7 章结语里作为定义的一部分出现过一次。
  • 这一章有六个配方的导入行粘在了一起,直接就跑不起来;另有四处正则用了全角引号。
  • 判断「这是不是数学题」的条件是「句子里有没有任何一个数字」 —— 「2008 年发生了什么」会被送进计算器。而计算器用的是直接求值, 其中一个配方连内置函数表都没清空。
  • 全书到此结束,而「怎么知道这套东西好不好」一个字都没有 —— 第 12 章讲。

9. 可带走的

全章那条走查,一行写完:

拿第 7 章结语那五格去量全书最后一个配方 —— ① 先给初步答案:缺。② 评估证据:缺。③ 改写重检索:有,但是无条件改,不是「不够才改」。 ④ 循环:缺。⑤ 停止条件:缺(因为它只跑一趟)。 它把「Tell me about health」扩成 8 个变体,其中「me」被扩成了缅因州; 把「History before 1900」的 before 扩成了「在前面」。 而它照样给出了正确结果(doc1 1.2788、doc3 1.3599) —— 靠的是两个兜底:原句留在候选里 + 同一篇文档跨变体只留最好的分。

  1. 链的根本毛病是没有回头路 —— 第一次没找到,它照样往下走、照样编一个答案;
  2. 代理式 RAG 是一个循环,不是一个框架: 先给初步答案 → 评估证据够不够 → 不够就改写重检索 → 直到满足停止条件;
  3. 只要有循环,就必须有停止条件。 三种:够有把握了、转够几轮了、花的钱到顶了; 三种一般同时设,任何一条满足就停;而轮数是唯一绝对可靠的那一条;
  4. 判断一个东西是不是「代理」,只看一件事:它会不会回头再来一遍。 会,就是;不会,再花哨也只是一条链;
  5. 朴素的同义词替换在生产里不能用 —— 它不看上下文,「me」会变成州名, 「before」的时间方向会翻转;
  6. 但「多试几种问法」这件事本身可以做得很安全,靠两条兜底: 原问法留在候选里、同一篇文档跨所有问法只留最好的分;
  7. 两道小模型串起来会把信息磨没 —— 同样的库同样的问题,单跑一次答对,串两次只剩一个词;
  8. 看到「流式」先看它是不是先生成完再打印;看到「重排」先看它按什么打分; 看到「自适应」先找那条分支会不会被触发;
  9. 模型小能解释输出难看,解释不了没有循环。 循环是代码结构,和模型多大没有关系;
  10. 书自己的定义写对了,代码一条都没做到。 这是全书最大的一处名实不符。

10. 原文地图

主题原书章原文位置
代理式 RAG 的完整定义(藏在第 7 章结语)CHAPTER 7 Response Generation with LLM in RAG Systemstext/14-fm-introduction.txt:1974(搜「Agentic RAG uses the LLM to control retrieval」)
主走查:五条语料CHAPTER 11 Agentic RAG with Dynamic Retrievaltext/18-fm-introduction.txt:1745(搜「Alice wrote about intermittent fasting」)
同义词扩展的代码与「原句留在集合里」同上text/18-fm-introduction.txt:1767(搜「expansions = set([query])」) · text/18-fm-introduction.txt:1771(搜「wn.synsets(word)」)
「跨变体只留最好的分」那一行同上text/18-fm-introduction.txt:1835(搜「seen[h[id]][score]」)
扩展出来的 8 个变体(含缅因州)同上text/18-fm-introduction.txt:1886(搜「Tell Maine about health」)
before 被扩成「在前面」同上text/18-fm-introduction.txt:1910(搜「History ahead 1900」)
两个查询的结果与分数同上text/18-fm-introduction.txt:1890(搜「id=doc1, score=1.2788」) · text/18-fm-introduction.txt:1914(搜「id=doc3, score=1.3599」)
「自查询」走了兜底分支,过滤条件是空的同上text/18-fm-introduction.txt:168(搜「{query: question, filters: {}}」) · text/18-fm-introduction.txt:204(搜「Parsed Query」)
对话历史只写不读,答案只剩一个词同上text/18-fm-introduction.txt:601(搜「conversation_history.append」) · text/18-fm-introduction.txt:669(搜「France」)
「动态重排」= 数共同词同上text/18-fm-introduction.txt:771(搜「score = len(set(doc.page_content.lower().split()) & query_words)」) · text/18-fm-introduction.txt:885(搜「1. Score: 4, Content: The capital of France is Paris.」)
「自适应摘要」的死分支同上text/18-fm-introduction.txt:967(搜「docs = [」) · text/18-fm-introduction.txt:1025(搜「if len(retrieved_docs) == 1」)
思维链的输出把提示标签一起抄了出来同上text/18-fm-introduction.txt:1264(搜「Context: RAG combines retrieval and generation」)
对一条结果排序同上text/18-fm-introduction.txt:1529(搜「results.sort(key=lambda x: x.metadata.get(date」) · text/18-fm-introduction.txt:1549(搜「time_aware_retrieve(q, top_k=1)」)
「流式」是先生成完再逐词打印同上text/18-fm-introduction.txt:1679(搜「simulate streaming」)
这一章用的模型同上text/18-fm-introduction.txt:111(搜「model=t5-small」) · text/18-fm-introduction.txt:53(搜「LLM model: Ollama」)
本章结语的结论同上text/18-fm-introduction.txt:1922(搜「evolve from static pipelines into adaptive, decision-making agents」)

Footnotes

  1. 出处:「CHAPTER 7 Response Generation with LLM in RAG Systems」第 1974 段(text/14-fm-introduction.txt:1974,搜「Agentic RAG uses the LLM to control retrieval」)。原文完整地说了四步与三种停止条件:生成初步答案后,模型评估检索回来的文档证据是否充分、是否相关;不够就改写查询或发出更有针对性的子查询、重新检索(可以换检索器、换过滤条件、换时间窗口),重复直到满足停止条件 —— 置信度阈值、最大轮数、标记或预算上限。它还说这套做法能减少胡编,并推荐用于「问题含糊或文档质量参差」的生产系统。这段话出现在讲生成的那一章的结语里,下一章讲的是提示写法,前后都接不上。

  2. 出处:「CHAPTER 11 Agentic RAG with Dynamic Retrieval」第 1743–1759 段(text/18-fm-introduction.txt:1745,搜「Alice wrote about intermittent fasting」)。这个配方的整个流程写在第 1819–1843 段(text/18-fm-introduction.txt:1821,搜「expanded = expand_query(query)」):扩展 → 逐个变体检索 → 合并 → 排序 → 返回,没有生成步骤,也没有第二轮。

  3. 出处:同章第 1771 段(text/18-fm-introduction.txt:1771,搜「wn.synsets(word)」)。补充(不在书里,来自通用知识): 这里用的是 WordNet —— 一份把英语单词按义项分组的公开词库,同一组里的词被认为意思相近。它不看上下文,所以一个词的所有义项会被一视同仁地取出来,这正是「me」变成州名的原因:在这份词库里,ME 是缅因州的邮政缩写,而「Pine Tree State」是缅因州的别称。

  4. 出处:同章第 1886 段(text/18-fm-introduction.txt:1886,搜「Tell Maine about health」)与第 1910 段(text/18-fm-introduction.txt:1910,搜「History ahead 1900」)。检索结果在第 1890 段(text/18-fm-introduction.txt:1890,搜「id=doc1, score=1.2788」)与第 1914 段(text/18-fm-introduction.txt:1914,搜「id=doc3, score=1.3599」)。两个兜底设计分别在第 1767 段(text/18-fm-introduction.txt:1767,搜「expansions = set([query])」——建集合时先把原句放了进去)与第 1835 段(text/18-fm-introduction.txt:1835,搜「seen[h[id]][score]」——只在分数更好时才替换)。书对这两处一个字没说。 2

  5. 出处:同章第 168 段(text/18-fm-introduction.txt:168,搜「{query: question, filters: {}}」)与第 204 段(text/18-fm-introduction.txt:204,搜「Parsed Query」)。输出里那份「解析出来的查询」逐字等于兜底分支写死的返回值,过滤条件是空的;于是检索结果里混进了「The capital of France is Paris.」(text/18-fm-introduction.txt:206,搜「The capital of France is Paris」),而问题问的是健康与胰岛素敏感性。

  6. 出处:同章第 601–603 段(text/18-fm-introduction.txt:601,搜「conversation_history.append」)与第 669 段(text/18-fm-introduction.txt:669,搜「France」)。那个列表只有 append,没有任何一处读它。 顺带:这个配方的清洗逻辑里出现了德语的「用户」一词(text/18-fm-introduction.txt:597,搜「Benutzer」)——显然是从别处抄来的。 2

  7. 出处:同章第 771 段(text/18-fm-introduction.txt:771,搜「score = len(set(doc.page_content.lower().split()) & query_words)」)与第 885 段(text/18-fm-introduction.txt:885,搜「1. Score: 4, Content: The capital of France is Paris.」)。那个 4 分是靠 is / the / of / France 四个词凑出来的,其中三个是没有区分力的常用词。 这一节的正文承诺的是「语义对齐、查询意图、回答质量」。 2

  8. 出处:同章第 967–972 段(text/18-fm-introduction.txt:967,搜「docs = [」)与第 1025 段(text/18-fm-introduction.txt:1025,搜「if len(retrieved_docs) == 1」)。文档库里只有一篇文档,而分支条件的第一项就是「只有一篇就直接返回」 —— 摘要那条路永远走不到。

  9. 出处:同章第 1264 段(text/18-fm-introduction.txt:1264,搜「Context: RAG combines retrieval and generation」)。答案的第一个词就是提示里的标签 Context: 这个配方用的模型写在第 1144 段(text/18-fm-introduction.txt:1144,搜「model=t5-small」)。补充(不在书里,来自通用知识): 正文那几段「预训练 / 微调 / 指令微调」书里没有,是我们补的背景。要点是:这个型号属于「原生前缀任务」那一代 —— 它训练时见到的指令是几种写死的开头(translate English to German:summarize: 之类),而不是自由格式的要求;后来在同一批权重上再拿大量「指令 + 理想回答」训一遍,才有了会听自由指令的那一版(名字里多了 flan-)。书用的是前一版。 2

  10. 出处:同章第 1529 段(text/18-fm-introduction.txt:1529,搜「results.sort(key=lambda x: x.metadata.get(date」)与第 1549 段(text/18-fm-introduction.txt:1549,搜「time_aware_retrieve(q, top_k=1)」)。检索器取回的条数由调用方传的 1 决定,而排序发生在这 1 条上。 2

  11. 出处:同章第 1677–1683 段(text/18-fm-introduction.txt:1679,搜「simulate streaming」)。先一次性把整段生成完,再按空格切开逐词打印。 这和第 7 章那个「渐进式披露」是同一个假动作。

  12. 出处:同章第 1922 段(text/18-fm-introduction.txt:1922,搜「evolve from static pipelines into adaptive, decision-making agents」)。原文说这一章展示了检索系统怎样从静态流水线演进成能就上下文、任务要求、时间与语义细节做推理的自适应决策代理,并说这一「代理层」带来了更大的灵活性、更高的准确率和更丰富的交互。这是结论,而这一章十个配方里没有一个包含决策或循环。