最近在梳理 AI Agent 落地路径时我一直在想一个问题大模型的能力边界越来越宽但为什么真正敢把 Agent 直接接到生产系统的团队还不多答案其实很简单——模型输出天然带有不确定性而业务系统要求的是确定性的参数、确定性的权限、确定性的副作用。你可以在沙箱里让 Agent 自由发挥但一旦它要操作订单、财务、库存就必须有一道“岩石般稳固”的边界。Stonefold 这个名字很有意思Stone 代表石头Fold 代表折叠合起来可以理解为“像石头一样坚硬的折叠层”。它把自己定位成一个 deterministic gateway也就是确定性网关专门放在 AI Agent 和业务系统之间用规则把模型的自由发挥约束在可控范围之内。这篇文章会围绕 Stonefold 的设计思路从核心概念、架构拆解到完整代码实现一步步带你落地一个最小可用的网关原型并讨论如何对这类网关做系统化的评估。如果你正在做 Agent 应用开发或者打算把 LLM 接到公司内部系统这篇文章应该能帮你理清“模型负责什么、规则负责什么”这个关键问题。即使你是初学者跟着文章把 demo 跑通也能对 Agent 生产化有一个非常具体的体感。1. 背景与核心概念1.1 AI Agent 的“能力”与“不确定性”是一体两面先从一个最简单的场景说起。假设你写了一个 Agent它能理解用户说“帮我查一下订单”然后调用一个后端接口。模型经过推理决定调用名为order.query的工具参数是{order_id: ORD-2025-001}。听起来很顺利但生产环境里会出现这些情况模型把参数名改成了id而不是order_id后端直接报参数缺失。模型调用了一个本不应该调用的工具比如user.delete。模型传了一个格式完全错误的订单号导致数据库扫描异常。模型在一次请求里连续执行了多个副作用操作但业务上根本没授权它这么做。这些问题的本质是模型的输出空间远大于业务系统的安全输入空间。LLM 是概率模型同一个 Prompt 多次调用结果可能不同工具调用时参数选择也可能漂移。这种不确定性在聊天场景下问题不大但在操作真实系统时任何一个偏差都可能造成线上故障。1.2 Stonefold 的定位把不确定性关进规则笼子Stonefold 要解决的核心问题就是在模型与业务系统之间增加一层确定性代理。它不是一个 Agent 框架也不负责写 Prompt 或者做模型推理。它的职责非常单一把 Agent 的“模糊意图”翻译成“确定性的系统调用”并在这一过程中完成白名单校验、参数校验、权限校验和全量审计。你可以把它理解成 API Gateway只不过 API Gateway 挡在客户端和后端服务之间而 Stonefold 挡在 Agent 和内部系统之间。Agent 请求 || \/ [Stonefold 网关] - 动作白名单校验 - JSON Schema 参数校验 - 角色权限校验 - 审计日志记录 || \/ [业务系统: 订单服务/用户服务/库存服务...]用一组对比来看会更直观对比项没有网关有确定性网关工具调用模型自由选择只能调用白名单内动作参数约束模型“尽量”按格式输出JSON Schema 强制校验权限控制后端接口自己判断网关统一鉴权审计靠应用日志拼接请求/响应全量落盘故障排查很难还原模型当时的行为每一次调用都可复现1.3 Stonefold 的三个核心设计目标围绕“确定性”这三个字Stonefold 的架构设计可以拆成三个目标第一个目标是可控性。模型不能直接访问业务系统它只能向网关发起请求而网关维护了一个工具白名单。白名单里有的动作才允许执行其他的全部拒绝。第二个目标是可校验性。每个工具都绑定一份 JSON Schema 参数约束。模型传过来的参数必须通过 Schema 校验类型不对、字段缺失、格式非法都会被拦截而不是把脏数据透传到业务层。第三个目标是可追溯性。每一次请求都必须记录 request_id、agent_id、action、parameters、响应结果、耗时这样一旦线上出问题可以精确还原出“是哪个 Agent、调了哪个动作、传了什么参数、得到了什么结果”。2. 确定性网关的架构拆解在设计 Stonefold 时整个网关的请求链路被拆成四个层。下面按一次请求从进入到返回的顺序来说明。2.1 入口层统一的请求协议Axent 内部的模型无论是什么最后都要把“调用动作”和“参数”抽象成统一结构。Stonefold 建议的最小请求结构如下{ request_id: req_20250101_001, trace_id: trace_xxxx, agent_id: agent_crm_01, action: order.query, parameters: { order_id: ORD-2025-001 } }统一协议的意义在于网关不关心模型是 GPT 还是 Claude也不关心 Agent 是怎么推理出这个动作的。它只认action和parameters两个字段。这样网关就能和底层模型解耦未来更换模型也不会破坏网关逻辑。2.2 路由层动作白名单机制网关维护了一个工具注册表Tool Registry里面注册了所有允许 Agent 调用的动作。每个动作包含以下元信息字段说明name动作唯一标识例如order.querydescription动作说明用于人工审查parameters_schemaJSON Schema 参数约束function实际执行业务逻辑的函数required_roles调用该动作所需的最小角色集合路由层做的事情很简单拿到action后在注册表里查找。如果找不到直接返回“action 不在白名单中”。这一步拦截了大量模型幻觉导致的非法调用。2.3 执行层参数校验与业务调用这是网关“确定性”最关键的一环。当模型说“我要调用order.query”时它往往不会严格遵循你期望的参数格式。执行层会用工具绑定的 JSON Schema 做参数校验。举个例子order.query期望的参数是{ type: object, properties: { order_id: { type: string, pattern: ^ORD-[0-9]{4}-[0-9]{3,}$ } }, required: [order_id], additionalProperties: false }多一个字段不行少一个字段不行格式不对也不行。只有校验通过网关才会调用真正的业务函数。2.4 审计层每一次调用都可回溯审计层在业务函数执行完成后记录一条完整的审计日志内容包括请求 ID、Agent ID、动作名称、原始参数、规范化后的参数、响应状态、耗时等。这些日志可以落地到文件、数据库或消息队列供后续排障、风控和评估使用。3. 环境准备与项目结构3.1 技术选型说明下面我们动手实现一个最小可用的 Stonefold 网关。示例采用 Python 3.10核心依赖只有两个jsonschema用于 JSON Schema 参数校验。pytest用于编写评估测试用例。为了方便演示这个项目不引入 FastAPI 或 Flask直接通过命令行调用运行这样你能更清楚地看到网关的核心逻辑。如果你后续要把它部署成 HTTP 服务可以用任意 Web 框架包一层接入层即可。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。安装依赖的命令如下pip install jsonschema pytest3.2 项目目录结构stonefold-demo/ ├── requirements.txt ├── app/ │ ├── __init__.py │ ├── audit.py │ ├── gateway.py │ ├── schema.py │ └── tool_registry.py ├── main.py └── tests/ └── test_gateway_evals.py下面我们按模块逐个实现。4. 完整实战从零实现一个最小可用的 Stonefold 网关4.1 定义工具注册表工具注册表是整个网关的“白名单底座”。它定义了一个ToolSpec数据类用来描述一个工具的全部元信息并提供一个统一的invoke方法把业务函数的调用包裹成确定性的返回结构。代码路径app/tool_registry.py# 文件路径stonefold-demo/app/tool_registry.py 工具注册表把外部系统能力统一注册为可调用的 Tool。 from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Callable, Dict, List dataclass(frozenTrue) class ToolSpec: Tool 的元信息描述。 属性说明 name: 工具唯一标识推荐使用“模块.动作”的命名法。 description: 工具说明用于人工审查和模型参考。 parameters_schema: JSON Schema 格式的参数约束。 function: 实际执行业务逻辑的函数。 required_roles: 调用该工具需要的最小角色集合。 name: str description: str parameters_schema: Dict[str, Any] function: Callable[..., Any] required_roles: List[str] field(default_factorylambda: [agent]) def invoke(self, params: Dict[str, Any]) - Dict[str, Any]: 以统一方式调用底层函数并捕获异常。 之所以在这里统一捕获异常是为了让网关主体逻辑不关心 业务函数内部抛出的异常细节而是把异常转换为可读的 结构化错误信息。 try: result self.function(**params) return {ok: True, data: result} except Exception as exc: return {ok: False, error: f{type(exc).__name__}: {exc}}这里有一个容易被忽略的点invoke方法统一捕获异常后会把异常类型和消息拼进error字段。这样网关层不需要用大段 try/except 包裹业务调用错误处理变得非常规整。4.2 实现参数校验模块参数校验模块是整个网关确定性的核心。它用 JSON Schema 对 Agent 传入的parameters做严格校验。校验失败时我们会收集所有错误路径拼成可读的错误信息并抛出ValueError。代码路径app/schema.py# 文件路径stonefold-demo/app/schema.py 使用 JSON Schema 对 Agent 传入的参数做确定性校验。 校验失败时统一抛出 ValueError网关层捕获后返回结构化错误响应。 from typing import Any, Dict from jsonschema import Draft7Validator, FormatChecker from jsonschema.exceptions import ValidationError def validate_parameters(schema: Dict[str, Any], parameters: Dict[str, Any]) - Dict[str, Any]: 根据 JSON Schema 校验参数。 这里使用 Draft7Validator 而不是便捷函数 validate是因为 我们需要获取全部的校验错误而不是遇到第一个错误就返回。 validator Draft7Validator(schema, format_checkerFormatChecker()) errors sorted(validator.iter_errors(parameters), keylambda e: list(e.path)) if errors: detail [] for err in errors: # 格式化错误路径例如 $.order_id 或 $.user.name path ..join([str(p) for p in err.path]) or $ detail.append(f{path}: {err.message}) raise ValueError(; .join(detail)) return parameters你可能会好奇为什么要用iter_errors而不是简单的validate。区别在于iter_errors会把所有问题一次性列出来而不是只报第一个。这在生产环境中非常重要因为你可以一次告诉调用方“你所有的参数问题”减少来回调试的次数。4.3 实现审计日志模块审计模块要做的很简单把网关处理的每一次请求和响应落盘。在生产环境这个模块可以替换为写入 Kafka、ES 或数据库但核心思路一致记录什么、以什么格式记录。代码路径app/audit.py# 文件路径stonefold-demo/app/audit.py 审计日志记录每一次进入网关的请求和响应便于事后追溯。 import json import time from pathlib import Path class AuditLogger: 简单的文件型审计日志器。 每条记录单独占一行便于 tail 和按行解析。 def __init__(self, log_file: str logs/audit.log): self._log_file Path(log_file) self._log_file.parent.mkdir(parentsTrue, exist_okTrue) def record(self, entry: dict) - None: line json.dumps(entry, ensure_asciiFalse, defaultstr) with self._log_file.open(a, encodingutf-8) as fp: fp.write(line \n)这里的defaultstr很关键。审计记录里可能包含非 JSON 序列化对象比如datetime、Decimaldefaultstr会把这些值自动转换成字符串避免序列化报错导致审计失败。4.4 实现核心网关逻辑核心网关是整个 Stonefold 的骨架。它负责把前面几个模块串起来形成完整的请求处理链路。代码路径app/gateway.py# 文件路径stonefold-demo/app/gateway.py Stonefold 核心网关路由、校验、执行、审计。 import time import uuid from typing import Any, Dict, List from .audit import AuditLogger from .schema import validate_parameters from .tool_registry import ToolSpec class DeterministicGateway: 确定性网关。 工作流程 1. 从请求中取出 action在工具注册表中查找 2. 用工具的 JSON Schema 校验 parameters 3. 调用工具绑定的业务函数 4. 记录审计日志返回结构化响应。 def __init__(self, tools: List[ToolSpec], audit_logger: AuditLogger | None None): self._tools: Dict[str, ToolSpec] {tool.name: tool for tool in tools} self._audit audit_logger or AuditLogger() def register(self, tool: ToolSpec) - None: 注册新工具重复注册会直接报错。 if tool.name in self._tools: raise ValueError(fTool[{tool.name}] 已经注册不允许重复注册) self._tools[tool.name] tool def list_tools(self) - List[str]: 返回当前网关中所有可用的动作名称。 return sorted(self._tools.keys()) def execute(self, request: Dict[str, Any]) - Dict[str, Any]: 处理一个 Agent 请求。 请求结构建议 { request_id: req_001, agent_id: agent_001, action: order.query, parameters: {...} } request_id request.get(request_id) or self._new_id() trace_id request.get(trace_id) or self._new_id() action request.get(action) parameters request.get(parameters, {}) agent_id request.get(agent_id, unknown) # 1. 动作白名单校验 tool self._tools.get(action) if tool is None: return self._build_response( request_id, trace_id, False, errorfaction[{action}] 不在白名单中可用动作: {self.list_tools()}, ) # 2. 参数确定性校验 try: normalized_params validate_parameters(tool.parameters_schema, parameters) except ValueError as exc: return self._build_response( request_id, trace_id, False, errorf参数校验失败: {exc}, ) # 3. 执行业务函数并记录耗时 started_at time.time() result tool.invoke(normalized_params) latency_ms round((time.time() - started_at) * 1000, 2) # 4. 记录审计日志 self._audit.record({ request_id: request_id, trace_id: trace_id, agent_id: agent_id, action: action, parameters: parameters, latency_ms: latency_ms, result: result, ts: time.time(), }) if not result[ok]: return self._build_response( request_id, trace_id, False, errorresult[error], ) return self._build_response(request_id, trace_id, True, dataresult[data]) staticmethod def _build_response( request_id: str, trace_id: str, success: bool, data: Any None, error: str | None None, ) - Dict[str, Any]: return { request_id: request_id, trace_id: trace_id, success: success, data: data, error: error, } staticmethod def _new_id() - str: return uuid.uuid4().hex[:12]这里最核心的一点是网关返回的响应结构是固定的。无论成功还是失败调用方拿到的都是success/data/error三段式结构。AI 应用可以基于这个稳定结构做后续处理而不需要猜测后端服务会返回什么样的字段。4.5 编写业务工具与运行入口现在我们把业务系统和网关组装起来。这里模拟一个极简的订单服务包含查询订单和取消订单两个动作。代码路径main.py# 文件路径stonefold-demo/main.py 一个完整的最小可运行 Stonefold 示例。 import json from app.audit import AuditLogger from app.gateway import DeterministicGateway from app.tool_registry import ToolSpec def query_order(order_id: str): 模拟订单查询接口。 order_db { ORD-2025-001: {status: PAID, amount: 199.00, user_id: U1001}, ORD-2025-002: {status: SHIPPED, amount: 59.90, user_id: U1002}, } if order_id not in order_db: raise ValueError(f订单 {order_id} 不存在) return order_db[order_id] def cancel_order(order_id: str, reason: str manual): 模拟订单取消接口。 order_db { ORD-2025-001: {status: PAID, amount: 199.00, user_id: U1001}, ORD-2025-002: {status: SHIPPED, amount: 59.90, user_id: U1002}, } if order_id not in order_db: raise ValueError(f订单 {order_id} 不存在) if order_db[order_id][status] SHIPPED: raise PermissionError(已发货订单不允许直接取消) order_db[order_id][status] CANCELLED return order_db[order_id] # 注册“查询订单”工具 ORDER_QUERY_SPEC ToolSpec( nameorder.query, description根据订单号查询订单状态、金额和用户信息。, parameters_schema{ type: object, properties: { order_id: { type: string, pattern: ^ORD-[0-9]{4}-[0-9]{3,}$, description: 订单号例如 ORD-2025-001, } }, required: [order_id], additionalProperties: False, }, functionquery_order, required_roles[agent], ) # 注册“取消订单”工具 ORDER_CANCEL_SPEC ToolSpec( nameorder.cancel, description取消一个未发货订单。, parameters_schema{ type: object, properties: { order_id: { type: string, pattern: ^ORD-[0-9]{4}-[0-9]{3,}$, description: 订单号例如 ORD-2025-001, }, reason: { type: string, maxLength: 200, description: 取消原因可选。, }, }, required: [order_id], additionalProperties: False, }, functioncancel_order, required_roles[agent, order_admin], ) def build_gateway() - DeterministicGateway: 构建网关实例。 return DeterministicGateway( tools[ORDER_QUERY_SPEC, ORDER_CANCEL_SPEC], audit_loggerAuditLogger(logs/audit.log), ) if __name__ __main__: gateway build_gateway() test_requests [ { request_id: req_001, agent_id: agent_order_01, action: order.query, parameters: {order_id: ORD-2025-001}, }, { request_id: req_002, agent_id: agent_order_01, action: order.query, parameters: {order_id: xxx}, }, { request_id: req_003, agent_id: agent_order_01, action: user.delete, parameters: {}, }, { request_id: req_004, agent_id: agent_order_01, action: order.cancel, parameters: {order_id: ORD-2025-002}, }, ] for req in test_requests: resp gateway.execute(req) print(请求: , json.dumps(req, ensure_asciiFalse)) print(响应: , json.dumps(resp, ensure_asciiFalse)) print(- * 60)运行命令python main.py预期输出省略部分日志如下请求: {request_id: req_001, agent_id: agent_order_01, action: order.query, parameters: {order_id: ORD-2025-001}} 响应: {request_id: req_001, trace_id: xxxxxxxxxxxx, success: true, data: {status: PAID, amount: 199.0, user_id: U1001}, error: null} ------------------------------------------------------------ 请求: {request_id: req_002, agent_id: agent_order_01, action: order.query, parameters: {order_id: xxx}} 响应: {request_id: req_002, trace_id: xxxxxxxxxxxx, success: false, data: null, error: 参数校验失败: order_id: xxx does not match ^ORD-[0-9]{4}-[0-9]{3,}$} ------------------------------------------------------------ 请求: {request_id: req_003, agent_id: agent_order_01, action: user.delete, parameters: {}} 响应: {request_id: req_003, trace_id: xxxxxxxxxxxx, success: false, data: null, error: action[user.delete] 不在白名单中可用动作: [order.cancel, order.query]} ------------------------------------------------------------ 请求: {request_id: req_004, agent_id: agent_order_01, action: order.cancel, parameters: {order_id: ORD-2025-002}} 响应: {request_id: req_004, trace_id: xxxxxxxxxxxx, success: false, data: null, error: PermissionError: 已发货订单不允许直接取消} ------------------------------------------------------------4.6 结果验证与现象说明从上面输出可以看出几个重要现象第一正确参数能够被正常执行返回结构化数据。第二模型传了非法参数时拦截发生在业务函数之前。网关直接返回参数校验失败业务系统根本不会收到脏数据。第三Agent 试图调用不存在的动作时网关返回白名单错误并且顺便把可用动作列表返回给了调用方。第四业务函数自身的业务校验也被捕获。比如取消已发货订单时PermissionError被 ToolSpec 的invoke捕获并转换为可读错误信息。这就是确定性网关的核心价值所有路径的输出都是确定且可预测的。无论 Agent 怎么乱来网关都能给出同样的错误结构让上层 AI 编排逻辑可以稳定处理。5. demystifying evals for AI agents如何评估网关的可靠性聊完了网关注册和执行接下来讨论一个在 Agent 生产化过程中最容易被忽略的问题怎么评估这个网关到底靠不靠谱。最近业内在讨论 demystifying evals for AI agents意思是把“评估 Agent”这件事从玄学变成工程学。对于网关这种偏规则的系统评估反而比评估模型本身更清晰。5.1 为什么 Agent 评估特别难评估普通模型时你只需要准备一批输入输出对算一下准确率就行。但 Agent 的评估难在Agent 的输出是动作序列而不是一句话难以直接判定对错。一次任务可能有多种合法路径单一标准答案会误伤合理行为。副作用很难量化比如“调用了取消接口”到底算成功还是失败取决于业务上下文。所以业界逐渐把 Agent 评估拆分成多个维度工具选择是否合理、参数是否合法、流程是否完整、副作用是否符合预期。Stonefold 这种确定性网关的好处是它把“动作合法性”和“参数合法性”这两件事从 Agent 能力中剥离出来变成了可自动化测试的规则。5.2 面向确定性网关的评估维度我们可以把网关的可靠性评估拆成以下五个维度评估维度评估目标示例用例白名单拦截率非法动作是否被正确拒绝调用user.delete应返回失败参数校验率非法参数是否被拦截order_id传数字应返回失败合法请求成功率合法请求是否正常执行order.query传合法订单号应成功响应格式一致性响应结构是否始终稳定无论成功失败都包含success/data/error权限边界有效性未授权角色是否被拒绝角色不足以取消订单时应被拦截5.3 编写最小回归测试集下面用pytest写一组最基础的评估用例。代码路径tests/test_gateway_evals.py# 文件路径stonefold-demo/tests/test_gateway_evals.py 用最小回归测试集来评估网关的确定性与安全性。 from app.gateway import DeterministicGateway from main import build_gateway def test_query_order_success(): 合法请求应当成功执行。 gw build_gateway() resp gw.execute({ request_id: req_t1, agent_id: agent_test, action: order.query, parameters: {order_id: ORD-2025-001}, }) assert resp[success] is True assert resp[data][status] PAID assert resp[error] is None def test_query_order_invalid_param(): 非法参数应当被拦截在业务函数之前。 gw build_gateway() resp gw.execute({ request_id: req_t2, agent_id: agent_test, action: order.query, parameters: {order_id: 12345}, }) assert resp[success] is False assert 参数校验失败 in resp[error] def test_unknown_action_rejected(): 不在白名单中的动作应当被拒绝。 gw build_gateway() resp gw.execute({ request_id: req_t3, agent_id: agent_test, action: user.delete, parameters: {}, }) assert resp[success] is False assert 不在白名单 in resp[error] def test_cancel_shipped_order_rejected(): 业务规则校验也应当被网关捕获。 gw build_gateway() resp gw.execute({ request_id: req_t4, agent_id: agent_test, action: order.cancel, parameters: {order_id: ORD-2025-002}, }) assert resp[success] is False assert 已发货订单不允许直接取消 in resp[error] def test_response_structure_always_stable(): 无论成功失败响应结构必须保持稳定。 gw build_gateway() cases [ {request_id: r1, agent_id: a1, action: order.query, parameters: {order_id: ORD-2025-001}}, {request_id: r2, agent_id: a1, action: order.query, parameters: {order_id: BAD}}, {request_id: r3, agent_id: a1, action: not_exist, parameters: {}}, ] for case in cases: resp gw.execute(case) # 每一个响应都必须包含这三个字段 assert set(resp.keys()) {request_id, trace_id, success, data, error}运行测试cd stonefold-demo pytest tests -v预期输出tests/test_gateway_evals.py::test_query_order_success PASSED tests/test_gateway_evals.py::test_query_order_invalid_param PASSED tests/test_gateway_evals.py::test_unknown_action_rejected PASSED tests/test_gateway_evals.py::test_cancel_shipped_order_rejected PASSED tests/test_gateway_evals.py::test_response_structure_always_stable PASSED5.4 评估结果如何驱动迭代当这些测试用例作为回归集被固化下来后后续改动网关逻辑或新增工具时你只需要跑一遍pytest就能快速发现是否破坏了原有行为。比如新增一个工具后原本不在白名单的动作是否可用了修改了订单号的正则表达式是否误伤了一部分合法订单修改了响应结构是否破坏了上层 Agent 的解析逻辑这类评估不需要真实模型参与完全基于规则所以结果确定、速度极快。这也是“demystifying evals for AI agents”的一个重要实践不要一上来就评测整个 Agent 的“智能程度”先把底层网关的确定性测试做扎实再向上评估模型选型和 Prompt 策略。6. 常见问题与排查思路在实际接入 Stonefold 的过程中最常见的问题集中在依赖、参数校验和部署三个方向。下面整理了一张排查表。问题现象常见原因解决思路ImportError: No module named jsonschema未安装依赖执行pip install jsonschema pytest合法参数被拦截JSON Schema 写得太严格检查pattern、type、additionalProperties是否合理非法参数透传到了业务函数网关未做参数校验确认execute方法中是否调用了validate_parameters业务异常堆栈直接暴露给上层ToolSpec 没有捕获异常检查invoke方法是否统一处理了ExceptionAgent 总是调用不到目标工具动作名与工具名不一致检查 Agent 的 system prompt 中工具描述和注册表是否一致审计日志文件里的中文乱码文件打开未指定 UTF-8 编码使用encodingutf-8打开文件新增工具后原有用例失败白名单或响应结构被修改运行回归测试对比失败用例这里我想特别强调一个容易被忽视的问题JSON Schema 的additionalProperties字段。如果不设置它为false模型传入的额外字段会被静默忽略这可能导致 Agent 误以为自己传的user_id生效了但业务函数根本没拿到。所以对于生产环境建议把所有工具的参数 Schema 都加上additionalProperties: false让多余参数明确报错。7. 生产环境落地建议上面的 demo 是一个功能最小集真正部署到生产环境时还有几个工程问题值得提前设计。7.1 权限模型从角色到最小授权当前示例中的required_roles字段尚未真正生效。生产环境里建议在网关层增加角色解析模块从请求中的身份信息如 JWT、API Key中解析出角色列表再与required_roles做交集判断。权限模型建议遵循最小授权原则Agent 默认没有任何权限每开放一个工具都要显式授予角色。不要因为“某个模型能力很强”就给所有 Agent 开放全部工具。7.2 配置与灰度发布工具注册表不应该硬编码在代码里而应该做成动态配置方便灰度发布。比如优先只对agent_crm_01开放order.cancel。新工具先注册 10% 流量验证无误后再全量。出现问题可以立刻从配置中心下线某个工具而不是发布代码。如果你已经用了 Apollo、Nacos 这类配置中心把工具注册表放在配置中心里是更优雅的方案。7.3 可观测性日志、指标、链路追踪审计日志只是第一步。生产环境还需要指标和链路追踪指标统计每个工具的调用量、失败率、P99 耗时。链路追踪把trace_id透传到业务系统方便跨系统排查。审计日志建议双写一份用于日常查询一份用于风控审计不可篡改。7.4 性能与隔离网关是同步调用链路的必经节点性能损耗必须控制住。批量请求和流式输出场景下建议参数校验使用预编译的 JSON Schema Validator避免每次请求都重新编译。网关与业务系统之间使用连接池。高频读操作和低频写操作拆成不同网关实例避免相互影响。从性能估算来说单纯的 JSON Schema 校验和路由查询在毫秒级甚至亚毫秒级相比大模型推理动辄几秒的耗时可以忽略不计所以网关基本不会成为性能瓶颈。7.5 安全边界Agent 的安全性往往不在于模型本身而在于暴露给模型的工具边界。以下几点需要格外注意不要把数据库连接、Redis 连接等底层能力直接注册为工具。工具参数要尽量收敛能用 ID 就不要用裸 SQL。对删除、更新类动作建议增加二次确认机制。网关本身也要处于内网边界之内只对可信的 Agent 服务开放端口。8. 总结与延伸学习通过这篇文章我们完整拆解了 Stonefold 的确定性网关设计思路并亲手实现了一个包含工具注册、参数校验、审计日志和回归测试的最小系统。核心收获可以归纳为三点第一AI Agent 落地生产的最大障碍不是模型能力而是缺乏对模型输出的确定性约束。Stonefold 给出的答案是“用规则包住模型”让模型在规则范围内自由发挥。第二确定性网关的四个核心模块是统一的请求协议、工具白名单、JSON Schema 参数校验、全量审计日志。这四个模块缺一不可。第三Agent 评估可以从规则层开始做起。通过一组自动化回归测试把“agent 是否可靠”这个模糊问题拆成“白名单是否生效”“参数校验是否正确”“响应结构是否稳定”等可验证的问题。如果继续深入你可以从这几个方向扩展把网关包装成 HTTP 服务接入 FastAPI 或 Spring Boot。增加动态工具注册和灰度配置。对接真实 LLM实现“模型端到端”的自动化评估集。把审计日志接入分析平台构建异常行为检测。这篇文章的完整示例代码可以直接应用到你的 Agent 网关建设中也可以作为设计参考来改造现有的工具调用框架。如果你正在做类似的方向建议先把最小的白名单 Schema 校验跑通再逐步叠加权限、灰度、可观测性能力不用一开始就追求大而全。