跳到主要内容
Router One

用 OpenAIChatCompletionClient 把 Microsoft AutoGen 接到 Router One

在 Microsoft AutoGen 里接入 Router One,就是用 autogen-ext 的 OpenAIChatCompletionClient,把 base_url 设为 https://api.router.one/v1,填入你的 Router One Key 和精确的目录 ID,再传一个 model_info 字典,因为 AutoGen 不认识 anthropic/claude-sonnet-5 这样的目录 ID。这样每次模型调用都是一次带成本和延迟 Trace 的 POST /v1/chat/completions,而 Agent、工具、记忆和团队轮次仍在你的进程里运行。AutoGen 是微软用于构建 Agent 和多 Agent 团队的 Python 框架(autogen-agentchat 提供 AssistantAgent、RoundRobinGroupChat 和 SelectorGroupChat,autogen-ext 提供模型客户端);该项目已进入维护模式,后继者是 Microsoft Agent Framework,最新版本是 2025 年 9 月 30 日发布的 0.7.5。本指南核对的版本为 autogen-agentchat 与 autogen-ext 0.7.5,搭配 openai 3.17.0,会说明 model_info 每个键改变了什么,文末给出 AG2 1.0.6 的对应设置,AG2 是一个自称前身为 AutoGen 的独立项目。

安装 autogen-agentchat 与 autogen-ext[openai],再设置两个变量

使用 Python 3.10 或更新版本。模型客户端在 autogen-ext 里,需要它的 openai extra,这会带上 openai 库(1.93 及以上任意版本;本指南运行时为 3.17.0)和 tiktoken;autogen-agentchat 则提供 Agent 与团队。把两个包都固定在 0.7.5,也就是本指南核对的版本。有两个名字相近的包,都不能装出本指南的这套环境:PyPI 上的 autogen 现在是 AG2 Classic;pyautogen 0.10.0 是微软发布的过渡包,只会拉取 autogen-agentchat,不带 autogen-ext[openai],也不固定 0.7.5,所以请直接安装上面两个包;如果你的代码写的是 import autogen,请看下方常见问题。微软建议新项目使用 Agent Framework;如果你正在迁移,本站的 Agent Framework 接入指南写明了 base URL 在那边填在哪里。下面是 macOS/Linux 的 shell 示例,ROUTER_ONE_* 是本示例自己定义并显式读取的变量名。省略 api_key 或 base_url 时,openai 库会回退读取 OPENAI_API_KEY 和 OPENAI_BASE_URL;完全没有 Key 时,构造函数在发出任何请求之前就会抛出 OpenAIError(openai 3.17.0 的报错消息以 Missing credentials 开头;1.93 等较早版本写的是 The api_key client option must be set)。

terminal
python -m pip install "autogen-agentchat==0.7.5" "autogen-ext[openai]==0.7.5"
export ROUTER_ONE_API_KEY="sk-your-router-one-key"
export ROUTER_ONE_MODEL_ID="anthropic/claude-sonnet-5"

把 AutoGen 配置到 Router One base URL

