☰
通用Agent架构实战:大脑+技能+护栏三层拆解与迁移路径
2026/10/14 4:55:30 网站建设 项目流程

1. 从“功能堆叠”到“意图驱动”:通用Agent到底改了什么

过去十几年,我们做软件的方式基本没变过:产品经理画流程图,开发把流程翻译成页面和按钮,用户按着设计好的路径一步步点。一个报销系统,你得先点“新建报销单”,再选费用类型,再上传发票,再填金额,再提交。每一步都是人迁就软件,软件用固定的菜单和表单来“框住”人的行为。

通用Agent的出现,把这个逻辑翻了个面。用户不再需要知道软件有哪些功能、功能藏在哪个菜单里,只需要说一句“帮我把上个月的差旅发票整理一下,按公司标准提交报销”。Agent自己去理解意图、拆解任务、调用工具、检查结果。软件从“人操作的工具”变成了“人指挥的执行体”。

这个变化听起来像是交互层的改进,但真正被撼动的是底层架构。传统软件的核心资产是“功能模块”和“业务流程”,而Agent时代,核心资产变成了三样东西:大脑(推理与规划)、技能(可调用的原子能力)、护栏(约束与安全边界)。功能不再需要被预先编排成固定流程,而是被拆成一个个可被Agent动态组合的技能单元。

我过去两年参与过几个从传统架构向Agent架构迁移的项目,最大的感受是:不是所有软件都值得改,但所有软件都需要重新思考“哪些部分应该交给Agent,哪些部分必须保留确定性”。这篇文章就把这套“大脑+技能+护栏”的架构拆开讲清楚,包括它为什么会出现、怎么落地、踩过哪些坑,以及传统软件团队该怎么一步步过渡。

2. 传统软件架构为什么撑不住Agent时代

2.1 传统架构的隐含假设正在失效

传统软件架构,不管是MVC、微服务还是事件驱动,底层都有一个隐含假设:用户的操作路径是可枚举的,业务流程是相对稳定的。所以我们可以把流程画成状态机,把功能拆成API,把权限绑在角色上。这套东西在“人操作软件”的时代非常有效,因为人的操作速度慢、路径有限、异常情况少。

但Agent不一样。Agent的推理路径是动态生成的,它可能今天用A技能+B技能完成任务,明天用C技能+D技能+E技能完成同一个任务。你没法提前把所有路径都画出来,也没法给每一条路径都配一个页面。更麻烦的是,Agent会“犯错”——它可能误解意图、调用错误的技能、传入非法参数。传统架构里,错误处理是写在代码里的if-else,但Agent的错误是语义层面的,if-else根本兜不住。

我见过一个典型的失败案例:某团队想把一个审批系统改造成Agent驱动,结果把原来的审批流引擎直接暴露给Agent调用。Agent在理解“紧急审批”时,自己组合了一个“跳过两级审批”的路径,直接绕过了风控。问题不在于Agent能力不够,而在于传统架构里根本没有“护栏”这个概念——权限是绑在角色上的,不是绑在意图上的。

2.2 功能模块的粒度太粗,Agent没法复用

传统软件的功能模块,粒度是按“页面”或“业务对象”来切的。一个“订单管理”模块,里面可能包含查询、创建、修改、取消、导出等十几个操作,每个操作又绑定了特定的表单和校验逻辑。这种粒度对人是友好的,因为人需要看到完整的上下文。但对Agent来说,它只需要“创建一个订单”这个原子能力,不需要知道这个能力在哪个页面、哪个按钮后面。

更麻烦的是,传统模块之间往往有隐式的状态依赖。比如“取消订单”必须先“查询订单”,而“查询订单”又依赖用户当前的会话状态。Agent在调用这些模块时,经常因为状态不对而失败。我试过让Agent直接调用一个传统CRM的API,结果它连续失败了七次,原因分别是:没登录、没选租户、没传分页参数、没传排序字段、没传时间范围、没传状态过滤、没传关联ID。这些参数对人来说是“常识”,对Agent来说全是坑。

