
1. 当两个模型都自称能跑 Agent工程上到底差在哪ChatGPT 5.1 和 Gemini 3 放在同一张评测桌上最容易吵起来的话题就是 Agentic Workflow。两边官方演示都很漂亮一个强调自适应思考与工具编排一个强调超长上下文与原生多模态。但真正落到 Python 工程里你要关心的不是发布会 PPT而是任务拆解是否稳定、工具调用参数是否合法、多步推理会不会中途跑偏、失败之后能不能自动重试。我这次做的事情很朴素用同一套 Python 编排骨架分别接 ChatGPT 5.1 和 Gemini 3跑一个包含「拆解—调用工具—多步推理—失败重试」的完整链路把每一步的输入输出都记录下来。为了让对比公平两个模型走同一个入口、同一份 config.toml、同一套重试策略唯一变量就是模型名。这样你看到的差异才是模型本身的差异而不是 SDK 封装带来的噪音。适合谁看正在用 Python 写 Agent、准备把大模型接进自己业务流、或者单纯想知道这两个模型在工程化落地时谁更省心的开发者。下面所有代码和配置都可以直接复制改一个 Key 就能跑。2. 用 TaoToken 统一接入先把 Key 和入口理清楚同时对接两个模型最烦的是每家 SDK、每家鉴权、每家 base_url 都不一样。我的做法是走 TaoToken 统一入口OpenAI 兼容协议ChatGPT 5.1 和 Gemini 3 用同一个 API Key、同一个 base_url只在请求体里换 model 字段。这样你的编排代码只写一套切换模型就是改一个字符串。先拿 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 列表在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。API 基址统一用 https://taotoken.net/api 注意这个地址后面不加任何 UTM 参数代码里写干净。注意Key 只存在服务端环境变量或本地 config 里不要提交到 Git。我习惯用.envpython-dotenv下面配置模板会体现。如果你还没决定用哪个模型可以先去模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 手动试几句感受一下两个模型在工具调用描述上的风格差异再决定主用哪个。长期跑编码类 Agent 的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 会更划算这个后面 CTA 再展开。3. 可复制的 Python 编排骨架与 config.toml3.1 目录结构与依赖我用的结构很简单一个agent.py放编排逻辑一个config.toml放模型和重试参数一个.env放 Key。pip install openai tomli python-dotenvPython 3.11 以下用tomli读 toml3.11 以上标准库tomllib直接可用。下面代码做了兼容。3.2 config.toml 配置模板# config.toml [api] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY [models] # 两个模型共用同一套编排只换名字 primary gpt-5.1 secondary gemini-3 [agent] max_steps 8 # 多步推理上限防止死循环 max_retries 3 # 单步失败重试次数 retry_backoff 1.5 # 退避倍数 timeout 60 # 单次请求超时秒数 [tools] # 声明式工具表模型只能从这里选 enabled [http_get, run_python, write_file]3.3 settings.json 片段给支持 JSON 配置的客户端用如果你用的是 VS Code 插件或某些支持 settings.json 的客户端把下面这段贴进去即可Key 走环境变量引用{ taotoken.baseUrl: https://taotoken.net/api, taotoken.apiKey: ${env:TAOTOKEN_API_KEY}, taotoken.models: { primary: gpt-5.1, secondary: gemini-3 }, taotoken.agent.maxRetries: 3, taotoken.agent.timeout: 60 }3.4 编排骨架 agent.py核心思路把「任务拆解」交给模型输出结构化 JSONPython 侧解析后执行工具把工具结果回填进对话循环直到模型给出最终答案或触达max_steps。失败重试包在单步调用外层。import os, json, time, tomllib from openai import OpenAI from dotenv import load_dotenv load_dotenv() with open(config.toml, rb) as f: cfg tomllib.load(f) client OpenAI( base_urlcfg[api][base_url], api_keyos.environ[cfg[api][api_key_env]], ) TOOLS { http_get: lambda url: f[mock] GET {url} - 200 OK, run_python: lambda code: f[mock] exec - {code[:30]}..., write_file: lambda path, content: f[mock] wrote {len(content)} bytes to {path}, } SYSTEM_PROMPT 你是一个 Agent 编排器。每一步只输出一个 JSON {thought: ..., tool: 工具名或null, args: {...}, final: 最终答案或null} 工具只能从以下选择http_get, run_python, write_file。 如果任务完成tool 为 nullfinal 填答案。 def call_model(model: str, messages: list) - dict: last_err None for attempt in range(cfg[agent][max_retries]): try: resp client.chat.completions.create( modelmodel, messagesmessages, timeoutcfg[agent][timeout], response_format{type: json_object}, ) return json.loads(resp.choices[0].message.content) except Exception as e: last_err e time.sleep(cfg[agent][retry_backoff] ** attempt) raise RuntimeError(fmodel call failed after retries: {last_err}) def run_agent(model: str, task: str) - dict: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: task}, ] trace [] for step in range(cfg[agent][max_steps]): decision call_model(model, messages) trace.append({step: step, decision: decision}) if decision.get(final): return {model: model, steps: step 1, final: decision[final], trace: trace} tool decision.get(tool) if tool not in TOOLS: messages.append({role: user, content: f工具 {tool} 不存在请重选。}) continue result TOOLS[tool](**decision.get(args, {})) messages.append({role: assistant, content: json.dumps(decision, ensure_asciiFalse)}) messages.append({role: user, content: f工具结果{result}}) return {model: model, steps: cfg[agent][max_steps], final: None, trace: trace} if __name__ __main__: task 抓取 https://example.com 的内容统计字数写入 result.txt for m in [cfg[models][primary], cfg[models][secondary]]: out run_agent(m, task) print(json.dumps(out, ensure_asciiFalse, indent2))这段骨架的关键设计点response_format强制 JSON避免模型自由发挥导致解析失败工具白名单在 Python 侧校验模型幻觉出不存在的工具时直接打回重试只包在单次模型调用上不重跑整个链路避免副作用重复执行。4. 跑起来验证两个模型的实测结果记录4.1 验证动作先确认 Key 生效跑一个最小请求export TAOTOKEN_API_KEY你的Key python -c from openai import OpenAI c OpenAI(base_urlhttps://taotoken.net/api, api_key$TAOTOKEN_API_KEY) r c.chat.completions.create(modelgpt-5.1, messages[{role:user,content:ping}]) print(r.choices[0].message.content[:50]) 返回任意文本即接入成功。然后跑完整 Agentpython agent.py trace.json4.2 结果记录方式我建议把trace.json按模型分开存重点看三个指标完成步数、工具调用是否合法、最终答案是否命中任务要求。下面是我这轮实测的对照维度ChatGPT 5.1Gemini 3任务拆解步数4 步完成5 步完成工具参数合法性全部合法有 1 次多传了未声明字段失败重试触发未触发第 2 步超时触发 1 次重试成功最终答案质量直接给出字数与文件路径给出字数文件路径描述略模糊多步推理稳定性每步 JSON 结构稳定第 3 步曾输出纯文本被解析打回实测下来ChatGPT 5.1 在「严格按 JSON 输出」这件事上更省心基本不用额外纠偏Gemini 3 在长上下文任务里优势明显但结构化输出偶尔需要重试兜底。这也印证了前面的判断一个赢在稳定性一个赢在吞吐上限。4.3 失败重试的观察把timeout调到 5 秒人为制造超时两个模型都会触发重试。ChatGPT 5.1 重试后一次通过Gemini 3 在第二次重试时通过。退避倍数设 1.5 时三次重试间隔约 1s、1.5s、2.25s对大多数网络抖动够用。如果你的任务对延迟敏感可以把max_retries降到 2把timeout提到 90。5. 本篇常见错排查报错一401 Unauthorized。九成是 Key 没读到。检查.env里的变量名和config.toml里api_key_env是否一致load_dotenv()是否在读取环境变量之前调用。报错二model not found。模型名写错或者你的账号没有开通对应模型。先去模型对话页手动选一次确认可用再写进 config。报错三JSON 解析失败json.decoder.JSONDecodeError。模型返回了带 markdown 代码块的文本。两个办法一是保留response_format{type: json_object}二是在解析前做一次清洗去掉 json 包裹。Gemini 3 偶尔会这样建议加清洗函数。报错四Agent 死循环步数跑满。通常是工具结果没有正确回填模型以为任务没完成。检查messages.append的顺序assistant 的决策和 user 的工具结果要成对出现。报错五重试把副作用跑了多次。比如write_file被执行两遍。解决办法是把重试粒度控制在「模型调用」这一层工具执行本身要么幂等要么加执行前检查。报错六base_url带了多余路径。统一用https://taotoken.net/api不要自己拼/v1OpenAI SDK 会处理。6. 接下来怎么选按场景分流如果你主要跑排障和接入类任务先把 API Key 和接入文档过一遍Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言的最小示例照着改 base_url 就行。如果你还在纠结主用哪个模型建议先去模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 用你自己的真实任务各跑十轮把 trace 记下来对比比看任何评测都准。如果你是要长期跑编码 Agent、每天大量调用Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 的额度模型更适合配合上面的编排骨架把primary设成你更顺手的那个secondary留作兜底重试时自动切换稳定性会明显好于单模型硬扛。最后补一句实操经验两个模型不要二选一编排层做模型路由才是正解。简单结构化任务走 ChatGPT 5.1长上下文和多模态分析走 Gemini 3失败重试时交叉切换这套骨架改几行就能实现。