1. 多 Agent 工具服务为什么总在 Key 上翻车AI Agent 落地到容器环境后最先暴露的往往不是模型能力问题而是 Key 管理问题。LangChain 的 Agent 要调模型AutoGen 的 GroupChat 要调模型Agent Mesh 里每个工具服务在需要做语义路由或结果摘要时也要调模型。如果每个容器都挂一份自己的OPENAI_API_KEY你会遇到三个典型症状一是密钥散落在十几份 YAML 和.env里轮换一次要改半天二是某个工具服务被反编译或日志打印Key 直接泄露三是不同服务走不同出口计费和限流完全对不上账。我试过在一个 8 服务的 Agent 编排里逐个改 Key改到第三个就放弃了。后来统一成一条 Key 通道所有容器只认一个环境变量指向同一个兼容 OpenAI 协议的入口模型名、路由、额度都在通道侧管理。这篇就按这个思路把 LangChain、AutoGen、Agent Mesh 三类 Agent 工具服务容器化并给出 Docker Compose 骨架、settings.json与config.toml的配置片段最后演示容器起来后怎么验证鉴权和路由真的生效。适合谁看已经在写 Agent、准备把 Agent 服务塞进容器或 K8s、被多份 Key 折磨过的后端和算法同学。读完你能拿到一套可直接跑的编排骨架而不是又一篇概念科普。2. TaoToken 统一 Key 通道前置准备核心思路是把模型调用收敛成一个 OpenAI 兼容端点。TaoToken 提供的就是这样一个入口官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。所有 Agent 容器只需要两个变量OPENAI_API_KEY和OPENAI_BASE_URL前者是你在控制台生成的 Key后者固定指向上面这个 API 地址。先去控制台建 Key入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 生成后复制保存。Key 的权限和额度管理都在这一层容器侧不再关心具体模型供应商。如果你要确认某个模型名是否可用可以直接在模型对话页试一条请求https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。注意Key 只放在编排的环境变量或 Secret 里不要写进镜像、不要提交到 Git。容器日志里也不要打印OPENAI_API_KEY。对长期跑编码类 Agent 的场景可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的 Agent 编码任务。接入细节和参数说明看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。3. Docker Compose 骨架与三类 Agent 配置先给一份能跑起来的 Compose 骨架。它包含三个服务langchain-agent、autogen-manager、agent-mesh-router全部通过同一个OPENAI_BASE_URL和OPENAI_API_KEY访问模型通道。# docker-compose.yml version: 3.9 x-agent-env: agent-env OPENAI_API_KEY: ${TAOTOKEN_API_KEY} OPENAI_BASE_URL: https://taotoken.net/api DEFAULT_MODEL: gpt-4o-mini services: langchain-agent: build: ./langchain-agent environment: : *agent-env AGENT_ROLE: tool-executor ports: - 8001:8000 healthcheck: test: [CMD, curl, -f, http://localhost:8000/health] interval: 15s timeout: 5s retries: 3 autogen-manager: build: ./autogen-manager environment: : *agent-env AGENT_ROLE: group-manager ports: - 8002:8000 depends_on: - langchain-agent agent-mesh-router: build: ./agent-mesh-router environment: : *agent-env AGENT_ROLE: mesh-router UPSTREAM_AGENTS: http://langchain-agent:8000,http://autogen-manager:8000 ports: - 8003:8000 depends_on: - langchain-agent - autogen-managerTAOTOKEN_API_KEY从宿主机.env注入Compose 只做变量透传不落盘到镜像。这样轮换 Key 时只改一处。3.1 LangChain 侧 settings.json 配置LangChain 的 Agent 服务里把模型初始化收敛到一个工厂函数读环境变量。同时用一份settings.json描述工具与模型映射避免硬编码。{ llm: { provider: openai-compatible, base_url_env: OPENAI_BASE_URL, api_key_env: OPENAI_API_KEY, default_model: gpt-4o-mini, temperature: 0, max_iterations: 8, max_execution_time: 60 }, tools: [ { name: search_repos, endpoint: http://mcp-github:8080/dispatch }, { name: query_db, endpoint: http://db-query:8080/query } ] }对应的加载代码# langchain-agent/app.py import json, os from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_openai_tools_agent with open(settings.json) as f: cfg json.load(f) llm ChatOpenAI( modelcfg[llm][default_model], base_urlos.environ[OPENAI_BASE_URL], api_keyos.environ[OPENAI_API_KEY], temperaturecfg[llm][temperature], ) agent_executor AgentExecutor( agentagent, toolstools, max_iterationscfg[llm][max_iterations], max_execution_timecfg[llm][max_execution_time], )base_url和api_key都从环境变量取settings.json只留模型名和策略参数。这样同一份镜像可以在不同环境复用Key 由编排层注入。3.2 AutoGen 侧 config.toml 配置AutoGen 支持从config.toml读取模型配置正好用来对接统一通道。# autogen-manager/config.toml [llm] provider openai base_url https://taotoken.net/api api_key_env OPENAI_API_KEY model gpt-4o-mini temperature 0 timeout 60 [group_chat] max_round 15 speaker_selection round_robin加载方式# autogen-manager/main.py import os, tomllib, autogen with open(config.toml, rb) as f: cfg tomllib.load(f) llm_cfg cfg[llm] config_list [{ model: llm_cfg[model], base_url: llm_cfg[base_url], api_key: os.environ[llm_cfg[api_key_env]], temperature: llm_cfg[temperature], }] manager autogen.GroupChatManager( groupchatautogen.GroupChat(agentsagents, messages[], max_roundcfg[group_chat][max_round]), llm_config{config_list: config_list}, )base_url写死在config.toml里没问题因为它不是敏感信息api_key仍然走环境变量。这样 AutoGen 的每个 Agent 共享同一份通道配置。3.3 Agent Mesh 路由侧配置Agent Mesh 这一层负责把请求分发到具体 Agent并在需要时做结果摘要。它同样只认统一通道。# agent-mesh-router/main.py import os, httpx from fastapi import FastAPI, Request app FastAPI() UPSTREAM os.environ[UPSTREAM_AGENTS].split(,) BASE_URL os.environ[OPENAI_BASE_URL] API_KEY os.environ[OPENAI_API_KEY] app.post(/route) async def route(req: Request): body await req.json() target pick_agent(body[task_type]) async with httpx.AsyncClient() as client: resp await client.post(target, jsonbody, timeout60) return resp.json() async def summarize(text: str) - str: async with httpx.AsyncClient() as client: resp await client.post( f{BASE_URL}/chat/completions, headers{Authorization: fBearer {API_KEY}}, json{model: gpt-4o-mini, messages: [{role: user, content: text}]}, ) return resp.json()[choices][0][message][content]路由层和摘要层共用同一 Key不需要额外配置。4. 启动容器并验证鉴权与路由配置齐了直接起。export TAOTOKEN_API_KEY你的Key docker compose up -d --build docker compose ps三个服务都应该是healthy或running。先验证鉴权是否生效直接打 LangChain Agent 的健康检查加一次模型调用curl -s http://localhost:8001/health curl -s -X POST http://localhost:8001/invoke \ -H Content-Type: application/json \ -d {input: 用一句话说明什么是 Agent}如果 Key 或base_url配错这里会返回 401 或连接错误。正常情况你会拿到一段模型输出。再验证 AutoGen 的多 Agent 协作curl -s -X POST http://localhost:8002/chat \ -H Content-Type: application/json \ -d {message: 让研究员和程序员协作输出一个快速排序示例}最后验证 Mesh 路由是否把请求分发到了正确的上游curl -s -X POST http://localhost:8003/route \ -H Content-Type: application/json \ -d {task_type: code, input: 写一个二分查找}成功的结果是路由层返回的响应里带有上游 Agent 的处理结果且三个服务的日志中都能看到对https://taotoken.net/api的请求记录而不是各自打向不同域名。这说明统一 Key 通道生效了。提示验证阶段可以把DEFAULT_MODEL换成更便宜的模型确认链路通了再切回目标模型避免调试期浪费额度。5. 本篇常见错排查报错一401 Unauthorized。九成是OPENAI_API_KEY没注入进容器。用docker compose exec langchain-agent env | grep OPENAI确认变量存在再检查宿主机.env是否被 Compose 读取。注意 Compose 的变量替换发生在宿主机侧容器内看不到TAOTOKEN_API_KEY这个名字只有OPENAI_API_KEY。报错二Connection refused 或超时。检查OPENAI_BASE_URL是否写成了带路径的完整地址。基址应该是https://taotoken.net/apiSDK 会自己拼/chat/completions。如果你手动拼了/v1可能重复。报错三模型名不存在。不同通道支持的模型名不完全一致。先在模型对话页确认可用模型再写进settings.json和config.toml。报错通常是 404 或model not found。报错四AutoGen 起不来提示 config 解析失败。tomllib是 Python 3.11 才进标准库的。如果你的基础镜像是 3.10需要装tomli并改导入。或者直接把config.toml换成 JSON减少依赖。报错五Mesh 路由 502。多半是UPSTREAM_AGENTS里的服务名解析不到。Compose 网络内用服务名互访确认depends_on和端口对得上。K8s 里则要用 Service 名加命名空间。报错六容器日志里出现 Key 明文。检查你的代码有没有在异常分支打印os.environ。统一通道的好处是 Key 只有一份但一旦泄露影响面也更大日志脱敏要做。6. 把 Key 通道固定下来之后统一 Key 通道真正的价值不在省事而在可观测。所有 Agent 的模型调用都经过同一个入口你可以在通道侧看到哪个 Agent 在烧额度、哪个工具服务在疯狂重试。容器化只是第一步把 Key 收敛成一条通道后面做限流、做审计、做成本分摊才有抓手。如果你还在逐个服务配 Key建议先按这篇的 Compose 骨架把三个服务跑通确认鉴权和路由都对了再往 K8s 迁移。接入参数和更多配置示例看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite Key 管理在控制台https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。编码类 Agent 长期跑的话Coding Plan 会更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。