ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LangChain 1.0 速通指南(三):create_agent API 高阶功能与 TaoToken 统一接入实践

LangChain 1.0 速通指南(三):create_agent API 高阶功能与 TaoToken 统一接入实践 1. 为什么 create_agent 高阶玩法绕不开统一接入LangChain 1.0 的create_agent能做什么简单说它把「模型 工具 记忆 中间件」打包成一个可复用的智能体运行时适合想用 Python 快速搭 Agent 的开发者。但真正落到项目里你会发现一个很现实的问题工具越接越多模型越换越勤API Key 和 endpoint 散落在十几个文件里改一次配置要翻半天。我试过在一个多工具 Agent 里同时接 DeepSeek、Qwen 和本地 MCP 服务结果光是base_url和api_key就写了四份调试时根本分不清哪次请求走了哪个通道。后来把模型调用统一收敛到 TaoToken 通道所有init_chat_model只认一组环境变量切换模型只改model字段其余不动。这篇就围绕这个思路把create_agent的高阶功能——多工具编排、结构化输出、中间件扩展——和统一接入实践串起来讲。适合谁看已经跑通过create_agent基础调用、想进一步做多工具 Agent 的开发者或者手上有一堆模型 Key、想统一管理调用入口的团队。下面所有代码都可以直接复制改掉 Key 就能跑。先明确一个概念TaoToken 在这里扮演的是「统一模型调用通道」的角色。你不需要在每个 Agent 里分别配置不同厂商的 endpoint而是把base_url指向同一个地址用同一个 Key 调用不同模型。这样做的好处是配置集中、切换成本低、日志好追踪。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。接下来我会按「环境准备 → 可复制配置 → 完整验证 → 报错排查」的顺序展开每个环节都给出具体命令和代码。如果你之前只写过单工具 Agent这篇能帮你把工具编排、结构化输出和中间件这三块补齐。2. TaoToken 前置准备与 LangChain 环境搭建在写 Agent 代码之前先把两件事做完一是拿到 TaoToken 的 API Key二是把 LangChain 1.0 的运行环境装好。这两步都不复杂但顺序别搞反否则后面调试时会分不清是环境问题还是 Key 问题。2.1 获取 API Key 与确认通道地址打开 TaoToken 控制台创建一个 API Key。创建时建议按项目命名比如langchain-agent-dev方便后续区分。Key 只在创建时完整显示一次复制后先存到安全的地方。拿到 Key 之后确认两个地址官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 调用地址https://taotoken.net/api注意 API 地址后面不加 UTM 参数这是给程序调用的加了反而可能影响请求。模型对话调试页面在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 你可以在那里先手动发一条消息确认 Key 能用、模型能通再去写代码。这一步能省掉很多「代码没问题但 Key 没生效」的排查时间。2.2 安装 LangChain 1.0 与依赖建议用 conda 或 venv 建一个独立环境避免和系统里的旧版 LangChain 冲突。LangChain 1.0 的包结构和 0.x 有差异混装容易出问题。conda create -n langchain10 python3.11 -y conda activate langchain10 pip install langchain1.0.0 langchain-core langgraph langchain-mcp-adapters pydantic如果你要用 MCP 工具langchain-mcp-adapters必须装。这个包负责把 MCP 服务端的工具描述转换成 LangChain 能识别的 tool 对象。装完后可以用pip show langchain确认版本号是 1.0.x。2.3 设置环境变量把 Key 和地址写进环境变量代码里只读变量不硬编码。这样换 Key 或换通道时不用改代码。export TAOTOKEN_API_KEY你的 TaoToken API Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows 下用set或写进.env文件配合python-dotenv。我习惯在项目根目录放一个.env然后在代码开头load_dotenv()这样本地开发和部署都能复用同一套配置。环境变量设好后先跑一个最小验证用init_chat_model连 TaoToken发一条消息看能不能返回。这一步过了再往上叠工具和中间件。import os from langchain.chat_models import init_chat_model model init_chat_model( modeldeepseek-chat, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) resp model.invoke(用一句话说明什么是智能体) print(resp.content)如果这行能打印出内容说明通道通了。如果报 401先检查 Key 有没有复制完整如果报连接错误检查base_url是不是写成了带 UTM 的官网地址。这个最小验证很重要后面所有高阶功能都建立在它之上。3. 可复制配置多工具编排与结构化输出这一节是全文的核心给出可以直接复制的 Agent 配置片段。我会分三块讲多工具编排、结构化输出、中间件扩展。每块都配完整代码和参数说明。3.1 多工具编排的配置结构create_agent的tools参数接受一个工具列表列表里可以混装本地tool函数和 MCP 工具。下面这个配置片段把两种工具放在一起模型调用统一走 TaoToken。import os import asyncio from langchain.chat_models import init_chat_model from langchain.agents import create_agent from langchain.tools import tool from langchain_mcp_adapters.client import MultiServerMCPClient model init_chat_model( modeldeepseek-chat, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) tool def get_weather(loc: str) - str: 根据地点返回天气情况 return f{loc} 天气晴气温 23 度 mcp_client MultiServerMCPClient( { amap-maps: { command: npx, args: [-y, amap/amap-maps-mcp-server], env: {AMAP_MAPS_API_KEY: 你的高德 Key}, transport: stdio, } } ) async def build_agent(): mcp_tools await mcp_client.get_tools() all_tools [get_weather] mcp_tools agent create_agent( modelmodel, toolsall_tools, system_prompt你是出行助手能查天气和规划路线, ) result await agent.ainvoke( {messages: {role: user, content: 北京今天天气怎么样}} ) for msg in result[messages]: msg.pretty_print() asyncio.run(build_agent())这里的关键点是model只配置了一次指向 TaoToken工具列表里本地函数和 MCP 工具共存模型会根据system_prompt和工具描述自动选择调用哪个。MCP 工具通过npx在本地启动服务端进程LangChain 作为客户端通过 stdio 通信。如果你要接多个 MCP 服务在MultiServerMCPClient的字典里加键值对即可每个服务独立配置command、args和env。注意transport目前常用stdio远程 MCP 服务可能用sse按服务端文档填。3.2 结构化输出的 JSON 配置结构化输出让 Agent 返回固定格式的数据方便后续程序处理。create_agent通过response_format参数支持这个能力。下面用 Pydantic 定义输出结构再用AutoStrategy包装。from pydantic import BaseModel from langchain.agents.structured_output import AutoStrategy class RouteResult(BaseModel): loc1: str loc2: str distance: float agent create_agent( modelmodel, toolsall_tools, system_prompt你是出行助手返回两地距离, response_formatAutoStrategy(RouteResult), ) result await agent.ainvoke( {messages: {role: user, content: 北京圆明园到西北旺地铁站多远}} ) print(result[structured_response])AutoStrategy会自动检测模型能力优先用提供商原生结构化输出不支持时回退到工具调用策略。如果你明确知道模型支持哪种也可以直接写ToolStrategy(RouteResult)或ProviderStrategy(RouteResult)。实测下来AutoStrategy兼容性最好推荐默认用它。输出结果会是一个RouteResult实例字段类型和你在类里定义的一致。如果模型返回的距离是字符串Pydantic 会尝试转换转换失败会报验证错误这时检查一下response_format有没有正确传入。3.3 中间件扩展的 settings 片段中间件是create_agent最灵活的部分可以在模型调用前后、工具调用前后插入自定义逻辑。下面这个配置片段实现「按用户级别动态选模型」的中间件。from dataclasses import dataclass from typing import Callable from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse dataclass class Context: user_level: str expert expert_model init_chat_model( modeldeepseek-reasoner, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) basic_model init_chat_model( modelQwen/Qwen3-8B, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) class LevelBasedMiddleware(AgentMiddleware): def wrap_model_call( self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse], ) - ModelResponse: level request.runtime.context.user_level request.model expert_model if level expert else basic_model return handler(request) agent create_agent( modelbasic_model, tools[], middleware[LevelBasedMiddleware()], context_schemaContext, )注意context_schemaContext这行它让 Agent 知道运行时上下文的结构。调用时通过contextContext(user_levelexpert)传入。中间件里用request.runtime.context.user_level读取。这个模式的好处是模型选择逻辑和业务逻辑解耦你可以在中间件里加日志、加限流、加缓存而不影响 Agent 主体代码。wrap_model_call拦截每次模型调用wrap_tool_call拦截每次工具调用按需重写即可。4. 验证请求一次完整的高阶 Agent 调用配置写完后跑一次完整调用确认多工具、结构化输出、中间件三块都能正常工作。下面给出完整脚本和预期输出。4.1 完整调用脚本import os import asyncio from langchain.chat_models import init_chat_model from langchain.agents import create_agent from langchain.tools import tool from langchain_mcp_adapters.client import MultiServerMCPClient from pydantic import BaseModel from langchain.agents.structured_output import AutoStrategy model init_chat_model( modeldeepseek-chat, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) tool def get_weather(loc: str) - str: 根据地点返回天气情况 return f{loc} 天气晴气温 23 度 class RouteResult(BaseModel): loc1: str loc2: str distance: float mcp_client MultiServerMCPClient( { amap-maps: { command: npx, args: [-y, amap/amap-maps-mcp-server], env: {AMAP_MAPS_API_KEY: 你的高德 Key}, transport: stdio, } } ) async def main(): mcp_tools await mcp_client.get_tools() print(f加载工具数{len(mcp_tools)}) agent create_agent( modelmodel, tools[get_weather] mcp_tools, system_prompt你是出行助手能查天气和两地距离, response_formatAutoStrategy(RouteResult), ) result await agent.ainvoke( {messages: {role: user, content: 北京圆明园到西北旺地铁站多远}} ) print(结构化输出, result[structured_response]) for msg in result[messages]: msg.pretty_print() asyncio.run(main())4.2 预期输出与结果解读运行后你会看到类似这样的输出加载工具数5 结构化输出 loc1北京圆明园 loc2北京西北旺地铁站 distance8.7消息流里会显示模型先调用maps_geo获取两个地点的经纬度再调用maps_distance计算距离最后按RouteResult格式返回。这说明三件事都成功了MCP 工具被正确加载和调用、结构化输出生效、模型调用走了 TaoToken 通道。如果structured_response是None检查response_format有没有传对以及模型是否支持结构化输出。如果工具没被调用检查system_prompt有没有说清楚工具用途或者工具描述是否足够明确。4.3 验证中间件切换模型再跑一次中间件验证确认不同user_level会走不同模型async def test_middleware(): agent create_agent( modelbasic_model, tools[], middleware[LevelBasedMiddleware()], context_schemaContext, ) for level in [expert, student]: result await agent.ainvoke( {messages: {role: user, content: 你好请问你是}}, contextContext(user_levellevel), ) print(f{level} - {result[messages][-1].content[:50]}) asyncio.run(test_middleware())专家级别会走deepseek-reasoner普通级别走Qwen3-8B。输出内容风格会有差异但都能正常返回。这一步验证的是中间件的wrap_model_call有没有被触发。5. 本篇常见报错排查这一节列出实际调试中最容易遇到的几个报错给出原因和解决办法。每个报错都对应真实场景不是编的。5.1 401 Unauthorized报错信息类似openai.AuthenticationError: Error code: 401 - {error: {message: Invalid API key}}原因通常是 Key 没设对。检查三处环境变量TAOTOKEN_API_KEY有没有导出成功echo $TAOTOKEN_API_KEY看一下代码里读的是不是这个变量名Key 有没有多余空格或换行。如果用的是.env文件确认load_dotenv()在init_chat_model之前调用。还有一种情况是base_url写错了。如果你把官网地址https://taotoken.net/?utm_source...填进了base_url请求会打到网页而不是 API也会报 401 或 404。正确地址是https://taotoken.net/api不带任何查询参数。5.2 local proxy failed 或连接超时报错信息类似httpx.ConnectError: [Errno 111] Connection refused或者openai.APIConnectionError: Connection error.这类报错一般是网络层问题。先确认本机能不能访问https://taotoken.net/api可以用curl测一下curl -I https://taotoken.net/api如果返回 200 或 401说明网络通问题在代码配置如果超时检查本机网络设置。注意不要使用任何非官方的网络工具直接用系统默认网络即可。另一个常见原因是base_url末尾多了斜杠。https://taotoken.net/api和https://taotoken.net/api/在某些客户端里行为不同建议统一不带末尾斜杠。5.3 reading choices 字段报错报错信息类似KeyError: choices或者openai.APIError: Expected choices field in response这个报错通常出现在模型返回格式和客户端预期不一致时。检查model字段填的模型名是否在 TaoToken 支持列表里。如果模型名拼错服务端可能返回错误结构客户端解析时就找不到choices。解决办法去模型对话页面确认可用模型名复制准确名称填进init_chat_model的model参数。另外确认model_provider参数有没有设对用 OpenAI 兼容接口时一般设openai或不设。5.4 OAuth 或鉴权头冲突报错信息类似openai.BadRequestError: Invalid Authorization header如果你同时设了api_key参数和环境变量或者用了多个鉴权来源可能会冲突。init_chat_model里显式传了api_key就以参数为准没传才读环境变量。建议只保留一种方式要么全用环境变量要么全用参数。还有一种情况是default_headers里手动加了Authorization和 SDK 自动加的重了。检查代码里有没有手动设 header有的话去掉。5.5 MCP 工具加载失败报错信息类似FileNotFoundError: [Errno 2] No such file or directory: npx或者MCPError: Failed to connect to servernpx找不到说明 Node.js 没装或没在 PATH 里。装一个 Node.js LTS 版本重启终端再试。MCP 服务连接失败则检查command和args是否和官方文档一致env里的 Key 有没有填对。如果 MCP 工具加载数量为 0但没报错检查transport字段。本地 stdio 服务用stdio远程服务用sse填错会静默失败。6. 统一接入后的下一步把模型调用收敛到 TaoToken 之后create_agent的高阶功能用起来会顺很多。多工具编排时不用再为每个工具配不同厂商的 Key结构化输出和中间件也只需要维护一套模型配置。我自己的项目里从单工具 Agent 扩展到五工具加中间件的复杂 Agent配置改动量不到十行主要就是加工具和加中间件类。如果你要长期跑编码类 Agent可以看看 Coding Plan 的接入方式地址在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。如果只是验证模型效果模型对话页面更轻量https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。需要管理多个 Key 或查看调用量控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。下一篇会讲怎么把create_agent和 LangGraph 的持久化检查点结合做跨会话的长记忆 Agent。如果你在配置过程中遇到上面没覆盖的报错先去接入文档里搜一下错误关键词大部分常见问题都有对应说明。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进