3. 实现工具调用参数校验
任务目标
在AI Agent Harness中,当大模型请求调用工具时,传入的参数可能不符合工具定义(类型错误、缺少必填字段、多余字段等)。本案例将集成JSON Schema校验器,在工具执行前拦截非法参数,返回结构化错误信息,确保只有符合工具参数定义的调用才会被真正执行。
核心实现
import json
from typing import Any, Callable, Dict
from jsonschema import Draft7Validator, ValidationError
class Tool:
"""工具定义:包含名称、参数JSON Schema及执行函数"""
def __init__(self, name: str, param_schema: Dict[str, Any], handler: Callable[..., Any]):
self.name = name
self.param_schema = param_schema
self.handler = handler
self._validator = Draft7Validator(param_schema)
def validate_params(self, params: Dict[str, Any]) -> None:
"""校验参数,不通过时抛出带详细信息的ValidationError"""
errors = sorted(self._validator.iter_errors(params), key=lambda e: list(e.path))
if errors:
details = []
for err in errors:
loc = "/".join(str(p) for p in err.path) or "(root)"
details.append(f"{loc}: {err.message}")
raise ValidationError("; ".join(details))
def invoke(self, params: Dict[str, Any]) -> Any:
"""先校验后执行"""
self.validate_params(params)
return self.handler(**params)
class Harness:
"""Agent Harness:管理工具注册与调用"""
def __init__(self):
self._tools: Dict[str, Tool] = {}
def register_tool(self, tool: Tool) -> None:
self._tools[tool.name] = tool
def call_tool(self, name: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""供LLM调用的入口,返回统一格式结果"""
tool = self._tools.get(name)
if not tool:
return {"success": False, "error": f"工具 '{name}' 不存在"}
try:
result = tool.invoke(params)
return {"success": True, "result": result}
except ValidationError as e:
return {"success": False, "error": f"参数校验失败: {e.message}"}
except Exception as e:
return {"success": False, "error": f"执行异常: {str(e)}"}
# ---------- 最小可复现示例 ----------
def get_weather(city: str, days: int = 1):
"""模拟天气查询工具"""
return f"{city}未来{days}天天气晴朗"
def send_email(to: str, subject: str, body: str):
"""模拟邮件发送工具"""
return f"邮件已发送至{to},主题:{subject}"
# 定义工具参数Schema
weather_schema = {
"type": "object",
"properties": {
"city": {"type": "string", "minLength": 1},
"days": {"type": "integer", "minimum": 1, "maximum": 7}
},
"required": ["city"],
"additionalProperties": False
}
email_schema = {
"type": "object",
"properties": {
"to": {"type": "string", "format": "email"},
"subject": {"type": "string", "minLength": 1},
"body": {"type": "string", "minLength": 1}
},
"required": ["to", "subject", "body"],
"additionalProperties": False
}
# 注册工具
harness = Harness()
harness.register_tool(Tool("get_weather", weather_schema, get_weather))
harness.register_tool(Tool("send_email", email_schema, send_email))
# 模拟LLM发起的工具调用
test_calls = [
("get_weather", {"city": "北京", "days": 3}), # 合法
("get_weather", {"city": "上海", "days": 0}), # days小于最小值
("get_weather", {"city": "广州", "extra": "x"}), # 多余字段
("get_weather", {"days": 2}), # 缺少必填city
("send_email", {"to": "a@b.com", "subject": "hi", "body": "hello"}), # 合法
("send_email", {"to": "not-an-email", "subject": "hi", "body": "hello"}), # 邮箱格式错误
]
if __name__ == "__main__":
for tool_name, params in test_calls:
resp = harness.call_tool(tool_name, params)
status = "✓" if resp["success"] else "✗"
print(f"{status} {tool_name}({json.dumps(params, ensure_ascii=False)})")
if resp["success"]:
print(f" → {resp['result']}")
else:
print(f" → {resp['error']}")
运行与验证
- 安装依赖:
pip install jsonschema - 运行代码:将上述代码保存为
tool_validation.py,执行python tool_validation.py - 预期输出(与代码逻辑严格对应):
✓ get_weather({"city": "北京", "days": 3})
→ 北京未来3天天气晴朗
✗ get_weather({"city": "上海", "days": 0})
→ 参数校验失败: days: 0 is less than the minimum of 1
✗ get_weather({"city": "广州", "extra": "x"})
→ 参数校验失败: extra: Additional properties are not allowed ('extra' was unexpected)
✗ get_weather({"days": 2})
→ 参数校验失败: city: 'city' is a required property
✓ send_email({"to": "a@b.com", "subject": "hi", "body": "hello"})
→ 邮件已发送至a@b.com,主题:hi
✗ send_email({"to": "not-an-email", "subject": "hi", "body": "hello"})
→ 参数校验失败: to: 'not-an-email' is not a 'email'
通过判定标准:6条测试中,3条合法调用返回success: true并正确执行,3条非法调用返回success: false且错误信息准确指出具体字段与原因。若输出与上述一致,说明JSON Schema校验已正确集成到Harness的工具调用链路中。
关键说明
- 校验时机:校验发生在
Tool.invoke()内部,即工具执行函数被调用之前,确保非法参数不会触达业务逻辑。 - 错误信息格式:使用
iter_errors收集所有错误(而非只报第一个),并拼接字段路径与具体原因,便于LLM根据错误信息自行修正参数后重试。 - Schema能力:本案例演示了
type、required、minimum/maximum、additionalProperties: false(禁止多余字段)及format: email(格式校验)。实际项目中可按需扩展enum、pattern、嵌套对象等JSON Schema特性。 - 集成方式:在真实Harness中,
call_tool方法通常由LLM的function calling机制触发,返回的{"success": false, "error": ...}结构可直接回传给模型作为工具调用的错误反馈。