ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

把 LangGraph 的模型通道改到 TaoToken 之后,ReAct 评测能测多模型

2026/9/19 21:27:37 拓冰建站 浏览量
把 LangGraph 的模型通道改到 TaoToken 之后,ReAct 评测能测多模型 把 LangGraph 的模型通道改到 TaoToken 之后ReAct 评测能测多模型把 LangGraph 的 ReAct Agent 从写死ChatOpenAI(modelgpt-4o-mini)改成可切换多模型真正麻烦的不是图结构而是模型通道和 Key 管理。TaoToken官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content在配置里只充当统一模型通道把base_url指向https://taotoken.net/api再换model字段就能在同一套 ReAct 评测代码里跑deepseek-v3、deepseek-r1等模型。下面从原问题、前置准备、可复制配置、验证请求、常见错排查到 CTA 逐步写。一、原问题与场景react_agent.py 里写死 ChatOpenAI(modelgpt-4o-mini)原场景很典型在react_agent.py里手搓了一个 LangGraph ReAct Agent工具包括搜索、计算器、摘要、邮件发送等核心推理节点call_model直接调用llm ChatOpenAI(modelgpt-4o-mini)然后call_model内部用await llm.ainvoke(...)拿模型输出再解析Thought、Action、Action Input必要时把工具调用写入response.tool_calls交给ToolNode执行。图结构没问题问题出在评测阶段今天想对比deepseek-v3明天想测deepseek-r1后天还想把开源蒸馏版本拉进来如果每次都改model...再重新跑一遍评测代码很快会变得不可维护。更关键的是ReAct Agent 对模型能力很敏感。通用模型和推理模型在任务步骤规划、结构化输出、工具调用参数生成上的表现差异明显。要让评测尽量公平必须保证同一套图、同一套 Prompt、同一套工具只切换模型通道。也就是说应该把“模型来自哪里”和“Agent 怎么推理”拆开。ChatOpenAI里的base_url、api_key、model正是拆开的切入点。TaoToken 在这里承担统一模型通道的角色不改 LangGraph 的 ReAct 逻辑只让同一个llm对象可替换。二、TaoToken 前置在官网创建 Key 并确认 API 地址先打开官网创建 Keyhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content创建完成后你会拿到类似YOUR_API_KEY的 Key。本文所有示例都用YOUR_API_KEY占位实际运行时替换成自己的 Key。API 地址使用https://taotoken.net/api注意这里不加 UTM也不要写成首页地址。LangGraph 侧仍然使用ChatOpenAI只是把base_url从 OpenAI 默认地址改为 TaoToken 的 API 地址把api_key改为 TaoToken 签发的 Key把model改为deepseek-v3或deepseek-r1。这样同一套 ReAct 代码不需要为了模型切换而改图结构。建议把 Key 放进环境变量不要硬编码到react_agent.py# .env TAOTOKEN_API_KEYYOUR_API_KEY TAOTOKEN_BASE_URLhttps://taotoken.net/api REACT_MODELdeepseek-v3如果你的项目没有自动加载.env可以用python-dotenv或者在 shell 里export。这一步做完后面就只剩配置和验证。三、可复制配置在 .env 与 react_agent_taotoken.py 中接入 TaoToken先安装依赖pip install -U langchain-openai langgraph langchain-core python-dotenv openai新建或修改react_agent_taotoken.py先写一个build_llm工厂函数。原代码里写死的llm ChatOpenAI(modelgpt-4o-mini)替换成按模型 ID 构建import os from langchain_openai import ChatOpenAI def build_llm(model_id: str | None None) - ChatOpenAI: return ChatOpenAI( modelmodel_id or os.getenv(REACT_MODEL, deepseek-v3), api_keyos.getenv(TAOTOKEN_API_KEY, YOUR_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), temperature0, timeout180, max_retries2, )原代码中# 旧llm ChatOpenAI(modelgpt-4o-mini) llm build_llm(deepseek-v3)评测时直接llm build_llm(deepseek-r1)接下来把 ReAct 图的创建封装成create_react_graph(llm)这样同一个图工厂可以接收不同模型。下面给出一个可运行的核心结构保留你原来的REACT_PROMPT和工具定义即可import json from dataclasses import dataclass, field from typing import Annotated, Dict, List, Literal, Sequence from langchain_core.messages import AIMessage, AnyMessage, HumanMessage, SystemMessage from langchain_core.runnables import RunnableConfig from langchain_core.tools import tool from langgraph.graph import StateGraph from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode REACT_PROMPT 你是一个 ReAct Agent。 你可以使用以下工具 {tools_desc} 可用工具名{tool_names} 需要调用工具时请严格输出 Thought: 你的推理 Action: 工具名 Action Input: {{参数名: 参数值}} 可以结束时输出 Thought: 我已经获得足够信息。 Answer: 最终答案 一次只输出一个 ActionAction Input 必须是合法 JSON。 tool def network_search(query: str) - str: 搜索关键词并返回模拟结果。 return f模拟搜索结果{query} tool def calculator(expression: str) - str: 计算简单数学表达式。 return 模拟计算结果 tool def send_email(recipient: str, subject: str, body: str) - str: 发送邮件。 return f邮件已发送给 {recipient} TOOLS [network_search, calculator, send_email] def create_react_graph(llm: ChatOpenAI): dataclass class InputState: messages: Annotated[Sequence[AnyMessage], add_messages] field(default_factorylist) dataclass class State(InputState): is_last_step: bool field(defaultFalse) tools_desc \n.join([f- {t.name}: {t.description} for t in TOOLS]) tool_names [t.name for t in TOOLS] async def call_model(state: State, config: RunnableConfig) - Dict[str, List[AIMessage]]: system_prompt REACT_PROMPT.format( tools_desctools_desc, tool_namestool_names, ) response await llm.ainvoke( [SystemMessage(contentsystem_prompt)] list(state.messages) ) content response.content or if Action: in content and Action Input: in content: action_lines [ line for line in content.split(\n) if line.startswith(Action:) or line.startswith(Action Input:) ] if len(action_lines) 2: tool_name action_lines[0].replace(Action:, ).strip() tool_input action_lines[1].replace(Action Input:, ).strip() try: args json.loads(tool_input) except json.JSONDecodeError: args {} response.tool_calls [{ id: call_1, type: function, name: tool_name, args: args, }] return {messages: [response]} def route_model_output(state: State) - Literal[__end__, tools]: last_message state.messages[-1] if not isinstance(last_message, AIMessage): raise ValueError(fExpected AIMessage, got {type(last_message).__name__}) if not getattr(last_message, tool_calls, None): return __end__ return tools builder StateGraph(State, inputInputState) builder.add_node(call_model, call_model) builder.add_node(tools, ToolNode(TOOLS)) builder.add_edge(__start__, call_model) builder.add_conditional_edges(call_model, route_model_output) builder.add_edge(tools, call_model) return builder.compile()然后写多模型评测入口import asyncio from langchain_core.messages import HumanMessage async def run_eval(): tasks [ 搜索 DeepSeek-R1 在 Agent 评测中的常见问题, 计算 12*8并把结果发送到 testexample.com, ] for model_id in [deepseek-v3, deepseek-r1]: llm build_llm(model_id) graph create_react_graph(llm) for task in tasks: try: result await graph.ainvoke({ messages: [HumanMessage(contenttask)] }) print(model_id, OK, result[messages][-1].content[:120]) except Exception as exc: print(model_id, ERROR, repr(exc)) if __name__ __main__: asyncio.run(run_eval())这段代码的重点不是工具逻辑而是build_llm(model_id)和create_react_graph(llm)的组合。只要模型 ID 变整个 ReAct 图就换模型通道但 Prompt、工具、路由逻辑不变。这样你才能在同一套评测框架里比较deepseek-v3、deepseek-r1以及其他模型。四、验证请求与成功结果先测 chat/completions 再跑 ReAct 图在跑 LangGraph 之前先用一个最小请求验证 TaoToken 的 Key 和 API 地址是否可用。用 Python OpenAI SDK 更接近ChatOpenAI底层行为import os from openai import OpenAI client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY, YOUR_API_KEY), base_urlhttps://taotoken.net/api, ) resp client.chat.completions.create( modeldeepseek-v3, messages[ {role: user, content: 只回复taotoken-ok} ], temperature0, ) print(resp.choices[0].message.content)也可以直接用 curlcurl -sS https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: deepseek-v3, messages: [{role: user, content: 只回复 taotoken-ok}], temperature: 0 }成功时你会看到类似结构{ choices: [ { message: { role: assistant, content: taotoken-ok } } ] }只要这个请求通了就说明base_url、api_key、model三件套至少有一组可用。然后再跑react_agent_taotoken.py。成功标志有几个deepseek-v3和deepseek-r1都能被build_llm创建不再出现 401 或 404。graph.ainvoke后result[messages][-1]是最终AIMessage内容包含Answer或最终答复。如果任务需要工具日志中能看到Action、Action Input并且ToolNode被调用。同一套任务在换model_id后可以继续复用不需要改create_react_graph内部代码。到这里ReAct 评测的多模型通道基本就打通了。接下来是准确率对比、推理过程对比、工具调用成功率对比那属于评测设计不是接入问题。五、本篇常见错排查401、404、模型名与 ToolNode 报错第一类401 或 403。通常是 Key 不对、Key 没放进环境变量、或者请求头格式不对。检查.env是否真的被加载检查api_keyos.getenv(TAOTOKEN_API_KEY)是否拿到了值。如果代码里还写着YOUR_API_KEY那请求必然失败。curl 时检查Authorization: Bearer $TAOTOKEN_API_KEY不要漏掉Bearer。第二类404。常见原因是base_url写错比如写成首页地址、写成https://taotoken.net/api/v1但 SDK 又自动拼了一层或者末尾多写斜杠导致路径重复。LangChain 的ChatOpenAI推荐用base_url不要和旧参数openai_api_base混用。如果出现/chat/completions路径异常先回到最小 OpenAI SDK 请求确认base_urlhttps://taotoken.net/api能通再放回 LangGraph。第三类模型名不存在。deepseek-r1、deepseek-v3只是本文示例实际模型 ID 以控制台或接入文档为准。注意大小写、空格、连字符。不要写DeepSeek-R1和deepseek_r1混用除非文档明确支持。模型名错误时通常返回 model not found 或 invalid model。第四类LangChain 参数报 unexpected keyword。新版langchain-openai支持api_key和base_url旧版可能要求openai_api_key和openai_api_base。如果你本地版本较旧要么升级pip install -U langchain-openai要么临时改成旧参数名。不要同时写两套避免覆盖。第五类ToolNode 报找不到工具。通常是因为 Prompt 里的tool_names和ToolNode(TOOLS)不一致或者模型输出的Action带了多余字符比如反引号、空格、冒号。解析时要先strip()再和已知工具名比对。如果模型输出多个Action当前解析逻辑可能只取前两个行容易错配。ReAct Prompt 里要强调一次只输出一个 Action。第六类ReAct 图不结束或反复调用工具。检查route_model_output是否只在tool_calls为空时返回__end__。如果模型输出了 Answer但还带了一个 Action就会被继续路由到tools。另外检查最大迭代步数避免异常情况下无限循环。第七类DeepSeek-R1 响应慢导致超时。推理模型本身思考时间长timeout180或更大是合理的max_retries2也可以减少偶发网络失败。如果并发评测多个模型建议串行或加并发限制避免触发限流。还要检查本机HTTP_PROXY、HTTPS_PROXY是否指向了不可用的代理错误的代理设置会让请求直接失败。第八类结构化输出解析失败。R1 可能输出 Markdown 代码块或者在 JSON 前后加说明文字。解析Action Input时不要直接json.loads整段内容先提取花括号部分再做容错。如果解析失败宁可让 Agent 重新推理一次也不要用空参数去调用工具。六、语义一致 CTAAPI Keys、接入文档与 Coding Plan如果你正在把 LangGraph ReAct Agent 接到 TaoToken建议先把 Key 管理和接入文档两个入口收好。创建和管理 Key 走这里https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keysbase_url、模型 ID、OpenAI 兼容参数和常见报错说明优先看接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc如果你只是想先验证deepseek-v3或deepseek-r1的返回不想立刻写代码可以去模型对话入口发一条最小请求https://taotoken.net/console/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat如果你长期要做 Agent 评测、编码助手接入、多模型对比Coding Plan 入口在这里https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_planTaoToken 在这个场景里只做统一模型通道真正决定 ReAct 评测结果的仍然是 Prompt、工具设计、解析容错和评测任务本身。把通道配置一次理顺后面再换deepseek-v3、deepseek-r1或其他模型时只需要改一个model_id。