1. 背景:AI 热点正在从模型转向 Agent 工程化
过去讨论 AI 应用,重点更多是模型本身。
例如模型理解能力、上下文长度、推理能力、多模态能力、生成质量等。
但最近一段时间,开发者更关注另一个方向:
AI 如何进入真实工作流。
OpenAI 的 Agents SDK 文档中将 Agent 描述为可以规划、调用工具、在不同专家之间协作,并保持足够状态来完成多步骤任务的应用。OpenAI 文档也提到,Responses API 是构建 Agent 的未来方向,Assistants API 已进入迁移和退场周期。
Google 也在推动 Agent 工程化。Google Cloud 在 2025 年推出 Agent Development Kit,官方介绍称 ADK 是一个开源框架,用于简化 Agent 和多 Agent 系统的端到端开发;Google Cloud 文档也将 ADK 描述为可以构建、调试和部署企业级可靠 AI Agent 的开源开发框架。
与此同时,MCP 和 A2A 也成为热门方向。Anthropic 将 MCP 定义为连接 AI 助手与外部数据源、业务工具、开发环境的开放标准;Google 发布的 A2A 协议则强调让不同企业平台或应用上的 AI Agent 能安全交换信息并协同行动。
这些变化说明,AI 应用的技术重点正在发生转移:
从“模型回答得好不好”,转向“Agent 能不能安全、稳定、可控地使用工具完成任务”。
对后端开发者来说,这里会出现一个非常具体的问题:
当 AI Agent 能调用工具、读取数据、操作系统、访问接口时,企业应该如何管理这些调用?
这就是本文要讨论的主题:AI Agent 工具调用网关。
2. 为什么需要 Agent 工具调用网关?
AI Agent 一旦可以调用工具,就不再只是聊天机器人。
它可能会做这些事情:
- 查询数据库;
- 读取文档;
- 调用内部 API;
- 创建工单;
- 修改代码;
- 运行测试;
- 发送通知;
- 创建 Pull Request;
- 查询订单;
- 调用 CRM;
- 操作业务系统。
这些能力很有价值,但也带来风险。
例如:
- Agent 调用了不该调用的接口;
- Agent 读取了敏感数据;
- Agent 在没有审批的情况下执行了高风险操作;
- Agent 调用失败后没有记录;
- Agent 输出结果无法追溯;
- 多个 Agent 同时调用工具,造成状态混乱;
- 某个工具被误注册,导致权限越界。
所以,企业不能简单让 Agent 直接连所有工具。
更合理的方式是增加一层网关:
Agent ↓ 工具调用网关 ↓ 权限校验 / 风险分级 / 审批 / 审计 ↓ 真实工具与业务系统这个网关的作用不是限制 AI,而是让 AI 工具调用变得可管理。
3. 系统目标
一个基础版 Agent 工具调用网关,需要解决 6 个问题。
3.1 工具能不能统一注册
每个工具都应该有清晰定义:
- 工具名称;
- 工具类型;
- 工具用途;
- 输入参数;
- 输出结构;
- 风险等级;
- 是否启用;
- 是否需要人工审批。
3.2 Agent 能不能按权限调用工具
不同 Agent 不应该拥有相同权限。
例如:
- 客服 Agent 可以查询 FAQ;
- 运营 Agent 可以生成内容;
- 数据 Agent 可以查询报表;
- 研发 Agent 可以读取代码;
- 管理 Agent 可以创建审批任务。
权限要按 Agent、工具、资源范围进行控制。
3.3 高风险调用能不能审批
低风险调用可以直接执行。
例如:
- 查询 FAQ;
- 读取公开文档;
- 生成摘要;
- 查询无敏感数据的统计报表。
高风险调用必须审批。
例如:
- 修改数据库;
- 调用付款接口;
- 批量发送通知;
- 修改生产配置;
- 创建外部发布任务;
- 删除数据。
3.4 每次调用能不能审计
Agent 调用工具时,必须记录:
- 谁调用;
- 调用哪个工具;
- 传入了什么摘要;
- 返回了什么摘要;
- 是否成功;
- 是否触发审批;
- 是否失败;
- 失败原因是什么;
- 调用时间是什么。
3.5 能不能兼容 MCP 和 A2A 思路
MCP 解决的是 Agent 与工具、数据源之间的连接问题。
A2A 解决的是 Agent 与 Agent 之间协作通信的问题。
企业内部可以不一开始完整实现协议,但架构设计要预留接口。
3.6 能不能先做 MVP
第一版不要追求大而全。
先做:
- 工具注册;
- 风险分级;
- 调用日志;
- 简单审批;
- FastAPI 接口;
- 后续再接 MCP Server、A2A Agent、企业内部系统。
4. 架构设计
可以设计成下面几层:
┌─────────────────────────────┐ │ Agent 应用层 │ │ 客服 Agent / 研发 Agent / 数据 Agent │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ 工具调用网关层 │ │ 统一入口 / 鉴权 / 风险判断 / 审计 │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ 工具注册中心 │ │ 工具定义 / 参数 Schema / 风险等级 │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ 审批与策略层 │ │ 人工审批 / 白名单 / 黑名单 / 限流 │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ 真实工具层 │ │ 数据库 / CRM / Git / CI / 文档 / API │ └─────────────────────────────┘这个架构的重点是:
Agent 不直接接业务系统,而是通过工具调用网关统一访问。
这样做有几个好处:
- 工具权限可控;
- 调用过程可追溯;
- 高风险操作可审批;
- 多 Agent 可以共享工具;
- 后期可以接入 MCP、A2A;
- 企业内部系统不会被 Agent 直接暴露。
5. 数据模型设计
下面用 Pydantic 模型描述核心数据结构。
5.1 工具风险等级
from enum import Enum class RiskLevel(str, Enum): low = "low" medium = "medium" high = "high"风险等级可以这样理解:
low:只读、无敏感信息、无业务副作用;medium:可能影响业务状态,但可回滚;high:可能影响资金、权限、生产环境、客户数据。
5.2 工具定义模型
from typing import Optional, Dict, Any from pydantic import BaseModel, Field class ToolDefinition(BaseModel): tool_name: str = Field(..., description="工具名称") tool_type: str = Field(..., description="工具类型,如 crm、database、git、search") description: str = Field(..., description="工具说明") risk_level: RiskLevel = RiskLevel.low require_approval: bool = False enabled: bool = True input_schema: Optional[Dict[str, Any]] = None例如:
tool = ToolDefinition( tool_name="query_customer_profile", tool_type="crm", description="查询客户基础资料", risk_level=RiskLevel.medium, require_approval=False, input_schema={ "type": "object", "properties": { "customer_id": {"type": "string"} }, "required": ["customer_id"] } )5.3 工具调用请求
class ToolCallRequest(BaseModel): agent_id: str tool_name: str arguments: Dict[str, Any] request_id: Optional[str] = None5.4 工具调用结果
class ToolCallResult(BaseModel): success: bool tool_name: str output: Optional[Dict[str, Any]] = None error_message: Optional[str] = None approval_required: bool = False audit_id: Optional[str] = None5.5 审计日志
from datetime import datetime class AuditLog(BaseModel): audit_id: str agent_id: str tool_name: str risk_level: RiskLevel input_summary: str output_summary: Optional[str] status: str approval_required: bool created_at: datetime审计日志不要直接保存敏感原文。
更合理的方式是保存摘要。
6. FastAPI 最小实现
6.1 安装依赖
pip install fastapi uvicorn pydantic6.2 完整示例代码
文件名:main.py
from enum import Enum from typing import Dict, Any, Optional from datetime import datetime from uuid import uuid4 from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field app = FastAPI(title="AI Agent Tool Gateway") class RiskLevel(str, Enum): low = "low" medium = "medium" high = "high" class ToolDefinition(BaseModel): tool_name: str = Field(..., description="工具名称") tool_type: str = Field(..., description="工具类型") description: str = Field(..., description="工具说明") risk_level: RiskLevel = RiskLevel.low require_approval: bool = False enabled: bool = True input_schema: Optional[Dict[str, Any]] = None class ToolRegisterRequest(BaseModel): tool_name: str tool_type: str description: str risk_level: RiskLevel = RiskLevel.low require_approval: bool = False input_schema: Optional[Dict[str, Any]] = None class ToolCallRequest(BaseModel): agent_id: str tool_name: str arguments: Dict[str, Any] request_id: Optional[str] = None class ToolCallResult(BaseModel): success: bool tool_name: str output: Optional[Dict[str, Any]] = None error_message: Optional[str] = None approval_required: bool = False audit_id: Optional[str] = None class AuditLog(BaseModel): audit_id: str agent_id: str tool_name: str risk_level: RiskLevel input_summary: str output_summary: Optional[str] status: str approval_required: bool created_at: datetime tool_registry: Dict[str, ToolDefinition] = {} audit_logs: Dict[str, AuditLog] = {} def summarize_arguments(arguments: Dict[str, Any]) -> str: keys = list(arguments.keys()) return f"args_keys={keys}" def write_audit_log( agent_id: str, tool: ToolDefinition, input_summary: str, output_summary: Optional[str], status: str, approval_required: bool ) -> str: audit_id = str(uuid4()) log = AuditLog( audit_id=audit_id, agent_id=agent_id, tool_name=tool.tool_name, risk_level=tool.risk_level, input_summary=input_summary, output_summary=output_summary, status=status, approval_required=approval_required, created_at=datetime.now() ) audit_logs[audit_id] = log return audit_id @app.post("/api/tools/register") def register_tool(payload: ToolRegisterRequest): if payload.tool_name in tool_registry: raise HTTPException(status_code=400, detail="tool already exists") tool = ToolDefinition( tool_name=payload.tool_name, tool_type=payload.tool_type, description=payload.description, risk_level=payload.risk_level, require_approval=payload.require_approval, enabled=True, input_schema=payload.input_schema ) tool_registry[payload.tool_name] = tool return { "message": "tool registered", "tool": tool } @app.get("/api/tools") def list_tools(): return list(tool_registry.values()) @app.post("/api/tools/call", response_model=ToolCallResult) def call_tool(payload: ToolCallRequest): tool = tool_registry.get(payload.tool_name) if not tool: raise HTTPException(status_code=404, detail="tool not found") if not tool.enabled: raise HTTPException(status_code=403, detail="tool disabled") input_summary = summarize_arguments(payload.arguments) if tool.require_approval or tool.risk_level == RiskLevel.high: audit_id = write_audit_log( agent_id=payload.agent_id, tool=tool, input_summary=input_summary, output_summary=None, status="pending_approval", approval_required=True ) return ToolCallResult( success=False, tool_name=tool.tool_name, output=None, error_message="approval required", approval_required=True, audit_id=audit_id ) # 模拟真实工具执行 output = { "message": f"tool {tool.tool_name} executed", "arguments_received": payload.arguments } audit_id = write_audit_log( agent_id=payload.agent_id, tool=tool, input_summary=input_summary, output_summary="tool executed successfully", status="success", approval_required=False ) return ToolCallResult( success=True, tool_name=tool.tool_name, output=output, error_message=None, approval_required=False, audit_id=audit_id ) @app.get("/api/audit/{audit_id}") def get_audit_log(audit_id: str): log = audit_logs.get(audit_id) if not log: raise HTTPException(status_code=404, detail="audit log not found") return log启动服务:
uvicorn main:app --reload打开接口文档:
http://127.0.0.1:8000/docs7. 注册工具示例
注册一个低风险工具:
{ "tool_name": "search_faq", "tool_type": "knowledge_base", "description": "查询企业 FAQ 知识库", "risk_level": "low", "require_approval": false }注册一个高风险工具:
{ "tool_name": "refund_order", "tool_type": "payment", "description": "执行订单退款操作", "risk_level": "high", "require_approval": true }低风险工具可以直接调用。
高风险工具会进入审批状态。
8. 调用工具示例
调用 FAQ 工具:
{ "agent_id": "customer-service-agent", "tool_name": "search_faq", "arguments": { "query": "如何申请售后" } }返回结果类似:
{ "success": true, "tool_name": "search_faq", "output": { "message": "tool search_faq executed", "arguments_received": { "query": "如何申请售后" } }, "error_message": null, "approval_required": false, "audit_id": "xxx" }调用退款工具:
{ "agent_id": "customer-service-agent", "tool_name": "refund_order", "arguments": { "order_id": "O20260529001", "amount": 99.00 } }返回结果会进入审批:
{ "success": false, "tool_name": "refund_order", "output": null, "error_message": "approval required", "approval_required": true, "audit_id": "xxx" }这就是工具调用网关的核心价值:
不是让 Agent 不能做事,而是让 Agent 在合适权限内做事。
9. 如何和 MCP 结合?
MCP 可以理解为 Agent 和外部工具之间的连接标准。
企业内部的工具调用网关可以和 MCP 这样结合:
Agent ↓ 工具调用网关 ↓ MCP Client ↓ MCP Server ↓ 数据库 / 文档 / Git / 内部系统也可以反过来:
Agent ↓ MCP Client ↓ 企业自定义 MCP Server ↓ 工具调用网关 ↓ 真实业务系统关键不是形式,而是要保持三个原则:
- 工具必须注册;
- 调用必须审计;
- 高风险操作必须审批。
如果 MCP Server 直接暴露敏感系统,而没有网关和权限控制,风险会比较高。
10. 如何和 A2A 结合?
A2A 更关注 Agent 与 Agent 之间的通信。
例如:
客服 Agent ↓ 调用订单 Agent ↓ 订单 Agent 调用库存 Agent ↓ 库存 Agent 返回缺货信息 ↓ 客服 Agent 生成用户回复在这种场景下,工具调用网关仍然有价值。
因为多个 Agent 协作时,更容易出现调用链复杂、权限边界不清、责任难以追溯的问题。
建议每个 Agent 调用外部工具时,都统一经过网关。
这样可以记录:
- 哪个 Agent 发起请求;
- 调用了哪个工具;
- 是否经过其他 Agent 中转;
- 是否触发高风险操作;
- 最终结果是什么。
11. 安全设计要点
11.1 不要让 Agent 直接访问生产数据库
Agent 可以生成查询建议,但不应该直接执行生产数据库写操作。
11.2 不要把密钥放进提示词
不要把 API Key、Token、数据库密码、私钥等内容放进 Agent 上下文。
11.3 高风险工具必须人工审批
例如:
- 退款;
- 删除数据;
- 修改权限;
- 执行部署;
- 批量发消息;
- 修改生产配置。
11.4 审计日志要脱敏
不要把完整用户隐私、敏感字段、业务机密直接写进日志。
11.5 工具要有启停开关
一旦工具异常,可以快速禁用。
12. MVP 落地路线
第一阶段:只做工具注册和审计
先统一管理工具清单。
做到:
- 工具可注册;
- 工具可启用和禁用;
- 调用可记录;
- 风险可标记。
第二阶段:增加审批流
针对中高风险工具增加审批。
做到:
- 高风险调用不直接执行;
- 生成审批记录;
- 人工确认后再调用真实系统。
第三阶段:接入 MCP Server
把内部文档、知识库、CRM、Git、数据系统逐步封装成 MCP Server 或工具接口。
第四阶段:支持多 Agent 协作
当多个 Agent 协作时,通过统一网关记录调用链,避免权限混乱。
13. 总结
近期 AI 行业的热点已经不只是模型本身。
OpenAI Agents SDK、Google ADK、MCP、A2A 等方向都说明,AI 应用正在进入工具调用、多步骤任务执行和多 Agent 协作阶段。
对后端开发者来说,真正要关注的不是“Agent 能不能调用工具”,而是:
- 工具如何注册;
- 权限如何控制;
- 风险如何分级;
- 审批如何设计;
- 调用如何审计;
- 多 Agent 协作如何追溯;
- 生产系统如何避免被误操作。
因此,企业在引入 AI Agent 时,不建议让 Agent 直接访问所有系统。
更合理的方式,是先建设一个工具调用网关。
让 Agent 通过网关调用工具,让网关负责鉴权、审计、审批和风险控制。
一句话总结:
Agent 越强,工具调用越要有边界。
这才是 AI Agent 真正进入企业系统时,后端架构需要补上的一层。