1. 从李宏毅第5讲出发:为什么单模型不够用,多 LLM agent 协作到底解决什么问题
李宏毅老师在《生成式AI导论 2024》第5讲里抛出一个很实在的问题:GPT-4 已经这么强了,为什么还要让它跟别的语言模型合作?答案其实就两个字——成本和分工。GPT-4 贵,而且不是所有任务都值得动用它。一个简单的格式转换、一段模板代码,交给便宜的小模型就够了;真正需要复杂推理、跨步骤规划的环节,再让 GPT-4 上场。这就是「语言模型彼此合作」最朴素的动机。
再往深一层,李宏毅讲了「模型反省」和「模型讨论」的区别。自我反省是同一个模型把自己的输出再看一遍,但因为它本来就认同自己之前的答案,推翻的概率有限。而让模型 A 和模型 B 互相看对方的输出、互相质疑,接受新刺激之后,推翻错误答案的概率会明显提高。文献里那张图说得很清楚:讨论轮次越多、参与模型越多,数学题正确率越高,但到三四个回合之后收益就趋缓了。
这就引出了工程上真正要解决的问题:怎么把「多模型讨论」和「多角色分工」落成可运行的代码。李宏毅提到的 meta GPT 思路,就是给不同 agent 分配 architect、project manager、engineer、tester 这些角色,让它们像一个小团队一样接力干活。而要让这个团队跑起来,第一道坎就是——你得同时调 GPT-4、GPT-3.5、Claude 等好几个模型,每个模型一套 Key、一套 Base URL、一套计费,管理起来非常碎。
这篇就按这个场景来:用 TaoToken 统一 Key 和 API 通道,把 GPT-4 和多个 LLM agent 串成一个能跑通的多角色协作流程。你会看到可复制的环境变量配置、多 agent 角色提示词模板、一次完整协作任务的运行日志,以及常见的报错排查。适合已经会写 Python 调用大模型 API、想进一步搭多 agent 协作的开发者。
2. 前置准备:用 TaoToken 统一 Key 打通 GPT-4 与多模型调用通道
在搭多 agent 团队之前,先把「模型接入层」统一掉。否则你的代码里会散落着 OpenAI 的 Key、Anthropic 的 Key、各种第三方平台的 Key,换一个模型就要改一处配置,agent 一多根本维护不过来。TaoToken 在这里扮演的角色就是统一入口:一个 API Key、一个 Base URL,背后可以路由到 GPT-4、GPT-3.5、Claude 等不同模型。
先注册并拿到 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,完成账号注册后进入控制台。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在里面可以创建 API Key。创建好之后复制出来,形如sk-xxxxxxxx,这个 Key 就是你后面所有 agent 共用的凭证。
API 的基础地址是 https://taotoken.net/api ,注意这个地址后面不加任何 UTM 参数,直接作为 OpenAI SDK 的base_url使用。如果你用的是 OpenAI 官方 Python SDK,它默认会去请求https://api.openai.com/v1,我们只需要把base_url换成 TaoToken 的地址,SDK 就会把请求发到统一通道,再由通道转发到对应模型。
这里有个关键点要理解:多 agent 协作里,不同角色可能用不同模型。比如 project manager 用 GPT-4 做规划,engineer 用 GPT-3.5 写代码,tester 用另一个模型做校验。如果每个模型都要单独配 Key,代码里就得维护一个映射表。而用 TaoToken 之后,所有角色共用同一个 Key 和 Base URL,只是在请求时通过model参数指定不同的模型 ID,配置量直接降下来。
模型 ID 的写法要跟通道支持的名称一致。常见的有gpt-4、gpt-4-turbo、gpt-3.5-turbo这类。你可以在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 先手动试一下某个模型 ID 能不能正常返回,确认可用之后再写进代码。这一步别省,很多人后面报model not found就是因为 ID 写错了。
如果你打算长期跑 agent 任务、消耗量比较大,可以看一下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它更适合持续性的编码和 agent 场景。API Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数问题先翻文档比瞎试快。
环境变量建议这样组织,把 Key 和 Base URL 都抽出来,代码里不硬编码:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows PowerShell 用:
$env:TAOTOKEN_API_KEY="sk-你的Key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"这样配置的好处是,你的多 agent 代码在任何机器上只要导入这两个环境变量就能跑,换 Key 也不用改代码。接下来所有 agent 都从这两个变量读取配置。
3. 可复制配置:多 agent 角色提示词模板与统一调用封装
这一节给出能直接复制的配置和代码。核心思路是:写一个统一的call_llm函数,所有 agent 都通过它调用模型,只是传入不同的model和system_prompt。这样模型接入层只有一处,角色差异全部体现在提示词里。
先看统一调用封装。用 OpenAI Python SDK,把base_url指向 TaoToken:
import os from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url=os.environ["TAOTOKEN_BASE_URL"], ) def call_llm(model: str, system_prompt: str, user_prompt: str, temperature: float = 0.7) -> str: resp = client.chat.completions.create( model=model, temperature=temperature, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], ) return resp.choices[0].message.content这段就是整个多 agent 团队的底座。注意base_url用的是环境变量,没有写死。model参数由每个角色自己决定。
接下来是角色提示词模板。参考李宏毅讲的 meta GPT 分工,我们定义四个角色:project manager、engineer、tester、reviewer(裁判模型)。每个角色一个 system prompt。
Project Manager 负责拆解任务、做规划,用 GPT-4:
PM_PROMPT = """你是一个项目管理者(Project Manager)。 你的职责是把用户的需求拆解成清晰的、可执行的子任务,并指定每个子任务的验收标准。 输出格式要求: 1. 任务目标(一句话) 2. 子任务列表(每条包含:做什么、由谁做、完成标准) 3. 风险点提示 不要写代码,只做规划。"""Engineer 负责按规划写代码,用 GPT-3.5 控制成本:
ENGINEER_PROMPT = """你是一个工程师(Engineer)。 你会收到项目管理者给出的子任务,请针对当前子任务写出可运行的 Python 代码。 要求: 1. 代码必须完整,包含必要的 import 2. 关键逻辑加注释 3. 如果子任务信息不足,先说明你的假设,再写代码 只输出代码和必要说明,不要重复规划内容。"""Tester 负责校验 engineer 的产出:
TESTER_PROMPT = """你是一个测试工程师(Tester)。 你会收到工程师写的代码,请检查: 1. 是否有语法错误或明显 bug 2. 是否满足子任务的验收标准 3. 边界情况是否处理 输出格式:先给结论(通过/不通过),再列具体问题。"""Reviewer 是裁判模型,判断讨论是否达成共识。这里参考李宏毅讲的「裁判模型」思路,提示词要明确让它判断一致性:
REVIEWER_PROMPT = """你是一个裁判模型(Reviewer)。 你会收到两个模型对同一问题的回答。请判断它们的核心结论是否一致。 输出格式: - 是否达成共识:是/否 - 理由:一句话 - 如果未达成共识,指出分歧点 注意:不要为了达成一致而强行判定一致,要基于内容判断。"""这里有个李宏毅特别强调的坑:模型天生「温良恭俭让」,被质疑就容易退缩,导致讨论太快结束。所以 engineer 和 tester 的提示词里要加一句「不需要一定同意对方,可以坚持自己的判断」。我在 tester 提示词里补上:
TESTER_PROMPT += "\n如果工程师的代码有问题,请明确指出,不要因为对方是工程师就妥协。"配置层面,如果你用 Cline 或类似工具做 MCP 接入,配置片段长这样,Base URL、Key、Model ID 三件套都要写全:
{ "mcpServers": { "taotoken": { "command": "npx", "args": ["-y", "your-mcp-server"], "env": { "OPENAI_BASE_URL": "https://taotoken.net/api", "OPENAI_API_KEY": "sk-你的Key", "OPENAI_MODEL": "gpt-4" } } } }如果你用 Claude Code 这类工具,配置里同样要写全 Base URL、Key、Model ID,缺一个都会连不上。Codex 的auth.json也是同理,把 base URL 和 key 填进去,model 指定清楚。
4. 验证请求:一次完整的多 agent 协作任务运行日志
配置好之后,跑一次完整任务来验证。任务设定:让团队写一个「读取 CSV 并统计每列缺失值」的小工具。这个任务足够简单,能在一轮里跑完,方便看日志。
先写主流程,把四个角色串起来:
def run_team(task: str): # 1. PM 规划 plan = call_llm("gpt-4", PM_PROMPT, task) print("=== PM 规划 ===") print(plan) # 2. Engineer 写代码 code = call_llm("gpt-3.5-turbo", ENGINEER_PROMPT, f"任务:{task}\n规划:{plan}") print("=== Engineer 产出 ===") print(code) # 3. Tester 校验 test_result = call_llm("gpt-3.5-turbo", TESTER_PROMPT, f"代码:{code}") print("=== Tester 结论 ===") print(test_result) # 4. Reviewer 判断是否达成共识 review = call_llm("gpt-4", REVIEWER_PROMPT, f"Engineer 产出:{code}\nTester 结论:{test_result}") print("=== Reviewer 判定 ===") print(review) return plan, code, test_result, review run_team("写一个 Python 脚本,读取 data.csv,统计每一列的缺失值数量并打印")实测下来,一次典型运行日志大致是这样(内容做了精简):
=== PM 规划 === 任务目标:实现 CSV 缺失值统计工具 子任务列表: 1. 读取 CSV 文件,由 Engineer 完成,标准:能正确加载 data.csv 2. 统计每列缺失值,由 Engineer 完成,标准:输出列名与缺失数量 3. 校验边界情况,由 Tester 完成,标准:空文件、全空列有处理 风险点:CSV 编码可能不是 UTF-8 === Engineer 产出 === import pandas as pd def count_missing(path): df = pd.read_csv(path) return df.isnull().sum() if __name__ == "__main__": print(count_missing("data.csv")) === Tester 结论 === 结论:不通过 问题: 1. 未处理文件不存在的情况 2. 未指定编码,遇到非 UTF-8 文件会报错 3. 未处理空文件 === Reviewer 判定 === 是否达成共识:否 理由:Tester 指出 Engineer 代码缺少异常处理,双方未达成一致 分歧点:是否需要处理文件不存在和编码问题看到 Reviewer 判定「否」,说明讨论没结束。按李宏毅讲的流程,这时候要把 Tester 的意见回传给 Engineer,让它修改。加一轮迭代:
code_v2 = call_llm("gpt-3.5-turbo", ENGINEER_PROMPT, f"任务:{task}\n上一版代码:{code}\nTester 意见:{test_result}\n请修改")第二轮 Engineer 会补上try/except和encoding参数。再让 Tester 校验,Reviewer 判定,通常这一轮就能达成共识。整个流程跑通,说明你的多 agent 团队已经能协作了。
验证成功的标志有三个:一是每个角色的输出格式符合提示词要求;二是 Reviewer 能正确判断一致/不一致;三是迭代后 Tester 结论从「不通过」变成「通过」。如果 Reviewer 一直判「否」,多半是提示词里没让它基于内容判断,或者两个模型输出差异太大。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 报错怎么解
多 agent 跑起来之后,报错基本集中在接入层。下面按真实遇到的报错逐个说。
401 Unauthorized。最常见的原因是 Key 没读到或者写错了。先确认环境变量有没有生效:
echo $TAOTOKEN_API_KEY如果输出为空,说明没 export 成功。另一个原因是 Key 复制时带了空格或换行,重新从 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 复制一次。还有一种情况是base_url写成了https://taotoken.net/api/带尾斜杠,某些 SDK 会拼出双斜杠导致鉴权失败,去掉尾斜杠即可。
local proxy failed。这个报错通常出现在你本地配了代理,但代理没启动或者端口不对。检查你的环境变量里有没有HTTP_PROXY、HTTPS_PROXY,如果有但代理服务没开,SDK 就会连不上。临时清掉:
unset HTTP_PROXY unset HTTPS_PROXY然后重跑。注意这里说的是本地开发环境的代理配置问题,不是让你去用什么网络工具,纯粹是排查环境变量冲突。
reading choices 报错,比如KeyError: 'choices'或AttributeError: 'NoneType' object has no attribute 'choices'。这通常说明返回体结构和你预期的不一样。先打印原始返回:
resp = client.chat.completions.create(...) print(resp)如果返回里没有choices,可能是模型 ID 写错了,通道返回了错误信息而不是正常补全。确认model参数用的是通道支持的 ID,比如gpt-4而不是gpt4。另外检查messages是不是空列表,空消息也会导致异常返回。
OAuth 相关报错。如果你用的是 Claude Code 或类似工具,报 OAuth 错误通常是因为工具默认走官方登录流程,而你想用 API Key 接入。这时候要在配置里显式指定 Base URL 和 Key,覆盖默认的 OAuth 流程。Claude Code 的配置参考文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 Base URL、Key、Model ID 三件套的完整写法。Codex 的auth.json同理,把OPENAI_BASE_URL和OPENAI_API_KEY填进去,别留空。
模型 ID 不匹配。报model not found或invalid model。解决方法是先去模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 手动选模型发一条消息,确认能通,再把页面显示的模型 ID 抄进代码。别凭记忆写。
多 agent 场景特有的问题:Reviewer 一直判「未达成共识」,导致死循环。这是提示词问题,不是接入问题。按李宏毅讲的,裁判模型要能判断「两段输出是否一致」,提示词里要明确让它输出「是/否」加理由。另外给迭代设一个上限,比如最多 3 轮,超过就强制结束并输出当前结果,避免无限循环烧 token。
排查顺序建议:先确认 Key 和 Base URL 能单独调通一个模型,再跑多 agent 流程。接入层没问题了,再调提示词。这样能把问题范围缩小。
6. 把团队跑起来之后:从单次协作到可持续的 agent 工作流
跑通一次多 agent 协作只是起点。真正有价值的是把它变成可复用的工作流。几个实践建议。
第一,把角色提示词抽成配置文件,别硬编码在 Python 里。可以用 YAML 或 JSON 存,改提示词不用动代码。比如:
roles: pm: model: gpt-4 prompt: "你是一个项目管理者..." engineer: model: gpt-3.5-turbo prompt: "你是一个工程师..."第二,给每个 agent 的调用加日志,记录模型、输入、输出、耗时、token 消耗。多 agent 跑起来之后,成本主要花在 GPT-4 角色上,有日志才能知道钱花在哪。如果发现 PM 和 Reviewer 用 GPT-4 太贵,可以试试把 Reviewer 换成便宜模型,看判断质量是否可接受。
第三,讨论轮次设上限。李宏毅提到文献里讨论到三四个回合收益就趋缓,工程上设 3 轮足够。超过就输出当前最优结果,别让它无限讨论。
第四,模型分工按任务复杂度动态调整。简单任务全用便宜模型,复杂任务才上 GPT-4。这其实就是李宏毅讲的「杀鸡焉用牛刀」——让分配工作的模型判断任务难度,再决定交给谁。你可以在 PM 之后加一个路由判断,根据任务类型选模型。
如果你要长期跑这类 agent 任务,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 比按次调用更适合,尤其是需要反复迭代的场景。接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 里有完整的参数说明,遇到模型 ID 或参数问题先查文档。
最后说一个我踩过的坑:多 agent 协作里,最容易出问题的不是模型能力,而是角色之间的信息传递。PM 的规划如果太模糊,Engineer 就会瞎猜;Tester 的意见如果太笼统,Engineer 改不对。所以每个角色的输出格式要在提示词里卡死,让上下游能稳定解析。格式稳定了,整个团队才能持续跑下去。