OpenAI Agents SDK 工程笔记:function_tool 的 name_override / description_override 命名分裂、白名单对不上与生产禁区 有个退款函数在重构时改名成refund_order_v2为了不让模型侧感知变化同事顺手加了name_overriderefund。代码评审很快过了上线后第二天审批系统报表里高危退款的人工复核数掉到零。查下去才知道审批表的键还是函数名refund_order_v2运行时传过来的却是refund查表落空走了默认放行。这类事故的根子是一个工具同时有两个名字。函数在 Python 里叫一个名字模型、trace、停机名单、审批钩子看到的是另一个。两个名字各自都对配置却分别读了不同的那个。本机openai-agents 0.23.1的function_tool签名里有name_override: str | None和description_override: str | None两个参数。源码注释写得很直白前者提供时替代函数名作为工具名后者提供时替代从 docstring 推出的描述。我在本机构造了一个带 docstring 的函数并加上两个覆盖读回tool.name得到refundtool.description得到覆盖文本原 docstring 完全不出现在 schema 里。没加覆盖的lookup_order则直接用函数名。图注 左侧为命名覆盖必须守住的契约右侧为 smoke 种植的五类串名。正确写法先钉死fromagentsimportAgent,StopAtTools,function_toolfunction_tool(name_overriderefund,description_override退款。仅限已核验订单。,)defrefund_order_v2(order_id:str)-str:returnfrefunded:{order_id}VISIBLE{refund_order_v2.name}# {refund}STOP[refund]APPROVAL{refund:high_risk}assertset(STOP)VISIBLEandset(APPROVAL)VISIBLE agentAgent(namebilling,instructions需要退款时调用 refund。,tools[refund_order_v2],tool_use_behaviorStopAtTools(stop_at_tool_namesSTOP),)要点只有一条。所有控制面一律从tool.name取值禁止手抄字符串。停机名单、审批表、启用开关、提示词里点名的工具、trace 过滤器都应该由装配器从工具对象上读出来再生成配置。函数名只在 Python 代码里有意义出了进程就别再用它。描述覆盖同样要管。description_override一旦写上docstring 改了也不会进 schema。代码作者以为改了 docstring 就更新了能力说明模型看到的仍是旧覆盖文本。两边必须挑一个做唯一来源。离线 smoke 结果P0 PASS 可见名取 override P1 PASS 描述取 overridedocstring 被替换 P2 PASS 停机名单 ⊆ 可见名 F1 FAIL 种植名单写实现名该停不停 F2 FAIL 种植审批表按旧名高危落入默认放行 F3 FAIL 种植开关读实现名能力静默丢失 F4 FAIL 种植提示点旧名模型调用不存在工具 F5 FAIL 种植两环境 override 表不同版本脚本在_w/name-override-smoke/用真实的function_tool构造工具对象读回name与description写进facts.json再用纯数据断言模拟各控制面查表结果落到name_fail.csv。不连模型不花钱几秒跑完。五类失败样本F1 停机名单写实现名。StopAtTools(stop_at_tool_names[refund_order_v2])运行时按工具的name做成员判断refund永远不在名单里。退款调完后又多跑一轮模型日志看起来像模型多嘴。这和上一篇讲的命名停机是同一个坑触发原因换成了覆盖名。F2 审批表按旧名放行。开头那个事故。审批钩子拿到refund表里只有refund_order_v2没有命中就走默认档。默认档如果是放行高危工具就裸奔了。好路径是审批表的键在 CI 里做子集断言并且默认档设成拒绝查不到的工具名直接拦下转人工。F3 开关读错名字静默丢能力。灰度开关配置写着enabled: [refund_order_v2]is_enabled回调按tool.name查结果永远为假退款工具本轮不可见。模型拿不到工具只好安抚用户监控里没有报错只有退款量悄悄归零。F4 提示词点旧名。instructions 里还写着「需要时调用 refund_order_v2」。模型照着提示去调一个 schema 里不存在的名字轻则报工具不存在重则模型自己编一个参数结构去调相近的工具。描述覆盖后能力说明变了提示词里还在讲旧能力也属于这一类。F5 两环境覆盖表漂移。预发的 override 写在代码里生产从配置中心读后者还是旧版本。同一份代码在两个环境里给模型的名字不同复现事故时怎么也对不上。覆盖表必须随代码版本走配置中心只能读不能改它。生产禁区表编号禁区smoke 证据替代做法D1停机名单写函数名F1从tool.name生成名单D2审批表按旧名且默认放行F2子集断言默认拒绝D3开关回调读实现名F3开关键同样取tool.nameD4提示词点名与可见名分裂F4提示词模板插值工具名D5覆盖表跨环境不同版本F5override 随代码发布D6把展示名当计费或权限主键对照另设稳定 id 字段D6 单独说一句。覆盖名的用途是给模型看的产品经理随时可能为了提示效果把refund改成issue_refund。计费、权限、审计如果拿它当主键一次改名就会把历史数据拆成两段。稳定主键应该在工具注册表里另设和展示名分开存。我见过的做法是注册表里每个工具有三列函数限定名、模型可见名、稳定业务 id。账单按业务 id 汇总trace 里三列都记改展示名只动第二列历史报表不受影响。描述覆盖也建议带一个版本号写进 trace事后复盘时能知道当时模型看到的到底是哪一版能力说明。什么时候值得用覆盖覆盖有它的正当用途。函数名受 Python 命名规则限制或者团队里有版本后缀模型侧却需要一个短而稳定的名字这时用name_override很合适。同一个实现要以两种语气暴露给不同 Agent也可以各包一层分别覆盖描述。不适合的场景是用覆盖来遮掩重构。重构时改了函数名又不想动下游配置于是加一个覆盖维持旧可见名。短期确实省事长期仓库里会积累一批「函数名和工具名对不上」的工具新人读代码时根本不知道该搜哪个字符串。这种情况宁可一次把配置迁过去。日志里怎么认出串名串名事故几乎不会抛异常。最常见的三种长相一是停机计数为零但工具调用正常二是审批复核量突然掉到零三是某个工具的调用量归零而用户投诉增加。看到其中任何一种先把本轮可见工具名列表和各控制面配置并排对比再去怀疑模型。建议在 Runner 钩子里把可见工具名、函数限定名、停机名单、审批表键集合一起写进 trace 属性。四列放在一起串名一眼就能看出来。预发检查仓库搜name_override逐个确认对应函数名没有出现在任何配置文件里。停机名单、审批表、启用开关、提示词点名四处做子集断言全部 ⊆ 可见名。审批默认档设为拒绝查不到的工具名进人工队列。description_override存在时docstring 里标注「描述以覆盖为准」避免两处各改各的。升级 SDK 后复跑 smoke确认tool.name仍取覆盖值。和 tool_choice、is_enabled 的交叉ModelSettings.tool_choice如果钉成某个具体工具填的也必须是覆盖后的名字。填函数名时请求会带上一个 schema 里不存在的名字报错发生在模型接口那一侧排查时很容易误判成接口抖动。is_enabled回调拿到的是工具对象本身从对象上取name去查开关表比在回调里写死字符串可靠得多。三处配置同一天改动时最危险。比如运营要求把退款工具对外改名开发改了覆盖忘了改tool_choice测试只覆盖了正常对话没有覆盖强制点名的那条链路。上线后只有走强制点名的那类会话出错比例不高告警阈值也不会触发。预发回归必须把强制点名、开关关闭、停机命中三条链路都跑一遍。测试替身怎么写不需要真模型。直接用function_tool装饰真实函数拿到工具对象读name和description这一步本身就是对 SDK 行为的回归。随后把各控制面配置当成纯数据加载进来逐个做集合比较。夹具至少包含正确覆盖、函数名残留、描述覆盖与 docstring 不一致、两环境覆盖表不同四种。每条夹具写一行期望结果进 CSV评审时和代码并排看。端到端测试可以再加一层假模型让它按提示词里点的名字发起调用断言调用名在可见名集合里。这一层能抓住 F4纯数据断言抓不到模型是否真的会照提示去调旧名。旧仓库怎么迁已经堆了一批覆盖名的仓库先跑一遍扫描列出所有带name_override的工具把函数名、覆盖名、出现过该字符串的配置文件路径打成一张表。函数名在配置里出现的条目就是潜在的 F1 到 F3。按审批相关、停机相关、普通工具的顺序逐批修每修一批跑一次 smoke。不要一次性全改审批表的默认档在修完之前先改成拒绝宁可多转几单人工。小结name_override和description_override改的是模型能看到的那一面函数本身不受影响。生产事故全部出在控制面读错了名字。把所有配置从工具对象上生成审批默认拒绝覆盖表跟代码一起发布再用一份带五类种植错的 smoke 守住命名分裂就很难溜进线上。本目录的 CSV 和 JSON 可以直接附进 code review。