1. 从 AutoGen 到 Agent Framework:多智能体协作到底变了什么
如果你最近在 GitHub 上翻微软的仓库,大概率会刷到microsoft/agent-framework这个新项目。它和几年前的 AutoGen 放在一起看,差别非常直观:AutoGen 更像一套“能跑起来就行”的对话编排脚本,而 Agent Framework 明显在往工程化、结构化方向收。对于需要在本地快速验证框架迁移的开发者来说,理解这个演进脉络,比单纯背 API 更有价值。
先说 AutoGen 的定位。它最早火起来,是因为把多智能体协作这件事变得极其简单:定义一个AssistantAgent,再定义一个UserProxyAgent,两者互相发消息,一个负责调用模型,一个负责执行代码或工具,循环几轮就能完成一个任务。这种模式在原型阶段非常爽,几行代码就能让两个 Agent 讨论出一个结果。但问题也很明显——模型客户端、系统提示词、工具注册、终止条件全部耦合在 Agent 构造参数里,参数碎片化严重。当你想要加入条件分支、循环、人工审核节点时,代码会迅速膨胀成一张难以维护的网。
Agent Framework 的变化,核心在于把“Agent 是什么”和“Agent 怎么被编排”拆开了。Agent 本身只关心三件事:指令(instructions)、工具集(tools)、模型客户端(chat_client)。而多个 Agent 之间的协作关系,交给更上层的 workflow 或 graph 来描述。这个拆分带来的直接好处是:你可以单独测试一个 Agent 的行为,也可以单独替换编排逻辑,而不用动 Agent 内部代码。
另一个关键变化是术语标准化。AutoGen 里叫system_message,Agent Framework 里叫instructions;AutoGen 里模型客户端往往和 Agent 绑得很紧,Agent Framework 里通过chat_client注入,底层细节被隐藏。这种命名上的调整看似小事,但它降低了不同开发者之间的沟通成本——大家说的“指令”就是同一个东西。
还有一个容易被忽略的点:状态管理。AutoGen 的对话状态通常散落在消息列表里,长线任务中很难追踪“当前进行到哪一步”。Agent Framework 引入了 thread 的概念,把一次会话或一个任务的状态集中管理,配合 graph 编排,可以表达条件分支和循环。这对于需要多轮工具调用、中间结果校验的场景,差别很大。
所以这一节想说明的是:从 AutoGen 到 Agent Framework,不是简单的版本升级,而是从“脚本式多智能体”走向“工程化多智能体”。你如果只是做 demo,AutoGen 依然够用;但如果你要把 Agent 放进一个需要长期维护、多人协作的项目里,Agent Framework 的结构化设计会省掉很多后期重构的麻烦。接下来我会先讲清楚接入前的准备工作,再给出可复制的配置片段。
2. TaoToken 统一 Key 接入前置准备:环境、依赖与 Base URL 配置
在本地验证 Agent Framework 之前,你需要先解决模型调用的问题。Agent Framework 本身不绑定某一家模型服务,它通过chat_client接收一个兼容 OpenAI 接口的客户端。这意味着你可以用任何提供 OpenAI 兼容 API 的服务来驱动它。这里我用 TaoToken 作为统一入口,原因是它把多个模型的 Key 收敛成一个,切换模型时不用改代码结构,只改 Model ID 即可。
先确认你的本地环境。Python 版本建议 3.10 以上,因为 Agent Framework 用到了较新的类型标注语法。我实测下来,3.11 和 3.12 都比较稳。虚拟环境用 venv 或 conda 都行,关键是隔离依赖,避免和系统里其他包冲突。
python -m venv agent-fw-env source agent-fw-env/bin/activate # Windows 用 agent-fw-env\Scripts\activate python -m pip install --upgrade pip接下来安装 Agent Framework 和 OpenAI 客户端。Agent Framework 的包名以官方仓库为准,通常可以通过 pip 从源码或发布包安装。如果你只是验证,先装核心包和 openai 客户端即可。
pip install agent-framework openai然后准备 TaoToken 的接入信息。你需要三样东西:Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api,注意这里不加任何查询参数。API Key 在控制台的 API Keys 页面生成,生成后只显示一次,记得立刻保存到环境变量里,不要硬编码进代码。
export TAOTOKEN_API_KEY="sk-你的实际key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows PowerShell 用$env:TAOTOKEN_API_KEY="sk-..."。如果你用.env文件管理,记得把.env加入.gitignore,避免 Key 泄露。这一步看起来基础,但我见过太多人把 Key 直接写进脚本然后推到公开仓库,后面只能紧急轮换。
Model ID 的选择取决于你要验证的能力。做工具调用和多轮对话,建议选一个支持 function calling 的模型。你可以在模型对话页面先确认目标模型是否可用,再把它填进配置。这里先不写死具体模型名,因为模型列表会更新,你以控制台实际显示的 ID 为准。
还有一个前置动作:确认你的网络能正常访问https://taotoken.net/api。可以用一个最简单的 curl 测试连通性,不需要复杂参数。
curl -s -o /dev/null -w "%{http_code}" https://taotoken.net/api如果返回 401 或 404,说明网络通了,只是没带认证或路径不对,这是正常的。如果直接超时,那要先排查本地网络。这一步能帮你把“网络问题”和“配置问题”提前分开,后面排错会轻松很多。
3. 可复制配置:Agent Framework 的 chat_client 与 settings 片段
这一节是核心,我给出可以直接复制运行的配置片段。Agent Framework 的客户端注入方式和 AutoGen 不同,它要求你显式构造一个 chat client,再传给 Agent。下面这段代码展示了如何用 TaoToken 的 Base URL 和 Key 构造一个兼容 OpenAI 的客户端,并创建一个带工具的 Agent。
先看配置部分。我习惯把模型配置单独放一个字典,方便切换。
import os from openai import AsyncOpenAI TAOTOKEN_BASE_URL = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") TAOTOKEN_API_KEY = os.environ["TAOTOKEN_API_KEY"] MODEL_CONFIG = { "model_id": "你的模型ID", "base_url": TAOTOKEN_BASE_URL, "api_key": TAOTOKEN_API_KEY, } client = AsyncOpenAI( base_url=MODEL_CONFIG["base_url"], api_key=MODEL_CONFIG["api_key"], )注意这里用的是AsyncOpenAI,因为 Agent Framework 的调用链是异步的。如果你用同步客户端,后面 await 会报错。Base URL 结尾不要加/v1,TaoToken 的路径已经处理好了,加了反而会 404。
接下来定义工具。Agent Framework 的工具注册比 AutoGen 更显式,你需要把函数和它的描述、参数 schema 一起传进去。下面是一个查询天气的示例工具,纯本地模拟,不依赖外部 API。
def get_weather(city: str) -> str: """根据城市名返回天气信息。""" fake_data = { "北京": "晴,18°C", "上海": "多云,22°C", "深圳": "小雨,26°C", } return fake_data.get(city, f"{city}:暂无数据") weather_tool = { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"], }, }, }然后创建 Agent。Agent Framework 里用ChatAgent,指令用instructions,工具通过tools传入。
from agent_framework import ChatAgent agent = ChatAgent( chat_client=client, instructions="你是一个助手,需要天气信息时调用 get_weather 工具。", tools=[weather_tool], model_id=MODEL_CONFIG["model_id"], )如果你用的是配置文件方式,比如 TOML,可以这样写,路径放在项目根目录的config.toml:
[model] base_url = "https://taotoken.net/api" model_id = "你的模型ID" [agent] instructions = "你是一个助手,需要天气信息时调用 get_weather 工具。"读取时用tomllib(Python 3.11+)或tomli。这样做的目的是把易变的配置和代码分离,切换模型时不用改 Python 文件。
这里要强调三件套的完整性:Base URL 必须是https://taotoken.net/api,Key 从环境变量读取,Model ID 和控制台一致。三者缺一,调用都会失败。我见过有人只填了 Base URL 和 Key,忘了 Model ID,结果请求发出去返回模型不存在,排查半天。
4. 端到端验证:一次工具调用请求与成功结果确认
配置写好后,最关键的是跑一次完整的端到端调用,确认工具调用和消息传递都正常。这一节我给出完整的验证脚本,并说明预期输出。
import asyncio from agent_framework import ChatAgent from openai import AsyncOpenAI import os async def main(): client = AsyncOpenAI( base_url=os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api"), api_key=os.environ["TAOTOKEN_API_KEY"], ) def get_weather(city: str) -> str: data = {"北京": "晴,18°C", "上海": "多云,22°C"} return data.get(city, f"{city}:暂无数据") weather_tool = { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, }, } agent = ChatAgent( chat_client=client, instructions="需要天气时调用 get_weather。", tools=[weather_tool], model_id="你的模型ID", ) response = await agent.run("北京今天天气怎么样?") print("最终回复:", response) asyncio.run(main())运行后,你期望看到的行为是:模型先返回一个工具调用请求,参数里包含city: "北京",框架执行get_weather,把结果回传给模型,模型再生成自然语言回复。最终打印出来的应该是类似“北京今天晴,18°C”的内容。
如果模型直接回答而没有调用工具,说明工具描述不够清晰,或者模型不支持 function calling。你可以把instructions写得更强制,比如“必须调用 get_weather 获取天气,不要凭记忆回答”。如果调用工具后报错,重点看错误信息里的choices字段,通常是返回结构解析失败。
验证成功后,你可以对比 AutoGen 的调用方式。AutoGen 里工具注册往往通过register_for_llm和register_for_execution两个装饰器,消息传递靠initiate_chat循环。Agent Framework 把工具调用收敛到一次run里,框架内部处理了工具执行和结果回传。这个差异意味着迁移时,你不需要再手写对话循环,但需要确保工具 schema 符合 OpenAI 规范。
实测下来,一次成功的端到端调用应该在一到三秒内完成,取决于模型响应速度。如果超过十秒还没返回,先检查网络,再检查模型 ID 是否正确。验证通过后,你就可以在这个基础上加更多工具、加第二个 Agent,测试多智能体协作。
5. 常见报错排查:401、local proxy failed 与 reading choices 对照
这一节我按真实遇到的报错来整理,每个都给出原因和动作。你迁移框架时大概率会碰到其中几个。
第一个是 401 Unauthorized。这个最直接,Key 不对或没带。检查TAOTOKEN_API_KEY是否真的注入到进程环境里。有时候你在 shell 里 export 了,但 IDE 的调试配置没继承,就会 401。动作:在代码里打印os.environ.get("TAOTOKEN_API_KEY")[:8],确认前几位存在。如果为空,说明环境变量没生效。
第二个是local proxy failed或连接被拒绝。这类报错通常和本地网络配置有关。检查是否有残留的代理环境变量,比如HTTP_PROXY、HTTPS_PROXY。如果有,先 unset 再跑。动作:unset HTTP_PROXY HTTPS_PROXY,然后重试。另外确认 Base URL 没有多余斜杠,https://taotoken.net/api/和https://taotoken.net/api在某些客户端里行为不同。
第三个是reading choices相关错误,比如KeyError: 'choices'或list index out of range。这通常意味着返回的 JSON 结构和你预期的不一样。原因可能是 Base URL 少了/api,请求打到了首页,返回的是 HTML 而不是 JSON。动作:用 curl 直接请求一次,看返回体。
curl -s https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"你的模型ID","messages":[{"role":"user","content":"hi"}]}'如果返回 HTML,说明路径不对。如果返回 JSON 但没有 choices,看 error 字段。
第四个是 OAuth 或认证方式不匹配。Agent Framework 某些版本可能默认走 Azure 认证链,如果你传的是普通 API Key,会报认证失败。动作:确认你用的是AsyncOpenAI而不是 Azure 专用客户端。如果你之前配过az login,它可能干扰,先清理相关环境变量。
第五个是模型 ID 不存在。报错通常是model not found。动作:去控制台确认模型 ID 拼写,注意大小写和连字符。不要凭记忆写,直接复制。
第六个是工具调用参数解析失败。报错里会提到function arguments或json decode。原因通常是模型返回的参数不是合法 JSON,或者你的 schema 里 required 字段和实际不符。动作:简化 schema,先只保留一个必填参数,跑通后再加。
把这些报错对照表放在手边,迁移时能省不少时间。大部分问题集中在认证、路径、模型 ID 这三类,逐个排除即可。
6. 从验证到落地:用统一 Key 驱动多智能体工作流
验证跑通之后,下一步是把单个 Agent 扩展成多智能体工作流。Agent Framework 的 graph 编排允许你把多个 Agent 节点连起来,中间加条件判断。比如一个“研究员”Agent 负责查资料,一个“写手”Agent 负责整理成文,两者通过共享的 thread 传递状态。
这里的关键是:所有 Agent 共用同一个 TaoToken 客户端,只是 Model ID 可以不同。你可以在配置里为每个 Agent 指定不同的模型,比如研究员用推理强的,写手用生成快的。切换时只改配置,不改代码结构。
researcher = ChatAgent( chat_client=client, instructions="你负责收集信息,输出要点。", tools=[search_tool], model_id="推理模型ID", ) writer = ChatAgent( chat_client=client, instructions="你负责把要点整理成通顺段落。", model_id="生成模型ID", )然后通过 workflow 把两者串起来。Agent Framework 的编排 API 以官方文档为准,核心思路是定义节点和边,把 researcher 的输出作为 writer 的输入。这样你就有了一条可复用的流水线。
长期做编码或 Agent 开发的话,可以考虑用 Coding Plan 来管理调用额度,避免每次验证都手动换 Key。接入文档里有完整的客户端示例,遇到配置问题可以先查那里。模型对话页面适合快速确认某个模型是否支持工具调用,省得在代码里反复试。
最后说一个实用技巧:把 Base URL、Key、Model ID 三件套写进一个settings.toml,代码里只读配置。这样你在本地、测试、生产之间切换时,只改配置文件,不动业务代码。迁移框架时也一样,Agent 逻辑可以重写,但模型接入层保持稳定。