Clipping 微信公众号

实践出真知!直接拿去用!生产级 AI 智能体 9 大子系统全景解析!

by ElonMax 原文 ↗
Created: 2026-07-03

公众号名称:ElonMax

作者名称:ElonMax

发布时间:2026-07-03 14:53

这是我个人参考了主流 agent 以及自身的实践需求和构想构建的一个真实运行的 Agent 运行时内核(Rust 实现,214 个源文件,11 万+ 行代码),目前已稳定运行 3 个月有余,今天先将整体框架分享出来,后续会不间断分析各个细节与后续进展,希望您能够帮助到大家。🤝🤝🤝

当一个开发者第一次用 LangChain 写完一个”能调用工具的 Agent demo”时,他会觉得这事儿很简单——写个 prompt,接个 function calling,跑通了,搞定。

但真到了要做一个 7×24 小时不挂、能同时服务多个用户、能自动纠错、能安全执行命令、能记住上下文、能接入真实消息平台的生产级 Agent 系统时,那个 demo 的代码量可能连整个系统的 5% 都不到。

一个生产级 AI Agent 运行时,不是一个 prompt 加一个 API 调用。它是一个完整的软件工程系统。

本文从一个真实运行的、超过 11 万行 Rust 代码的 Agent 运行时内核出发,拆解一个生产级智能体系统到底需要哪些核心组件、每个组件解决什么问题、它的技术路径和关键设计决策是什么。

读完这篇文章,你会获得一张完整的”生产级 Agent 架构地图”——不是 PPT 上画几个框的那种,而是每个框里到底跑着什么代码、为什么这么设计、它的物理约束在哪里。

1 |Agentic Execution Loop:Agent 的心脏,远不止 ReAct

大多数人理解的 Agent 循环是 ReAct 模式:思考 → 行动 → 观察 → 再思考。这个概念没错,但如果直接拿这个循环跑生产环境,你会发现 Agent 经常陷入三种灾难:空转(无限循环没有进展)、跳步(声称完成了但实际没做)、跑偏(用户打断了又补充了新需求,Agent 不知道该继续旧任务还是开始新任务)。

解决这三个问题的答案,是一套带确定性守卫的自纠正循环引擎

确定性预循环契约注入

最关键的设计是:在每一轮 Agent 循环开始之前,运行时不经过 LLM,而是用纯确定性代码注入一段执行契约

这段契约包含什么?当前循环处于第几次迭代(比如 1/20)、当前处于哪个执行阶段、这个阶段有哪些必须满足的验收检查项还没通过、工作区根目录是否缺失、上一轮的评估理由是什么。

为什么这一步不能用 LLM 来做?因为 LLM 会”忘”。当对话变长、工具调用变多时,模型会丢失对当前状态的精确感知。而这段确定性注入就像每轮给 Agent 发一张”任务工单”——不管你脑子里在想什么,先看清楚工单上写的当前进度和未完成项。

LLM-as-Judge 循环评估器

每轮循环结束后,系统不直接相信 Agent 自己说的”我做完了”。它用一次独立的 LLM 调用来评估这一轮的真实进展。

这个评估调用的关键参数值得注意:temperature: 0.0(追求确定性输出)、系统提示词是一套严格的评分标准、输出被强制要求是结构化 JSON。评估器需要回答:这一轮有可见进展吗?当前阶段完成了吗?Agent 产出了具体证据吗?如果没有,应该重试、继续还是放弃?

更精妙的是 enforce_skill_phase_evaluation 机制:如果当前处于技能强制的阶段,即使评估器判定”完成了”,但只要 Agent 没有产出任何具体证据(没有工具调用、没有证据提示),系统会直接推翻评估器的判断,强制要求继续。这等于在说:你以为你做完了?拿出证据来。

阶段验收门禁

这是防止”跳步”的核心机制。每个执行阶段可以配置四种类型的验收检查:

  • script

    :指定的命令脚本是否成功执行过

  • file_exists

    :指定路径的文件是否存在

  • file_glob_count

    :匹配某个 glob 模式的文件数量是否达到最低要求

  • user_confirmation

    :是否获得了用户的人工确认

