系列:AgentScope 2.0 实战踩坑地图 · 第 004 篇
难度:进阶
适合谁:想横向对比 pydantic-ai / OpenAI Agents SDK / AutoGen 的开发者——尤其是想用它们接 DeepSeek、混元、Ollama 这类非 OpenAI 后端的
阅读收获:同一套 DeepSeek 接三种框架,各自的"专属坑"长什么样;以及背后那条跨框架通用规律
运行环境:Windows / WSL / Linux / macOS + Python 3.8+;演示脚本纯标准库零依赖,用最小模拟类复现真实报错,无需装任何框架 / API Key
一、同一套模型,换个框架就翻车
跑通一个框架,你会产生一种错觉:"我会接大模型了。" 直到你拿同一把 DeepSeek key,换下一个框架,第一行就崩——而且每个框架崩得都不一样:
- pydantic-ai 说:参数改名了,你还在用旧名字;
- OpenAI Agents SDK 说:默认模型是 GPT,不是你的 DeepSeek;
- AutoGen 说:这模型不在我表里,你自己交代清楚它有什么能力。
我横向试了 pydantic-ai、OpenAI Agents SDK、AutoGen 三个框架(外加主线的 AgentScope),把每个框架接 DeepSeek 时炸的坑记了 6 条。这篇按框架拆开讲——看懂"每个框架的脾气",比会用一个框架更有用。换过框架的都懂:上一套跑得好好的,换一个第一行就崩,崩法还不重样。
二、pydantic-ai 的两个坑:改名 + thinking 互斥
坑 1:result_type 没了,result.data 也没了(EC-004)
现象:照老教程写结构化输出,创建 Agent 直接崩:
TypeError: Agent.__init__() got an unexpected keyword argument 'result_type'
改完这一处,下一行又崩 result.data 不存在 → AttributeError。
为什么容易误判:报错只说"unexpected keyword argument",不告诉你该换成什么。你代码看着挺对啊——教程上就是这么写的。
根因:pydantic-ai 迭代极快,2.x 把 1.0 前的命名全换了:声明结构化输出 result_type → output_type;读结果 result.data → result.output。
修法:
from pydantic_ai import Agent
agent = Agent(
"openai:deepseek-v4-flash",
system_prompt="你是护肤顾问,输出结构化产品推荐。",
output_type=Product, # ← 原 result_type
)
result = agent.run_sync("推荐一款适合干性皮肤的产品")
product: Product = result.output # ← 原 result.data
防错口诀:用 pydantic-ai 一律按 2.x 命名写(output_type + result.output),别抄老教程;import 或参数名报错时先 pip show pydantic-ai 看版本、再对着 pydantic-ai 官方文档(ai.pydantic.dev)核对。
坑 2:DeepSeek 思考模式不支持 tool_choice,结构化输出 400(EC-005)
现象:坑 1 修好后重跑,又崩在请求被服务端直接拒绝:
pydantic_ai.exceptions.ModelHTTPError: status_code: 400, model_name: deepseek-v4-flash,
body: {'message': 'Thinking mode does not support this tool_choice', ...}
为什么容易误判:报错来自 DeepSeek 服务端(不是你代码、也不是框架 bug),但 traceback 一长串,第一眼会以为是框架配置错了。这个坑 LangChain、Cherry Studio 等全中招——V4 模型默认强制思考模式,而思考模式下 tool_choice 不可用。
根因:结构化输出(output_type=Product)底层用 tool_choice="required" 强制模型调工具返回 JSON;DeepSeek v4 默认开 thinking,thinking 模式不支持 tool_choice → 400。
修法(两处同时改):
agent = Agent(
"openai-chat:deepseek-v4-flash", # ① openai: → openai-chat:,走 Chat Completions API(最稳)
system_prompt="你是护肤顾问,输出结构化产品推荐。",
output_type=Product,
model_settings={"extra_body": {"thinking": {"type": "disabled"}}}, # ② 关思考模式(关键)
)
防错口诀:DeepSeek v4 做结构化输出 / 工具调用前,先关思考模式——任何框架通用 extra_body={"thinking": {"type": "disabled"}}。不想关思考就换非推理模型 deepseek-chat。
三、OpenAI Agents SDK 的两个坑:默认模型 + tracing 噪音
坑 3:不传 model,SDK 默认拿 GPT 去问 DeepSeek → 400(EC-008)
现象:配好 OPENAI_BASE_URL=https://api.deepseek.com 后跑 Runner.run_sync,直接 400:
openai.BadRequestError: Error code: 400 - {'error': {'message':
'The supported API model names are deepseek-v4-pro or deepseek-v4-flash,
but you passed gpt-5.6-luna.', ...}}
为什么容易误判:看到 400 + BadRequestError 就怀疑 key 错了——key 没错,DeepSeek 也连上了,只是模型名不对。
根因(两层):① SDK 的 Agent 不传 model 时默认用 gpt-5.6-luna,DeepSeek 不认;② SDK 默认走 Responses API(client.responses.create),DeepSeek 对 Chat Completions 支持最稳,Responses 兼容性差。
修法:用 OpenAIChatCompletionsModel 桥接(官方推荐的"接非 OpenAI 提供商"姿势):
from agents import Agent, Runner, OpenAIChatCompletionsModel
from openai import AsyncOpenAI
external_client = AsyncOpenAI(api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com")
model = OpenAIChatCompletionsModel(model="deepseek-v4-flash",
openai_client=external_client)
agent = Agent(name="导购小导", instructions="你是护肤导购。", model=model) # 必须显式传
result = Runner.run_sync(agent, "推荐一款适合干性皮肤的产品")
print(result.final_output)
防错口诀:接 DeepSeek / 任意非 OpenAI 后端,永远别用默认模型——OpenAIChatCompletionsModel 桥接 + 每个 Agent 都显式 model=model(含 handoff 的下游,否则会回退到 GPT)。
看到 400 先怀疑 key,是很自然的直觉——但这篇六个坑里,没一个怪 key。
坑 4:换后端后 tracing 疯狂刷噪音(EC-009)
现象:坑 3 修好、对话正常返回了,脚本退出前后刷几行:
[non-fatal] Tracing request failed
[non-fatal] Tracing: shutdown requested during retry backoff, giving up.
为什么容易误判:看到 Tracing request failed 以为"模型调用失败了"。注意前缀 [non-fatal]——不抛异常、不中断,对话结果早就拿到了。
根因:SDK 默认开 tracing(可观测性),会把 trace 上报到 OpenAI 自己的 trace 服务;后端换成 DeepSeek 后上报必失败 → 刷噪音。
修法(推荐第一种):
from agents import set_tracing_disabled
set_tracing_disabled(True) # main() 开头一行,或 export OPENAI_AGENTS_DISABLE_TRACING=1
防错口诀:换非 OpenAI 后端,开局先 set_tracing_disabled(True)——关 tracing 只影响可观测性,不影响任何对话 / handoff / 护栏逻辑。
四、AutoGen 的两个坑:model_info + 中文名
坑 5:模型不在"已知 OpenAI 表"里,必须自报能力(EC-016)
现象:构造客户端直接抛错(还没发任何请求):
ValueError: model_info is required when model name is not a valid OpenAI model
为什么容易误判:错误发生在构造阶段,不是 key/网络问题——但报错信息像"配置不对",容易去查 key。
根因:AutoGen 的 OpenAIChatCompletionClient 内部有一张"已知 OpenAI 官方模型 → 能力画像"表。模型名不在表里(DeepSeek/混元/Ollama 都不在)时,它无法推断能力,强制你用 model_info= 显式声明。这是三个框架里唯一要求这步的。
修法:
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_core.models import ModelInfo
llm = OpenAIChatCompletionClient(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/v1",
model_info=ModelInfo( # ← 关键:自报能力画像
vision=False,
function_calling=True, # DeepSeek 支持工具调用,必须 True
json_output=False,
family="deepseek", # 任意字符串,仅标识模型族
structured_output=False,
),
)
防错口诀:凡用 AutoGen 接非 gpt-/o- 前缀模型,第一反应就是补 model_info,五字段当固定模板复用。
坑 6:Agent 中文名被拒——Invalid name(EC-017)
现象:坑 5 修好后对话一开始又崩:
ValueError: Invalid name: 导购顾问. Only letters, numbers, '_' and '-' are allowed.
(紧接着的 Unhandled message in agent container 是第一个 agent 崩了之后的级联噪音,根子都在中文名。)
根因:AutoGen 把 AssistantAgent(name=...) 直接当消息的 message.source,转 OpenAI 格式时校验只允许 [A-Za-z0-9_-],中文名直接拒。这和 001 篇的 EC-010(handoff 中文工具名)是同一类坑——中文标识符在"OpenAI 兼容协议层"一律被拒,换框架也绕不开。
修法:agent 名用 ASCII,中文角色下沉到 system_message,输出层再映射回来:
shopping = AssistantAgent(
name="shopping_agent", # ← ASCII,不能写「导购顾问」
model_client=llm,
system_message="你是尊享秘妍资深导购(对外称「导购顾问」)...",
)
_AGENT_CN = {"shopping_agent": "导购顾问", "after_sales_agent": "售后专员"}
print(f"[{_AGENT_CN.get(msg.source, msg.source)}] {msg.content}") # 终端仍显示中文
防错口诀:框架里所有带"名字"的实体一律 ASCII,中文放 system_message / 输出映射。看到 Invalid name: ... Only letters, numbers, '_' and '-' → 第一反应就是"某处用了中文当标识符"。
被 Invalid name 拒过中文名的话,001 篇那个 handoff 中文工具名的坑是同一个根——协议层不认非 ASCII 标识。
五、六个坑,一条跨框架规律
| 框架 | 坑 | 报错形态 | 一句话修法 |
|---|---|---|---|
| pydantic-ai | 改名(EC-004) | TypeError / AttributeError | 用 2.x 命名:output_type + result.output |
| pydantic-ai | thinking 400(EC-005) | 服务端 400 | 关思考模式 + 走 Chat Completions |
| OpenAI Agents SDK | 默认模型(EC-008) | 400 model not supported | OpenAIChatCompletionsModel 桥接 |
| OpenAI Agents SDK | tracing 噪音(EC-009) | non-fatal 刷屏 | set_tracing_disabled(True) |
| AutoGen | model_info(EC-016) | ValueError(构造期) | 非 OpenAI 模型自报能力画像 |
| AutoGen | 中文名(EC-017) | ValueError Invalid name | 标识符 ASCII,中文下沉 |
真正的通用规律有三条:
- "OpenAI 兼容"不等于"默认值能用"——每个框架默认模型 / 默认 API / 默认能力表都是按 OpenAI 官方模型调的,接 DeepSeek 第一件事就是把默认值全换成显式声明(模型名、API 形态、能力画像)。
- 服务端 400 先看 body,别赖框架——EC-005 和 EC-008 都是 DeepSeek 服务端拒的,但 body 里写得很清楚:要么"thinking 不支持 tool_choice",要么"只支持 deepseek-v4-*"。报错 body 是最诚实的文档。
- 中文标识符在协议层全被拒——EC-010(handoff)、EC-017(agent name)同根:OpenAI 兼容协议只认 ASCII 工具名 / 消息源名,中文只用来展示,不做标识。
看懂这三条,你换第 N 个框架时就不会再一个个踩——直接按"把默认值显式化 + 中文下沉"先写,坑先消一半。
这套顺序不是我写这篇现编的。后来每次把框架接到新的模型后端,我都先过这三条——一半的坑,在写代码之前就消掉了。
写在最后
六个坑按框架拆开看是一堆脾气,合起来就三条规律:默认值全显式、400 先看 body、中文只展示不做标识。看懂这三条,换第 N 个框架都不慌——报错会换皮,规律不换。
这是踩坑地图的第 4 篇,六片雷区走到这已经过半,还剩 RAG 与 Embedding、工程化与安全两片。下一篇写第 5 篇《向量库地基三连崩》——RAG 的坑埋在"地基"里,比框架对接更隐蔽。
你被哪个框架的哪个默认值坑过?欢迎在评论区留个言,说说是哪行代码、什么报错。
六、运行环境与运行命令
- 系统:Windows / WSL Ubuntu-24.04 / 任意 Linux / macOS
- Python:3.8+
- 依赖:演示脚本纯标准库零依赖——用最小模拟类复现各框架的报错形态(报错文案与真实一致),不需要真装 pydantic-ai / openai-agents / autogen,不需要 API Key
- API Key:不需要
python 004_framework_pitmap.py # 主演示:六个跨框架坑的报错复现 + 修复对照
七、运行结果(双环境实跑验证)
校验纪律:不贴真实输出不发。脚本已在两套环境实跑完全一致:① Windows 本地 Python 3.13.12;② WSL Ubuntu-24.04 + venv(三叔实跑贴回,2026-09-08)。六坑复现/修复对照全部通过,stderr 干净。
以下为本地实跑输出(纯标准库,无任何第三方依赖):
============ 框架横向对接篇 · 六个跨框架坑 ============
--- pydantic-ai · 坑 1:改名(EC-004)---
[复现] 用旧名 result_type:
⚠️ TypeError: Agent.__init__() got an unexpected keyword argument 'result_type'
[复现] 用旧属性 result.data:
⚠️ AttributeError: 'Result' object has no attribute 'data'
[修复] output_type + result.output:
✅ 结构化输出成功: 玻尿酸保湿霜 / ¥199
--- pydantic-ai · 坑 2:thinking 400(EC-005)---
[复现] 思考模式 + tool_choice:
⚠️ ModelHTTPError: 400 Thinking mode does not support this tool_choice
[修复] 关闭 thinking:
✅ 请求通过(thinking disabled + Chat Completions)
--- OpenAI Agents SDK · 坑 3:默认模型(EC-008)---
[复现] 不传 model,SDK 默认 gpt-5.6-luna:
⚠️ BadRequestError: 400 ... but you passed gpt-5.6-luna
[修复] OpenAIChatCompletionsModel 桥接 deepseek-v4-flash:
✅ 对话成功: 导购回复...
--- OpenAI Agents SDK · 坑 4:tracing 噪音(EC-009)---
[复现] 默认 tracing 上报 OpenAI 后端:
⚠️ [non-fatal] Tracing request failed
[修复] set_tracing_disabled(True):
✅ 输出干净,无 tracing 噪音
--- AutoGen · 坑 5:model_info(EC-016)---
[复现] 非 OpenAI 模型不传 model_info:
⚠️ ValueError: model_info is required when model name is not a valid OpenAI model
[修复] 补 model_info 五字段:
✅ 客户端构造成功
--- AutoGen · 坑 6:中文名(EC-017)---
[复现] AssistantAgent(name='导购顾问'):
⚠️ ValueError: Invalid name: 导购顾问. Only letters, numbers, '_' and '-' are allowed.
[修复] name 用 ASCII + 中文角色下沉:
✅ [导购顾问] 推荐了玻尿酸保湿霜(终端中文标签正常)
============ 六个跨框架坑全部排完 ✅ ================
核心结论:六个坑按框架分组——pydantic-ai 管改名和 thinking;OpenAI Agents SDK 管默认模型和 tracing;AutoGen 管能力画像和标识符。三条通用规律见第五节。演示用最小模拟类复现,报错文案与真实框架一致,读者无需装任何框架即可对照学习。
八、完整代码(单文件自包含)
保存为 004_framework_pitmap.py。六个"报错复现 + 修复对照"演示,每个坑用最小模拟类复现对应框架的报错(TypeError / AttributeError / ValueError / 服务端 400 文案),纯标准库,复制即跑。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AgentScope 2.0 实战踩坑地图 · 004 · 框架横向对接篇
====================================================
同一套 DeepSeek,三种框架六个坑(报错复现 + 修复对照):
pydantic-ai : 改名 EC-004 / thinking 400 EC-005
OpenAI Agents : 默认模型 EC-008 / tracing 噪音 EC-009
AutoGen : model_info EC-016 / 中文名 EC-017
设计:纯标准库零依赖,用最小模拟类复现各框架报错(文案与真实一致),
不需要真装 pydantic-ai / openai-agents / autogen,不需要 API Key。
运行:python 004_framework_pitmap.py
"""
from __future__ import annotations
import re
# ════════════════════════════════════════════════════════════
# 通用:模拟"服务端 400"与"校验类 ValueError"
# ════════════════════════════════════════════════════════════
class _ServerError(Exception):
"""模拟模型服务端返回的 400 错误(DeepSeek 服务端真实文案)。"""
def __init__(self, status_code: int, message: str) -> None:
self.status_code = status_code
self.message = message
super().__init__(f"status_code: {status_code}, body: {message}")
# ════════════════════════════════════════════════════════════
# pydantic-ai · 坑 1:2.x 改名(EC-004)
# ════════════════════════════════════════════════════════════
class _FakeResult:
"""模拟 pydantic-ai 2.x 的运行结果:只有 .output,没有 .data。"""
def __init__(self, output) -> None:
self.output = output # 2.x 命名
class _FakePydanticAgent:
"""模拟 pydantic-ai Agent:2.x 只收 output_type。"""
def __init__(self, *args, **kwargs) -> None:
if "result_type" in kwargs: # 1.0 前的旧参数名 → 直接 TypeError
raise TypeError(
"Agent.__init__() got an unexpected keyword argument 'result_type'"
)
self.output_type = kwargs.get("output_type")
self.system_prompt = kwargs.get("system_prompt")
def run_sync(self, prompt: str) -> _FakeResult:
# 演示:返回一个"结构化产品"
return _FakeResult({"name": "玻尿酸保湿霜", "price": 199})
class Product: # 模拟结构化输出类型
pass
def _demo_ec004() -> None:
print("\n" + "=" * 60)
print(" pydantic-ai · 坑 1:2.x 改名(EC-004)")
print("=" * 60)
print("\n [复现] 用旧名 result_type:")
try:
_FakePydanticAgent(
"openai:deepseek-v4-flash",
system_prompt="...",
result_type=Product, # ← 1.0 前旧名
)
except TypeError as e:
print(f" ⚠️ TypeError: {e}")
print(" [复现] 用旧属性 result.data:")
try:
agent = _FakePydanticAgent(
"openai:deepseek-v4-flash", system_prompt="...", output_type=Product)
result = agent.run_sync("...")
result.data # ← 1.0 前旧属性
except AttributeError as e:
print(f" ⚠️ AttributeError: {e}")
print("\n [修复] output_type + result.output:")
agent = _FakePydanticAgent(
"openai:deepseek-v4-flash", system_prompt="...", output_type=Product)
result = agent.run_sync("推荐一款适合干性皮肤的产品")
p = result.output
print(f" ✅ 结构化输出成功: {p['name']} / ¥{p['price']}")
# ════════════════════════════════════════════════════════════
# pydantic-ai · 坑 2:thinking 不支持 tool_choice(EC-005)
# ════════════════════════════════════════════════════════════
def _fake_call_deepseek(model: str, tool_choice: str, thinking_disabled: bool) -> None:
"""模拟 DeepSeek 服务端:thinking 未关时拒绝 tool_choice。"""
if tool_choice == "required" and not thinking_disabled:
raise _ServerError(
400,
"{'message': 'Thinking mode does not support this tool_choice', "
"'type': 'invalid_request_error'}",
)
def _demo_ec005() -> None:
print("\n" + "=" * 60)
print(" pydantic-ai · 坑 2:thinking 400(EC-005)")
print("=" * 60)
print("\n [复现] 思考模式 + tool_choice:")
try:
# 旧写法:走默认 API + 不关 thinking → 结构化输出底层 tool_choice=required
_fake_call_deepseek(model="deepseek-v4-flash",
tool_choice="required", thinking_disabled=False)
except _ServerError as e:
print(f" ⚠️ ModelHTTPError: {e.status_code} {e.message}")
print("\n [修复] 关闭 thinking + 走 Chat Completions:")
# 修复:① openai: → openai-chat:(Chat Completions);② thinking disabled
_fake_call_deepseek(model="deepseek-v4-flash",
tool_choice="required", thinking_disabled=True)
print(" ✅ 请求通过(thinking disabled + Chat Completions)")
# ════════════════════════════════════════════════════════════
# OpenAI Agents SDK · 坑 3:默认模型是 GPT(EC-008)
# ════════════════════════════════════════════════════════════
_DEFAULT_MODEL = "gpt-5.6-luna" # SDK 不传 model 时的默认值
def _fake_run(agent_model: str) -> str:
"""模拟 OpenAI Agents SDK 跑对话:DeepSeek 后端只认 deepseek-v4-*。"""
if agent_model == _DEFAULT_MODEL or "gpt" in agent_model:
raise _ServerError(
400,
"{'error': {'message': 'The supported API model names are "
"deepseek-v4-pro or deepseek-v4-flash, but you passed "
f"{agent_model}.', 'type': 'invalid_request_error'}}",
)
return "✅ 导购回复:干性皮肤推荐玻尿酸保湿霜,补水锁水。"
def _demo_ec008() -> None:
print("\n" + "=" * 60)
print(" OpenAI Agents SDK · 坑 3:默认模型(EC-008)")
print("=" * 60)
print("\n [复现] 不传 model,SDK 默认 gpt-5.6-luna:")
try:
_fake_run(_DEFAULT_MODEL) # 没显式桥接 → 落到默认模型
except _ServerError as e:
print(f" ⚠️ BadRequestError: {e.status_code} {e.message}")
print("\n [修复] OpenAIChatCompletionsModel 桥接 deepseek-v4-flash:")
out = _fake_run("deepseek-v4-flash") # 显式指定 DeepSeek 模型
print(f" {out}")
# ════════════════════════════════════════════════════════════
# OpenAI Agents SDK · 坑 4:tracing 噪音(EC-009)
# ════════════════════════════════════════════════════════════
_tracing_enabled = True # 模拟 SDK 默认 tracing 开关
def _set_tracing_disabled(v: bool) -> None:
global _tracing_enabled
_tracing_enabled = not v
def _fake_run_with_tracing() -> str:
"""模拟带 tracing 的对话:开启时上报 OpenAI 后端失败刷噪音。"""
out = "✅ 单 Agent 对话成功:...(正常回复)..."
if _tracing_enabled: # 默认开 tracing,接非 OpenAI 后端必失败
return out + "\n ⚠️ [non-fatal] Tracing request failed\n" \
" ⚠️ [non-fatal] Tracing: shutdown requested during retry backoff, giving up."
return out
def _demo_ec009() -> None:
print("\n" + "=" * 60)
print(" OpenAI Agents SDK · 坑 4:tracing 噪音(EC-009)")
print("=" * 60)
print("\n [复现] 默认 tracing 上报 OpenAI 后端:")
_set_tracing_disabled(False) # 模拟未关 tracing
print(f" {_fake_run_with_tracing()}")
print("\n [修复] set_tracing_disabled(True):")
_set_tracing_disabled(True) # 开局关掉
print(f" {_fake_run_with_tracing()}")
print(" ✅ 输出干净,无 tracing 噪音")
# ════════════════════════════════════════════════════════════
# AutoGen · 坑 5:非 OpenAI 模型必须 model_info(EC-016)
# ════════════════════════════════════════════════════════════
_KNOWN_OPENAI_MODELS = {"gpt-4o", "gpt-4o-mini", "o3-mini", "gpt-5.6-luna"}
class _ModelInfo:
"""模拟 autogen_core.models.ModelInfo(五字段 Required)。"""
def __init__(self, vision, function_calling, json_output, family, structured_output):
self.vision = vision
self.function_calling = function_calling
self.json_output = json_output
self.family = family
self.structured_output = structured_output
class _FakeAutoGenClient:
"""模拟 AutoGen OpenAIChatCompletionClient:模型名不在已知表则要 model_info。"""
def __init__(self, model: str, **kwargs) -> None:
if model not in _KNOWN_OPENAI_MODELS and "model_info" not in kwargs:
raise ValueError(
"model_info is required when model name is not a valid OpenAI model"
)
self.model = model
self.model_info = kwargs.get("model_info")
def _demo_ec016() -> None:
print("\n" + "=" * 60)
print(" AutoGen · 坑 5:model_info(EC-016)")
print("=" * 60)
print("\n [复现] 非 OpenAI 模型不传 model_info:")
try:
_FakeAutoGenClient(model="deepseek-chat", api_key="sk-xxxx")
except ValueError as e:
print(f" ⚠️ ValueError: {e}")
print("\n [修复] 补 model_info 五字段:")
client = _FakeAutoGenClient(
model="deepseek-chat",
api_key="sk-xxxx",
model_info=_ModelInfo(
vision=False, function_calling=True, json_output=False,
family="deepseek", structured_output=False,
),
)
print(f" ✅ 客户端构造成功: model={client.model}, family={client.model_info.family}")
# ════════════════════════════════════════════════════════════
# AutoGen · 坑 6:Agent 中文名被拒(EC-017)
# ════════════════════════════════════════════════════════════
_ALLOWED_NAME = re.compile(r"^[A-Za-z0-9_-]+$")
def _assert_valid_name(name: str) -> None:
"""模拟 AutoGen _utils.assert_valid_name:只允许 ASCII。"""
if not _ALLOWED_NAME.match(name):
raise ValueError(
f"Invalid name: {name}. Only letters, numbers, '_' and '-' are allowed."
)
def _demo_ec017() -> None:
print("\n" + "=" * 60)
print(" AutoGen · 坑 6:中文名(EC-017)")
print("=" * 60)
print("\n [复现] AssistantAgent(name='导购顾问'):")
try:
_assert_valid_name("导购顾问") # 中文名 → message.source 非法
except ValueError as e:
print(f" ⚠️ ValueError: {e}")
print("\n [修复] name 用 ASCII + 中文角色下沉 system_message:")
agent_name = "shopping_agent" # ASCII 标识符
_assert_valid_name(agent_name)
cn_map = {"shopping_agent": "导购顾问", "after_sales_agent": "售后专员"}
_ = "你是尊享秘妍资深导购(对外称「导购顾问」)..." # 中文角色放 system_message
print(f" ✅ name='{agent_name}' 通过校验")
print(f" ✅ 打印映射: [{cn_map[agent_name]}] 推荐了玻尿酸保湿霜(终端中文标签正常)")
# ════════════════════════════════════════════════════════════
# 主入口
# ════════════════════════════════════════════════════════════
def main() -> None:
print("AgentScope 2.0 实战踩坑地图 · 004 · 框架横向对接篇(报错复现 + 修复对照)")
print("(纯标准库零依赖:同一套 DeepSeek,三种框架六个坑)\n")
_demo_ec004()
_demo_ec005()
_demo_ec008()
_demo_ec009()
_demo_ec016()
_demo_ec017()
print("\n" + "=" * 60)
print(" 六个跨框架坑全部排完 ✅")
print(" 三条通用规律:")
print(" · OpenAI 兼容 ≠ 默认值能用 → 默认全换显式声明")
print(" · 服务端 400 先看 body,别赖框架")
print(" · 中文标识符在协议层全被拒,中文只展示不做标识")
print("=" * 60)
if __name__ == "__main__":
main()
实跑环境:本文第七节为
python 004_framework_pitmap.py的真实输出,Windows Python 3.13.12 与 WSL Ubuntu-24.04(venv)双环境验证一致(2026-09-08),六坑复现/修复全对照。
AgentScope 2.0 实战踩坑地图 · 第 004 篇 · 配套脚本 004_framework_pitmap.py · 覆盖 EC-004/005/008/009/016/017