30秒速览
- 用了AWS Bedrock原生的多Agent协作模式做采购审批,比之前用LangGraph自己搭要省心太多,尤其是死锁和上下文丢失的问题平台直接兜底了。三个专业Agent的模型和工具根据任务属性做了差异化配置,法务用Sonnet+合同解析器,财务用便宜Haiku+ERP直连,采购用知识库查供应商。成本从200刀降到80刀的关键是加入前置过滤规则、用Guardrail限制无效输出、以及高峰时段锁定Provisioned Throughput。
“让Agent自己找人协作”?Bedrock的多智能体编排是真把戏还是假把式
我至今记得去年秋天那个周五下午,财务总监拍着桌子对我说:“这个系统要么这周上线,要么你就别想下班了。”当时我们的采购审批平均要走11天,法务、财务、采购三个部门各自为政,邮件来邮件去,一份合同能被打回来六七次。我作为架构组的老人,被点名用人工智能“彻底解决”这件事。那时候AI Agent的概念已经满天飞,LangChain、CrewAI之类的框架我也都玩过,但说实话,一提到多Agent协作,我脑子里先跳出来的不是优美的流水线,而是死锁、状态丢失和没完没了的token账单。
之前用LangGraph搭过一个订单处理的多Agent系统,上线第三天就出现了循环调用死锁——财务Agent说请先确认库存,库存Agent说请先得到财务批准,两个Agent互相踢皮球,两万笔交易卡在那里。那次教训让我明白了一件事:用通用框架拼多Agent的编排,就像用积木搭一座桥,能过小溪,但你要让三辆卡车同时过,它大概率会塌。所以当AWS在re:Invent前后放出了Bedrock原生多智能体协作功能时,我没有像其他团队那样立刻冲进去,而是花了两周翻文档、跑demo、故意制造各种异常场景,就是想看看这套原生编排到底能不能扛住真实企业审批链的折磨。
Bedrock Agents的多Agent模式最大的不同在于,它把协作逻辑从应用层下放到了平台层。你不用再自己去写一个“调度员”程序维护对话状态、处理重试、管理token消耗,而是通过定义Agent间的协作关系,让Bedrock的运行时自己搞定消息路由、上下文传递和冲突处理。我对比了三种方案:自己用Lambda搭状态机、用LangGraph写有向图、直接用Bedrock Multi-Agent Collaboration。前两种我都尝试过,状态机写起来简单,但法务、财务、采购之间的复杂条件分支会让代码变成意大利面条;LangGraph灵活但调试成本极高,而且每增加一个Agent,图的复杂度呈指数增长,你不得不处理每一个可能的失败路径。
而Bedrock的做法是提供两种协作模式:Supervisor Routing和全网状直接协作。我最终采用的是Supervisor模式,也就是设置一个Orchestrator Agent作为调停者,它不执行具体审批逻辑,只负责接收请求、按流程顺序调用其他三个专业Agent,并在出现分歧时触发重协商机制。这个模式跟现实中审批链的结构高度吻合——有一个类似于办公室主任的角色,拿着文件跑各个部门。Bedrock自动维护了对话链的完整上下文,你不需要手工拼装每次子调用的history,也不会丢失中间结果。更让我放心的是,每个子Agent的调用痕迹、推理步骤都会被捕获到CloudWatch和X-Ray里,你可以像看聊天记录一样回溯每一次决策,而不是面对一坨无状态的JSON。
下面这行代码是我创建Supervisor Agent的核心部分,它明确告知Bedrock该Agent负责协调其他Agent,并且定义了调用规则。我特别喜欢instruction字段的力量——它就像给Agent写的“员工手册”,直接决定了它在什么情况下叫谁、不叫谁。
response = bedrock_agent_client.create_agent(
agentName='PurchaseApprovalSup',
foundationModel='anthropic.claude-3-sonnet-20240229',
agentResourceRoleArn=role_arn,
instruction='''你是采购审批协调员。收到采购请求后,严格按顺序执行:
1. 调用法务Agent审查合同条款,若被驳回,直接终止流程并通知用户。
2. 若法务通过,调用财务Agent验证预算,若超预算且无特殊说明,流程暂停,要求用户补充说明。
3. 若财务通过,调用采购Agent检查供应商和库存,生成最终意见。
绝不跳过任何步骤,发生冲突时必须记录各方观点后请求用户干预。''',
orchestrationType='SUPERVISOR_ROUTER',
...
)
这个instruction花了最多时间打磨,因为它不像传统程序那样有确定性的分支,LLM对自然语言的理解总有模糊地带。我后来发现加上“绝不跳过任何步骤”这种强硬措辞能显著降低跳步的概率。就这样,一套原生多Agent审批链条的骨架在一个小时内立起来了。接下来真正掉头发的,是怎么让这三个Agent各司其职又能好好“吵架”。
法务说不行财务说超预算,我靠Agent间的“规则打架”机制硬推审批链
审批链最麻烦的地方不是顺序执行,而是当两个部门的结论冲突时,系统如何自处。比如法务认为条款有严重风险,直接给了否决;而财务那边却显示预算充足、投资回报率极好,采购又急着要锁定供应商的折扣窗口。现实里这需要VP级别的人拍板,但在纯Agent流程里,你没办法随便暂停等人类介入。Bedrock的协作模式支持“协作指令”(collaboration instruction),这个指令可以写在Supervisor Agent里,也可以定义在每个Agent的响应格式中。
我的做法是,让每个专业Agent在回复时不仅给出结论,还要带一个结构化的“冲突标记”,比如用JSON包裹在响应的特定分隔符内。这个标记会被Supervisor Agent解析,而不是完全依赖语义理解。我承认,一开始我想全靠LLM的推理能力来处理冲突,但在测试时发现,Agent有时候会“礼貌性”地让步,或者自作主张地折中,导致审批质量不稳定。加入这种半结构化标记后,流程的确定性提升了三成以上。
具体来说,法务Agent的输出模版要求它必须包含一个`legal_risk_level`字段,取值`low`, `medium`, `high`。当风险等级为high时,Supervisor会直接把该审批置为终态“法务否决”,不再往下传。财务Agent则要输出`budget_status`和`remaining_budget`,采购Agent提供`vendor_score`和`stock_availability`。Supervisor拿到这些结构化数据后,不用二次推理,直接走我预设的规则矩阵。
但问题来了:如果法务给medium风险,财务却说完全超出预算,而采购那边又显示这是唯一供应商,怎么办?我设计了一个“冲突升级”路径:Supervisor检测到这类矛盾时,会自动生成一个汇总报告,通过SNS推送到审批人的企业微信上,要求人工介入,同时把上下文冻结在Bedrock的会话存储中。审批人在聊天里回复“批准”或“驳回”后,一个Lambda函数会捕获消息并更新会话状态,让Supervisor继续执行。这套机制让我省掉了开发整套Web界面的时间,而且因为直接复用了公司已有的IM通道,业务方接受度意外地高。
协作指令的具体配置我用的是`put_collaborator` API,它允许定义Agent之间的交互协议。比如我指定法务Agent只能被Supervisor调用,而不能直接叫采购Agent,防止它们自己私聊出岔子。
# 把法务Agent注册为Supervisor的collaborator
client.associate_agent_collaborator(
agentId=supervisor_agent_id,
agentDescriptor={
'aliasArn': legal_agent_alias_arn
},
collaborationInstruction='''
你只能由Supervisor直接调用。
每次响应必须在开头用##LEGAL_START##和结尾用##LEGAL_END##包裹。
在结尾标记前输出风险等级:RISK: low/medium/high。
''',
collaborationName='LegalCollab',
relayConversationHistory=True
)
注意`relayConversationHistory`设为True,这会自动把之前的对话历史传递给被调用的Agent,否则它根本不知道上下文,会重复提问。早期我没开这个,法务Agent见了采购Agent的结论就像第一次见到这个案子一样,又从头问合同号、供应商名,用户体验一塌糊涂。开了之后,对话丝滑得像同一个人的左右手在配合。
冲突解决这块,我在设计矩阵时故意留下了“灰度区”:medium风险且超预算在10%以内,系统会自动向用户发送一个简化版的风险提示,如果用户在一小时内不回复,默认通过。这听着有点激进,但业务部门反馈非常好,因为绝大多数这类case最终都会被手工放行,我们只是用沉默投票替代了邮件催办。当然,这背后有审计日志作为安全网,每一笔自动通过的记录都标记了原因。
每个Agent的脑子(模型)、手脚(工具)和记忆(知识库)我是怎么配的
搭多Agent系统最容易犯的错误是给所有Agent用同一个模型和同一套配置,觉得反正都是LLM,区别不大。但审批链里的三个角色需要的“智力”和“装备”完全不同。法务需要细抠文本,逻辑严密;财务需要算数精准,与ERP系统交互;采购则需要调用外部SaaS和供应商数据库。我分别测试了不同模型组合,最后敲定了一套省成本又不掉链子的方案。
法务Agent我坚持用Claude 3 Sonnet,它的合同条款分析准确度明显高于同期的其他模型,尤其是在识别免责条款和不合理赔偿条款时。但是Sonnet的推理较慢,所以我给法务Agent配了一个小技巧:先用Action Group把合同PDF转成纯文本,并抽取关键字段(合同金额、有效期、双方主体),然后再送入模型。这么一来,模型不需要处理文档解析,专注法律推理,延迟从8秒压到了3秒左右。
# 法务Agent挂载的Action Group - 合同解析Lambda
legal_action_group = {
'actionGroupName': 'ContractParser',
'actionGroupExecutor': {'lambda': 'arn:aws:lambda:...'},
'apiSchema': {'payload': open('contract_parser_api.json').read()},
'description': '解析采购合同PDF,提取关键条款'
}
client.create_agent_action_group(
agentId=legal_agent_id,
agentVersion='DRAFT',
**legal_action_group
)
财务Agent我用的是Claude 3 Haiku,因为它的运行成本只有Sonnet的三分之一,而预算核对和财务计算这种任务不需要顶级的语言理解,反而需要高并发和低延迟。Haiku配合一个连接公司Oracle ERP的Lambda(通过VPC内网访问),能在500毫秒内拉取项目预算和已用额度,然后在模型内部做简单的减法比较。有趣的是,Haiku在处理小数精度时有过一次幻觉,算错了尾数差额,导致一个5000块的采购被误判为超预算。我后来在Action Group的Lambda里加了一层数值校验,只有经过Lambda计算确认超预算了,才把结论递交给模型做自然语言输出,彻底杜绝了模型算错账的可能。
知识库(Knowledge Base)这块,三个Agent各有一个专属的S3向量库。法务的知识库存了公司历史上通过的3000多份合同片段和对应的法务批注,利用Bedrock的向量化能力,让Agent在遇到陌生条款时能检索到最相似的历史案例。财务知识库存了项目预算明细表和部门费用政策。采购的知识库则是合格供应商名录,并且每周自动从SRM系统同步一次。
设置知识库其实不复杂,但是得注意检索精度。我踩的一个坑是,法务知识库里有些旧条款的批注包含否定性结论,而Agent有时候会把这些批注当成“可接受”来模仿。我在instruction里专门加了一句:“历史批注仅供参考,不得直接作为批准理由。”情况才好转。另外,我开启了一个叫做`orchestrationTrace`的实验性功能(目前需要联系SA开通),它能把每次知识检索的chunk原文都记录到trace里,这让调试法律误判变得有据可查。
整体配置下来,三个Agent的总固定成本(不含推理调用费)就是三个knowledge base的存储和三个Lambda的常驻内存,每个月不到30美元。推理成本大头在法务,但因为审批量其实不大(每天约120笔),所以财务上完全可控。最满意的地方是,我把所有Agent的配置都用CDK写成了Infrastructure as Code,以后要复制一套到子公司只需改几个参数。
成本从每天200刀压到80刀,靠的不是幻觉而是路由策略与护栏
系统刚上线那一周,我每天睁开眼第一件事就是看Cost Explorer,心里咯噔一下:日均推理费用冲到了近200美元,远高于预估。仔细一分析,发现有两个吞噬token的黑洞。一是法务Agent经常被调用去处理一些其实不需要法律审查的小额采购(比如一两百块的办公用品),但依照最初的流程设定,所有采购统统走法务。二是财务Agent在遇到查询预算超时或ERP返回慢的时候,Haiku模型会不断重试生成,产生大量无效token。
我当即在Supervisor Agent的instruction里加入了一条前置过滤规则:采购总金额低于5000美元且合同为标准化模板的请求,跳过法务Agent,直接进入财务环节。这个简单的规则改动让法务Agent的调用量直接腰斩,每日成本骤降40美元。但这还不够,更狠的在后头。
Bedrock Agent提供了一个叫“Invocation Filter”的功能(现在叫Guardrail过滤器),可以定义敏感信息脱敏和输入输出长度限制。我针对财务Agent配置了输出token上限为500,因为预算核对的结果通常就两三句话,不需要长篇大论。此外,我给所有Agent启用了Bedrock的原生Guardrail,屏蔽了幻觉中最常见的“我会联系人工处理”这种模糊表述,强制Agent如果无法确定就输出明确的“需要人工介入”并停止推理,避免它进入漫无目的的自我对话。这套组合拳之后,无效token下降了约35%。
接下来是模型选择上的精打细算。我把Supervisor Agent的模型从Sonnet换成了最新的Claude 3.5 Haiku(当时刚可用),因为Supervisor只做路由和简单规则判断,不需要Sonnet那么强的推理。Haiku比Sonnet便宜了将近80%,而且路由准确率几乎没差别,只是偶尔在解析复杂冲突时需要更明确的指令,多给几个few-shot例子就能补回来。我还发现了一个省钱大招:利用Bedrock的“Provisioned Throughput”模式,为每天早9点到晚6点的审批高峰锁定一个模型单元,夜间自动降级到on-demand。这样既保证了高峰期的低延迟(模型已经预热),又避免了夜间闲置费用的浪费。这一项让日均成本再降了约30美元。
护栏(Guardrails)不仅省钱,也是安全合规的底线。作为采购系统,Agent会接触到真实的合同金额、供应商报价和预算数据,绝不能把这些信息泄露到模型推理输出的日志里。因此我在每个Agent的Guardrail中配置了正则表达式掩码,凡匹配到金额格式、公司名称和内部项目代号,都会在日志中替换为`[REDACTED]`。更关键的是,我关闭了Bedrock的模型训练数据收集选项,杜绝了数据被AWS用于服务改进的可能。这一点对于需要SOC2合规的企业是强制要求,别等到审计的时候再补救。
成本优化的最后一块,是监控和异常告警。我在CloudWatch里设了一个异常探测器,当某Agent的单次推理token超过1000时立即触发警报,这在早期帮我抓到了一个因为无限循环导致token爆炸的bug。现在这个系统的日均花费稳定在80美元左右,处理超过120笔审批请求,每笔成本不到0.7美元,比起之前人工审批平均每笔超过15美元的人力成本,这个数字让CFO在季度会上主动要跟我握手——虽然那时候是线上会议,但我看见他在屏幕那头笑了。