只有所有验收检查全部通过,当前阶段才能标记为完成,才能进入下一阶段。如果验收门禁拦截了”完成”请求,系统会把缺失项注入到下一轮的执行契约里,让 Agent 明确知道还差什么。

这套机制把”任务完成”从 LLM 的自我宣称变成了可验证的客观事实。

目标连续性裁决

当 Agent 正在执行一个多步骤任务时,用户突然发了一条消息,系统怎么判断用户是想补充当前任务、还是想切换到一个全新任务?

这里采用了一个两级混合决策架构:

第一级是确定性规则:先做词汇归一化,然后检查明确的意图标记(“继续”、“确认”、“重新开始”)。如果意图不明确,检查当前任务是否被用户中断过。如果没被中断,新输入默认按当前任务的补充处理。

第二级是 LLM 兜底:当确定性规则返回”模糊”时,系统才会发起一次 LLM 调用来做最终裁决。LLM 收到的输入包含当前任务目标的摘要和用户新输入的对比,要求只输出 JSON 格式的判断结果。

这种”先确定性、后 LLM”的分层策略,既保证了大多数情况下的快速响应(不需要额外的 API 调用),又处理了边缘情况的准确性问题。

2 |Multi-Provider LLM Orchestration:一个 Trait 统一所有大模型

生产环境的第一个现实问题是:你不可能只用一个模型提供商。

OpenAI 的 GPT 系列在 tool calling 上最成熟,但贵;Anthropic 的 Claude 在长上下文推理上更强;本地部署的 Ollama 在隐私敏感场景下不可替代;有时候你还需要根据延迟和成本动态切换。如果你的业务代码和某个特定厂商的 API 深度绑定,你就失去了灵活切换的能力。

能力协商机制

核心设计是一个 ProviderCapabilities 结构体,在每个 Provider 注册时声明自己的能力画像:

  • 是否支持流式输出(streaming)

  • 是否支持结构化输出(structured_output)

  • 是否支持工具调用(tool_calling)

  • 是否支持 JSON 模式(json_mode)

  • 是否支持多模态视觉输入(vision)

  • 是否支持推理过程返回(reasoning)

运行时根据任务需求选择合适的 Provider。比如需要工具调用的任务,不会发给不支持 tool_calling 的模型;需要图像理解的任务,不会发给不支持 vision 的模型。

结构化输出的五重策略

让 LLM 输出结构化 JSON 是生产环境中最脆弱的环节之一。不同 Provider 支持的程度不同,同一个 Provider 在不同 prompt 下的表现也不同。

这套系统设计了五种结构化输出策略,按优先级降级:

  1. ProviderJsonSchema

    :Provider 原生支持 JSON Schema 约束(最强保证)

  2. ProviderJsonObject

    :Provider 支持 JSON 模式但不支持 Schema 约束

  3. ProviderToolSchema

    :利用 tool calling 的参数 Schema 来间接约束输出格式

  4. ProviderFormatSchema

    :利用 response_format 参数指定 Schema

  5. PromptOnly

    :只在 prompt 中描述格式要求(最弱保证,但兼容性最广)

每种策略还配备了独立的 max_retries(最大重试次数)和 repair_retries(修复重试次数)。当 JSON 解析失败时,系统会把解析错误信息拼回请求,让模型修复自己的输出。

Provider Data 透传

一个容易被忽略但极其关键的工程细节:不同 Provider 有自己独有的参数(比如 OpenAI 的 frequency_penalty、Anthropic 的 top_k)。系统设计了一个 provider_data 字段,允许原始的 Provider 特定数据在消息中透传。

这意味着当对话历史中包含某个 Provider 的特殊元数据时,这些数据不会被其他组件清洗掉,而是在路由到对应 Provider 时恢复。这是保证多 Provider 环境下对话一致性的关键设计。

3 |Hierarchical Context Assembly:System Prompt 不是一段文本,是一座七层建筑

大多数开发者的 System Prompt 写法是:把所有指令揉成一大段文本,然后祈祷模型能记住。

生产级 Agent 的做法完全不同。System Prompt 不是一个字符串,而是一个分层组装的结构化文档

