给 AI Agent 写一份 Action Manifest:让工具调用从“能跑”变成“可控”
很多团队第一次做 AI Agent,工具调用都是这样开始的:
```python
tools = {
"send_email": send_email,
"query_db": query_db,
"create_ticket": create_ticket
}
```
然后把工具列表交给模型,让 Agent 自己决定调用哪个。
Demo 阶段这样没问题。
但一旦进入真实业务,很快会出现几个问题:
工具用途描述不清。
参数格式不稳定。
必填字段缺失。
高风险动作没有声明。
重复执行导致副作用。
失败后不知道能不能重试。
不同工具返回格式不一致。
所以,AI Agent 要接入业务系统,不能只给它一个函数列表,而应该给每个工具写一份 Action Manifest。
Action Manifest 是什么?
简单说,就是一个工具动作声明文件。它告诉 Agent 和系统:
这个工具能做什么;
需要什么参数;
哪些参数必填;
动作风险等级是什么;
是否允许自动执行;
是否幂等;
失败后能否重试;
返回结构是什么。
Google I/O 2026 之后,Agent 调用工具这件事会越来越常见。Google 官方把 I/O 2026 称为 agentic Gemini era;The Verge 也报道 Gemini Spark 将通过 MCP 扩展到第三方平台,连接更多外部服务。
这意味着,工具调用会从“内部 Demo”变成“真实产品能力”。
越是这样,Action Manifest 越重要。
一个简单的 Manifest 可以这样设计:
```json
{
"name": "send_email",
"description": "Send an email to a specified recipient.",
"risk_level": "high",
"auto_execute": false,
"idempotent": false,
"retryable": false,
"required_permissions": ["email:send"],
"parameters": {
"type": "object",
"required": ["to", "subject", "body"],
"properties": {
"to": {
"type": "string",
"description": "Recipient email address"
},
"subject": {
"type": "string",
"maxLength": 120
},
"body": {
"type": "string",
"maxLength": 5000
}
}
},
"returns": {
"type": "object",
"properties": {
"message_id": {"type": "string"},
"status": {"type": "string"}
}
}
}
```
这个声明比一句“send_email 可以发邮件”清楚很多。
它明确告诉系统:
这是高风险动作。
不能自动执行。
不可幂等。
失败后不建议自动重试。
需要 email:send 权限。
必须有 to、subject、body。
subject 和 body 有长度限制。
接下来,我们可以用 Python 定义 Manifest 数据结构。
```python
from dataclasses import dataclass
from typing import Dict, Any, List
from enum import Enum
class RiskLevel(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
@dataclass
class ActionManifest:
name: str
description: str
risk_level: RiskLevel
auto_execute: bool
idempotent: bool
retryable: bool
required_permissions: List[str]
parameters_schema: Dict[str, Any]
returns_schema: Dict[str, Any]
```
然后写一个参数校验函数:
```python
def validate_required_fields(arguments: Dict[str, Any], schema: Dict[str, Any]) -> List[str]:
missing = []
required = schema.get("required", [])
for field in required:
if field not in arguments or arguments[field] in [None, ""]:
missing.append(field)
return missing
```
再写一个执行前检查:
```python
def validate_action_call(
manifest: ActionManifest,
arguments: Dict[str, Any],
user_permissions: set
) -> Dict[str, Any]:
missing_fields = validate_required_fields(arguments, manifest.parameters_schema)
if missing_fields:
return {
"valid": False,
"reason": "missing_required_fields",
"fields": missing_fields
}
for permission in manifest.required_permissions:
if permission not in user_permissions:
return {
"valid": False,
"reason": "permission_denied",
"permission": permission
}
if manifest.risk_level in [RiskLevel.HIGH, RiskLevel.CRITICAL] and manifest.auto_execute:
return {
"valid": False,
"reason": "high_risk_action_cannot_auto_execute"
}
return {
"valid": True,
"reason": "validated"
}
```
注意,这里不是在写完整权限系统,而是在确保工具调用前有明确声明和参数约束。
Action Manifest 还应该声明幂等性。
为什么?
因为 AI Agent 很容易出现重复调用。
网络抖动后重试一次。
模型以为上次没执行成功又调用一次。
用户刷新页面导致任务重新触发。
多个 Agent 同时处理同一任务。
如果工具不是幂等的,就会出问题。
比如:
发送两封邮件。
创建两张工单。
重复扣款。
重复修改状态。
重复提交表单。
所以 Manifest 里一定要有 idempotent 字段。
对于非幂等动作,系统应该要求 idempotency_key。
```python
def require_idempotency_key(manifest: ActionManifest, arguments: Dict[str, Any]) -> bool:
if manifest.idempotent:
return False
return "idempotency_key" not in arguments
```
执行前检查可以补上:
```python
def preflight_check(manifest: ActionManifest, arguments: Dict[str, Any], user_permissions: set):
base_result = validate_action_call(manifest, arguments, user_permissions)
if not base_result["valid"]:
return base_result
if require_idempotency_key(manifest, arguments):
return {
"valid": False,
"reason": "idempotency_key_required"
}
return {
"valid": True,
"reason": "ready_to_execute"
}
```
这样,Agent 即使想重复调用,也会被系统要求提供幂等控制。
最后,还要统一返回结构。
不要让每个工具随便返回不同格式,否则 Agent 后续处理会很麻烦。
建议统一成:
```json
{
"ok": true,
"action": "send_email",
"result": {
"message_id": "msg_123",
"status": "queued"
},
"error": null
}
```
失败时:
```json
{
"ok": false,
"action": "send_email",
"result": null,
"error": {
"code": "permission_denied",
"message": "User does not have email:send permission"
}
}
```
对应 Python 封装:
```python
def action_success(action_name: str, result: Dict[str, Any]) -> Dict[str, Any]:
return {
"ok": True,
"action": action_name,
"result": result,
"error": None
}
def action_error(action_name: str, code: str, message: str) -> Dict[str, Any]:
return {
"ok": False,
"action": action_name,
"result": None,
"error": {
"code": code,
"message": message
}
}
```
Action Manifest 的价值在于,它把“模型想调用工具”这件事变成了工程系统可控的动作。
如果你只是做个人 Demo,可以直接写函数。
如果你要做团队系统,至少要有参数 schema。
如果你要接入真实业务,必须声明风险、权限、幂等和返回结构。
如果你要支持多 Agent 并行执行,幂等和审计更不能少。
前期测试不同模型的工具调用规划能力时,可以通过 gpt0424.com 这类综合入口比较 ChatGPT、Claude、Gemini、Grok 在结构化输出、工具选择、参数生成方面的表现。但进入工程落地后,不能只相信模型,要把工具动作写成 Manifest。
Agent 不是不能调用工具。
问题是:你有没有告诉它,每个动作的边界在哪里。
