跳到主要内容

造一个对话 agent — 上下文、循环与 UX

这一章讲三件事: 对话型任务要带哪四种料、各留多少;怎么用十几行代码把 前两章的零件拼成一个能一直聊下去的 agent;以及用户盯着的那块屏幕, 为什么直接决定他敢不敢让 agent 动手。 这是原书第 8 章的下半场:能力(第 10 章)已备齐,这一章负责组装

1. 这一章讲什么

第 10 章结束时,模型有了手(工具)和草稿纸(思维链), 但每次互动还是「问一句、答一句」。想让它连着办一串事, 还缺一样东西:这一次对话攒下的上下文1

「上下文」听起来像聊天记录的代名词。这一章会把它拆成四种料—— 只有分清哪句是背景、哪句是现场指令,才知道该多远留多少。

主走查接着用恒温器:同一间屋,连续三轮人话 (「热死了」「猛降温」「给我调回去」), 看 agent 靠什么接住每一轮——答案分别是常识、字面理解和记性, 而「记性」正是本章的头号机制。

2. 顶层全景:一个循环,一份不断变长的文档

用户输入 ──→ messages 列表(整段对话文档)


process_messages(第 10 章的五步)
│ 模型回话:要调工具吗?
│ ├─ 要 → 应用执行 → 结果也追加进 messages → 再问模型一轮
│ └─ 不要 → 把模型的答话追加进 messages,打印给用户


回到「等下一个用户输入」

图说:所谓 agent,就是外层「等用户说话」和内层「跑 process_messages」
两个 while(满足条件就一直重复的循环结构)。模型每一步看到的,
只是这份越来越长的 messages 文档。

作者的原话是:第 10 章演示完工具调用时,离完整的对话 agent 只差两件事——给用户一个输入口,再把 process_messages 套进一个循环里2。 没有第三件。

3. 核心原理

3.1 主走查:三轮人话,三种本事

主走查开始。 还是那两个工具(get_room_temp / set_room_temp), 但这次用户不配合机器语法了3:

第 1 轮 用户:「Golly gee, it's hot in here. Let's cool this place down.」
agent:[调用 get_room_temp() → 返回 64ºF]
agent:「The current room temperature is 64ºF, which is actually
quite cool……您确定还要降温吗?」

第 2 轮 用户:「Make it LOTS cooler.」
agent:[调用 set_room_temp(temp=50)]
agent:「已经设到 50ºF,很快就会凉快下来!」

第 3 轮 用户:「Whoa……有点过了吧?Put it back to where it started.」
agent:[调用 set_room_temp(temp=64)]
agent:「已经调回 64ºF。」

图说:三轮对话对应三种本事:第 1 轮靠常识听出「64 度其实不热,
这话有蹊跷」;第 2 轮靠字面服从执行「LOTS」;
第 3 轮靠先前的对话,把「started」还原成数字 64。

第 3 轮是全书的点睛之笔:调回 64 这个数,任何一条当前消息里都没出现。 它只能来自先前对话(prior conversation)——从这个对话一开始,它读过一次 get_room_temp 返回的 644。把那段历史扔掉,「调回去」就无从谈起。

顺带值得停一秒的是第 1 轮那句「其实挺凉的」。 模型对人类体感、季节、建筑环境的全部推断, 没有任何一条写在提示词里——作者管这叫常识免费赠送4。 训练集替你交了这笔学费;你要做的只是别让它失聪(别喂错料)。

3.2 对话这份文档由四种料组成

第 06、07 两章教的是「给补全模型挑料」;到了 agent 这儿,料还是那些料, 容器换成了对话记录——全部四种料都装进这份壳里1。按离「当下」从远到近:

装什么类比
开场白(preamble)设定行为的规则、期望与工具定义,一般进系统消息;必要时附 few-shot 示范5上岗培训
先前对话(prior conversation)从头到上一轮为止的全部往来消息及其附件5会议纪要
挂件(artifact)挂在某条消息上、与谈话相关的一块数据——航班列表就是典型5夹在纪要里的附件
当前往返(current exchange)用户此刻的请求 + 它引发的全部工具调用与结果;直到 agent 给出面向用户的答话才算结束5正在讨论的这一页

书里的表 8-1 是个旅行助手,四个位置各走一遍6:

[开场白/系统消息] "You are a helpful and knowledgeable travel assistant.
The current date is 8/9/2023."
[先前对话] 用户:「Are there any flights from Dulles to Seattle next Monday?」
助手:两班 + <artifact>(JL5441 周一 9:20AM / AS325 周一 4:50PM)
[当前往返] 用户:「Are there any tickets available first one?」
助手:[调用 get_ticket_info(flight_num="JL5441")]
工具:{"price": 350.00, "currency": "USD",
"stops": ["ORD"], "duration": "7h40m"}
[最终答话] 「有一班 $350,经停芝加哥。」