七层 Prompt 架构

一个完整的 Agent System Prompt 由七个独立层次构成,每层有明确的职责边界:

第 1 层 - Identity(身份层):定义 Agent 的核心身份、角色和行为准则。这是不可协商的硬约束。

第 2 层 - System(系统层):运行时的全局规则,包括输出格式约束、安全策略、语言偏好。

第 3 层 - Shell(Shell 层):执行环境信息——操作系统类型、Shell 类型、工作目录、当前时间。这些信息让 Agent 知道自己在什么环境里运行,避免产生不兼容的命令。

第 4 层 - Context(上下文层):持久化记忆、用户偏好、会话摘要。这一层是跨会话的,让 Agent 具备”记住你上次说过什么”的能力。

第 5 层 - Toolset(工具集层):动态生成的工具清单和使用指导。当可用工具集发生变化时(比如 MCP 服务器上线了新工具),这一层会自动更新。

第 6 层 - Skills(技能层):从技能库中加载的专业知识。每个技能可能包含分步骤的执行指令和验收标准。

第 7 层 - Enforcement(执行契约层):每轮循环开始前注入的执行契约(就是前面讲的确定性预门禁输出)。

分层组装的技术实现

每层内容被包裹在带 ID 和 title 的 XML 标签中:

<beneva_prompt_layer id=“identity” title=“Identity”> … </beneva_prompt_layer>

这种设计不是装饰性的。XML 标签给 LLM 提供了明确的结构边界信号,让模型能更准确地区分”这是系统规则”还是”这是上下文记忆”。多个主流模型(包括 GPT-4 和 Claude)在训练数据中大量接触过 XML 结构化的指令格式,使用 XML 标签的 prompt 在指令遵循率上有可测量的提升。

关键工程决策:每一层独立组装、独立裁剪。当对话历史过长需要压缩时,系统可以按层优先级决定保留什么、丢弃什么——Identity 和 System 层永远保留,Context 层可以摘要,Toolset 层根据实际可用工具动态调整。

4 |Tool Execution Safety Framework:当 AI 学会执行命令

Agent 有了工具调用能力之后,安全问题就从理论变成了物理现实。一个能执行 Shell 命令的 Agent,如果 rm -rf / 会怎样?一个能调用 git push 的 Agent,如果把代码推到生产分支会怎样?

四级风险分级

系统对每一个工具调用进行风险评级:

  • Low(低风险)

    :只读操作,如查看文件内容、搜索文件、读取内存。不会改变任何系统状态。

  • Medium(中风险)

    :有限写入操作,如创建文件、修改本地文件。影响范围局限于工作目录。

  • High(高风险)

    :系统级操作,如执行任意 Shell 命令、安装软件包、修改系统配置。

  • Critical(严重风险)

    :不可逆操作,如删除文件、网络写入、生产环境部署。

三种访问模式

根据运行场景,系统提供三种访问模式:

Default(默认模式):所有非低风险操作都需要人工确认。系统暂停 Agent 执行,等待用户批准或拒绝。超时时间默认 300 秒。

AutoReview(自动审查模式):系统对命令和代码进行静态分析,自动判断安全性。比如解析命令行参数,检查是否包含危险模式(递归删除、强制覆盖、网络上传等),检查代码是否访问了敏感路径。只有通过自动审查的操作才会被放行,未通过的仍然降级为人工确认。

FullAccess(完全访问模式):所有操作直接执行,不做拦截。适用于受信任的 CI/CD 环境或开发者本地调试。

命令与代码的静态分析

自动审查模式不是简单地做关键词匹配。它对 Shell 命令做语法解析,拆解管道符、重定向符、环境变量展开,然后对每个原子命令做风险评估。对代码片段,它分析文件路径访问模式、网络调用、进程操作等行为特征。

这套机制的工程价值在于:它让 Agent 可以在”几乎全自动”的模式下安全运行,而不是在”全自动”和”每步都问你”之间做非此即彼的选择。

5 |Multi-Agent Orchestration:一个 Agent 不够用