所以Agent时代的第一件事,就是把功能模块重新拆成无状态、自描述、可组合的技能单元。每个技能只做一件事,输入输出明确,不依赖调用方的上下文。这听起来像是微服务的翻版,但区别在于:微服务是给服务调服务用的,技能是给Agent调用的。Agent需要知道“这个技能能干什么、需要什么参数、会返回什么结果”,而不是“这个接口的URL是什么”。

2.3 护栏缺失导致Agent行为不可控

传统软件的安全模型是基于“最小权限原则”:用户只能访问他被授权的功能。但这个模型在Agent场景下完全不够用。因为Agent不是“用户”,它是一个代理执行体。它可能代表用户去调用一个用户本来有权限的功能,但调用的方式、频率、上下文可能完全超出预期。

举个例子:一个财务Agent被授权访问报销系统,用户让它“整理上个月的发票”。Agent可能会:查询所有发票、下载发票附件、调用OCR识别、调用汇率转换、调用报销标准校验、最后提交报销单。这一连串操作里,每一步都是用户有权限的,但组合起来的行为可能触发风控——比如短时间内大量下载附件、频繁调用外部OCR服务、提交了超出预算的报销单。

传统架构里没有地方放这些约束。你不可能在每一个API里都写一遍“如果调用方是Agent且连续调用超过10次就拦截”。所以Agent架构必须把护栏抽出来,做成一个独立的、可配置的、跨技能生效的约束层。护栏要管的事情包括:调用频率、参数范围、数据访问边界、操作序列合法性、结果合规性。没有护栏的Agent,就像没有刹车的车,跑得越快越危险。

3. “大脑+技能+护栏”三层架构的完整拆解

3.1 大脑层:意图理解与任务规划

大脑层是Agent的核心,负责把用户的自然语言指令翻译成可执行的任务计划。它通常由一个大语言模型驱动,但光有模型不够,还需要一套规划框架。我见过三种常见的规划方式:

  • ReAct模式:推理一步、执行一步、观察结果、再推理下一步。适合任务路径不确定、需要根据中间结果调整的场景。
  • Plan-and-Execute模式:先一次性生成完整计划,再逐步执行。适合任务路径相对确定、但步骤较多的场景。
  • 混合模式:先做粗粒度规划,执行过程中根据反馈动态调整。这是目前最实用的方式,兼顾了效率和灵活性。

大脑层的关键设计点是任务分解的粒度。分解得太粗,技能调用会失败;分解得太细,规划开销会爆炸。我的经验是:每个子任务应该对应一个技能调用,或者一个明确的判断逻辑。比如“整理发票”可以分解为“查询发票列表→逐张识别→校验报销标准→生成报销单→提交”。每一步都对应一个技能,中间不需要再插入额外的推理步骤。

还有一个容易被忽略的点:大脑层需要维护任务状态。Agent执行到一半失败了,不能从头再来,得知道哪些步骤已经完成、哪些还没做、失败的原因是什么。这个状态管理不能放在技能层,因为技能是无状态的。所以大脑层需要一个轻量的任务上下文存储,记录每一步的输入、输出和状态。

3.2 技能层:原子能力的标准化封装

技能层是Agent的“手脚”,每个技能就是一个可被调用的原子能力。技能的设计原则有四条:

  1. 单一职责:一个技能只做一件事。比如“查询发票”和“下载发票附件”应该是两个技能,而不是一个“获取发票”技能。
  2. 自描述:每个技能必须提供清晰的描述,包括功能说明、输入参数、输出格式、可能的错误码。这个描述是给大脑层看的,不是给人看的。
  3. 无状态:技能不依赖调用方的上下文,所有需要的数据都通过参数传入。如果确实需要状态,状态应该由大脑层维护,技能只负责读写。
  4. 幂等性:同一个技能用同样的参数调用多次,结果应该一致。这对于Agent的重试机制非常重要。

技能的实现方式可以很灵活:可以是一个HTTP API、一个本地函数、一个数据库查询、甚至是一个外部服务的调用。关键是封装——把底层的复杂性藏起来,只暴露一个干净的接口给大脑层。

我通常会把技能分成三类:

技能类型说明示例
数据技能读取或写入数据查询订单、更新用户信息
计算技能执行计算或转换汇率转换、金额校验
动作技能触发外部动作发送邮件、提交审批