注意第二处用户输入只说了「first one」。 「first one」指哪一班?线索躺在先前对话的那个 artifact 里。 这个例子同时演示了挂件的两个用法:上一轮把航班列表挂出来, 下一轮当前往返就能指着它说「第一个」6

3.3 挑料与摆料:每一样都在跟预算谈判

怎么从这四种料里取舍,作者先亮态度再列清单: 没有万能配方,唯一可靠的纪律是反复评估(evaluate, evaluate, evaluate)7。 清单本身是一条条权衡8:

  • 工具要不要在场? 某些阶段明知用不上某件工具,就把它撤下—— 给模型减一件分心的东西8;
  • 挂件全给还是让模型挑? 全给,信息最全但杂音大; 让模型挑,要多搭一次「请你选哪些相关」的旁路请求,应用复杂度上一个台阶9;
  • 挂件怎么呈现? 直接塞正文(XML 标签或 markdown 小节)即可, 作者的口碑是格式影响不大——但要自己验。还有个白捡的技巧: 如果挂件本来全都来自函数调用,原样保留那些调用,等于顺手给模型 多看几遍工具示范10;
  • 单件挂件切多大? 整本书当然塞不下。接力第 06 章的弹性片段: 作者提了一个自己都注明「还没试过」的巧招——摘要每条后面缀一句 「详情请调 details('section 5')」,模型追要哪节,应用就把哪节展开11; 更传统的做法是对大挂件挂一套检索(RAG)11;
  • 先前对话留多远? 话题换了就该丢。怎么知道换没换? 要么按时间一刀切(隔了一阵的旧会话一律丢弃), 要么训一个小模型专门判相关性——大模型干这事太贵太慢12

作者收尾时的坦白值得原文照录意思:要是当初能把话说死就好了—— 可惜不能。多放怕撑爆提示词又贵又慢,少放怕它瞎猜; 唯一的盼头是模型越来越强、窗口越来越大, 将来也许真能「拿不准就全塞给它」。在那之前,还是那三个词:评估、评估、评估13

3.4 组装:十几行代码的事

如顶层全景所示,run_conversation 就是围着 process_messages 转的两个圈14: 外圈读用户输入、空字符串即退出;内圈反复跑五步流程, 最后靠「模型既给了答话、又没要调工具」这个条件退出、打印给用户14。 第 10 章那张手绘时序图放到这里依然成立,只是循环的入口从「脚本跑三次」 变成了「永远等下一个输入」15

作者在这里给出了完整代码(例 8-2)后写了句 "What will you make first?"—— 组装阶段的神秘感到此清零。难的从来不是循环,是上一节的挑料与下一节的界面。

3.5 界面不是装饰品,是授权链路

书里的 UI 一节反直觉地把几件「美观问题」逐一钉成了「信任问题」16:

零件解决什么问题
spinner(等待时转圈的动画)让用户知道 agent 在干活、没死机16
工具 pill(消息里的药丸形小标签)让用户看见「它在后台调工具」,而不是莫名卡住16
工具调用可展开(书里用 Dave/HAL 的截图演示)答得出乎意料时,点开能看到工具名、填的参数表单、拿到的结果17
参数表单可改 + 改完重提交用户不只是旁观:改一个参数,对话从那一点起重新生成——纠偏的最小操作18
危险操作授权弹窗只要调用可能动真实世界资产,执行前必须过一道人工确认19

作者举的场景(Dave 与 HAL)值得细看一次: HAL 答非所问,Dave 点开「Tool calls」,发现它拿到的结果有问题, 于是在 webform 里直接改参数重新提交, 后面的对话从头再生一遍——没有一句「抱歉请重新描述需求」18

最后还有一条留给「挂件」的:让用户看得见 agent 正在看什么。 界面上显示当前挂着的 artifact,谈得拢就继续,发现它盯错了东西 就把那件挂件关掉——注意力对齐,比事后解释便宜得多20

4. 作者的判断与证据

有硬证据的:

  • 三轮恒温器对话是书里印出的真实运行记录,包括 agent 自发的 「64 度其实挺凉」和最后的 set_room_temp(temp=64)3;
  • 「离完整 agent 只差一个循环」这句结论附带完整代码(例 8-2), 书里明确说所有代码原样可跑14;
  • 四种料的划分与旅行助手例(Table 8-1)是书自带的演示数据6

必须标明的:

  • 挑料的整份清单,作者自认是权衡而非定论, 「evaluate, evaluate, evaluate」出现了两次当作免责声明713;
  • details('section 5') 这个展开挂件的巧招,作者原话注明 「though one that we haven't yet tried」——是设想,不是实测11;
  • 挂件用什么格式(XML/markdown/纯文本)书里只给口碑 「it doesn't seem to matter much」,并叮嘱读者自己测10