复杂任务需要拆分。一篇深度研究报告可能需要:一个 Agent 去搜索引擎收集素材,一个 Agent 去做数据分析,一个 Agent 去写初稿,一个 Agent 去做质量审核。

子代理委派架构

父 Agent 通过一个委派工具创建子 Agent。每个子 Agent 拥有:

  • 独立上下文

    :子 Agent 看不到父 Agent 的完整对话历史,只收到一个精简的任务目标和可选的背景上下文。这避免了上下文窗口爆炸。

  • 受限工具集

    :父 Agent 可以指定子 Agent 能使用哪些工具。一个负责搜索的子 Agent 不应该有写文件的权限。

  • 最大递归深度控制

    max_spawn_depth 参数控制子 Agent 是否还能继续创建孙 Agent。默认值为 1(只有父 Agent 可以创建子 Agent,子 Agent 不能再委派)。

批量执行与计划去重

当父 Agent 一次性创建多个子 Agent 时(批量委派),系统会检查每个子任务的 dedupe_key(去重键)。如果两个子任务的 dedupe_key 相同,说明它们在做同一件事,系统会自动合并为一次执行。这防止了 LLM 在一次委派中重复分配相同任务导致的资源浪费。

心跳监控与取消树

子 Agent 的生命周期管理依赖两个机制:

心跳监控:子 Agent 每 30 秒报告一次存活状态。如果超过阈值没有心跳,系统判定子 Agent 卡死,触发超时清理。

CancellationToken 取消树:每个子 Agent 绑定一个 CancellationToken,这些 token 构成一棵树结构。当父 Agent 被取消时,所有子 Agent 的 token 自动级联取消。当用户中断一个任务时,整棵 Agent 树会被干净地终止,不会有孤儿进程残留。

协作暂停协议

子 Agent 的执行可以被父 Agent 暂停和恢复。这套协议通过一个原子状态机实现,状态用 AtomicU8 存储(0=运行中、1=正常完成、2=被取消、3=异常终止)。父 Agent 可以在子 Agent 执行的任意时刻发出暂停信号,子 Agent 在下一次工具调用前检查状态,如果被暂停则进入等待。

6 |Omnichannel Messaging Gateway:连接真实世界的桥梁

一个只存在于终端里的 Agent 是没有产品价值的。用户需要通过他们日常使用的消息平台来与 Agent 交互。

多平台协议适配

系统需要同时对接多个消息平台,每个平台有自己的协议栈和限制:

  • 即时通讯平台

    :使用私有长轮询协议,35 秒超时的 HTTP 长轮询拉取消息。通信链路需要 AES-CBC 加密,媒体文件通过 CDN 上传和下载。消息需要分块发送(避免超长消息被截断),每块之间有 0.35 秒的延迟来模拟人类发送节奏。

  • 企业协作平台

    :使用 WebSocket 持久连接,支持富文本卡片和交互式组件。

  • 邮件通道

    :使用 IMAP/SMTP 协议,需要处理邮件线程的关联和引用关系。

跨平台消息去重

当用户在多个平台同时发送消息时,系统需要避免重复处理。实现方式是一个带 TTL(默认 300 秒)的消息去重缓存,用消息内容的哈希值作为去重键。在 TTL 窗口内,相同内容的消息只会被处理一次。

统一投递路由

Agent 的响应需要投递回正确的平台和会话。系统设计了一个 ResolvedDeliveryTarget 结构,包含平台标识、目标地址和可选的线程 ID。当 Agent 被 Cron 任务触发时,投递目标从任务配置中解析;当 Agent 被消息平台触发时,投递目标回写为来源会话。

这套设计让 Agent 的核心逻辑完全不感知”用户是从哪个平台来的”——它只管产出结果,投递路由由网关层处理。

7 |Persistent Memory & Self-Evolution:让 Agent 越用越聪明

一个不积累经验的 Agent,每次都在从零开始。生产级 Agent 需要两套机制:跨会话的持久记忆,和从交互中自动提取可复用知识的能力。

双轨记忆系统

记忆分为两个独立轨道:

Memory 轨道(项目/工具事实):存储环境配置、工具使用技巧、项目约定。比如”用户的项目使用 React 19 和 TypeScript”或”某个 API 需要特殊请求头”。