技能层还有一个重要设计:技能注册与发现。Agent需要知道当前有哪些技能可用,每个技能的描述是什么。这通常通过一个技能注册表来实现,大脑层在规划时先查询注册表,再决定调用哪些技能。

3.3 护栏层:约束、校验与安全边界

护栏层是Agent架构里最容易被低估的部分。很多人觉得护栏就是“加个权限校验”,但实际上护栏要管的事情多得多。我把护栏分成四类:

  • 输入护栏:校验用户指令是否合法、是否包含敏感信息、是否超出Agent的能力范围。
  • 调用护栏:校验技能调用是否合规,包括调用频率、参数范围、调用序列。
  • 输出护栏:校验Agent的最终输出是否符合规范,包括格式、内容、合规性。
  • 审计护栏:记录所有调用和输出,用于事后追溯和问题排查。

护栏的实现方式有两种:前置校验和后置校验。前置校验在技能调用之前执行,拦截不合规的调用;后置校验在技能调用之后执行,检查结果是否合规。两种方式需要配合使用,因为有些约束只能在调用后才知道是否满足。

我踩过的一个坑是:护栏规则写得太死,导致Agent的正常任务也被拦截。比如我设置了一个“单次任务最多调用10次技能”的护栏,结果一个正常的发票整理任务需要调用15次技能,直接被拦死了。后来改成“动态阈值”——根据任务类型和历史数据动态调整上限,才解决了问题。护栏不是越严越好,而是要在安全和可用之间找平衡。

4. 从传统架构迁移到Agent架构的实操路径

4.1 第一步:识别可Agent化的场景

不是所有功能都适合交给Agent。我通常用三个维度来筛选:

  • 任务复杂度:步骤多、路径不固定、需要根据中间结果调整的任务,适合Agent。
  • 交互频率:高频、重复、规则明确的任务,适合Agent。
  • 容错空间:允许一定程度的错误、可以通过护栏兜底的任务,适合Agent。

反过来,以下场景不建议Agent化:涉及资金安全的核心交易、需要严格审计的合规操作、对延迟极度敏感的实时交互。

我一般会先做一个“场景清单”,把候选场景列出来,然后按优先级排序。优先级最高的通常是内部效率工具,比如数据整理、报告生成、工单分类。这些场景容错空间大、影响范围小,适合用来验证架构。

4.2 第二步:把功能拆成技能

这一步是最耗时的,也是最关键的。我的做法是:先不管Agent,先把现有系统的功能按“原子操作”拆一遍。比如一个订单系统,拆出来可能是:查询订单列表、查询订单详情、创建订单、修改订单、取消订单、导出订单。每个原子操作对应一个技能。

拆完之后,给每个技能写描述。描述要包含:功能说明、输入参数(名称、类型、是否必填、取值范围)、输出格式、错误码。这个描述不是给人看的文档,而是给大脑层看的“技能说明书”。描述的质量直接决定Agent的调用成功率。

我试过用自动生成的方式从OpenAPI规范里提取技能描述,效果一般。因为OpenAPI的描述是给开发者看的,很多隐含的约束没有写出来。比如“查询订单列表”这个接口,OpenAPI里可能只写了“page”和“pageSize”两个参数,但实际上还有“租户ID”和“用户ID”是必须的,只是通过请求头传递的。Agent不知道这些隐含参数,调用就会失败。所以技能描述需要人工补充,把隐含约束显式化。

4.3 第三步:搭建大脑层的规划框架

大脑层的搭建有两种路径:从零自研和基于现有框架改造。从零自研适合有较强AI工程能力的团队,可以完全控制规划逻辑和状态管理。基于现有框架改造适合快速验证,但需要花时间理解框架的抽象和限制。

我建议先从简单的ReAct模式开始,跑通“理解意图→调用技能→返回结果”的闭环。然后再逐步引入更复杂的规划逻辑,比如多步规划、条件分支、循环重试。

大脑层的提示词设计非常关键。我通常会把提示词分成三部分:角色定义、技能清单、输出格式。角色定义告诉模型“你是一个任务规划助手”;技能清单列出所有可用技能及其描述;输出格式规定模型必须输出结构化的任务计划,比如JSON格式的步骤列表。