判断(我们的,不是书里的): 「agent = while 循环」这件事越早破除迷信越好, 因为它把工程难点放回了正确的地方:难的不是自主性,是喂料与止损。 而止损(改参数重提交、关挂件、授权弹窗)全是 UI 层的动作—— 在这本书的框架里,UI 不是产品的皮肤,是控制系统的一部分。 如果错,会错在: 若未来的推理类模型把「规划若干步」内化成单次输出, 循环的意义会缩水成「执行引擎」;但只要动作仍发生在真实世界, 授权与止损那一层就不会消失。

5. 边界与局限

  • 书的 UI 蓝图是文字版:spinner/pill/webform 都是示意, 语音、富媒体场景要自行迁移;书里也没碰日志(系统运行时留下的记录)、鉴权、API 封装这些上线必答题21;
  • 对话 agent 有天花板:作者明说它需要人不时拽一把才能不跑偏—— 这句话是通往原书第 9 章(LLM 工作流,我们的第 12 章)的门22;
  • 「将来也许可以无脑全塞」被作者自己标注为期许,不是现状; 当下的决定仍然要拿评估支撑13

6. 可带走的

主走查一行回顾:「热死了」→ 查温 64 并直言其凉 → 「LOTS cooler」设到 50 → 「调回去」凭 prior conversation 还原成 64—— 第一第二轮免费,第三轮花的是此前存对了账。

  1. agent = 双层循环,不用神化;装它的代码比想象短得多;
  2. 对话有四种料:开场白、先前对话、挂件、当前往返; 能力来自「料放在正确的坑里」,比如「调回去」吃的就是先前对话;
  3. 撤下闲着的工具,少一件是一件;危险操作拦在应用层等真人签字(第 10 章规矩在本章落地);
  4. 大挂件别硬塞:要么可展开(details 型小钩子),要么上检索;
  5. 旧会话按时限或相关性裁剪,相关性判断交给小模型而非主力模型;
  6. 把工具调用做成可视对象:pill 显示进行中,点开见全貌,webform 改参重跑;
  7. 动真钱的动作必须过授权 UI——这条和第 10 章的应用层拦截互为表里;
  8. 让用户看见 agent 的注意力(当前挂件),发现跑偏当场掐掉;
  9. 「常识免费」是真的,但它依赖你选对料——错误料会教会模型错误的常识。

7. 原文地图