保存为 autogen_router_one.py,再运行 python autogen_router_one.py。model 填目录里的模型 ID,原样发送:客户端既不会去掉也不会解析 provider 前缀,所以 Dashboard → Logs 里每条记录的模型应与 /models 逐字一致。base_url 填带 /v1 的 base URL,它会交给 openai 库的 AsyncOpenAI 客户端,由后者拼接 /chat/completions;少了 /v1,请求会发到 /chat/completions,而不是 /v1/chat/completions。api_key 显式传入。model_info 告诉 AutoGen 这个模型能做什么,因为它内置的能力表只收录 gpt-5、gemini-2.5-flash 这类厂商原始模型名,没有任何目录 ID 能匹配上:vision、function_calling、json_output 和 family 是必填项,缺任何一个都会抛出 ValueError;structured_output 缺失时在构造阶段只发出警告,但一用到结构化输出就会抛出 KeyError,所以务必填写;multiple_system_messages 可选。vision 和 function_calling 按模型详情页的「视觉理解」与「工具调用」填写;本示例不发图片,所以 vision 保持 False。详情页不列 JSON 模式和结构化输出,这两项在一次真实请求成功之前保持 False。这些标志决定 AutoGen 同意发送什么,下一节列出每个键的作用。family 保持 ModelFamily.UNKNOWN,这是目录 ID 的中性取值。AutoGen 自己的参考文档也写明,用这个客户端调用非 OpenAI 模型未经测试、也不作保证,所以工具、流式和图片都要各用一次真实请求确认。第一个 Agent 带一个 Python 函数作为工具:AutoGen 把它的 JSON schema 放进 tools 字段并带上 tool_choice auto,模型调用它时在你的进程里执行该函数;max_tool_iterations=2 让它在第二次请求里把工具结果发回去,这次请求带着同样的 tools。如果模型对这次请求用文本作答,这条回复就结束运行;如果它再次调用工具,这第二轮就是最后一轮,运行以 ToolCallSummaryMessage 结束。保持默认设置时,运行在第一次请求后就结束,返回一条装着工具原始输出的 ToolCallSummaryMessage。reflect_on_tool_use=True 同样会多发一次请求,但那次请求去掉了 tools 字段、只保留工具调用历史,所以依赖它之前,先用一次真实请求在 Logs 里确认你的 ID 能处理。total_usage() 汇总该客户端各次调用中 API 报告的 prompt 与 completion token。第二个 Agent 走流式:model_client_stream=True 会把 stream 设为 true,stream_options include_usage 则要求最后返回一个带 token 用量的分块。它单独用一个客户端,因为传给构造函数的 stream_options 会随该客户端的每个请求发送,非流式的 create() 调用也不例外。

autogen_router_one.py
import asyncio
import os

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
from autogen_core.models import ModelFamily
from autogen_ext.models.openai import OpenAIChatCompletionClient

# AutoGen cannot look up capabilities for any catalog ID, openai/ ones included.
# vision / function_calling: True only if the model's page lists Vision / Tool calling.
# json_output / structured_output: True only after one real request works.
MODEL_INFO = {
    "vision": False,
    "function_calling": True,
    "json_output": False,
    "structured_output": False,
    "family": ModelFamily.UNKNOWN,
    "multiple_system_messages": False,
}


def router_one_client(**extra) -> OpenAIChatCompletionClient:
    # Every model call is one POST /v1/chat/completions
    return OpenAIChatCompletionClient(
        model=os.environ["ROUTER_ONE_MODEL_ID"],
        api_key=os.environ["ROUTER_ONE_API_KEY"],
        base_url="https://api.router.one/v1",
        model_info=MODEL_INFO,
        **extra,
    )


async def get_order_status(order_id: str) -> str:
    """Look up the shipping status of an order."""
    return f"Order {order_id} shipped yesterday and arrives on Friday."


async def main() -> None:
    # 1. One tool call, then a second request (with the same tools) that writes the answer
    client = router_one_client()
    agent = AssistantAgent(
        "support_agent",
        model_client=client,
        tools=[get_order_status],
        system_message="You answer order questions. Call the tool for order data.",
        max_tool_iterations=2,
    )
    result = await agent.run(task="Where is order A-1042?")
    print(result.messages[-1].to_text())
    print(client.total_usage())  # usage the API reported, summed
    await client.close()

    # 2. Streaming: without stream_options the reported usage stays at 0
    stream_client = router_one_client(stream_options={"include_usage": True})
    streamer = AssistantAgent("streamer", model_client=stream_client, model_client_stream=True)
    await Console(streamer.run_stream(task="Say hello in one sentence."))
    await stream_client.close()


if __name__ == "__main__":
    asyncio.run(main())

AutoGen 0.7.5 里 model_info 每个键的作用

AutoGen 在构造请求之前先检查 model_info,并拒绝标志不允许的功能,所以标志填错,要么挡住模型本来具备的功能,要么放行模型无法处理的请求。把标志设为 True 并不会增加任何能力:以 /models 上的模型详情页为准,再用一次真实请求确认。另有两个构造参数独立于 model_info 影响消息格式:user 消息会带一个取值为消息来源的 name 字段(任务消息为 user),如果模型拒绝这个字段,可用 include_name_in_message=False 去掉;add_name_prefixes=True 会在正文开头额外写入「<source> said:」,但 name 字段仍会发送,需要在正文里标明发言者、又不想带这个字段时,要同时设 include_name_in_message=False。下表各行都对照 0.7.5 源码和本地模拟服务核对过。