一个常见的坑是:模型输出的计划格式不稳定,有时候是JSON,有时候是自然语言。解决办法是在提示词里加few-shot示例,并且用结构化输出约束(比如JSON Schema)来强制格式。

4.4 第四步:配置护栏规则

护栏规则的配置需要和业务方一起做,因为很多约束是业务层面的,技术人员不一定清楚。我通常会把护栏规则分成三档:

  • 硬拦截:违反直接拒绝,比如调用未授权的技能、传入非法参数。
  • 软拦截:违反后触发人工审核,比如大额报销、敏感数据导出。
  • 告警:违反后记录日志但不拦截,比如调用频率接近阈值。

护栏规则的配置要可动态调整,不能写死在代码里。我一般会用配置中心来管理护栏规则,支持热更新。这样业务方可以根据实际情况调整阈值,不需要重新发版。

还有一个经验:护栏规则要有优先级。当多条规则同时触发时,按优先级决定执行哪条。比如“硬拦截”优先于“软拦截”,“软拦截”优先于“告警”。这个优先级机制可以避免规则冲突导致的混乱。

5. 实操案例:一个报销Agent的完整实现

5.1 场景描述与任务拆解

假设我们要做一个报销Agent,用户输入“帮我整理上个月的差旅发票并提交报销”。这个任务可以拆解为以下步骤:

  1. 查询上个月的差旅发票列表
  2. 逐张识别发票信息(金额、日期、类型)
  3. 校验发票是否符合公司报销标准
  4. 生成报销单
  5. 提交报销单
  6. 返回提交结果

每一步对应一个技能。大脑层负责规划这些步骤,并根据中间结果动态调整。比如如果某张发票不符合标准,大脑层需要决定是跳过还是标记异常。

5.2 技能定义与参数设计

每个技能的定义如下:

{ "name": "query_invoices", "description": "查询指定时间范围内的发票列表", "parameters": { "start_date": {"type": "string", "format": "date", "required": true}, "end_date": {"type": "string", "format": "date", "required": true}, "category": {"type": "string", "enum": ["travel", "meal", "office"], "required": false} }, "output": { "invoices": {"type": "array", "items": {"type": "object"}} } }
{ "name": "validate_invoice", "description": "校验发票是否符合报销标准", "parameters": { "invoice_id": {"type": "string", "required": true}, "amount": {"type": "number", "required": true}, "category": {"type": "string", "required": true} }, "output": { "valid": {"type": "boolean"}, "reason": {"type": "string"} } }
{ "name": "submit_reimbursement", "description": "提交报销单", "parameters": { "invoice_ids": {"type": "array", "items": {"type": "string"}, "required": true}, "total_amount": {"type": "number", "required": true} }, "output": { "reimbursement_id": {"type": "string"}, "status": {"type": "string"} } }

这些技能描述会被注入到大脑层的提示词里,模型根据描述来决定调用哪个技能、传什么参数。

5.3 大脑层规划逻辑与提示词设计

大脑层的提示词大致如下:

你是一个报销任务规划助手。你的任务是根据用户指令,规划一系列技能调用步骤。 可用技能: - query_invoices: 查询指定时间范围内的发票列表 - validate_invoice: 校验发票是否符合报销标准 - submit_reimbursement: 提交报销单 输出格式: { "steps": [ {"skill": "技能名", "params": {...}, "reason": "调用原因"} ] } 用户指令:帮我整理上个月的差旅发票并提交报销

模型输出的计划可能是:

{ "steps": [ {"skill": "query_invoices", "params": {"start_date": "2024-04-01", "end_date": "2024-04-30", "category": "travel"}, "reason": "查询上个月差旅发票"}, {"skill": "validate_invoice", "params": {"invoice_id": "$step1.invoices[0].id", "amount": "$step1.invoices[0].amount", "category": "travel"}, "reason": "校验第一张发票"}, ... ] }

这里用了变量引用($step1.invoices[0].id)来表示步骤之间的数据依赖。大脑层在执行时会把上一步的输出注入到下一步的输入里。

5.4 护栏规则配置与拦截效果

针对这个报销Agent,我配置了以下护栏规则:

