【Agent】把通用 Agent 装进生产环境:一套基于 Hermes Agent 的垂类架构
本文讨论如何把通用 Agent 收敛为面向单一业务场景的生产级智能体,并通过可信会话、Skill、Action Tool 和受控后端划清推理、权限与执行边界。
背景
大语言模型已经能让通用 Agent 听懂需求、规划步骤、调用工具。做一个演示不难,难的是把它放进真实业务里,并且让人敢用。
到了生产环境,问题很快会从“模型够不够聪明”变成另一组更麻烦的事:用户身份从哪里来?模型可以执行哪些动作?谁来校验权限?流程中断后怎么接着跑?路径、凭据和基础设施信息又该如何藏在边界以内?
下文把基于 Hermes Agent、面向单一业务场景收敛出来的生产级智能体称为 ScenarioAgent。这里不讨论某个具体功能,而是拆解它从通用 Agent 走向生产环境时需要的分层架构:
用户负责表达目标,
ScenarioAgent负责理解与编排,Skill 按需提供流程提示词,Action Tool 收窄动作,CLI/API 受控后端负责可信执行。
总体分层架构
flowchart TB
User(["用户"]):::user
Gateway["Hermes Gateway<br/>可信会话上下文"]:::hermes
ScenarioAgent["ScenarioAgent<br/>意图理解 · 流程编排 · 用户沟通"]:::agent
Skill["Skill<br/>渐进式披露的场景提示词<br/>流程规范 · 交互策略"]:::skill
Action["Action Tool<br/>有限动作 · 有限参数"]:::boundary
Backend["CLI / API 受控后端<br/>身份 · 权限 · 状态机 · 资源派生 · 审计"]:::backend
subgraph Infra["受保护的基础设施"]
direction LR
Docker["Docker"]:::infra
Profile["Profile"]:::infra
API["飞书 API"]:::infra
SMB["SMB"]:::infra
Other["其他业务系统"]:::infra
end
User -->|"自然语言请求"| Gateway
Gateway -->|"消息 + 可信身份上下文"| ScenarioAgent
ScenarioAgent -->|"按当前意图加载"| Skill
Skill -->|"规范下一步动作"| Action
Action -->|"结构化调用"| Backend
Backend -->|"派生并执行复杂指令"| Docker
Backend -->|"派生并执行复杂指令"| Profile
Backend -->|"派生并执行复杂指令"| API
Backend -->|"派生并执行复杂指令"| SMB
Backend -->|"派生并执行复杂指令"| Other
Backend -. "结构化状态 · 安全用户投影" .-> Action
Action -. "Tool Result" .-> ScenarioAgent
ScenarioAgent -. "自然语言回复" .-> Gateway
Gateway -.-> User
classDef user fill:#E8F3FF,stroke:#1677FF,color:#102A43,stroke-width:2px;
classDef hermes fill:#F0F5FF,stroke:#597EF7,color:#1D2B53,stroke-width:2px;
classDef agent fill:#F6FFED,stroke:#52C41A,color:#234D20,stroke-width:2px;
classDef skill fill:#FCFFE6,stroke:#A0D911,color:#3F4A0B,stroke-width:1.5px;
classDef boundary fill:#FFF1F0,stroke:#F5222D,color:#5C1014,stroke-width:3px;
classDef backend fill:#FFF7E6,stroke:#FA8C16,color:#613400,stroke-width:2px;
classDef infra fill:#E6FFFB,stroke:#13C2C2,color:#124B4B,stroke-width:1.5px;
图中画的是一次业务请求的调用链,不是 Hermes Agent 内部模块的代码归属。ScenarioAgent 先判断意图并加载对应 Skill。Skill 只补充当前任务需要的提示词,并规定该调用哪个 Action Tool。Action Tool 再把开放式请求压缩成有限动作和有限参数,交给 CLI/API 后端执行。
结果按原路返回。Action Tool 接收后端给出的结构化状态和安全用户投影,再通过 Tool Result 交还给 ScenarioAgent。后端不会绕开 Tool,直接回调 ScenarioAgent。
这套分层有一条很直观的规律:越靠近用户,越要理解开放的自然语言;越靠近基础设施,越要确定、可验证、可审计。Skill 避免上下文被全部场景规则塞满,Action Tool 卡住能力边界,受控后端则把简单业务动作翻译成经过校验的底层操作。
用户只说目标,不填写系统参数
用户只需要描述想完成什么,不该替系统填写底层参数。
例如,用户可以表达:
- 创建一项业务任务;
- 继续上一次流程;
- 查询当前状态;
- 对某个业务对象执行允许的操作。
但普通用户不应该直接提供:
- 宿主机路径;
- 容器名;
- 内部端口;
- 服务地址;
- API 密钥;
- 任意 Shell 命令;
- 可以改变安全策略的配置片段。
这里要守住一条线:自然语言用来表达业务意图,不能直接穿透成系统控制参数。
消息平台不只是聊天界面,也是认证链路的一部分。它把用户输入和平台确认过的会话身份一起交给下一层。用户当然可以在聊天里说“我是谁”,但系统只能信任通道提供的身份上下文,不能把这句话当作身份证明。
具体接入飞书、企业微信、Slack、Web 应用还是内部 API,并不影响这条原则:
用户提供意图,可信通道提供身份。
Hermes Gateway 建立可信会话上下文
Hermes Gateway 连接消息平台和 Agent Runtime。它接收消息、管理会话,并把平台已经认证的信息带入 Agent 的执行上下文。
Gateway 不负责理解业务。它先把请求边界钉牢:
- 消息来自哪个平台;
- 当前会话属于哪个用户;
- 请求发生在哪个会话中;
- 哪些上下文由平台提供、不能由模型修改;
- 当前入口允许装配哪些工具。
可信会话上下文需要与普通模型参数分开。模型可以使用这些信息发起动作,却不能伪造或覆盖它们。
如果先让模型从聊天内容里提取用户名,再拿这个名字做权限判断,身份校验就退化成了概率问题。Gateway 应该提供经过认证、对模型只读的身份上下文,最后仍由受控后端校验。
Gateway 还决定当前入口能看到哪些工具。普通用户入口只装配业务必需的能力,管理员维护入口与普通会话隔离。安全不能靠提示词里一句“请勿使用危险工具”,更可靠的办法是运行时根本不提供这些工具。
ScenarioAgent 理解意图、编排流程,也负责把话说清楚
ScenarioAgent 运行在 Hermes Agent Runtime 中。Gateway 把消息和可信会话上下文交给它,它再按当前意图选择 Skill,并依照 Skill 调用 Action Tool。它站在自然语言与确定性系统之间,处理传统后端不擅长的开放式工作。
它主要做三件事。
意图理解
同一个目标,不同用户可能有完全不同的说法。ScenarioAgent 结合当前消息、会话上下文和场景 Skills,把这些表达归一成有限的业务意图。
流程编排
流程跨越多个阶段时,ScenarioAgent 根据后端返回的当前状态和允许动作选择下一步。模糊表达、重复请求、中途离开后再回来,这些都由它消化,用户不必理解后端状态机。
用户沟通
后端返回稳定的结构化事实,ScenarioAgent 把事实说成人话:已经完成了什么、还在等什么、用户现在要做什么。
ScenarioAgent 不证明身份,不决定权限,也不直接操作基础设施。它可以判断“下一步想做什么”,但不能自己认定“现在实际上走到哪一步”。
两边的分工可以浓缩成一句话:
ScenarioAgent负责智能判断,受控后端负责事实判断。
这不意味着要把所有场景规则长期塞进系统提示词。ScenarioAgent 先从简短的 Skill 索引里找到相关能力,再按需读取详细流程,最后选择 Action Tool。有限的上下文因此能留给眼前的任务,无关规则也不容易干扰判断。
ScenarioAgent 的上下文从哪里来
一个能长期运行的 ScenarioAgent,需要几类来源不同、边界清楚的上下文:
SOUL.md提供长期身份、职责边界和表达风格上下文,让ScenarioAgent在不同会话中保持一致的角色判断。AGENTS.md提供当前工作目录的项目规则和文件纪律上下文,让ScenarioAgent知道在这个环境中应该如何工作、哪些边界不能越过。- Skill 提供按需加载的场景流程上下文,让
ScenarioAgent只在任务相关时读取详细步骤,避免把所有规则一次性塞进模型窗口。 - Memory 提供跨会话的用户偏好、稳定环境事实和长期经验上下文,让
ScenarioAgent不必每次重新询问或重新发现,但不用于保存短期任务状态。 - Session 提供当前对话的消息历史和本轮任务上下文,让
ScenarioAgent能理解指代、承接多轮交流并维持当前目标。 config.yaml提供模型、toolset 和 runtime 策略上下文,.env提供运行所需的秘密输入,使能力装配和凭据来源由部署控制而不是由用户文本决定。- Profile 把 SOUL、配置、Skills、Memory、Session、插件和平台连接状态组合成独立运行上下文,使不同职责的
ScenarioAgent可以在同一机器上相互隔离。 - Docker 可以进一步固定依赖、系统工具、文件系统、网络和运行用户上下文,使
ScenarioAgent的环境更可复现、更容易限制和回滚。
不过,ScenarioAgent 本身并不总该放进 Docker。本文所依据的 Creator 场景就需要创建和管理下游 Docker Agent。如果 Creator 也在容器里运行,就不得不额外处理宿主机 Docker socket、挂载、权限以及服务生命周期,边界反而更复杂。这个场景更适合让 ScenarioAgent 作为受控的 Hermes Agent Profile 运行,严格收窄普通用户的能力面,再由 CLI/API 后端创建和管理下游 Docker 实例。
可以用下面几条来判断:
- 如果
ScenarioAgent主要消费外部 API 或处理普通文件任务,可以优先考虑整体容器化; - 如果
ScenarioAgent本身要管理宿主级容器、系统服务或其他高权限运行时,先用 Profile、toolset 白名单和受控后端收窄能力,再谨慎决定是否容器化; - 不论是否容器化,身份、权限、状态机、资源派生和审计都仍应放在确定性后端,而不是交给 Prompt 或 Skill。
Skill 按需补充场景知识
Skill 回答的是“ScenarioAgent 应该怎样完成这类任务”。系统先给模型一份精简的 Skill 索引,只包含名称和用途。确认与当前任务相关后,ScenarioAgent 才加载详细目标、步骤、判断规则、工具选择、沟通方式和异常恢复说明。
比起一次性注入所有规则,按需加载更省上下文,也更容易控制:
- 只为当前任务占用上下文;
- 减少无关指令之间的干扰;
- 场景流程可以独立维护、测试和复用。
生产环境不该默认加载所有内置 Skill。更稳妥的做法是使用白名单,只装配当前 ScenarioAgent 确实需要的 Skill,并让 Skill 来源随部署版本一起受控。这样既能减少无关能力对模型路由的干扰,也能防止通用 Skill 把流程带出场景边界。
在本文所依据的实际场景里,ScenarioAgent 只装配两个与职责直接相关的项目 Skill。其他 Hermes bundled、profile-local 或外部 Skill 都不进入活动集合。
但 Skill 不是权限边界。归根结底,它只是提供给模型的流程知识:
- Skill 可以说明何时调用某个动作;
- Skill 可以要求先查询状态再继续;
- Skill 可以约束用户回复中应展示什么;
- Skill 不能证明调用者身份;
- Skill 不能授予资源权限;
- Skill 不能绕过后端执行系统操作。
Skill 位于 ScenarioAgent 和 Action Tool 之间。它告诉 ScenarioAgent 该走哪条流程、何时调用哪个 action,但不执行动作,也不做最终授权。白名单缩小的是模型的认知和路由空间,真正的执行权限仍由 Action Tool 与受控后端决定。
Action Tool 只暴露有限业务动作
Action Tool 决定模型究竟能对生产系统表达哪些动作。这比在 Prompt 里写“不要使用危险工具”可靠得多:没有装配到当前入口的 Tool,模型就调用不了。
Hermes Runtime 仍需保留少量基础工具,用于发现和读取 Skill、查询工具目录,但这些工具不承载业务副作用:
- Skill 使用工具:用于发现和读取受控 Skill,包括
skills_list、skill_view、skill_manage; - Tool Search 基础工具:用于查询按需加载的工具目录,包括
tool_search、tool_describe、tool_call;
真正能产生生产业务副作用的只有一个类型化 Action Tool:cri_hermes_action。Skill 规定调用时机,这个 Tool 只接受预先定义的 action 和低风险业务参数。
通用高权限工具则明确禁用或不装配,不会出现在普通用户所用的 ScenarioAgent 能力面中:
terminal;file,包括任意文件读取、写入和修改;code_execution;- 通用飞书文档和云盘工具;
- Docker、computer use 和系统服务操作能力。
“Hermes Agent 里存在某个工具”和“当前 ScenarioAgent 能看到这个工具”是两回事。Hermes Agent 可以有丰富的基础能力,生产场景则应通过 platform_toolsets、disabled_toolsets 等装配边界,为每个入口生成最小工具集合。实际工具 schema 中没有高权限能力,才是真正的安全边界,不能指望模型自觉不用。
Tool Search 也不意味着放开全部能力。它只是 Hermes 的按需发现机制。某项工具能否被发现、描述和调用,仍受当前 Profile、插件注册、平台 toolset 和禁用策略限制。所有业务副作用依旧只能通过 Action Tool 进入后端。
Action Tool 是这套架构的执行窄腰。它把模型依据 Skill 作出的开放判断,收敛成有限、结构化、可校验的业务动作。
危险往往来自过于通用的接口:
1 | execute(command, arguments) |
这种接口把“做什么”和“怎样操作系统”都交给模型。只要参数拼错,或把用户文本直接带进命令,业务边界就可能被突破。
生产环境更适合这样的接口:
1 | create_task(task_type, description) |
类型化工具至少要满足这些要求:
- 动作集合是有限的;
- 每个动作具有明确 schema;
- 参数只表达业务含义;
- 身份不由模型填写;
- 系统路径和资源名称不由模型指定;
- 未知字段和越界值默认拒绝;
- 每个动作映射到固定的后端能力;
- 返回值是稳定、结构化的业务状态。
Action Tool 不只是 API 包装。它定义了模型影响生产系统的最大范围:Skill 告诉 ScenarioAgent 怎么走流程,Action Tool 决定副作用可以携带哪些动作和参数。
设计新场景时,应该先枚举模型需要哪些业务动作,再为每个动作设计最小参数。不要先开放通用工具,然后靠 Prompt 提醒模型谨慎使用。
CLI / API 后端掌管可信事实和副作用
CLI/API 后端是安全边界的最终执行者。Action Tool 只表达简单、有限的业务动作,后端再把它们翻译成针对 Docker、Profile、飞书 API、SMB 或其他系统的复杂指令。凡是需要系统信任的判断,都要在这一层完成。
身份校验
后端从可信会话上下文中取得调用者身份,不接受模型或普通用户自报身份。拿不到可信身份,就直接拒绝动作。
权限与资源归属
资源引用只能说明“要操作哪个对象”,不能证明调用者有权访问。后端每次都要重新校验资源归属、当前状态和允许的操作。
持久状态机
对话历史可以帮助模型理解上下文,却不能充当业务事实源。跨步骤流程要由后端持久化状态,并明确合法转换、幂等语义、超时和恢复规则。
用户换了一个会话,ScenarioAgent 也能重新查询后端状态并继续,不必指望模型记住之前发生过什么。
资源派生
模型只接触业务对象,后端负责把它派生为真实的基础设施资源。运行单元、Profile、外部 API 目标和文件空间,都不需要出现在模型参数里。
这样既守住了安全边界,也保留了迁移空间。底层部署方式可以变化,上层业务动作不必跟着改。
幂等、事务与补偿
网络、进程和外部系统都可能让生产流程失败。后端需要明确提交点,避免重复调用产生重复副作用,并在跨系统操作失败时执行补偿。
无法证明安全时就 Fail Closed:停下来,不猜测缺失的身份、路径、配置或状态。
审计
后端需要记录谁从哪个可信入口发起了什么动作、结果如何、是否触发恢复或补偿。日志要留下足够的诊断证据,同时对身份标识、凭据和内部资源脱敏。
安全结果投影
后端不能把原始异常、内部路径和基础设施信息直接扔给模型。它应先生成一份安全结果投影,只包含:
- 当前业务阶段;
- 已满足的条件;
- 仍在等待的条件;
- 是否需要用户操作;
- 是否可以重试;
- 下一项允许动作。
ScenarioAgent 再根据这些受控事实组织回复。脱敏由确定性程序完成,不留给模型临场发挥。
底层复杂指令只能由后端派生和执行
Docker、Profile、飞书 API、SMB 和其他业务系统位于架构最底层。它们常常涉及复杂命令、路径、凭据、调用顺序和恢复逻辑。这些细节不能直接暴露给普通用户、ScenarioAgent、Skill 或 Action Tool。
这里有三条硬约束。
模型不直接访问
ScenarioAgent 不需要知道容器名、宿主路径、内部端口、凭据位置和服务拓扑,只需要读取后端返回的业务状态。
用户不直接指定
普通用户不能通过自然语言提交底层资源参数。后端根据可信身份、业务对象和部署策略派生真实资源。
基础设施差异不向上泄漏
底层可以运行在容器、虚拟机或云任务中,也可以使用本地文件、对象存储或网络文件系统。只要后端保持同一份业务契约,上层 Hermes Agent 和 Skills 就不用跟着基础设施重写。
复用也由此变得实际:上层沿用智能编排方式,中间保留安全边界,底层按场景替换具体系统。
这套架构如何复用到其他垂类场景
这套架构不是靠削弱模型换安全,而是把不同职责放回合适的位置。
模型仍然有足够的发挥空间:
- Hermes Agent 理解自然语言;
ScenarioAgent处理多轮上下文和流程编排;- 白名单 Skill 提供可组合、按需加载的场景提示词;
- Action Tool 提供明确的能力窄腰;
- 模型可以在合法动作空间中自主选择下一步。
系统的稳定性则来自:
- 业务状态由后端持久化;
- 动作具有幂等和恢复语义;
- 资源由系统确定性派生;
- 外部事实可以重新验证;
- 基础设施变化不会直接影响上层交互。
安全边界包括:
- 身份由可信通道提供;
- Skill 与 Action Tool 都采用白名单装配;
- terminal、file、code execution 等通用高权限 toolset 不进入普通用户能力面;
- Action Tool 只允许有限动作和参数;
- 后端执行权限、归属和状态校验;
- 模型与用户只看到安全结果投影;
- 所有关键副作用都可以审计。
目的不是把 ScenarioAgent 改造成固定脚本,而是给它一个边界清楚的业务世界。模型仍可理解和组合用户意图,但越不过系统允许的动作空间。
复用方法
迁移到新的垂类场景时,可以按下面的顺序设计:
- 明确用户通过哪个可信通道进入系统。
- 明确 Gateway 能提供哪些不可伪造的会话上下文。
- 定义
ScenarioAgent需要理解和编排的业务流程。 - 只装配当前场景需要的有限 Skill。
- 以白名单定义当前入口可见的 Action Tool,并禁用通用高权限 toolset。
- 枚举有限的业务动作,不暴露底层命令。
- 为每个动作设计最小、类型化参数。
- 把身份、权限、状态和资源派生放入受控后端。
- 将跨步骤流程建模为持久状态机。
- 为每个副作用定义幂等、提交和补偿语义。
- 先设计安全结果投影,再设计用户回复。
- 让基础设施只能通过受控后端访问。
换一个场景,需要调整的是 ScenarioAgent 的业务流程、Skill、Action Tool 的动作集合以及底层资源适配。主调用链不用变:
1 | 可信通道 |
比如:
- 知识管理场景:
ScenarioAgent理解内容需求,后端负责文档归属、审批和发布; - 数据分析场景:
ScenarioAgent拆解分析目标,后端负责数据权限和作业提交; - 研发协作场景:
ScenarioAgent收集需求,后端负责资源配额和生命周期; - 部署场景:
ScenarioAgent解释变更,后端负责审批、发布和回滚; - 合规场景:
ScenarioAgent辅助审阅,后端负责材料范围、签署状态和审计。
给模型自由,但只在受控世界里
生产级 Agent 不该同时承担推理、授权、状态存储和基础设施安全。把所有能力直接交给模型,看似灵活,其实是把系统正确性押在一次次概率生成上。
更可靠的做法,是让每一层只承担自己擅长的职责:
- 用户表达目标;
- Hermes Gateway 提供可信会话上下文;
ScenarioAgent负责理解、编排和沟通;- 有限 Skill 提供渐进式披露的场景提示词;
- Action Tool 定义模型能够表达的有限动作边界;
- CLI / API 受控后端证明身份、权限、状态和资源;
- 底层复杂指令只由受控后端派生和执行。
模型不需要拥有整个系统。给它足够理解业务和编排流程的自由,再把身份、权限、状态与副作用交给确定性后端,这样的 Agent 才能真正进入生产环境。