User 轨道(用户偏好):存储用户的工作风格、沟通偏好、反复出现的修正。比如”用户偏好简洁回答”或”用户习惯用英文写代码注释”。

记忆的写入方式有严格规则:必须以声明性事实的形式存储,不能是指令。“用户偏好简洁回答”是正确的记忆条目;“总是简洁回答”是错误的——后者是行为指令,会在未来的交互中覆盖用户的当前需求。

记忆提取本身由 LLM 驱动。系统在每次对话结束后,取最近 14 条消息,用一个专门的提取 prompt 让 LLM 判断哪些内容值得持久化。提取 prompt 明确禁止保存临时任务进度、TODO 列表或一次性操作日志。

会话后经验审查

更高级的机制是进化审查。系统会根据”学习强度”配置决定何时触发深度经验审查:

  • High 模式

    :5 次工具调用或 3 轮对话后触发

  • Medium 模式

    :10 次工具调用或 5 轮对话后触发

  • Low 模式

    :20 次工具调用或 8 轮对话后触发

审查由一次独立的 LLM 调用完成,使用一套 class-first 评分量规(以”可复用性”为第一优先级)。LLM 需要回答五个维度的问题:

  1. class_value

    :这次经验能帮助未来同类工作吗?

  2. reuse_frequency

    :它可能再次出现吗?

  3. correction_value

    :用户是否纠正了一个持久行为?

  4. skill_fit

    :它应该存在技能里还是记忆里?

  5. confidence

    :这个经验有多确定?

只有当 LLM 判定”值得保留”时,系统才会执行写入。如果一次对话什么有价值的经验都没有产出,审查直接返回空数组,不写入任何垃圾数据。

技能自动生成

当经验审查判定某个经验足够有价值时,系统会尝试将其转化为一个可复用技能草案。关键策略是:优先更新已有技能,而不是创建新技能。只有当经验确实无法归入任何现有技能时,才会创建新技能。

这防止了技能库膨胀成一堆窄化的、只用过一次的”微技能”。

8 |MCP Protocol Integration:让 Agent 接入万物

Model Context Protocol(MCP)是 2024 年由 Anthropic 提出的开放标准,目标是统一 AI 模型与外部工具/数据源之间的连接方式。一个面向未来的 Agent 运行时必须支持 MCP 协议。

JSON-RPC 双向通信

MCP 客户端通过 JSON-RPC 2.0 协议与 MCP 服务器通信。系统支持两种传输层:

Stdio 传输:通过子进程的标准输入/输出进行通信。适合本地运行的工具服务器。客户端作为父进程启动服务器子进程,通过管道发送和接收 JSON-RPC 消息。

HTTP 传输:通过 HTTP 请求进行通信。适合远程运行的工具服务器。支持 SSE(Server-Sent Events)做服务端推送。

三维资源发现

连接到 MCP 服务器后,客户端发现三种类型的资源:

  • Tools

    :可调用的工具函数,包含名称、描述和 JSON Schema 参数定义

  • Resources

    :可读取的数据资源,用 URI 标识,包含 MIME 类型

  • Prompts

    :预定义的 prompt 模板,可以被 Agent 直接引用

发现的工具会自动注册到 Agent 的工具注册中心,对 Agent 来说,MCP 工具和内置工具在调用上没有任何区别。

OAuth 认证流

对于需要认证的 MCP 服务器,系统实现了完整的 OAuth 2.0 认证流。认证会话管理包括令牌获取、刷新和过期处理。

Sampling 回调

MCP 协议定义了一个 sampling 能力,允许 MCP 服务器反向请求客户端进行 LLM 推理。系统实现了一个 McpSamplingRuntime 来处理这种反向调用——当 MCP 服务器需要 LLM 能力时,它不是自己调用 OpenAI API,而是通过 sampling 接口请求 Agent 运行时代理执行。

9 |CDP Browser Automation:让 Agent 拥有眼睛和手

浏览器自动化是 Agent 获取实时信息和操作 Web 界面的核心能力。

CDP 协议直连