主题原书章原文位置
四种料的定义Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:733(搜「there is a preamble」) · :738(搜「recent back-and-forth」) · :742(搜「attached artifacts」) · :748(搜「current exchange begins」)
旅行助手四料实例Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:787(搜「Dulles to Seattle」) · :815(搜「350.00」) · :821(搜「flight for $350」)
挑料清单与告诫Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:832(搜「evaluate, evaluate」) · :838(搜「one less distraction」) · :857(搜「preserve the function calls」)
details() 展开挂件Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:869(搜「section 5」) · :874(搜「traditional RAG」)
先前对话留多远Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:878(搜「inactive for」) · :889(搜「When in doubt」)
run_conversation 组装Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:901(搜「tossing a loop around」) · :942(搜「process_messages(client」)
主走查三轮对话Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:972(搜「Golly gee」) · :984(搜「LOTS cooler」) · :996(搜「reset the room temperature」)
常识免费与最后交换Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:1004(搜「common sense reasoning for free」) · :1010(搜「back to its starting point」)
UI 六零件Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:1045(搜「spinner that indicates」) · :1051(搜「pill button」) · :1062(搜「modify arguments from the webform」) · :1073(搜「authorize any request」)
挂件可见可关闭Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:1083(搜「dismiss an artifact」)
通向工作流Chapter 8text/11-ch08-chapter-8-conversational-agency.txt:1096(搜「corrective influence」)

Footnotes

  1. 出处:「Chapter 8. Conversational Agency」第 723-731 段(text/11-ch08-chapter-8-conversational-agency.txt:726,搜「some new things to consider」)。原书指出第 5、6 章找料的方法依然成立,但任务型交互另有新考虑。 2

  2. 出处:「Chapter 8. Conversational Agency」第 893-902 段(text/11-ch08-chapter-8-conversational-agency.txt:899,搜「The only two things remaining」);「tossing a loop around the process_messages function」(:901)。

  3. 出处:「Chapter 8. Conversational Agency」Table 8-2(text/11-ch08-chapter-8-conversational-agency.txt:970,搜「Executing run_conversation」)。「Golly gee」(:972);agent 查温并回「actually quite cool」(:976);「Make it LOTS cooler」→ temp=50(:984);「set_room_temp(temp=64)」(:994)。 2

  4. 出处:「Chapter 8. Conversational Agency」第 1001-1014 段(text/11-ch08-chapter-8-conversational-agency.txt:1004,搜「common sense reasoning for free」);「you have to know a lot about humans to get that right」(:1005);最后交换之所以可行,是因为「tracking not only the current exchange but the prior conversation as well」(:1012)。 2

  5. 出处:「Chapter 8. Conversational Agency」第 733-747 段(text/11-ch08-chapter-8-conversational-agency.txt:733,搜「there is a preamble」)、(:738,搜「recent back-and-forth」)、(:742,搜「attached artifacts」)与(:748,搜「current exchange begins」)。「an artifact is any piece of data that is relevant to the conversation」(:743)。 2 3 4

  6. 出处:「Chapter 8. Conversational Agency」Table 8-1(text/11-ch08-chapter-8-conversational-agency.txt:768,搜「Anatomy of a conversational agent」)。"Are there any flights from Dulles to Seattle next Monday?"(:786);票价 350.00 与 ORD 经停(:815);最终答话(:823)。 2 3

  7. 出处:「Chapter 8. Conversational Agency」第 829-832 段(text/11-ch08-chapter-8-conversational-agency.txt:829,搜「no one-size-fits-all」);「the key is to constantly try new ideas and then evaluate, evaluate, evaluate」(:832)。 2

  8. 出处:「Chapter 8. Conversational Agency」第 836-848 段(text/11-ch08-chapter-8-conversational-agency.txt:837,搜「Drop them from consideration」);「one less distraction」(:838)。 2

  9. 出处:「Chapter 8. Conversational Agency」第 839-848 段(text/11-ch08-chapter-8-conversational-agency.txt:847,搜「side request」)。让模型挑选挂件需要在应用里另开一趟请求。

  10. 出处:「Chapter 8. Conversational Agency」第 849-859 段(text/11-ch08-chapter-8-conversational-agency.txt:856,搜「preserve the function calls」);「more examples of tool invocation」(:858);「it doesn't seem to matter much (but test this for yourself)」(:854)。 2

  11. 出处:「Chapter 8. Conversational Agency」第 860-874 段(text/11-ch08-chapter-8-conversational-agency.txt:867,搜「haven't yet tried」);details('section 5') 的展开玩法见 :869-872(搜「unfurl」);「a traditional RAG」备选项(:874)。 2 3

  12. 出处:「Chapter 8. Conversational Agency」第 875-881 段(text/11-ch08-chapter-8-conversational-agency.txt:878,搜「user has been inactive」);「train a smaller model to do this」(:881)。

  13. 出处:「Chapter 8. Conversational Agency」第 883-890 段(text/11-ch08-chapter-8-conversational-agency.txt:887,搜「getting smarter and faster」);「When in doubt, add it to the prompt」是作者的期许语气(:889)。 2 3

  14. 出处:「Chapter 8. Conversational Agency」例 8-2(text/11-ch08-chapter-8-conversational-agency.txt:916,搜「run_conversation function manages」)。双层 while 结构与「tool_calls 为 None 才跳出」见 :941-954 2 3

  15. 出处:「Chapter 8. Conversational Agency」第 958-967 段(text/11-ch08-chapter-8-conversational-agency.txt:958,搜「sequence diagram illustrating」);「not truly a conversational agent until it's running inside the run_conversation loop」(:965)。

  16. 出处:「Chapter 8. Conversational Agency」第 1037-1052 段(text/11-ch08-chapter-8-conversational-agency.txt:1045,搜「spinner that indicates」);「with a pill button inside of the agent message」(:1051)。 2 3

  17. 出处:「Chapter 8. Conversational Agency」第 1053-1059 段(text/11-ch08-chapter-8-conversational-agency.txt:1055,搜「Tool calls」按钮);「the name of the tool, the arguments presented as a webform, and the results」(:1057)。

  18. 出处:「Chapter 8. Conversational Agency」第 1060-1066 段(text/11-ch08-chapter-8-conversational-agency.txt:1062,搜「modify arguments from the webform」);「the conversation can be regenerated from that point onward」(:1064)。 2

  19. 出处:「Chapter 8. Conversational Agency」第 1071-1076 段(text/11-ch08-chapter-8-conversational-agency.txt:1073,搜「remote chance of being dangerous」)。

  20. 出处:「Chapter 8. Conversational Agency」第 1077-1084 段(text/11-ch08-chapter-8-conversational-agency.txt:1080,搜「see the same artifacts」);「the ability to dismiss an artifact」(:1083)。

  21. 出处:「Chapter 8. Conversational Agency」第 1017-1022 段(text/11-ch08-chapter-8-conversational-agency.txt:1021,搜「behind an API」)。作者列了部署期还要补的活:接口、报错、日志。

  22. 出处:「Chapter 8. Conversational Agency」第 1096-1101 段(text/11-ch08-chapter-8-conversational-agency.txt:1096,搜「corrective influence of a human」)。引向下一章的工作流方案。