model_info 键AutoGen 0.7.5 怎么用它Router One ID 该填什么
visionFalse:AssistantAgent 发送前把每张图片替换成文本 <image>,直接调用 create() 并传入图片会抛出 ValueError。True:图片以 image_url 内容块发送只有模型详情页列出「视觉理解」时才填 True
function_callingFalse:带工具构造 AssistantAgent 时,在发出任何请求之前就抛出 ValueError。True:每个工具以函数 schema 放进 tools 字段,除非工具自己设置,否则 strict 为 false只有模型详情页列出「工具调用」时才填 True
json_output控制 JSON 模式:json_output=True 会加上 response_format json_object;该标志为 False 时,发送前就抛出 ValueError用一次真实请求确认 JSON 模式可用后再填 True
structured_output控制 Pydantic 输出:output_content_type,或把 json_output 设为一个模型类时,会通过 openai 库的 parse 辅助方法发送 strict 为 true 的 response_format json_schema;False 会抛出 ValueError;缺失这个键时构造阶段只警告,一旦请求结构化输出就会抛出 KeyError: 'structured_output'显式填写;确认该模型的结构化输出可用后再填 True
family必填。它会影响(包括但不限于)消息的预处理方式和一项团队行为:Claude 常量会丢弃内容为空或只含空白的 user 与 assistant 文本消息,Gemini 常量会把空内容换成一个空格,OpenAI 常量会让 SelectorGroupChat 把选人提示作为 system 消息而不是 user 消息发送,R1 会解析 <think> 标签。这些常量只到 gpt-5、claude-4 和 gemini-2.5 为止ModelFamily.UNKNOWN("unknown"):AutoGen 随后按模型名做前缀匹配,当前目录里的 ID(无论带不带 provider 前缀)都匹配不上,因此按默认方式处理消息
multiple_system_messages缺失或 False:相邻的 system 消息合并为一条;与前一条 system 消息不相邻的第二条 system 消息会在发送前触发 ValueError。True:每条 system 消息按原位置发送保持 False,除非记忆或其他组件会追加 system 消息;那种情况下设为 True,并用一次请求确认

AutoGen 的哪个类发出哪种请求

OpenAIChatCompletionClient 是本指南配置的类,它只调用 Chat Completions。AutoGen 的其他组件自带客户端,会调用别的端点,其中一些 Router One 并不提供,所以把组件指向网关之前先确认它用的是哪个端点。

AutoGen 组件发出的请求要确认什么
OpenAIChatCompletionClient(create 与 create_stream)POST /v1/chat/completions,流式时带 stream true当前任一聊天模型;Logs 记录会显示精确的模型和状态
ChatCompletionClient.load_component(config),provider 为 autogen_ext.models.openai.OpenAIChatCompletionClient同一个客户端、同样的请求,只是从组件配置构造;这是 AutoGen 为 AutoGen Studio 等基于配置的工具准备的声明式格式config 使用相同的键,包括 model_info。dump_component() 本身把 api_key 保留为密文值,但它的 JSON 形式(dump_component().model_dump_json(),或由它写出的文件)会把 api_key 写成 **********,从这份 JSON 加载的配置会把这串字符当作 Key 发送,被网关当作无效 Key 以 401 拒绝;配置里不写 api_key、改设 OPENAI_API_KEY,或在加载时补上 Key
autogen_ext.agents.openai.OpenAIAgent通过你传入的客户端发送 POST /v1/responses,store 默认为 true只适用于详情页列出 /v1/responses 的 ID(当前在售的 GPT 系列、DeepSeek 与 Grok 对话模型),其他 ID 会返回 400 must be called via。它只接受 Responses 内置工具,不接受你的 Python 函数:image_generation 工具会被 400 拒绝(生图请走 /v1/images/generations),file_search 需要 vector_store_ids,而 Router One 不提供创建它们所需的文件或向量库 API。从第二轮起它用 previous_response_id 续接,所以多轮对话以及 web_search_preview 等托管工具,都要先用一次真实请求确认再依赖
autogen_ext.agents.openai.OpenAIAssistantAgentAssistants API:assistants、threads 和 filesRouter One 不提供;改用 AssistantAgent 配 OpenAIChatCompletionClient
ChromaDBVectorMemory 配 OpenAIEmbeddingFunctionConfig发往 /v1/embeddings 的 embedding 请求Router One 不提供;保留 ChromaDB 默认的 embedding 函数(all-MiniLM-L6-v2),或使用其他 embedding 服务商