系统通过 Chrome DevTools Protocol (CDP) 直接控制浏览器,而不是依赖 Selenium 或 Playwright 等中间层。CDP 是 Chrome 浏览器原生支持的调试协议,功能最完整、延迟最低、与浏览器版本兼容性最好。

两种运行模式:

Host 模式:连接用户本地正在运行的 Chrome 实例,复用用户的登录状态和 Cookie。适合需要操作已登录网站的场景。连接地址通常是 http://127.0.0.1:19222

Managed 模式:系统自行启动和管理浏览器实例。适合不需要登录状态的公开网页信息采集。

会话隔离与下载管理

每个浏览器操作绑定到一个会话(session),不同会话的浏览器状态相互隔离。系统自动管理下载目录,操作完成后报告新下载的文件列表(包含文件路径、大小和修改时间)。

质量门禁

浏览器操作完成后,系统不只是返回”成功了”,而是通过一个质量门禁检查页面状态:页面是否真正加载了预期内容、是否有可见的错误提示、关键元素是否存在。这防止了 Agent 在一个加载失败的页面上做出错误判断。

整体架构:这些组件如何咬合在一起

数据流向是:消息从 Gateway 进入 → Context 层组装完整 Prompt → Agentic Loop 调用 LLM 并执行工具 → 工具执行经过安全审批 → 子代理可以并行处理子任务 → MCP 扩展外部能力 → 浏览器提供 Web 信息获取 → 每次会话结束后,记忆和进化系统提取可复用知识。

为什么这些组件缺一不可?

我们逐个做个思想实验:去掉任何一个,会发生什么?

没有 Agentic Loop 的确定性守卫:Agent 会在复杂任务中空转、跳步、跑偏。你以为它做完了,实际产出是空的。

没有 Multi-Provider 抽象层:你的 Agent 被锁死在一家厂商的 API 上。当那家厂商涨价、限流或服务质量下降时,你没有退路。

没有分层 Context 组装:对话变长后,Agent 开始遗忘关键指令。工具变多后,prompt 膨胀到超出上下文窗口。

没有工具执行安全框架:Agent 执行了一条危险命令。你的服务器被清空了,或者生产代码被意外覆盖了。

没有多 Agent 编排:复杂任务无法拆分。单个 Agent 的上下文窗口成了任务复杂度的天花板。

没有消息网关:Agent 只活在终端里,用户无法通过他们习惯的方式(微信、邮件)使用它。

没有记忆和进化系统:Agent 每次对话都从零开始,永远不会记住你的偏好,永远不会从错误中学习。

没有 MCP 支持:每接入一个新工具都需要写定制代码,无法利用 MCP 生态中已经存在的数百个工具服务器。

没有浏览器自动化:Agent 无法获取实时 Web 信息,无法操作需要登录的 Web 界面。

写在最后:Agent 不是一个 API 调用

回到开头的问题。一个能跑通 demo 的 Agent 和一个生产级 Agent 运行时之间,差的不是代码行数,而是对失败模式的理解深度

LLM 是不确定的——同一个 prompt 在不同时刻可能产出不同结果。工具调用是危险的——一个 rm -rf 能毁掉一切。用户行为是不可预测的——他们会在任务执行中途打断、补充、切换。系统资源是有限的——上下文窗口、API 配额、内存、磁盘。

生产级 Agent 的本质,就是在这些不确定性之上,构建一层又一层的确定性约束。

确定性预门禁约束 Agent 不跳步。验收门禁约束 Agent 不撒谎。风险分级约束工具不伤人。取消树约束子代理不留孤儿。消息去重约束网关不重复处理。学习阈值约束记忆系统不写入垃圾。

每一层约束,都是在为 LLM 的不确定性兜底。

这就是为什么一个生产级 Agent 运行时需要 11 万行代码,而不是 100 行。

当你下次看到一个 Agent demo 时,问自己四个问题:它有确定性守卫吗?它有安全审批吗?它能记住你的偏好吗?它能在微信上回复你吗?

如果答案是否定的,那它就还只是一个 demo。别急着上线。


内容效果不满意?点此反馈

输入关键词开始搜索