
HelloAgents 智能旅行助手的 llm_service 改走 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_end这件事起因很具体行程总结那一步又抛了一次DeepSeek 最终连接断开trace 里前面几个 Agent 都正常返回唯独最后把「三天杭州行程」翻成中文总结那一次链断在半路。笔记写到第 13 篇前十二篇基本都在折腾 Agent 怎么分工、工具怎么注册、路由怎么串模型调用一直是被当作「先跑通再说」的一环等到四个 Agent 真的拼成一条旅行规划流水线出口就只剩 llm_service 一个它一抖整条链一起红。所以这篇不做架构重构只做一件事把原先写在 .env 里的模型 Key换成从 TaoToken 控制台创建的 Key把代码里 llm_service 的 base_url指向https://taotoken.net/api结尾不带 /v1也不要填官网首页。改完之后景点检索、天气交通、日程编排、翻译总结这四个 Agent 的模型请求会统一从一条出口走原有的编排逻辑、Prompt、工具函数一行都不用动直接调/api/trip/plan就能把完整旅行计划跑出来。1. llm_service 报 DeepSeek 最终连接断开问题在出口只有一条1.1 四个 Agent 分工不同模型出口却是同一个智能旅行助手的骨架并不复杂一个 Agent 负责查景点和开放时间一个负责天气与交通一个把候选景点串成日程最后一个把结果润色成一段能直接发给朋友的说明。四个角色的职责边界很清楚但它们拿模型能力的方式完全一样——都从 llm_service 里取。这种写法在本地跑单点 demo 时很舒服一个 .env一把 Key调一次函数拿一段文本。真正联调时才暴露短板llm_service 这一层只要出现超时、连接被对端提前关闭四个 Agent 会一起变慢最后统一挂在同一条报错上。你以为坏的是一个 Agent实际上是唯一的模型出口出了问题。1.2 断连为什么总挑行程总结那一步把四个环节的输入输出摊开看规律很清楚。景点检索的输入是短查询输出是结构化片段天气交通更短。到了行程总结前三个 Agent 的输出全被塞进上下文输入最长它自己还要生成整段自然语言输出也最长再加上这一步通常是最后执行的整条链已经跑了几十秒长连接挂在中间的概率自然最高。这也是为什么单纯在某一个 Agent 里包一层 try/except 没用重试发生在最外层前面的检索结果没缓存重试一次等于把整条流水线重跑一遍用户等两分钟拿到同样的报错。提示与其在四个 Agent 里分别加固不如把模型出口收敛成一个稳定、可重试、可观测的入口。这也是把 llm_service 改走统一 API 通道的直接理由。2. .env 里的模型 Key 改成从 TaoToken 控制台创建2.1 注册、建 Key、复制三步走完原笔记里的做法是去模型厂商那边申请一把 Key粘进 .env。现在这一步整体挪到 TaoToken打开页面登录进控制台找到 API Keys 那一栏新建一把 Key复制出来先存进密码管理器或本地记事本。需要注意的是多数控制台只在创建的那一刻展示完整 Key页面一刷新就只剩前后几位。所以别顺手关掉页面再去翻历史记录先粘走再说。2.2 .env 到底改哪几行项目根目录下的 .env本质上是三个变量在起作用Key、接口地址、模型名。对照着改一遍即可。变量用途改造前的写法改造后的写法模型 Key原来那把厂商 KeyTAOTOKEN_API_KEYYOUR_API_KEY接口地址厂商直连地址TAOTOKEN_BASE_URLhttps://taotoken.net/api模型名原来写死的模型TAOTOKEN_MODEL按模型广场填落到文件里是这样# .env TAOTOKEN_API_KEYYOUR_API_KEY TAOTOKEN_BASE_URLhttps://taotoken.net/api # 模型 ID 以 TaoToken 模型广场当时的列表为准不要凭记忆写 TAOTOKEN_MODEL模型广场里选中的模型 IDYOUR_API_KEY是占位符真 Key 从 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建后替换掉。注意填进代码的接口地址是https://taotoken.net/api末尾不要加 /v1。带?utm_source...的那个官网地址是用来注册、建 Key、看模型广场和查用量的不能塞进 base_url两者用途完全不同。3. llm_service.py 的三处改动地址、模型名、客户端复用3.1 换成 OpenAI 兼容写法改动量控制在十行内原来的 llm_service 大概率是直接 new 了一把厂商 SDK 客户端或者手写 requests 拼 URL。换成兼容写法之后代码会更短# llm_service.py import os from openai import OpenAI _client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), timeout60.0, max_retries2, ) def chat(messages, modelNone, temperature0.3): resp _client.chat.completions.create( modelmodel or os.getenv(TAOTOKEN_MODEL), messagesmessages, temperaturetemperature, ) return resp.choices[0].message.contentSDK 会自动在 base_url 后面拼/chat/completions所以最终请求打到的是https://taotoken.net/api/chat/completions。如果你把 base_url 写成带 /v1 的形式路径就会变成/api/v1/chat/completions那是另一个地址不是本篇要用的那个。3.2 模型 ID 从模型广场抄不要凭记忆写四个 Agent 里翻译和总结对模型的要求和检索不太一样可以给它们指定不同的模型。具体能选哪些、叫什么名字以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时的列表为准页面上选中哪一行就填哪一行的 ID不要自己加日期后缀也不要照着别处的命名习惯猜。想给不同 Agent 用不同模型把 chat() 的 model 参数透出来就够了# agents/summary_agent.py from llm_service import chat def summarize(plan_text: str) - str: return chat( messages[ {role: system, content: 你是旅行行程总结助手输出简洁中文。}, {role: user, content: plan_text}, ], modelNone, # 为 None 时走 .env 里的默认模型 )3.3 四个 Agent 复用同一个 client原笔记里一个容易忽略的细节是如果每个 Agent 各自 new 一次客户端、各自读一次 .env超时和重试策略就会散在四个文件里改一次要改四遍。把_client放在 llm_service 模块级别只在导入时构造一次四个 Agent 通过from llm_service import chat拿函数超时、重试、日志就都只有一处需要维护。这样做还有个副作用是好的一旦某次调用失败堆栈里打印的永远是同一个模块名排查时不用先判断「这次是哪个 Agent 把 Key 读错了」。4. 调一次 /api/trip/plan确认四个 Agent 都落在同一条通道上4.1 先用一条 curl 确认通道本身是通的改完代码先别急着起服务用一条最小请求验证 Key 和地址curl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer YOUR_API_KEY \ -H Content-Type: application/json \ -d { model: 模型广场里选中的模型 ID, messages: [{role: user, content: 用一句话介绍西湖}] }能拿到一句正常回答说明 Key、地址、模型名这三样都没写错。这一步失败就别往下走先把报错对照第 5 节。4.2 再发一次真实的旅行规划请求后端起来之后按项目里注册的路由发请求。字段名以你自己项目里的定义为准下面这条只是形状参考curl -X POST http://127.0.0.1:8000/api/trip/plan \ -H Content-Type: application/json \ -d {city: 杭州, days: 3, preferences: [博物馆, 本地小吃]}正常返回应该是一段包含日程安排和说明文字的整体结果而不是某一环的中间态。如果检索部分有结果、总结部分是空的那说明总结 Agent 那次请求出了问题回去看 llm_service 的日志。4.3 从日志核对每个 Agent 的返回在 chat() 里加一行耗时打印跑一次就能看清四个环节各自的耗时分布import time, logging def chat(messages, modelNone, temperature0.3): t0 time.time() resp _client.chat.completions.create( modelmodel or os.getenv(TAOTOKEN_MODEL), messagesmessages, temperaturetemperature, ) logging.info(llm done in %.2fs, time.time() - t0) return resp.choices[0].message.content四个耗时都打印出来、没有中断就说明原本卡在总结那一步的链路已经整条走通了。5. 断连、401、404 出现时按配置位置逐项对照5.1 三类报错分别对应哪一处配置现象大概率原因处理方式401 未授权Key 没读到或少了 Bearer 前缀检查 .env 是否被加载、变量名是否拼错404 找不到路径base_url 多写了 /v1 或漏了 /api统一改回https://taotoken.net/api超时、连接中断单次输入过长或超时设置太短放宽 timeout必要时拆小总结的输入长度三类里最容易踩的是 404因为地址看着「只差几个字符」但 SDK 拼接规则是死的多一段就是另一个路径。5.2 断连还复现时先看这三个地方第一确认四个 Agent 是否真的都走了 llm_service有的项目里会残留一处直接 requests 的旧代码。第二确认 max_retries 是否生效重试发生在客户端层才能救回偶发的连接抖动。第三看总结环节的输入是不是把前三个 Agent 的原始输出全塞进去了——把中间结果先压缩成要点再送进去长连接的存活压力会小很多。按这三步走完笔记里那种「前三个环节正常、最后一个环节整条链断掉」的形态基本不会再出现。5.3 收尾把这次调用在控制台里对一下跑通之后回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 看一眼这次的调用记录和用量确认四个 Agent 的请求都记在了同一把 Key 下。如果打算长期跑这类多智能体项目可以在 模型对话 里用同一把 Key 手动发几条消息对比一下同模型在你的 Prompt 下的表现Key 不够用或要换一把直接在 控制台 API Keys 新建调用量上来了再去 Coding Plan 看看套餐是否够用。改动清单其实只有三行Key 换成从控制台创建的那把base_url 填https://taotoken.net/api模型名照着模型广场抄。Agent 编排、工具注册、路由定义这些真正花时间写的东西一行都不用动。