规则类型条件动作
单次任务最多查询100张发票硬拦截query_invoices返回超过100条拒绝执行
单张发票金额不超过5000元软拦截validate_invoice返回amount>5000标记异常,转人工
单次任务最多提交10张发票硬拦截submit_reimbursement的invoice_ids长度>10拒绝执行
连续调用validate_invoice超过20次告警调用次数>20记录日志

实测下来,这些护栏规则拦截了大部分异常情况。比如有一次Agent试图查询一整年的发票(因为用户说“整理发票”没指定时间),被“单次任务最多查询100张”的规则拦住了,然后Agent自动调整了查询范围,改成“上个月”。

6. 常见问题与排查技巧实录

6.1 技能调用失败的高频原因

Agent调用技能失败,90%的情况是以下四种原因:

  • 参数缺失:技能描述里没写清楚哪些参数是必填的,或者参数名和实际接口不一致。
  • 参数类型错误:比如技能要求整数,Agent传了字符串。
  • 状态依赖:技能依赖调用方的上下文,但Agent没有维护这个上下文。
  • 权限不足:Agent使用的凭证没有调用该技能的权限。

排查方法:先看技能调用的原始请求和响应,确认参数是否正确;再看技能描述是否完整;最后检查权限配置。

6.2 大脑层规划不稳定的调优方法

大脑层规划不稳定,通常表现为:同样的输入,有时候输出正确的计划,有时候输出错误的计划。调优方法包括:

  • 增加few-shot示例:在提示词里加几个正确的规划示例,让模型模仿。
  • 降低温度参数:把模型的temperature调低,减少随机性。
  • 结构化输出约束:用JSON Schema强制输出格式。
  • 分步规划:把复杂任务拆成多个简单的规划步骤,每一步只规划一个子任务。

我试过把temperature从0.7降到0.2,规划稳定性明显提升,但灵活性有所下降。后来改成“先粗规划再细规划”的两阶段方式,兼顾了稳定性和灵活性。

6.3 护栏误拦截的排查与调整

护栏误拦截是指正常的任务被护栏规则拦住了。排查方法:先看护栏日志,确认是哪条规则触发的;再看触发时的上下文,判断是否真的是误拦截;最后调整规则阈值或条件。

我遇到过一次典型的误拦截:一个正常的报销任务因为“连续调用validate_invoice超过20次”被告警了。原因是这个任务有25张发票,每张都需要校验。后来把告警阈值改成“连续调用超过50次”,并且加了“如果任务类型是报销且发票数量已知,则动态调整阈值”的逻辑。

6.4 常见问题速查表

问题现象可能原因排查方法解决方案
Agent不调用任何技能提示词里没有技能清单检查提示词注入技能描述
技能调用返回参数错误技能描述不完整对比实际接口文档补充参数说明
规划结果格式不稳定输出约束不够强检查输出格式加JSON Schema约束
任务执行到一半失败状态管理缺失检查任务上下文引入状态存储
护栏频繁误拦截阈值设置过严查看护栏日志动态调整阈值
Agent重复调用同一技能规划逻辑有循环检查规划步骤加最大步数限制

7. 我对这套架构的几点个人体会

这套“大脑+技能+护栏”的架构,我从去年开始在一个内部效率工具上落地,到现在跑了大概半年。最大的体会是:Agent架构的难点不在AI,而在工程。模型的能力已经足够强,真正花时间的是技能封装、状态管理、护栏配置这些“脏活累活”。

另一个体会是:不要试图一步到位。我一开始想把整个系统都Agent化,结果发现很多功能根本不需要Agent,传统方式反而更稳定。后来改成“先Agent化高频、容错空间大的场景,再逐步扩展”,节奏就顺了很多。

还有一个坑:护栏规则一定要和业务方一起定。技术人员觉得合理的规则,业务方可能觉得太严;业务方觉得必要的规则,技术人员可能觉得没必要。只有一起定,才能找到平衡点。

最后分享一个小技巧:给Agent加一个“解释模式”。当Agent执行任务时,让它输出每一步的推理过程和调用原因。这样出问题时,你可以快速定位是规划错了、技能调用错了、还是护栏拦错了。这个功能在调试阶段特别有用,上线后也可以作为审计日志的一部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询