数清一次运行或一个团队背后的请求

一次 AutoGen 运行是一串模型调用,每次调用在 Dashboard → Logs 里都是独立请求,有各自的 request_id、token 和费用。AssistantAgent 不调用工具时,一次请求就给出回答。带工具且保持默认设置(max_tool_iterations 为 1、reflect_on_tool_use 为 False)时仍然只有一次请求:工具执行后,它的原始输出就是回复。reflect_on_tool_use=True 会多一次请求来写回答;调高 max_tool_iterations 则允许相应轮数的工具调用,每一轮都会重发到目前为止的对话,所以输入 token 逐轮增长。团队会把这些成倍放大。在 RoundRobinGroupChat 和 SelectorGroupChat 里,每个带模型客户端的 Agent(如 AssistantAgent)每发言一轮,至少产生一次请求,而 max_turns 默认为 None,即不设上限,所以要设置 max_turns 或 MaxMessageTermination 之类的终止条件。SelectorGroupChat 还会在有多个候选参与者、且没有 selector_func 做决定时,让它自己的 model_client 挑选下一位发言者;如果回复里没有点名有效参与者,会重试到 max_selector_attempts(默认 3)次,之后团队退回上一位发言者或第一位参与者。在一次本地测试中,模拟服务的回复从不点名任何 Agent,一个 max_turns=2 的双 Agent SelectorGroupChat 发出了 5 次请求,其中 3 次是选人尝试。在框架之下,openai 库默认对连接错误以及 408、409、429 和 5xx 响应重试 2 次,所以一次失败的调用可能是 3 个请求;测试时给客户端传 max_retries=0。402 不会重试。给每个 Agent 进程单独一把设了 maxSpend 的 Key:失控的循环会在上限处收到 HTTP 402 而停下,花费不会超过这个上限,你的其他 Key 也照常可用。每条消息上的 models_usage 和 client.total_usage() 是 API 报告用量的累加,不是费用;不带 include_usage 的流式调用报告为 0;费用以 Logs 记录为准。有一个上下文类需要 AutoGen 查不到的数字:不传 token_limit 的 TokenLimitedChatCompletionContext 会调用 remaining_tokens,而它对目录 ID 会抛出 KeyError,所以要显式传入 token_limit,取值不超过模型详情页上的上下文窗口,并把它的计数视为本地 tiktoken cl100k_base 估算值。Router One 记录每次模型调用,但不运行 AutoGen 的 Agent、工具、记忆或团队轮次。

AG2:自带配置类的独立项目

AG2 自称前身为 AutoGen,但它是一个独立项目,有自己的维护者、包和类,也不需要 model_info。AG2 1.x(pip install "ag2[openai]",import ag2;本指南核对的是 1.0.6)用 OpenAIConfig 配置模型:model 原样填目录 ID,base_url 填带 /v1 的 base URL,api_key 填 Key,省略时回退读取 OPENAI_API_KEY。每次调用都是 POST /v1/chat/completions。streaming=True 会自动加上 stream_options include_usage;工具调用之后,Agent 无需额外设置就会在第二次请求里把结果发回去。同一模块里的 OpenAIResponsesConfig 则调用 /v1/responses,store 默认为 true,所以只适用于详情页列出该端点的 ID。代码里 import autogen 并构造 ConversableAgent 或 LLMConfig 的,是 AG2 Classic,见下方常见问题。AG2 1.x(OpenAIConfig)和 AG2 Classic 都用本地模拟服务核对过。

