HelloAgentsLLM 多模型切换要改代码?TaoToken 这样填 Base URL
1. SimpleAgent 跑 ReAct 循环先卡在 HelloAgentsLLM 的模型通道上第 7 章动手搭 HelloAgents 框架时HelloAgentsLLM是第一个会让人停下来多看两眼的类它把 OpenAI、Qwen、Claude 这些来源不同的模型调用收拢成同一个接近chat.completions的方法SimpleAgent的 ReAct 循环再往上叠一层。真正上手才会发现麻烦不在推理逻辑而在base_url、api_key、模型 ID 这三样东西——它们经常被写死在代码里或者散落在.env换一条模型通道就得改一次文件、重跑一次脚本。想让 HelloAgentsLLM 的多模型切换只动配置、不动代码先把统一入口准备好打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并创建一把 Key后面 Base URL 一律填https://taotoken.net/api。TaoToken 在这条链路里只负责两件事——给出 Key 和兼容通道地址模型怎么调度、工具怎么被调用仍然是 HelloAgentsLLM 和 SimpleAgent 自己的活。1.1 第 7 章里 HelloAgentsLLM 封装了什么按原文的写法HelloAgentsLLM干的是「抹平差异」这件事上层的 Agent 只认「传一组 messages拿回一段文本」这一个约定底下到底是哪家的模型接口、走的哪个 endpoint全被这个类吃掉。这样SimpleAgent写 ReAct 时就不用关心厂商参数只关心Thought / Action / Observation的循环怎么转。问题也正出在这一层。为了让HelloAgentsLLM能对外只露出一个方法它内部必然要持有三样东西通道地址、鉴权凭证、模型标识。原文倾向于把它们放在环境变量或者构造函数参数里读者照着填一遍能跑但等到你想在同一天里先用便宜的模型跑批量测试、再用强一点的模型跑复杂推理就会发现每次都得回去改这三个值然后重启进程。这不是 HelloAgentsLLM 设计得不好而是「可切换后端」这个目标本身需要有一个稳定的、不随厂商变化的统一地址兜底。厂商域名换一个你的.env就要跟着换一次多模型并行的场景下这份配置文件会越来越难维护。1.2 换模型就要改代码根子在 base_url 写死很多人第一次踩到的具体现象是这样的SimpleAgent.run()一跑报一个连接类错误或者干脆卡住不返回。翻回代码一看base_url那里填的是某家厂商的域名而手头这把 Key 根本不是那家的又或者换模型时只改了LLM_MODEL_ID忘了一起改地址于是请求打到 A 通道、用的是 B 通道的凭证。更隐蔽的一种是把地址写进函数默认参数例如def __init__(self, base_urlhttps://某家域名/v1)。这种情况下你改.env也没用因为默认值优先于环境变量除非你显式传参。排查这类问题时建议先把整个类的取值顺序理一遍是「传参 环境变量 默认值」还是反过来的这决定了你到底该动哪一层。统一地址的价值就在这把「谁提供通道」这件事从代码里摘出去变成一个可以随时替换的字符串。https://taotoken.net/api就是一个这样的固定填法写在.env里模型 ID 随便换地址不动。2. 把 Key 和统一 Base URL 准备好创建与模型 ID 确认2.1 打开官网注册创建一条 API Key这一步对应原文里「申请密钥」那个动作只是把入口换了个地方。打开 TaoToken 完成注册登录进控制台创建一个 API Key复制出来先放到手边。这把 Key 就是后面LLM_API_KEY的值本文所有示例统一写成占位符YOUR_API_KEY你实际填的时候换成自己复制的那串。顺手在同一个入口里看一眼模型广场。Hello-Agents 原文强调 LLM 后端可切换列举了 OpenAI、Qwen、Claude 这几个方向落到具体配置时你需要的是一个真实存在的模型标识而不是网帖里抄来的、带日期后缀的臆造名字。2.2 模型 ID 以模型广场当时列表为准LLM_MODEL_ID这个值建议你直接从模型广场的列表里复制别凭记忆写。原因是模型命名在不同通道下经常带版本后缀、带供应商前缀少一个字符就是model not found。本文示例里统一写成YOUR_MODEL_ID替换成你在列表里看到的那一串即可。如果你原来在别的项目里已经跑通某个模型号也可以把那串照抄过来先用着只要它在当前列表里仍然存在。切换模型时只动这一行是整篇文章想达成的效果。2.3 .env 三个变量怎么写Hello-Agents 的惯例是用python-dotenv读.env文件放在项目根目录。三个变量名不用改值按下面的填LLM_API_KEYYOUR_API_KEY LLM_BASE_URLhttps://taotoken.net/api LLM_MODEL_IDYOUR_MODEL_ID两个容易写错的地方LLM_BASE_URL结尾不要带/v1也不要写成官网首页地址LLM_API_KEY前后不要留空格有些编辑器会自动补行尾空白粘过去就变成鉴权失败。改完.env记得重启 Python 进程load_dotenv()只在启动时读一次。3. HelloAgentsLLM 接上 https://taotoken.net/api 的两种写法3.1 环境变量方式代码零改动的推荐路径最省事的做法是让HelloAgentsLLM自己从环境变量取值代码一个字都不用动只换.env。下面这段是取值逻辑的示意写法结构跟原文第 7 章的封装思路一致import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() class HelloAgentsLLM: def __init__(self, modelNone, api_keyNone, base_urlNone): self.model model or os.getenv(LLM_MODEL_ID) self.api_key api_key or os.getenv(LLM_API_KEY) self.base_url base_url or os.getenv(LLM_BASE_URL) missing [k for k, v in { LLM_MODEL_ID: self.model, LLM_API_KEY: self.api_key, LLM_BASE_URL: self.base_url, }.items() if not v] if missing: raise ValueError(f缺少配置{, .join(missing)}) self.client OpenAI(api_keyself.api_key, base_urlself.base_url) def chat(self, messages, temperature0.7): resp self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content多出来的那个missing检查是我自己习惯加的原文没有它的用处是把「Key 没读到」这种沉默失败变成一句明确的报错省掉一半的排查时间。OpenAI客户端这里只是当 HTTP 封装用base_url指向哪里请求就发到哪里。3.2 构造函数传参方式临时切模型时用有时候你不想动.env比如在一个 notebook 里快速对比两个模型的输出。这时直接给构造函数传值更直接llm_fast HelloAgentsLLM( modelYOUR_MODEL_ID, api_keyYOUR_API_KEY, base_urlhttps://taotoken.net/api, ) llm_strong HelloAgentsLLM( modelYOUR_MODEL_ID, api_keyYOUR_API_KEY, base_urlhttps://taotoken.net/api, )两个实例除了模型 ID 不同其余完全一样。注意这里的取值优先级是「传参 环境变量」所以传参时如果写错地址.env里正确的值也救不回来。做对照实验时建议在实例创建后打印一次llm.base_url和llm.model肉眼确认一遍。3.3 SimpleAgent ToolRegistry 的最小可跑例子配置接好之后把SimpleAgent的 ReAct 循环和工具注册串起来才算是真的验证通过。原文里ToolRegistry负责登记工具、把模型的Action解析成函数调用下面用一个纯计算类工具做演示from hello_agents import HelloAgentsLLM, SimpleAgent from hello_agents.tools import ToolRegistry llm HelloAgentsLLM() # 三个值全部来自 .env registry ToolRegistry() registry.register( namecalculator, description四则运算工具入参是一个表达式字符串例如 (12872)*3/4, ) def calculator(expression: str) - str: allowed set(0123456789-*/(). ) if not expression or not set(expression) allowed: return 只接受四则运算表达式 return str(eval(expression, {__builtins__: {}}, {})) agent SimpleAgent( nameassistant, llmllm, tool_registryregistry, system_prompt你是助手。需要计算时调用 calculator 工具拿到结果后再给出解释。, ) print(agent.run(帮我算 (12872)*3/4并说明每一步。))如果你的工具不是算术而是「查库」「跑脚本」这一类务必要把边界收住工具只负责生成 SQL 文本或者把待执行的语句返回给对话真正去执行的是你自己——在本地终端、在 SQL*Plus 里手动跑跑完把输出或者报错贴回对话让模型接着分析。别让 Agent 拿着生产库的连接串自己去连。这条边界跟用哪条通道无关是 Agent 工具设计本身的纪律。4. 跑一次 agent.run看 ReAct 循环有没有真的转起来4.1 先确认请求真的发出去了第一次跑先别急着看答案对不对看请求有没有出去。最省事的办法是在chat()里临时打一行日志把self.base_url、self.model和 messages 的长度打出来def chat(self, messages, temperature0.7): print(f[LLM] base_url{self.base_url} model{self.model} msgs{len(messages)}) resp self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content只要这行日志出现了而且base_url打出来的确实是https://taotoken.net/api说明配置层没问题。如果日志都没出现那说明卡在更前面的地方多半是.env没被读到或者load_dotenv()的调用位置在类定义之后。4.2 看工具返回之后有没有回到模型做第二轮ReAct 的完整一轮是模型输出Thought和Action→ 框架解析出工具名和参数 → 调工具 → 把Observation塞回 messages → 再调一次模型。判断循环有没有转完整最简单的依据是看[LLM]日志出现了几次一次说明模型没提要调工具直接给了答案两次以上说明工具被调用过并且结果回传了。如果日志只出现一次但agent.run()返回的文本里明显有「我要调用 calculator」这类话那就是解析环节没匹配上。常见原因是system_prompt里没有把工具调用格式讲清楚或者ToolRegistry注册时的description写得太含糊模型不知道该在什么时候用它。把描述改具体一点比如把入参格式写进 description命中率会明显上升。4.3 去控制台对一下这次调用跑通之后回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的控制台看一眼刚才那几次请求有没有记上账。这一步的价值不只是看消耗更重要的是它能反过来验证你的LLM_API_KEY用的是哪一把、请求落在了哪个模型上。如果控制台里空空的而本地日志显示请求成功那大概率是你本地还留着旧的环境变量请求走了别的通道。5. 报错对照401、model not found、base_url 多写 /v15.1 鉴权失败这类报错报错里带401、invalid api key、Unauthorized字样全部指向 Key 这一层。按顺序检查三件事.env里的LLM_API_KEY是不是还留着YOUR_API_KEY这个占位符没换复制的时候有没有把首尾空格带进去传参版本里api_key是不是覆盖成了另一个值。还有一种情况是 Key 建好之后被删了或者过期了回控制台确认一眼就好。5.2 模型相关的 404 与 not foundmodel not found、does not exist、带404的错误基本都是LLM_MODEL_ID跟当前通道对不上。别凭记忆写直接回模型广场复制。另一种常见触发方式是模型号大小写不一致很多通道对这块是敏感的Claude和claude是两个结果。5.3 base_url 结尾多带 /v1这是最容易被忽略的一类.env里写成了https://taotoken.net/api/v1客户端内部又自己拼了一次版本路径最终请求打到一个不存在的地址上报错信息往往还是 404跟模型不存在混在一起很难一眼分辨。正确填法是LLM_BASE_URLhttps://taotoken.net/api末尾不带斜杠不带/v1。改完这一行重启进程再跑。5.4 ReAct 第二轮断掉工具返回值格式不对日志显示调用了工具但循环没有回到模型或者第二轮直接抛异常。这类问题多半出在工具的返回值上ToolRegistry通常期望一个字符串或者可序列化的结构如果你的工具函数返回了自定义对象、None、或者一个没法转成文本的东西序列化那一步就会失败。把工具函数的返回统一成str问题基本就消了。6. 以后切模型只动 LLM_MODEL_ID 这行6.1 从 Qwen 换到 Claude 的完整改动量假设你现在.env里用的是某个 Qwen 系列的模型号想换成 Claude 方向。整个改动是LLM_API_KEYYOUR_API_KEY LLM_BASE_URLhttps://taotoken.net/api LLM_MODEL_IDYOUR_MODEL_ID # 这一行换成模型广场里 Claude 对应那一串HelloAgentsLLM的代码不用动SimpleAgent的代码不用动ToolRegistry里注册的工具也不用动。改完重启进程agent.run()再跑一次同一个问题对比两次输出的差异。这就是把base_url固定下来的收益变的只有标识符链路本身是稳定的。6.2 这一步 TaoToken 不该替你做有一点需要说清楚HelloAgentsLLM里的模型选择逻辑、失败重试、多模型并行的调度策略都还是你自己的代码。TaoToken 提供的是 Key 和https://taotoken.net/api这个统一地址让你在切换时少改几行它不参与SimpleAgent的推理也不替ToolRegistry决定该调哪个工具。把职责分清楚出问题时才知道该往哪一层查——是通道配置错了还是 Agent 逻辑本身有 bug。想让整套东西跑得再顺一点可以先在 模型对话 里用同一把 Key 发一条消息确认模型 ID 和地址都没填错如果打算把 Agent 跑成长时间的任务去 Coding Plan 看套餐够不够用Key 不够或者要新建在 控制台 API Keys 里操作。将来你想把这套 Agent 顺手接到终端里当编程助手环境变量对照可以看 Claude Code 接入文档填的还是同一个 Base URL。