function_tool 报上下文参数不在首位,RunContextWrapper 的签名怎样写
function_tool 用首个参数的位置和类型识别注入上下文。将 RunContextWrapper 或 ToolContext 移到首位,保留业务参数与授权检查,再核对工具 schema 和本地调用。
function_tool 报上下文参数不在首位,RunContextWrapper 的签名怎样写
刚给函数加上 function_tool,还没有调用模型,注册阶段就出现:
RunContextWrapper/ToolContext param found at non-first position in function
这条错误指向函数签名。Agents SDK 用首个参数的位置与类型注解识别注入上下文;如果它出现在普通业务参数后面,就会拒绝构建工具。
把上下文移到首位
例如,工具只允许读取一小组公开记录,同时由模型提供 record_key。上下文应使用专门的 public_records 映射,不要把 API key、个人信息或其他内部配置混入其中:
from dataclasses import dataclass
from typing import Mapping
from agents import RunContextWrapper, function_tool
ALLOWED_RECORD_KEYS = frozenset({"welcome", "hours"})
@dataclass(frozen=True)
class PublicRecordContext:
public_records: Mapping[str, str]
def lookup_public_record(context: PublicRecordContext, key: str) -> str:
if key not in ALLOWED_RECORD_KEYS:
raise ValueError("该记录不允许通过工具读取")
return context.public_records.get(key, "暂无公开记录")
@function_tool
def read_record(
ctx: RunContextWrapper[PublicRecordContext],
record_key: str,
) -> str:
"""读取允许公开的 welcome 或 hours 记录。"""
return lookup_public_record(ctx.context, record_key)
# 仅使用可公开的测试数据作为本例运行上下文。
example_context = PublicRecordContext(
public_records={"welcome": "欢迎使用示例", "hours": "工作日 09:00–17:00"}
)
这里的 ctx 由应用运行上下文提供,模型只负责填写 record_key。允许读取哪些键由应用白名单决定,不能由模型修改;上下文注入本身也不构成读取授权。如果原来把 record_key 写在前面,应调整顺序,同时修改应用中直接调用原函数的代码与测试。
保留类型注解同样重要。为了躲过报错而删掉 RunContextWrapper 注解,会破坏上下文识别;把 context 改成模型需要生成的普通字段,也改变了原本的数据来源。
不调用模型也能检查修复
工具注册和 schema 构建发生在本地。重新加载工具模块后,先确认不再抛出该错误,再查看工具的输入 schema:
- 业务参数 record_key 应出现
- 注入的 ctx 不应要求模型填写
- 必填字段应与业务函数的参数设计一致
接着用受控的本地工具调用测试,确认函数取得的是运行时提供的上下文对象。业务读取逻辑还可以单独做不联网测试:
assert lookup_public_record(example_context, "welcome") == "欢迎使用示例"
for denied_key in ("api_key", "__dict__", "not_allowed"):
try:
lookup_public_record(example_context, denied_key)
except ValueError:
pass
else:
raise AssertionError("未授权键必须被拒绝")
不要只检查“schema 能生成”。还应确认任意未授权键都会在读取前被拒绝;正式应用需要用户或租户隔离时,应另外实现对应授权检查。
版本与适用边界
这个判断可在 Agents SDK 固定源码快照 中直接核对:扫描首个参数之后的参数时,发现 RunContextWrapper 或 ToolContext 就抛出 UserError。该快照体现的是 SDK 上下文注入规则,不能据此推定最早引入该限制的发行版本。
如果 context 本来就是模型应填写的业务字段,应给它真实的业务类型,而不是注入上下文类型。参数名字相同不代表用途相同,位置约定针对的是 SDK 上下文注入。


