用 LangChain 表达式语言构建 Agent (二)
·
https://python.langchain.com.cn/docs/expression_language/cookbook/agent
一、先解决你的第一个困惑:XML到底是啥?为啥要用它?
你可以把XML理解成一种“固定格式的对话语言”——就像老师给学生制定的“答题模板”,要求必须按这个格式写,不然老师看不懂。
具体到文档里:
- Agent的“大脑”(模型)输出内容时,必须用XML的标签包裹(比如
<tool>工具名</tool>表示要调用工具,<final_answer>答案</final_answer>表示要输出最终结果)。 - 这样做的目的:让“翻译官”(输出解析器)能准确看懂模型想干啥——如果模型随便写“我要查天气”,解析器不知道这是要调用工具;但写成
<tool>search</tool>,解析器一眼就知道“哦,要调用search工具”。
关于prompt = hub.pull("hwchase17/xml-agent-convo"):
这句话的意思是“从网上下载一个现成的‘答题模板’”,这个模板里已经写好了规则:
- “如果你不知道答案,就用
<tool>标签告诉我要调用哪个工具” - “如果你知道答案了,就用
<final_answer>标签告诉我最终结果”
你完全不用自己写这个模板,就像做题时老师直接给了答题卡,你按格子填就行。
二、再解决第二个困惑:所有函数和组件是怎么串起来的?用“工厂流水线”打比方
把整个Agent比作一个“自动答题工厂”,每个组件都是一个工位,数据(用户的问题)从第一个工位流到最后一个,最终产出答案。我们按数据的流动顺序讲,对应文档里的代码:
工位1:原材料处理(输入处理)
作用:接收用户的问题,同时整理“之前做过的事”(比如之前调用过什么工具,得到了什么结果)。
对应代码:
{
"input": lambda x: x["input"], # 接收用户的问题(比如“纽约天气如何?”)
"agent_scratchpad": lambda x: convert_intermediate_steps(x["intermediate_steps"]) # 整理历史记录
}
- 你可以把
lambda x: x["input"]理解成“把用户输入的问题原封不动拿过来”。 convert_intermediate_steps函数:把“之前调用过search工具,得到32度”这种历史,转换成XML格式的字符串(比如<tool>search</tool><observation>32度</observation>),方便后面的工位处理。
工位2:填写答题卡(提示模板)
作用:把“答题模板”(从hub拉取的prompt)和“原材料”(用户问题+历史记录)结合起来,形成给“大脑”的完整指令。
对应代码:
| prompt.partial(tools=convert_tools(tool_list))
convert_tools(tool_list):把工具列表(比如只有search工具)转换成字符串“search:用来查当前事件”,告诉“大脑”有什么工具可用。partial:把工具信息提前填到模板里,就像答题卡里先印好“可用工具列表”。
工位3:大脑思考(模型调用)
作用:根据“答题卡”的指令,判断“要不要调用工具”或“直接写答案”,并按XML格式输出想法。
对应代码:
| model.bind(stop=["</tool_input>", "</final_answer>"])
model就是“大脑”(文档里的claude-2模型),它会读工位2传来的完整指令。bind(stop=...):给“大脑”戴个“紧箍咒”,让它输出到<tool_input>或<final_answer>结束,避免说废话(比如只写到</tool>就停,后面不瞎写)。
工位4:翻译官(输出解析器)
作用:把“大脑”用XML写的想法翻译成机器能执行的指令。
对应代码:
| XMLAgentOutputParser()
- 如果“大脑”输出
<tool>search</tool><tool_input>纽约天气</tool_input>,解析器就翻译出“调用search工具,参数是纽约天气”。 - 如果“大脑”输出
<final_answer>纽约32度</final_answer>,解析器就翻译出“最终答案是纽约32度”。
工位5:总指挥(AgentExecutor)
作用:管理整个流水线,决定下一步该干啥:
- 如果解析器说“要调用工具”,就去调用对应的工具(比如调用search工具查天气),然后把工具返回的结果(32度)送回工位1,让流水线再走一遍(看看是否需要再调用工具)。
- 如果解析器说“要输出最终答案”,就把答案返回给用户,流水线结束。
对应代码:
agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True)
- 这里的
agent就是前面4个工位串起来的流水线,tool_list是可用的工具(比如search)。 verbose=True:让总指挥把整个过程打印出来(就像工厂的监控录像),方便我们看“大脑”是怎么思考的。
三、用“查纽约天气”完整走一遍流程(对应文档的执行过程)
- 用户提问:“纽约天气如何?”(数据进入流水线)。
- 工位1:拿到问题,此时还没有历史记录,所以
agent_scratchpad是空的。 - 工位2:把问题、空的历史记录、工具列表(search:查当前事件)填到模板里,形成指令:“用户问纽约天气,你可以用search工具查,不知道就调用工具,用XML格式告诉我”。
- 工位3:“大脑”读了指令,觉得“需要查实时天气,得用search工具”,于是输出
<tool>search</tool><tool_input>纽约天气</tool_input>(被“紧箍咒”拦住,只输出到这里)。 - 工位4:解析器翻译:“调用search工具,参数是纽约天气”。
- 总指挥:收到指令,调用search工具,工具返回“32度”。然后总指挥把“调用过search工具,得到32度”这条历史记录送回工位1,让流水线再走一遍。
- 第二次走流水线:
- 工位1:这次的
agent_scratchpad里有历史记录(转换成XML字符串:<tool>search</tool><tool_input>纽约天气</tool_input><observation>32度</observation>)。 - 工位2:模板里多了这条历史记录,指令变成:“用户问纽约天气,你之前调用search得到32度,现在够不够回答?够的话用
<final_answer>告诉我”。 - 工位3:“大脑”觉得够了,输出
<final_answer>纽约天气是32度</final_answer>。 - 工位4:解析器翻译:“最终答案是纽约天气是32度”。
- 总指挥:收到最终答案,返回给用户,流程结束。
- 工位1:这次的
四、关键总结:初学者只需记住3点
- XML是“规定格式”:就像写信要写“称呼+正文+落款”,Agent用XML确保“大脑”和“翻译官”能沟通,不用深究XML语法,知道是固定格式就行。
- 组件串联靠“|”:
A | B表示“先执行A,再把A的结果传给B”,整个Agent就是“输入处理 | 提示 | 模型 | 解析器”串起来的流水线。 - 总指挥是AgentExecutor:它负责让流水线循环运行(调用工具→看结果→再判断),直到得出最终答案,不用我们手动控制循环。
更多推荐

所有评论(0)