ag2_router_one.py
# AG2 1.x: python -m pip install "ag2[openai]"
import asyncio
import os

from ag2 import Agent
from ag2.config import OpenAIConfig

agent = Agent(
    "assistant",
    prompt="You answer order questions.",
    config=OpenAIConfig(
        model=os.environ["ROUTER_ONE_MODEL_ID"],
        api_key=os.environ["ROUTER_ONE_API_KEY"],
        base_url="https://api.router.one/v1",
        streaming=True,  # also requests stream_options include_usage
    ),
)


async def main() -> None:
    reply = await agent.ask("Say hello in one sentence.")
    print(reply.body)


asyncio.run(main())

AutoGen 该填哪个模型 ID?

从 /models 页复制精确的模型 ID,保留大小写、连字符和版本后缀,不要用展示名称代替。打开该模型的详情页,核对支持的 API 端点、上下文窗口和工具调用等能力,再与 AutoGen 当前选择的 provider 和功能对应。模型出现在目录里,不等于当前客户端能调用它的所有功能。建议为每个客户端或应用单独建 API Key,并设置 maxSpend 消费上限。

AutoGen 用的是哪种 API 协议?

OpenAI 兼容描述的是接口格式,不能据此推断 Chat Completions(/v1/chat/completions)、Responses(/v1/responses)和 Anthropic Messages(/v1/messages)可以互换。先核对工具当前版本、provider 配置和实际请求路径,再查模型详情与 API 兼容性事实页。一次普通对话成功,也不能证明服务端工具、历史状态或文件编辑功能都受支持。

在 trace 里验证 AutoGen 的调用

先在 AutoGen 发出一次简单文本请求,再到 Dashboard → Logs 按时间、模型和 request_id 核对 trace:tokens、花费、延迟和状态码。成功后再分别验证流式输出、工具调用和多轮历史。失败时保留实际请求路径、完整错误消息及 request_id;没有对应日志时,先查客户端配置和网络,不能仅凭客户端报错认定是网关或上游故障。

常见问题

OpenAIChatCompletionClient 为什么抛出 ValueError: model_info is required when model name is not a valid OpenAI model?

因为 AutoGen 在 autogen-ext 自带的一张厂商模型名表里查找能力,而 Router One 的目录 ID 都不在表里:anthropic/claude-sonnet-5 和 openai/gpt-5.5 带前缀,grok-4.7、deepseek-v4.1-flash 则根本没有收录。这个错误在构造函数里抛出,所以请求没有到达 Router One,Logs 里也没有记录。传入 model_info,至少包含 vision、function_calling、json_output 和 family;缺少 family 时,报错消息是 Missing required field 'family' in ModelInfo,后面注明从 v0.4.7 起强制要求这些必填字段。structured_output 也一并填上:省略它时构造阶段会发出 UserWarning,警告写明该字段在未来版本会变成必填,而一旦请求结构化输出就会抛出 KeyError。model_capabilities 是已弃用的旧参数,不能和 model_info 同时使用。这些值是你的代码向 AutoGen 做的声明,不是网关读取的设置:标志为 True 只是让 AutoGen 发送相应功能,能否生效取决于模型,所以 vision 和 function_calling 按模型详情页的「视觉理解」与「工具调用」填写,再用真实请求分别确认工具、图片和结构化输出。

流式输出正常,但 models_usage 和 total_usage() 显示 0 token,这次调用免费吗?

不免费。流式的 Chat Completions 响应只有在请求用 stream_options include_usage 要求时才会带 token 计数,而 AssistantAgent 调用 create_stream 时不带这个选项,所以 AutoGen 记录的是 RequestUsage(prompt_tokens=0, completion_tokens=0);官方 Agent 教程里流式回复的输出也是同样的 0。像上面的示例那样,给流式 Agent 使用的 OpenAIChatCompletionClient 传入 stream_options={"include_usage": True},最后一个分块就会带上用量,AutoGen 再把它累加起来。自己直接调用客户端时,create_stream(..., include_usage=True) 对单次调用有同样效果。这个客户端只用于流式:构造函数里的值也会随非流式的 create() 调用发送,表现为 stream 为 false 时仍带 stream_options,而 openai 库自己的参数文档说明只应在 stream 为 true 时设置 stream_options。无论 AutoGen 报告什么,每个请求的费用都以 Dashboard → Logs 里的记录为准;应用里用量为 0 或缺失,只表示未知,不表示没有计费。

