
1. 为什么内置 Agent 类型选型会卡住你LangChain 里的 Agent 不是「一个东西」而是一族按输出协议区分的执行器。XMLAgent、JSONAgent、AgentExecutor 这三个名字经常被混着用但它们在代码里承担的角色完全不同XMLAgent 和 JSONAgent 是「怎么把 LLM 的输出解析成工具调用」的协议层AgentExecutor 是「拿到工具调用后怎么循环执行、怎么把观察结果塞回上下文」的运行时层。我见过太多项目卡在第一步模型明明返回了工具名AgentExecutor 却报Could not parse LLM output。原因往往不是模型不行而是 prompt 里的格式约定和 Agent 的解析器对不上。XMLAgent 期望toolsearch/tooltool_input.../tool_inputJSONAgent 期望一个 markdown 代码块包着的{action: ..., action_input: ...}你把 JSON 格式的 prompt 喂给 XMLAgent解析器当然找不到标签。这篇面向需要快速搭建多工具调用链的开发者给出三类 Agent 的可复制初始化配置、工具注册示例以及一次完整的端到端调用验证。适合已经跑通过一次create_react_agent、想搞清楚「换 Agent 到底换的是什么」的人。读完之后你应该能按场景选型模型擅长 XML 就用 XMLAgent需要严格 JSON 结构就用 JSONAgent而 AgentExecutor 是所有类型共用的执行外壳参数调优直接决定你的链路稳不稳。先说结论性的选型逻辑后面再展开代码。XMLAgent 适合 Claude 系列这类对标签结构敏感的模型输出天然带tool标签解析容错高JSONAgent 适合 GPT 系列和大部分国产模型因为它们在指令遵循上对 JSON schema 更熟AgentExecutor 不挑模型它只负责max_iterations、handle_parsing_errors、return_intermediate_steps这些运行时行为。三者不是三选一而是「协议 运行时」的组合。2. TaoToken 前置把模型接入和 Key 管理先理顺在写 Agent 之前得先有一个稳定的模型调用入口。LangChain 的ChatOpenAI默认打 OpenAI 官方地址但你可以通过base_url指向兼容 OpenAI 协议的服务。TaoToken 提供的就是这样一个兼容入口官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 路径不带 UTM 参数。接入方式很直接在环境变量里配好 Key 和 Base URLLangChain 侧只改base_url一个参数。我习惯把配置写进.env避免 Key 硬编码进代码。# .env OPENAI_API_KEYsk-你的TaoToken密钥 OPENAI_BASE_URLhttps://taotoken.net/api然后在 Python 里这样初始化模型import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() llm ChatOpenAI( modelgpt-4o-mini, base_urlos.getenv(OPENAI_BASE_URL), api_keyos.getenv(OPENAI_API_KEY), temperature0, )temperature0对 Agent 场景很重要。Agent 需要模型稳定输出工具调用格式温度高了它会开始「自由发挥」把tool标签写成自然语言描述解析器直接崩。这一点在 XMLAgent 上尤其明显。Key 的获取和模型列表可以在控制台里看地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果你要长期跑编码类 AgentCoding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 里有针对性的额度方案比按量调用更适合高频迭代。API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 建议给 Agent 项目单独建一个 Key方便按项目排查用量。这里有个容易踩的坑base_url到底要不要带/v1。TaoToken 的 API 根路径是https://taotoken.net/apiLangChain 的ChatOpenAI会自动在末尾拼/chat/completions所以你不要手动加/v1否则会变成/api/v1/chat/completions导致 404。实测下来直接写https://taotoken.net/api就能通。模型选型上XMLAgent 建议配 Claude 系列对标签结构天然友好JSONAgent 配 GPT-4o-mini 或同级别模型即可。如果你想先验证模型连通性可以用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 发一条消息确认 Key 有效再去写 Agent 代码能省掉一半排障时间。3. 可复制配置XMLAgent、JSONAgent 与 AgentExecutor 三件套这一节给出完整可跑的配置。先定义工具再分别建 XMLAgent 和 JSONAgent最后用 AgentExecutor 包起来。工具用两个最简单的一个加法计算器一个模拟天气查询避免依赖外部搜索 API 导致排障复杂化。先装依赖pip install langchain langchain-openai langchain-community python-dotenv工具定义from langchain_core.tools import tool tool def add(a: int, b: int) - int: 计算两个整数之和。输入必须是两个整数。 return a b tool def get_weather(city: str) - str: 查询指定城市的天气。输入是城市名。 fake {beijing: 晴25度, shanghai: 多云28度} return fake.get(city.lower(), f{city} 暂无数据) tools [add, get_weather]XMLAgent 的 prompt 必须包含{tools}、{tool_names}、{input}、{agent_scratchpad}这几个变量缺一个都会在运行时抛 KeyError。下面这份是我实测能稳定解析的版本from langchain_core.prompts import ChatPromptTemplate from langchain.agents import create_xml_agent xml_prompt ChatPromptTemplate.from_messages([ (human, You are a helpful assistant. Answer the question using tools when needed. You have access to these tools: {tools} Use this format: tooltool_name/tooltool_inputinput here/tool_input Then you will receive observationresult/observation. When done, respond with final_answeryour answer/final_answer. Available tool names: {tool_names} Question: {input} {agent_scratchpad}), ]) xml_agent create_xml_agent(llmllm, toolstools, promptxml_prompt)JSONAgent 的 prompt 要求模型输出 markdown 代码块包裹的 JSON解析器会去抓 json 块。注意{tool_names}在 JSONAgent 里通常写在 action 的约束说明中from langchain.agents import create_json_agent json_prompt ChatPromptTemplate.from_messages([ (system, You are an assistant that calls tools by emitting JSON.), (human, TOOLS: {tools} Respond with a markdown json code block in one of two formats. To call a tool: json {{action: tool_name, action_input: input}}To finish:{{action: Final Answer, action_input: your answer}}action must be one of: {tool_names}Question: {input} {agent_scratchpad}), ])json_agent create_json_agent(llmllm, toolstools, promptjson_prompt)两个 Agent 建好后统一交给 AgentExecutor。这里的参数是稳定性的关键 python from langchain.agents import AgentExecutor def build_executor(agent): return AgentExecutor( agentagent, toolstools, verboseTrue, max_iterations5, handle_parsing_errorsTrue, return_intermediate_stepsTrue, ) xml_executor build_executor(xml_agent) json_executor build_executor(json_agent)handle_parsing_errorsTrue让解析失败时把错误信息回灌给模型重试而不是直接抛异常终止。max_iterations5防止模型陷入工具调用死循环。return_intermediate_stepsTrue让你能看到每一步的 tool 和 observation排障时非常有用。如果你用 Claude Code 或 Cline 这类工具做本地开发配置里同样要写全三件套Base URL 填https://taotoken.net/apiKey 填你的 TaoToken 密钥Model ID 填具体模型名如claude-3-5-sonnet。Cline 的 MCP 配置里如果引用模型也要保证这三项一致否则会出现local proxy failed或 OAuth 相关报错。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 有各客户端的完整字段说明。4. 验证请求一次完整调用与成功结果对照配置写完必须跑一次端到端验证。用同一个问题分别打 XMLAgent 和 JSONAgent观察中间步骤和最终输出。question {input: 北京天气怎么样顺便算一下 12 加 30 等于多少} print( XMLAgent ) xml_result xml_executor.invoke(question) print(xml_result[output]) print( JSONAgent ) json_result json_executor.invoke(question) print(json_result[output])XMLAgent 的 verbose 输出大致长这样 Entering new AgentExecutor chain... toolget_weather/tooltool_inputbeijing/tool_input observation晴25度/observation tooladd/tooltool_input{a: 12, b: 30}/tool_input observation42/observation final_answer北京今天晴25度12 加 30 等于 42。/final_answer Finished chain.JSONAgent 的输出则是 Entering new AgentExecutor chain... json {action: get_weather, action_input: beijing}晴25度{action: add, action_input: {\a\: 12, \b\: 30}}42{action: Final Answer, action_input: 北京晴25度123042}Finished chain.两个都成功返回了 output 字段。你可以通过 xml_result[intermediate_steps] 拿到每一步的 (AgentAction, observation) 元组用来做日志或前端展示。 验证成功的判断标准有三个第一output 字段非空且语义正确第二intermediate_steps 里能看到至少一次工具调用第三verbose 日志里没有出现 Could not parse LLM output 或 Invalid or incomplete response。三个都满足说明协议层和运行时层都通了。 如果只想快速验证模型本身能不能按格式输出可以先用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 手动发一条带格式要求的 prompt看模型返回是否符合预期再回到代码里调 Agent。这样能把「模型问题」和「Agent 配置问题」分开定位。 ## 5. 本篇常见错排查401、解析失败与 OAuth 报错 排障按报错信息对号入座下面这几个是我实际遇到频率最高的。 **401 Unauthorized**Key 没读到或 Base URL 拼错。先确认 .env 被 load_dotenv() 加载再打印 os.getenv(OPENAI_API_KEY) 看是否为空。如果 Key 正常检查 base_url 是不是误加了 /v1。TaoToken 的地址是 https://taotoken.net/api不要写成 https://taotoken.net/api/v1。401 还有一种情况是 Key 被禁用或额度耗尽去控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 看用量。 **Could not parse LLM output**这是 Agent 场景最典型的错。XMLAgent 报这个通常是模型没输出 tool 标签而是用自然语言说「我将调用 get_weather」。解决办法是把 temperature 降到 0并在 prompt 里加一句「Do not explain, output the tag directly」。JSONAgent 报这个多半是模型输出的 JSON 没被 json 包裹或者 action 值不在 {tool_names} 里。把 handle_parsing_errorsTrue 打开让错误回灌重试能自动救回大部分情况。 **local proxy failed**出现在 Cline、Claude Code 这类客户端里通常是 Base URL 或网络配置问题。确认客户端里填的是 https://taotoken.net/apiKey 和 Model ID 三项齐全。如果客户端有代理设置关掉再试。这个报错和 Agent 代码无关是客户端到服务端的链路问题。 **reading choices of undefined**说明返回体结构不对通常是 Base URL 指向了一个不兼容 OpenAI 协议的端点。检查你的 base_url 是否指向 https://taotoken.net/api以及模型名是否在服务端存在。模型名写错有时不会返回 404而是返回一个空结构LangChain 去读 choices 就报 undefined。 **OAuth 相关报错**在 Claude Code 或 Codex 的 auth.json 里如果同时存在 OAuth token 和 API Key可能冲突。建议只用 API Key 方式把 auth.json 里的 OAuth 字段清掉Base URL 填 https://taotoken.net/apiModel ID 填对应模型。Codex 的 auth.json 三件套是 base_url、api_key、model缺一不可。 **AgentExecutor 无限循环**模型反复调用同一个工具不收敛。把 max_iterations 设成 5 以内并在 prompt 里强调「如果已有足够信息直接输出 final_answer」。return_intermediate_stepsTrue 能帮你看到它到底卡在哪一步。 排障时优先看 verbose 日志的最后一次 LLM 原始输出90% 的解析错误都能从那里看出格式偏差。接入层面的问题可以对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 逐字段核对。 ## 6. 按场景选型与后续接入 选型其实就三条判断。模型是 Claude 系列、对 XML 标签敏感用 XMLAgent解析容错高prompt 里标签写清楚就行。模型是 GPT 系列或国产模型、指令遵循强用 JSONAgent结构严格方便你做二次校验。两者都跑不通时先别急着换 Agent 类型把 temperature 降到 0、把 prompt 里的格式示例补全往往就好了。 AgentExecutor 是共用外壳不管里面是 XML 还是 JSONmax_iterations、handle_parsing_errors、return_intermediate_steps 这三个参数都建议显式设置。默认值在生产环境里不够稳。 如果你要把这套链路接到本地编码工具里Claude Code 的接入配置在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 有完整字段说明Base URL、Key、Model ID 三件套照填即可。长期跑 Agent 任务的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 比按量更适合高频调用。Key 统一在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 管理给每个 Agent 项目分一个 Key出问题时能快速定位是哪个项目打爆了额度。 最后留一个实操建议把 XMLAgent 和 JSONAgent 的 prompt 都存成独立的 .py 或 .txt 文件用 load_prompt 加载而不是硬编码在业务逻辑里。这样换模型、调格式时只改 prompt 文件不用动 Agent 构建代码。我试过在同一个项目里同时保留两套 prompt用环境变量切换 Agent 类型A/B 对比不同模型下的工具调用成功率比拍脑袋选型靠谱得多。