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. 用户提问:“纽约天气如何?”(数据进入流水线)。
  2. 工位1:拿到问题,此时还没有历史记录,所以agent_scratchpad是空的。
  3. 工位2:把问题、空的历史记录、工具列表(search:查当前事件)填到模板里,形成指令:“用户问纽约天气,你可以用search工具查,不知道就调用工具,用XML格式告诉我”。
  4. 工位3:“大脑”读了指令,觉得“需要查实时天气,得用search工具”,于是输出<tool>search</tool><tool_input>纽约天气</tool_input>(被“紧箍咒”拦住,只输出到这里)。
  5. 工位4:解析器翻译:“调用search工具,参数是纽约天气”。
  6. 总指挥:收到指令,调用search工具,工具返回“32度”。然后总指挥把“调用过search工具,得到32度”这条历史记录送回工位1,让流水线再走一遍。
  7. 第二次走流水线
    • 工位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度”。
    • 总指挥:收到最终答案,返回给用户,流程结束。

四、关键总结:初学者只需记住3点

  1. XML是“规定格式”:就像写信要写“称呼+正文+落款”,Agent用XML确保“大脑”和“翻译官”能沟通,不用深究XML语法,知道是固定格式就行。
  2. 组件串联靠“|”A | B表示“先执行A,再把A的结果传给B”,整个Agent就是“输入处理 | 提示 | 模型 | 解析器”串起来的流水线。
  3. 总指挥是AgentExecutor:它负责让流水线循环运行(调用工具→看结果→再判断),直到得出最终答案,不用我们手动控制循环。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