智能旅行助手Agent实战:用TaoToken统一Key打通前后端分离的多Agent系统

发布时间:2026/9/28 18:23:02
智能旅行助手Agent实战:用TaoToken统一Key打通前后端分离的多Agent系统 1. 为什么旅行规划场景特别适合多 Agent 架构做智能旅行助手这个项目时我一开始想的是一个 Agent 全包了不就行了。结果第一次跑通就发现让单个 Agent 同时干景点搜索、天气查询、酒店推荐、行程编排四件事提示词会膨胀到 800 字以上而且工具调用经常串味——明明该查天气它去搜了酒店。旅行规划天然是一个可分解的任务景点、天气、酒店、行程编排这四块彼此独立输入输出边界清晰。前端负责表单交互和结果渲染后端负责编排多个 Agent 依次执行这就是典型的前后端分离多 Agent 系统。前端不需要知道后端有几个 Agent只需要调一个/api/trip/plan接口后端也不需要关心前端用什么框架只返回结构化的 JSON。这套架构落地时最容易被忽略的一环是统一 Key 管理。四个 Agent 都要调 LLM如果每个 Agent 各自配一套 API Key、各自的 base_url配置会散落在四五个文件里换一次 Key 要改半天。这篇就用 TaoToken 的统一 Key 把前后端的配置收敛到两个文件后端的config.toml和前端的settings.json然后演示一次多 Agent 串联调用的完整验证。适合谁看已经写过单 Agent demo、想往多 Agent 编排走一步的开发者正在做前后端分离项目、被 Key 管理搞烦的工程师想跑通一条端到端旅行规划链路的学习者。下面所有配置和命令都可以直接复制。2. TaoToken 统一 Key 的前置准备TaoToken 在这里扮演的角色是统一的模型接入层。四个 Agent 不管各自用什么模型都通过同一个 Key、同一个 base_url 发起请求后端只需要维护一份配置。这样做的直接好处是换模型只改一个字段加 Agent 不用重新配 Key前端也不需要接触任何密钥。2.1 拿到统一 Key先去控制台创建一个 API Key。地址是 https://taotoken.net/console 登录后在 API Keys 页面点新建复制生成的sk-开头的字符串。这个 Key 就是后面config.toml和settings.json里要填的东西。注意Key 只显示一次复制后先存到本地密码管理器。不要提交到 Git 仓库后面会用.gitignore排除配置文件。2.2 确认接入地址后端所有 Agent 的请求都打到同一个 base_urlhttps://taotoken.net/api这个地址不加任何 UTM 参数直接写进配置即可。前端如果也需要直连模型比如做流式对话预览同样用这个地址。2.3 项目结构约定为了让配置收敛我们约定后端只读一个config.toml前端只读一个settings.json。目录大致是这样trip-planner/ ├── backend/ │ ├── app/ │ │ ├── agents/ # 四个 Agent 实现 │ │ ├── api/ # FastAPI 路由 │ │ ├── models/ # Pydantic 数据模型 │ │ └── config.py # 读取 config.toml │ ├── config.toml # 后端统一配置含 TaoToken Key │ └── requirements.txt ├── frontend/ │ ├── src/ │ │ ├── services/api.ts # 调用后端接口 │ │ └── settings.json # 前端配置含后端地址 │ └── package.json └── .gitignore.gitignore里至少加上这两行避免 Key 泄露backend/config.toml frontend/src/settings.json3. 可复制的 config.toml 与 settings.json 骨架这一节给出两个配置文件的完整骨架直接复制改 Key 就能用。3.1 后端 config.toml后端用 Python 的tomllib3.11 内置或tomli读取。四个 Agent 共享同一个[llm]段不需要各自配置。# backend/config.toml [llm] # TaoToken 统一接入地址不加任何参数 base_url https://taotoken.net/api # 从控制台复制的 Key api_key sk-你的Key填这里 # 默认模型四个 Agent 共用 model gpt-4o-mini # 单次请求超时秒多 Agent 串联时建议放宽 timeout 60 # 失败重试次数 max_retries 2 [agents] # 各 Agent 的模型可以覆盖默认值不写就用 [llm].model attraction_model gpt-4o-mini weather_model gpt-4o-mini hotel_model gpt-4o-mini planner_model gpt-4o [server] host 0.0.0.0 port 8000 # 前端开发服务器地址用于 CORS cors_origins [http://localhost:5173]对应的config.py读取逻辑# backend/app/config.py import tomllib from pathlib import Path from functools import lru_cache CONFIG_PATH Path(__file__).resolve().parent.parent / config.toml lru_cache def get_config() - dict: with open(CONFIG_PATH, rb) as f: return tomllib.load(f) def get_llm_config() - dict: return get_config()[llm]3.2 前端 settings.json前端不直接持有模型 Key只持有后端地址和一些 UI 相关配置。这样即使前端代码被打包分发也不会泄露密钥。{ apiBaseUrl: http://localhost:8000/api, requestTimeout: 120000, pollInterval: 1000, features: { enableMap: true, enableExport: true, enableEdit: true }, ui: { defaultCity: 北京, defaultDays: 3, maxDays: 10 } }前端读取方式Vite 项目// frontend/src/services/config.ts import settings from ../settings.json export const API_BASE_URL settings.apiBaseUrl export const REQUEST_TIMEOUT settings.requestTimeout export const FEATURES settings.features3.3 四个 Agent 如何共享同一份 LLM 配置后端初始化 LLM 客户端时只读一次config.toml然后把同一个客户端实例注入到四个 Agent 里。这样四个 Agent 用的是同一个 Key、同一个 base_url只是model字段可能不同。# backend/app/agents/llm_client.py from openai import OpenAI from app.config import get_llm_config _llm_config get_llm_config() client OpenAI( base_url_llm_config[base_url], api_key_llm_config[api_key], timeout_llm_config[timeout], max_retries_llm_config[max_retries], ) def chat(messages: list, model: str | None None) - str: resp client.chat.completions.create( modelmodel or _llm_config[model], messagesmessages, ) return resp.choices[0].message.content四个 Agent 各自调用chat()传入自己的model覆盖值即可。Key 只在config.toml里出现一次。4. 多 Agent 串联调用的验证请求配置就绪后跑一次端到端验证前端提交表单 → 后端依次调用四个 Agent → 返回结构化行程。4.1 后端编排入口# backend/app/agents/orchestrator.py from app.agents.llm_client import chat from app.config import get_config cfg get_config()[agents] def run_attraction_agent(city: str, preferences: str) - str: return chat( [ {role: system, content: 你是景点搜索专家只返回景点名称和一句话描述。}, {role: user, content: f搜索{city}符合{preferences}的3个景点。}, ], modelcfg[attraction_model], ) def run_weather_agent(city: str, days: int) - str: return chat( [ {role: system, content: 你是天气查询专家返回未来几天的天气温度用纯数字。}, {role: user, content: f查询{city}未来{days}天的天气。}, ], modelcfg[weather_model], ) def run_hotel_agent(city: str, accommodation: str) - str: return chat( [ {role: system, content: 你是酒店推荐专家返回酒店名称和价格区间。}, {role: user, content: f推荐{city}的{accommodation}。}, ], modelcfg[hotel_model], ) def run_planner_agent(city, days, attraction_out, weather_out, hotel_out) - str: prompt f根据以下信息生成{city}的{days}日行程返回 JSON 景点{attraction_out} 天气{weather_out} 酒店{hotel_out} JSON 字段city, days[{date, attractions[], meals[], hotel}], weather_info[], budget{{total}} return chat( [ {role: system, content: 你是行程规划专家只返回合法 JSON。}, {role: user, content: prompt}, ], modelcfg[planner_model], ) def plan_trip(city: str, days: int, preferences: str, accommodation: str) - dict: attraction_out run_attraction_agent(city, preferences) weather_out run_weather_agent(city, days) hotel_out run_hotel_agent(city, accommodation) planner_out run_planner_agent(city, days, attraction_out, weather_out, hotel_out) return {raw: planner_out}4.2 FastAPI 路由# backend/app/api/trip.py from fastapi import APIRouter from pydantic import BaseModel from app.agents.orchestrator import plan_trip router APIRouter() class TripRequest(BaseModel): city: str days: int preferences: str 历史文化 accommodation: str 经济型酒店 router.post(/trip/plan) async def create_plan(req: TripRequest): return plan_trip(req.city, req.days, req.preferences, req.accommodation)4.3 用 curl 验证一次串联调用启动后端cd backend pip install fastapi uvicorn openai tomli uvicorn app.api.trip:router --reload --port 8000发一次请求curl -X POST http://localhost:8000/trip/plan \ -H Content-Type: application/json \ -d {city:北京,days:3,preferences:历史文化,accommodation:经济型酒店}预期返回里能看到raw字段包含一段 JSON 字符串里面有city、days、weather_info、budget这些字段。如果四个 Agent 都正常返回说明统一 Key 配置生效了。4.4 前端调用// frontend/src/services/api.ts import axios from axios import { API_BASE_URL, REQUEST_TIMEOUT } from ./config const api axios.create({ baseURL: API_BASE_URL, timeout: REQUEST_TIMEOUT, }) export async function generateTripPlan(payload: { city: string days: number preferences: string accommodation: string }) { const { data } await api.post(/trip/plan, payload) return data }前端表单提交后调用generateTripPlan拿到结果渲染到 Result 页面。整个链路里前端只碰后端地址不碰模型 Key。5. 本篇常见错排查5.1 401 Unauthorized最常见的原因是config.toml里的api_key没填对或者复制时带了空格。检查方法grep api_key backend/config.toml确认是sk-开头、没有多余引号。如果 Key 是从控制台复制的注意不要漏掉末尾字符。5.2 连接超时 / Connection refused先确认base_url写的是https://taotoken.net/api没有多余路径。然后单独测一下连通性curl -X POST https://taotoken.net/api/chat/completions \ -H Authorization: Bearer sk-你的Key \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:hi}]}如果这条命令能返回说明 Key 和地址都没问题问题出在代码里。如果这条也失败检查网络和 Key 状态。5.3 多 Agent 串联时某个 Agent 返回空四个 Agent 是串行执行的前一个的输出作为后一个的输入。如果景点 Agent 返回空字符串天气 Agent 还能跑但规划 Agent 拿到的输入就不完整。排查方法是在orchestrator.py里每个 Agent 调用后加一行日志import logging logger logging.getLogger(__name__) def run_attraction_agent(city, preferences): out chat([...], modelcfg[attraction_model]) logger.info(attraction_agent output: %s, out[:200]) return out看日志里哪个 Agent 的输出是空的再针对性检查它的提示词和模型配置。5.4 前端 CORS 报错后端config.toml里的cors_origins要包含前端实际地址。如果前端跑在http://localhost:5173就写这个如果换了端口同步改。FastAPI 里这样挂from fastapi.middleware.cors import CORSMiddleware from app.config import get_config app.add_middleware( CORSMiddleware, allow_originsget_config()[server][cors_origins], allow_methods[*], allow_headers[*], )5.5 规划 Agent 返回的不是合法 JSON规划 Agent 的提示词里要明确只返回合法 JSON不要加解释。如果模型还是加了 markdown 代码块可以在解析前做一次清洗import json, re def parse_planner_output(raw: str) - dict: cleaned re.sub(r^json\s*|\s*$, , raw.strip()) return json.loads(cleaned)如果清洗后还是解析失败把planner_model换成更强的模型比如gpt-4o弱模型在长 JSON 输出上容易出错。6. 下一步把配置和验证动作固化下来到这里一条端到端链路已经跑通了前端表单 → 后端/trip/plan→ 四个 Agent 串联 → 返回结构化行程。统一 Key 的价值在于后面不管加多少个 Agentconfig.toml里的[llm]段都不用动只需要在[agents]里加一行模型覆盖。如果你要接着往下做建议先把两个配置文件固化到项目模板里再写一个make verify脚本把 4.3 节那条 curl 命令包进去每次改完 Agent 逻辑跑一次确认四个 Agent 都正常返回。这样多 Agent 系统的回归成本会低很多。需要查 Key 用量或新建 Key去控制台https://taotoken.net/console 。接入文档在 https://taotoken.net/doc 里面有各语言 SDK 的 base_url 配置示例。如果后面要把这套多 Agent 编排用到长期编码或 Agent 工作流里可以看 Coding Planhttps://taotoken.net/coding-plan 。