给 AssistantAgent 加上记忆后抛出 ValueError: Multiple and Not continuous system messages are not supported,怎么解决?

ListMemory 这类记忆类会把检索到的内容作为一条 system 消息加到模型上下文里、排在已有对话之后,而 Agent 自己的 system 消息仍在最前面;不传 system_message 时 AssistantAgent 也会带默认 system 消息,只有显式传 None 才会去掉。multiple_system_messages 缺失或为 False 时,客户端会合并相邻的 system 消息,但与第一条不相邻的第二条会被拒绝,所以这个错误在发出任何请求之前就出现。在 model_info 里设置 "multiple_system_messages": True,记忆的 system 消息就会按原位置发送,排在 user 轮次之后;再用一次真实请求确认所选模型接受这个位置上的 system 消息。另一个办法是给 Agent 传 system_message=None,让记忆的 system 消息成为唯一一条;这时把你的指令移到记忆内容或任务里。任何会追加 system 消息的组件都可能触发同样的错误,不只是记忆。

我的代码写的是 import autogen,用的是 ConversableAgent 或 config_list,这篇指南适用吗?

不直接适用。那是 AutoGen 0.2 的 API,微软在 0.4 里用上文的 autogen-agentchat 和 autogen-ext 包取代了它。同一套 API 以 AG2 Classic 的形式延续,其维护者把它以 autogen 的名字发布在 PyPI 上(核对本指南时为 0.14.1,处于维护模式),所以 pip install autogen 装到的是 AG2 Classic,而不是微软的包;安装时用 pip install "autogen[openai]",才会带上 api_type openai 所需的 openai 库。在 AG2 Classic 里,把 Router One 的值填进 LLMConfig({"api_type": "openai", "model": "<exact-model-id>", "api_key": ..., "base_url": "https://api.router.one/v1"}),或者让 OAI_CONFIG_LIST 文件的每个条目带上同样的键,再用 LLMConfig.from_json(path="OAI_CONFIG_LIST") 加载。字典要按位置参数传入:LLMConfig(api_type=...) 或 LLMConfig(config_list=...) 这样的关键字参数在 0.14.1 里会抛出 TypeError。AG2 1.x(import ag2)又是另一套 API,用的是上文的 OpenAIConfig。它们都不需要 model_info;本地测试中,AG2 1.x 和 AG2 Classic 都把目录 ID 原样发到了 /v1/chat/completions。迁到微软 0.4+ 的 API 或 Agent Framework 属于代码迁移,不是改配置就能完成的。

AutoGen 能通过网关用哪些模型?

选用当前目录中同时支持 AutoGen 所用端点和所需功能的模型。精确 ID、当前单价与能力以 /models 及模型详情为准;不要仅按 GPT、Claude 等系列名判断兼容性。客户端能列出模型,只说明它从自身配置或 GET /v1/models 读到了这个 ID,仍需验证实际调用。

能列出模型,但调用报 400 或 404,怎么办?

先记录实际请求路径和错误消息,再核对精确模型 ID。400 可能是参数、工具类型或模型与端点不匹配;404 可能是请求路径或资源不存在,不能直接判定模型下线。若错误提示 must be called via,按它指明的端点调整客户端 provider,或换用支持当前端点的模型。不要在所有工具里统一增删 /v1 或 /chat/completions。

中国大陆能直连吗?

能。网关在大陆可直连、无需 VPN,配置与全球环境完全一致。

报 401/402/403/429 怎么排查?

先到 Dashboard → Logs 核对请求及错误消息。401 查 Key 是否传入和有效;402 查钱包余额与 maxSpend;403 查 Key 权限和访问限制;429 查请求频率、token 限额及上游限流,按错误来源处理。保留 request_id,再按错误码速查页逐项排查。