一个 Production Agent Runtime 到底需要什么?
很多 Agent 架构图看起来都差不多。
User
↓
LLM
↓
Tools
↓
Result
这个图没错。它只是太薄了。
如果你的 Agent 只是演示'模型会调用工具',这张图够用。如果它要进生产环境,要读用户数据、改代码、查数据库、发请求、执行命令、处理权限、控制成本、支持恢复和事故复盘,这张图就会把真正的问题藏起来。
我更愿意把生产级 Agent Runtime 画成这样:
User / Product / Workflow
↓
┌──────────────────────────────────────────────────┐
│ Agent Runtime │
│ │
│ Router / Session / Run State │
│ Loop Controller │
│ Context Builder │
│ Model Provider Adapter │
│ Tool Registry / Tool Executor │
│ Permission Gate / Policy Engine │
│ Message Store / Memory │
│ Checkpoint / Recovery / Rollback │
│ Trace / Metrics / Cost │
│ Eval Harness / Replay / CI │
└──────────────────────────────────────────────────┘
↓ ↑
MCP / 外部工具 人工审批 / 证据面板
这不是为了把架构图画复杂。是因为生产环境真的会问这些问题。
先说一句实话:框架不是答案
LangGraph 官方文档会强调 durable execution、human-in-the-loop、persistence、streaming 这些能力。OpenAI Agents SDK 也提供 tracing、sessions、guardrails、handoffs 和 human approval flows。MCP 规范把工具暴露、工具调用和用户授权放进协议讨论里。OWASP 在 LLM 应用安全风险里也把 Excessive Agency 列成问题:Agent 被授予过多权限、动作过宽、缺少确认,都会出事。
这些材料说明一件事:社区已经不再只讨论'怎么让模型调用函数'。真正的焦点在执行系统。
但你不能把某个框架的功能列表直接当成自己的架构。生产级 Runtime 的设计要从责任出发。
下面这 10 个模块,是我认为最小可讨论的生产级骨架。
1. Router / Session / Run State
Agent 不是一次函数调用。
你需要知道这次请求属于哪个用户、哪个 session、哪个 run、哪个 turn、哪个 step。没有这些 id,后面的 trace、checkpoint、permission、eval 都串不起来。
最小结构可以是:
type RuntimeIds = {
sessionId: string;
runId: string;
turnId: string;
stepId: string;
traceId: string;
checkpointId?: string;
};
很多 demo 不做这层,因为 demo 只有一个用户、一次任务、一个终端窗口。生产环境不是这样。用户会刷新页面,会断网,会开多个任务,会取消,会回来继续。你必须知道'哪一次执行'发生了什么。
2. Loop Controller
Loop Controller 负责让 Agent 一步步跑起来,也负责让它停下来。
它要管:
- max steps。
- max run time。
- token/cost budget。
- repeated tool call。
- repeated error。
- user cancellation。
- permission denied。
- final answer。
没有 Loop Controller,Agent 很容易变成一个热心但停不下来的实习生。它会一直搜索、一直重试、一直读错文件,直到撞上 max iterations 或把预算烧完。
停止原因必须进入 trace,也最好进入最终回答。用户不一定关心内部 step,但会关心为什么任务停在这里。
3. Context Builder
Context Builder 决定模型这一轮看见什么。
它不是把所有历史拼起来。它要在有限 token 里选择:
- 系统与安全指令。
- 当前用户目标。
- 运行状态。
- 最近对话。
- 相关文件片段。
- 工具结果。
- 历史摘要。
- 可用工具说明。
每次构建上下文,都应该输出 budget report:
type ContextReport = {
budget: number;
used: number;
mustKeep: string[];
included: string[];
summarized: string[];
dropped: string[];
reasons: Record<string, string>;
};
没有 report,你只能说'模型忘了'。有 report,你能查它到底有没有看见正确文件,是否丢掉了用户约束,是否被旧日志干扰。
4. Model Provider Adapter
生产环境很少只接一个模型。
你可能会接 OpenAI、Anthropic、本地模型、云厂商模型和 mock provider。不同 provider 对 tool calling、structured outputs、streaming、parallel tool calls、context length、prompt caching、response continuation 的支持都不同。
Provider Adapter 的作用不是把所有模型抹平到最低能力,而是让 runtime 清楚知道当前模型能做什么。
type ModelCapabilities = {
toolCalling: boolean;
strictToolSchema: boolean;
parallelToolCalls: boolean;
streaming: boolean;
promptCaching: boolean;
maxContextTokens: number;
};
有了这个,runtime 才能决定:是否启用 strict schema,是否允许并行工具,是否切换降级策略,是否使用 mock provider 做 eval replay。
5. Tool Registry / Tool Executor
工具不是一个 Map<string, Function>。
真实工具要带 schema、风险等级、副作用、资源范围、超时、输出策略、预览、执行函数。
type ToolDefinition = {
name: string;
description: string;
inputSchema: unknown;
riskLevel: "read" | "write" | "network" | "install" | "destructive";
sideEffects: string[];
resources: { kind: string; scope: string }[];
timeoutMs: number;
outputPolicy: ToolOutputPolicy;
};
Executor 负责超时、取消、输出限制、错误分类、结果归一化。工具失败不能只有 error: string,至少要有 INVALID_ARGUMENTS、NOT_FOUND、PERMISSION_DENIED、TIMEOUT、OUTPUT_TOO_LARGE、SIDE_EFFECT_UNCERTAIN。
模型看到清楚的 observation,才可能做出下一步安全动作。
6. Permission Gate / Policy Engine
权限系统先回答三件事:
谁要做事?
要做什么?
谁批准或拒绝?
run_command("ls src") 和 run_command("rm -rf ./tmp") 不能因为工具名一样就走同一条策略。权限判断要看参数、资源、副作用和上下文。
一个决策对象可以这样写:
type PermissionDecision = {
callId: string;
toolName: string;
decision: "allow" | "ask" | "deny";
decidedBy: "policy" | "user" | "system";
reason: string;
};
MCP 接入后,这层更重要。MCP 让外部工具更容易进入系统,但不会自动替你判断工具安全。Host 应让用户能理解和授权工具调用,这不是文档里的客套话,是生产边界。
7. Message Store / Memory
Message Store 保存一次 run 的对话和工具 observation。Memory 保存跨 run 的可复用信息。两者不要混。
很多 Agent Memory 做乱,就是因为把短期消息、长期偏好、项目事实、用户隐私、失败摘要都塞进一个向量库。最后模型检索到一堆'看似相关但不可验证'的旧信息。
生产级 Runtime 里,Message Store 应该可审计,Memory 应该有来源、作用域、过期时间和删除机制。摘要只能作为导航,不能替代事实。
8. Checkpoint / Recovery / Rollback
长任务一定会中断。
用户取消、浏览器关闭、网络断开、进程崩溃、工具超时、机器重启,都可能发生。没有 checkpoint,恢复就只能从头来。对有副作用的工具来说,从头来很危险。
Checkpoint 要保存:
- run state。
- message history。
- tool ledger。
- permission decision。
- 文件 patch / hash。
- 最后一个安全 step。
Rollback 也不是一个简单 undo 按钮。Agent 改文件时,要区分用户原本的改动和 Agent 产生的 diff。回滚前要检查当前文件是否已经被用户继续修改。
9. Trace / Metrics / Cost
聊天记录不是 trace。
Trace 要回答系统实际做了什么:
- 模型看见了哪些上下文?
- 调用了哪些工具?
- 工具输入摘要是什么?
- 用户批准了什么?
- 哪个输出被截断?
- 哪一步耗时最长?
- token 和成本花在哪?
- 文件发生了什么 diff?
一个最小 span:
{
"name": "tool.run_command",
"status": "error",
"durationMs": 8120,
"attributes": {
"tool.name": "run_command",
"tool.risk": "write",
"exit_code": 1,
"output.truncated": true
}
}
没有 trace,事故复盘就是猜。模型输出可以很会解释自己,但你需要的是系统事实。
10. Eval Harness / Replay / CI
最后是 eval。
Agent 不能只靠'我试了几次感觉不错'上线。Runtime 策略变化,比如 maxSteps、工具描述、上下文压缩、retry policy、工具暴露范围,都可能让某些任务退步。
Eval Harness 至少要覆盖:
- golden task。
- mock model replay。
- must-not-call。
- secret 和路径越界。
- trace 转 replay。
- 成本指标。
- CI 门禁。
报告不能只看成功率。还要看 step 数、tool call 数、latency、token、cost、权限请求次数、重复错误次数、回滚是否成功。
有些改动通过率高一点,但成本翻倍、危险工具审批翻倍,不一定能发。
这 10 个模块怎么分阶段做
第一版不要全做满。可以按风险和收益排序。
第一阶段:Run State + Loop Controller + Tool Registry + Trace
第二阶段:Context Builder + Permission Gate + Budget
第三阶段:Checkpoint + Tool Ledger + Recovery
第四阶段:Eval Harness + Replay + CI
第五阶段:MCP、Memory、Provider Adapter 深化
先把系统骨架立起来。哪怕 trace 只是 JSON 文件,checkpoint 只是本地目录,eval 只有 10 个 golden task,也比没有强。
生产级不是一夜之间做出来的。它是每次事故后,系统多记住一点,多拦住一点,多能复盘一点。
最后
一个 Production Agent Runtime 的核心,不是'让模型更像人'。
它要把一个会犯错、会误解、会积极行动的模型,放进一个可控系统里。
这套系统要允许它做事,也要知道什么时候拦住它;要让它继续任务,也要知道什么时候不能重放;要让它接工具,也要知道工具越权时谁负责;要让它回答用户,也要留下足够证据给工程师复盘。
如果你的架构图里只有 LLM 和 Tools,说明你还在 demo 阶段。
参考资料
- LangGraph Docs: Overview
- LangGraph Docs: Persistence
- OpenAI Agents SDK Docs: Agents
- OpenAI Agents SDK Docs: Tracing
- Model Context Protocol Docs: Tools
- OWASP: LLM06: Excessive Agency
- OpenTelemetry Docs: Traces


