ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从Kiro架构拆解看AWS上生产级Agent的工程实践

从Kiro架构拆解看AWS上生产级Agent的工程实践 最近我把一个在 AWS 上跑了大半年的 Agent 项目从头到尾复盘了一遍越复盘越觉得真正决定 Agent 上限的往往不是模型本身而是包围模型的那一圈工程架构。这两天正好又看到 Kiro 这套 Agent 方案的讨论度上来正好借着这个话题把架构拆开讲透。这篇文章适合两类人一类是正准备往 AWS 上迁自己的 Agent想搞明白入口、编排、记忆、工具、权限这些模块到底怎么串另一类是已经在用 SAM、Lambda、Step Functions 跑业务但是看 Agent 项目还是觉得一团雾水的朋友。我会按一个 Agent 请求从进门到出门的完整路径来讲不会只停留在概念层面。1. Kiro 到底是什么我为什么要拆它的架构先说个结论Kiro 不是一个单纯的对话机器人它本质上是把多 Agent 协作、工具调用、记忆管理、模型路由这几件事打包成一个可以部署在云上的运行时。你在搜索结果里看到 kiro crew 这个词所谓 crew 在它内部指的是一个多 Agent 协同小组通常包含一个 supervisor调度者和若干个执行 Agent每个 Agent 分工不同有的负责检索记忆有的负责操作外部工具有的负责生成最终结果。这种设计跟 LangChain、CrewAI 这种框架的定位不一样。框架给的是拼积木的零件你怎么拼、拼完怎么运维是你自己的事而 Kiro 这类落地方案更像半成品楼架构已经把水电气管道预留好了你要做的是按你自己的业务去刷墙隔间。这也是我觉得值得拆解它的原因它里面有很多 AWS 原生产品的组合方式几乎每个模块你都能对应到一个具体服务。拆之前先明确一个认知一个生产级 Agent 的请求路径不会是用户发消息 - 模型返回这么短。真实路径是用户请求进入 API 网关经过身份验证后拿到会话 ID编排引擎读取会话状态和记忆组装系统提示词和上下文调用模型模型返回文本或者工具调用指令如果是工具调用编排引擎执行对应工具把结果塞回上下文重复第 4 步到第 5 步直到模型不再请求工具最终答案返回给用户同时把整个轨迹写入记忆和日志整条链路里模型只负责思考和决定调用什么工具这一小段剩下的大量工作全在架构层。我把这条链路对应的 AWS 组件画了一遍分配关系基本上是一一对应API Gateway 对应入口Step Functions 对应编排Bedrock 对应模型接入DynamoDB 对应状态S3 和向量库对应记忆Lambda 对应工具执行体IAM 对应权限边界。2. 入口层先从为什么每次都要点 Allow说起很多人在搜索栏里问 kiro 为什么每次都要点击 allow这个问题看似是个使用困扰实际上是入口层的授权设计问题。要解释清楚得先看 Agent 入口层的两个核心职责认证和会话管理。2.1 入口认证API Gateway 加上 Lambda AuthorizerKiro 这类 Agent 的对外入口通常是一个 HTTPS API部署在 AWS API Gateway 后面。API Gateway 在这个架构里不只是转发请求的管道它承担了三件事鉴权、限流、协议转换。鉴权这块常用的是 Lambda Authorizer 方案。每一个请求进来API Gateway 先触发一个授权函数这个函数负责解析请求头里的 JWT、校验签名、查询用户状态然后返回一个 IAM policy 给 API Gateway决定允许还是拒绝这次请求。这个模式的好处是授权逻辑可以完全自定义比如你可以对接 Cognito User Pool也可以对接企业自己的 Okta甚至直接查 DynamoDB 里的 token 表。那每次都要点 Allow是怎么回事最常见的原因是客户端凭证的持久化没做好。很多 Agent 桌面端或 Web 端用的是 OAuth 授权码流程用户第一次登录会跳转到授权页点 Allow 之后拿到 authorization code然后换 access token 和 refresh token。如果你只把 access token 存在内存里进程一重启、或者临时凭证一个小时过期下次请求就得重新走一遍授权。所以每次重新打开应用都要再点一次不是产品脑残而是代码里没把 refresh token 落地保存。我见过一个更隐蔽的情况用的是 AWS IAM 临时凭证比如 AssumeRole 换来的默认有效期最短 15 分钟最长 12 小时。如果前端没有在凭证过期前主动刷新那应用每次重启后第一次调用 API 就会收到 403逻辑上只能让用户重新授权。2.2 会话状态DynamoDB 做状态存储认证过了之后Agent 需要记住这个请求属于哪个会话。会话状态必须放在独立存储里不能放在 Lambda 内存里因为 Lambda 冷启动后会丢内存而且并发实例之间不共享内存。Kiro 这类架构里最常见的做法是 DynamoDB 存 session record主键用 session_id属性里存用户 ID、对话轮次、当前编排状态、上下文指针另外设一个 TTL 字段做自动过期清理。DynamoDB 的 TTL 机制是按时间自动删除过期记录不额外收存储费用非常适合会话数据这种有时效性的东西。这里有一个经常被忽略的点DynamoDB 按设计是最终一致性读默认读是强一致的。对于会话恢复场景如果前一个请求刚写入会话状态后一个请求立刻去读可能会读到旧数据。解决方法是把读操作设置为 ConsistentRead 为 true或者更推荐的做法是把每一步的增量更新做成幂等的让编排引擎不依赖精确的实时状态。2.3 WebSocket 还是 REST要不要上 WebSocket 取决于 Agent 的交互形态。如果 Agent 是单轮问答或者非流式响应REST 完全够用。但如果 Agent 要支持打字机流式输出那 REST 实现起来会很别扭要么用 SSEServer-Sent Events要么直接上 WebSocket。AWS API Gateway 的 WebSocket 方案在这套架构里可行但是你得自己管理连接 ID 和在线状态通常还要配一个 DynamoDB 表存 connection mapping。这里我给个实用建议如果没有强交互需求刚开始做的时候别上 WebSocket用 API Gateway 的 REST 轮询或者用 Lambda 响应流response streaming实现 SSE 效果复杂度低一大截。Kiro 官方在早期版本也是这么收敛的等到用户量大起来才引入了连接管理服务。3. 编排内核状态机驱动的 Agent 循环长什么样Agent 架构中间那层是编排引擎对应到 AWS 生态里最典型的角色就是 Step Functions。很多人不理解为什么 Agent 的核心要放在状态机里而不是写一段 Python while 循环。原因很简单生产环境的 Agent 调用可能持续几十秒甚至几分钟一个普通 Lambda 函数最长只能跑 15 分钟而且调用模型、执行工具的过程中任何一步失败都要有重试和降级策略这些用代码硬写非常容易变成一团乱麻用状态机描述则天然清晰。3.1 一个典型的 Agent 状态机定义Kiro 的单 Agent 任务循环可以抽象成四个状态PrepareContext准备上下文、InvokeModel调用模型、ExecuteTool执行工具、CheckFinish检查是否结束。用 Step Functions 定义的话大概是这个 JSON 骨架{ StartAt: PrepareContext, States: { PrepareContext: { Type: Pass, Parameters: { session_id.$: $.session_id, messages.$: $.messages, user_input.$: $.user_input }, Next: InvokeModel }, InvokeModel: { Type: Task, Resource: arn:aws:states:::lambda:invoke, Parameters: { FunctionName: ${AgentModelFunction}, Payload: { input.$: $ } }, Next: CheckFinish, Retry: [ { ErrorEquals: [States.TaskFailed], IntervalSeconds: 3, MaxAttempts: 2, BackoffRate: 2 } ], Catch: [ { ErrorEquals: [States.ALL], Next: FallbackResponse } ] }, CheckFinish: { Type: Choice, Choices: [ { Variable: $.tool_calls, IsPresent: true, Next: ExecuteTool } ], Default: ReturnFinalResponse }, ExecuteTool: { Type: Task, Resource: arn:aws:states:::lambda:invoke, Parameters: { FunctionName: ${ToolExecutorFunction}, Payload: { tool_calls.$: $.tool_calls, session_id.$: $.session_id } }, Next: InvokeModel }, ReturnFinalResponse: { Type: Pass, End: true }, FallbackResponse: { Type: Pass, End: true } } }这个状态机里有一个很重要的设计模型调用在 Lambda 里是一个单独的封装函数轮次控制和判断逻辑放在状态机的 Choice 状态里。工具执行结果会追加到 messages 里然后重新进入 InvokeModel。这样每一轮模型调用都是独立的 Lambda 实例规避了长任务在单个函数里超时的问题。3.2 Express 工作流和 Standard 工作流怎么选Step Functions 分 Express 和 Standard 两种类型Agent 场景经常有人选错。Standard Workflow 的特点是执行历史完整可查、支持秒级甚至天级的长任务、有动态并发上限但起步延迟较高价格按状态转换计费。Express Workflow 的特点是起步延迟低、吞吐高、按执行次数和持续时间计费适合高并发短任务但最多只能跑 5 分钟而且不保留完整执行历史。对于 Kiro 这类以交互为主的 Agent如果单轮任务预期在几秒内完成Express Workflow 在绝大多数情况下是更合适的成本可以做到很低。但如果你的 Agent 里有等待人工审批跑一个异步数据任务这种长时态就得切到 Standard。我的做法是拆成两条链路对话链路走 Express后台长任务链路走 Standard。别试图用一个 workflow 覆盖所有情况那样成本和排查难度都会上去。3.3 Multi-Agent 协作supervisor 模式落地kiro crew 这个概念对应的工程实现我见到最多的是 supervisor 模式。supervisor Agent 自己不直接干活它负责拆解任务然后把子任务派发给 worker Agent。在 AWS 上落这套模式时一个容易踩的坑是把子任务派发做成同步嵌套调用。如果你在 supervisor 的 Lambda 里直接同步 Invoke worker 的 Lambda一旦 worker 数量多调用深度和超时都会出问题。正确做法是引入一个任务队列比如 SQS。流程大概是supervisor 把子任务写入 SQSworker Agent 从队列里拉取任务执行完把结果写入 DynamoDB 或者 S3supervisor 轮询子任务完成状态。这个模式看起来多了一步但好处是天然支持水平扩展、限流和重试worker 挂了消息也不会丢。SQS 的 Visibility Timeout 参数要按 worker 的最长执行时间配不然 worker 还在跑消息已经被其他实例消费了就会重复执行。4. 模型接入与上下文管理记忆不是简单把历史拼进去模型接入层在 Kiro 架构里不仅仅是装一个 SDK 调 API它要解决三个问题不同任务用哪个模型、上下文窗口怎么塞才不爆、记忆到底怎么读出来。4.1 模型路由别让一个模型干所有事我拆过的 Agent 架构里凡是把单一模型用于全部场景的最后都会遇到两个问题太贵或者太慢。Agent 的日常流量里其实只有很少一部分请求需要顶级推理模型剩下的大部分是意图分类、实体提取、工具参数规范化这种简单任务。Kiro 在这块的做法是模型网关Model Gateway它会根据请求特征把任务路由到不同模型。这层可以直接用 Bedrock 来承载Bedrock 本身提供了 unified API通过配置不同 model ID 就可以在同一个代码路径里切换模型。举一个我实际用过的路由策略用户意图识别和工具调用参数提取用小模型比如 Claude Haiku 这类快且便宜复杂推理、多步规划、生成代码用大模型比如 Claude Sonnet 或 Opus只需要抽取结构化 JSON 的任务用带强制 JSON 输出的模型减少解析错误路由判断本身可以用一个小模型判断也可以写规则。我的建议是第一版先用规则路由比如按问题长度、是否包含特定动词、会话轮次等简单特征把准确率基线跑出来再用模型路由去替换否则你根本判断不了模型路由到底有没有带来收益。4.2 上下文窗口管理Agent 每执行一次工具调用把工具结果追加进对话上下文就会膨胀一轮。如果 Agent 需要调用五次工具初始的 10 条历史消息很可能膨胀成 30 条甚至更多。上下文过大不仅烧 token而且模型在长上下文里更容易抓不住重点。处理方式一般分三级截断超出窗口长度时把最早的历史直接丢掉。最简单但会丢信息摘要每 N 轮对话后让模型生成前面内容的摘要把摘要替换进上下文检索保留完整对话到外部存储每次只把跟当前问题最相关的片段检索出来放回上下文生产环境我用得最多的是第二和第三级的组合。用户的长期偏好、项目背景这种信息放到长期记忆里每次请求前向量检索相关段落短期的多轮对话则做滚动摘要。这个方案能兼顾效果和成本而且实现起来并不复杂。4.3 记忆架构区分短期、长期和工作记忆关于 agent 记忆 这类搜索词有一个认知要明确Agent 的记忆不是一个单一数据库它至少分三层。短期记忆就是当前会话里的消息序列通常直接放在编排状态里以 JSON 形式传给模型。长期记忆是用户跨会话的偏好和事实信息存在向量数据库里比如 OpenSearch Serverless 或 Bedrock Knowledge Base每次请求前做语义检索。工作记忆是 Agent 在当前任务里的临时状态比如已检索到三份文档其中一份过期存到 DynamoDB 的一张 task_state 表里。曾经踩过的坑是把长期记忆设计成每次请求把用户全部历史都放进上下文结果响应变慢、成本飙升、效果反而不如做检索。长期记忆的价值不取决于存了多少而取决于检索多准。要持续观察检索出的内容对模型最终答案的影响必要时给不同的记忆片段加时间衰减权重。5. 工具层与权限边界Agent 能调用的资源如何收敛工具层是 Agent 区别于普通聊天机器人的关键也是安全性最容易被忽视的地方。Agent 的模型本身没有权限真正有权限的是承载工具执行的 Lambda 函数和它们的 IAM Role。5.1 工具列表与函数调用协议在往模型传工具定义时格式上每个工具都包含名称、描述、输入参数 JSON Schema。描述写得越具体模型选对工具的概率越高。比如你提供一个 get_order_status 工具描述不能只写获取订单状态要写获取订单当前物流状态输入参数为订单 ID适合用户询问我的包裹到哪了订单发货没这类问题时调用。模型对工具描述的语言理解是多走一步的事情这一步直接决定工具调用的准确率。Kiro 的架构里工具执行统一收敛到一个 ToolExecutor Lambda它做的事情是校验传入参数 - 按工具名分发到具体处理器 - 执行 - 把执行结果格式化为模型可读文本返回。工具处理器本身也是 Lambda但通常作为独立函数部署这样每个函数可以用独立的 IAM Role权限最小化。5.2 IAM 权限边界最小授权是底线这是整个架构里我反复强调的一条模型所在的推理函数不应该有任何云资源权限。即使模型被提示词注入它也只能输出文字和工具调用指令真正执行权限在看门人那一层。具体做法是三权分离推理函数InvokeModel无任何 AWS 资源权限只调用 Bedrock编排状态机Step Functions只允许调用指定的推理函数和 ToolExecutor工具函数ToolExecutor每个工具一个 Role只有该工具需要的权限比如有一个工具是查询 S3 文件清单那这个工具的 Role 里只需要 s3:ListBucket连 s3:GetObject 都不要给。有一个工具是写 DynamoDB那 Role 只给对应表的 PutItem 权限不给 DeleteItem不给 Scan。另外强烈建议对 Agent 所有工具函数的 IAM Role 设置 Permission Boundary。你会发现 Agent 场景比普通服务更容易出现需求变了直接往 Role 里加权限的冲动Permission Boundary 能拦一道防止权限无限膨胀。5.3 密钥与敏感信息处理Agent 要调用外部 API免不了要存各种密钥。这个问题上只有一条稳妥的路密钥放 AWS Secrets Manager你在代码里永远不硬编码密钥。工具函数启动时从 Secrets Manager 取密钥缓存到 Lambda 层或内存里。取密钥的权限也要收敛不同的工具用不同的 Secret 条目避免一个工具函数能拿到全部密钥。BatchGetSecret 这类批量读的操作不推荐一次只读自己需要的。5.4 工具执行的兜底保护给工具执行加保护是很多人忽略的步骤但有三个防护我个人认为是必须的一是超时控制。工具执行函数全部配死超时网络请求用短一点的客户端超时不能让一个第三方 API 的慢响应拖死整个 Agent 编排。二是调用频控。用户请求到工具执行之间加配额比如每个会话每分钟最多调用十次工具。没有这个限制一个写坏了的循环状态机可能在一个小时内烧掉整月预算。三是输出长度限制。工具返回给模型的内容要截断一个工具调用返回几十万字符的日志是大忌模型吃不消成本也扛不住。工具执行器要对输出做截断和摘要只留给模型必要信息。6. 用 SAM 把这套架构落到云上部署链路复盘聊完架构组件说说实际部署。很多人搜索 aws sam 实际开发中的应用 其实是想看一个完整项目的部署链路我就把 Kiro 这套架构的 SAM 落地过程复盘一遍。6.1 SAM 项目目录和模板骨架SAMServerless Application Model在 Agent 项目里的作用是把前面说的 Lambda、状态机、API Gateway、DynamoDB 这些资源用一份 template.yaml 描述出来一条命令完成打包和部署。Kiro 这类项目的 SAM 模板骨架大概是这个思路AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Globals: Function: Timeout: 60 MemorySize: 512 Runtime: python3.12 Tracing: Active Resources: SessionTable: Type: AWS::Serverless::SimpleTable Properties: PrimaryKey: Name: session_id Type: String ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 AgentModelFunction: Type: AWS::Serverless::Function Properties: CodeUri: functions/model_caller/ Handler: app.lambda_handler Policies: - Statement: - Effect: Allow Action: - bedrock:InvokeModel Resource: * ToolExecutorFunction: Type: AWS::Serverless::Function Properties: CodeUri: functions/tool_executor/ Handler: app.lambda_handler Policies: - Statement: - Effect: Allow Action: - dynamodb:PutItem - dynamodb:GetItem Resource: !GetAtt SessionTable.Arn - Statement: - Effect: Allow Action: - states:StartExecution Resource: !Ref AgentWorkflow AgentWorkflow: Type: AWS::Serverless::StateMachine Properties: DefinitionUri: statemachine/agent_workflow.asl.json Role: !GetAtt WorkflowRole.Arn Type: EXPRESS ApiGateway: Type: AWS::Serverless::Api Properties: StageName: prod Auth: DefaultAuthorizer: LambdaTokenAuthorizer Authorizers: LambdaTokenAuthorizer: FunctionArn: !GetAtt AuthorizerFunction.Arn这里值得展开说几个细节。第一个是函数拆分粒度。一个 Agent 项目里模型调用、工具执行、授权、会话管理建议拆成四个以上的独立 Lambda 函数不要全塞进一个函数里。独立函数的优势是可以单独调内存、超时和 IAM Role。工具函数的内存可以小一点模型调用函数如果使用了很大的上下文内存可以给到 1G 以上因为 Python 运行时内存和 CPU 是绑定的处理大 payload 时大内存能明显降低延迟。第二个是 DynamoDB 的容量配置。开发阶段用 on-demand 计费最省心不用预估读写下限。这个配置在测试环境很合适生产环境可以再根据流量改成预置容量加自动扩缩容。千万别一上来就按生产流量配预置容量尤其是项目初期一个月没几个请求时按量计费一天可能只要几分钱。6.2 本地开发调试的三板斧SAM 本地调试我最常用的三个命令一定要说sam validate sam build sam local start-apisam validate 检查模板语法sam build 会把本地依赖打成构建产物sam local start-api 会启动一个本地模拟 API Gateway。跑通这三个就能在本地用 Postman 或者 curl 直接测 Lambda 函数不用每次改代码都往 AWS 推。但 sam local 有个限制它是在本地跑 Docker 容器模拟 Lambda 环境不支持一部分 AWS 服务的真实调用。比如你的函数要读写 DynamoDB本地模式下它会访问你在 AWS 上真实存在的 DynamoDB 表所以本机 AKSK 凭证一定要配好而且要使用低权限的开发凭证防止本地调试误操作生产资源。6.3 Windows 环境安装 SAM 的隐性坑每次看到有人搜 kiro windows 安装我都想顺便提醒一句 SAM 生态在 Windows 下的坑。SAM CLI 本身是 Python 工具在 Windows 上安装很容易踩 Python 版本不对的问题。官方要求 Python 3.7 以上但有个坑是如果系统装了多个 Pythonpip 装到的 SAM CLI 可能绑定到了一个已经被卸载的版本路径上。这会导致命令行找不到 sam或者能执行但 sam build 莫名失败。我的解法是先用 py -0 确认当前 Python 版本列表然后创建虚拟环境安装python -m venv .venv .venv\Scripts\activate pip install aws-sam-cli sam --versionWindows 下还需要注意 Docker Desktop 必须处于启动状态sam build 用不到 Docker但 sam local start-api 一定需要。很多人第一步装了 SAM CLI 就以为万事大吉跑 sam local 时弹容器引擎错误浪费很多时间本质上是 Docker Desktop 没启动或者 Docker 引擎版本过旧。7. 实测阶段最值得说的四个坑部署上线只是开始实测阶段我踩过几个坑每次都能感觉到Agent 项目的坑跟普通后端项目的坑完全不是一个维度写出来希望帮你省几个无眠夜。7.1 冷启动导致的会话状态丢失头几次联调时经常发现用户发了一条消息Agent 正常回复等五分钟再发第二条Agent 完全忘了之前聊了什么。这是因为会话状态被放在 Lambda 内存或者全局变量里冷启动之后内存全清。这也是我前面强调状态必须落 DynamoDB 的原因。排查这种问题时先别急着改代码。查 CloudWatch 日志里的 RequestId看两条请求是不是落在同一个 Lambda 实例上。如果每次请求都是新实例那一定是状态载体选错了。把消息历史从函数的全局变量改为 DynamoDB 记录之后这个问题彻底消失。顺便一提代码里尽量不要有读一次状态然后缓存下来的逻辑Agent 的状态读写频率很高缓存带来的收益远小于状态不一致的代价。7.2 Lambda 默认超时和服务端模型等待时间Lambda 默认超时只有 3 秒而一个 Agent 工具循环一轮可能就要 10 秒往上。跑实测前第一件事就是把所有相关函数的超时改到 60 秒以上。但这里有个更隐蔽的问题Lambda 的超时时间只是同步调用的限制如果你用 Step Functions 编排Task 节点的超时是由状态机控制的。服务端同步调用一个模型如果模型响应特别慢Lambda 最多等 60 秒超过就返回超时错误。我的经验是给每个环节设定明确的上限Step Functions 里配再说一次重试策略模型调用函数在同一模型实例里最多重试两次工具执行函数配 10 秒的客户端超时。如果整体链路需要超过一分钟就考虑把长任务切到异步模式用户那边先拿到任务已开始的响应完成后通过 WebSocket 推送结果。7.3 IAM Role 的循环依赖和 Unable to assume role用 SAM 部署 Agent 项目时经常遇到 CloudFormation 创建资源失败错误信息类似 Unable to assume the role please verify that the role exists and its trust policy allows assume。这个错误的本质是资源的依赖顺序出错了。比如你的 Step Functions 状态机要调用一个 Lambda状态机 Role 需要信任这个 Lambda 的资源同时这个 Lambda 的权限策略又要引用状态机的 ARN。CloudFormation 在创建阶段没办法确定先建谁就会报循环依赖。解决办法是拆开依赖链常见的做法是先用 SAM 的 Role 资源单独创建一个 Role把 trust policy 和允许调用的权限定义好再把这个 Role 的 ARN 通过参数传给 State Machine避免 CloudFormation 隐式生成的规则导致循环。另一个做法是用 DependsOn 显式指定资源创建顺序但排查起来比较麻烦我还是建议显式创建 Role。7.4 每次都要点 Allow 这类授权交互问题这个坑属于入口层的老问题前文已经讲了原理这里补充一个排查思路。如果你开发的是 Web 端 Agent用户每刷新一次页面就要重新授权先看浏览器的 localStorage 或者 IndexedDB 有没有把 refresh token 持久化下来。如果每次刷新页面后 refresh token 都没了说明前端代码在应用初始化时把 token 清掉了这种情况通常是初始化函数里混了一个 clearSession 方法。另外如果 Agent 要访问用户的第三方服务比如 Google Calendar、GitHub授权时要用服端 token 交换流程不能在浏览器里直接处理 client_secret。很多初版 Agent 项目为了省事把 client_secret 放在前端结果用户每次授权都会触发安全告警最后被迫切到后端代理模式。Kiro 早期版本在这一点上做得比较保守是经过大量用户反馈才改成后端代持 token 的模式这个演进路径很值得自己做 Agent 时参考。8. 架构之外的优先级可观测性、成本评估和后续演进方向架构跑通、功能稳定之后真正区分一个 Agent 项目能不能持续维护的关键反而是一些不显眼的东西。8.1 可观测性缺失会拖垮你的排障能力Agent 项目的系统调用链比普通 API 复杂得多因为同一个请求会经过入口 - 编排 - 模型 - 工具 - 编排 - 模型 - 返回这一整条循环中途任何一环出错都需要完整链路日志才能定位。我的建议是全部函数开 X-Ray tracing然后在模型调用层做专门的结构化日志。所谓结构化日志不是记一句模型调用成功而是记录完整的输入输出去敏快照、模型名、Token 消耗、耗时、是第几轮工具调用。这样出了问题可以像回放录像一样把整个 Agent 的思考轨迹拉出来看。实践下来最有用的一条日志字段是本轮由哪个工具返回了哪段结果摘要。Agent 经常出现问题不是模型不好而是工具返回的结果里有噪声把模型带偏了。没有这个字段你只能对着模型原始输出猜原因有了它一眼就能看出哪一次工具调用丢了信息。8.2 Token 成本要按一轮完整任务去评Agent 项目的成本模型和纯 API 调用不一样。普通 API 调用的成本是多少 token 明码标价Agent 的成本取决于它调了多少次工具、上下文膨胀到什么程度。我的经验是评估 Agent 成本一定要按一个完整任务来算而不是按单轮模型调用算。比如一个任务平均要跑五轮模型调用每次上下文增长中间还穿插大模型和小模型的切换。我会在模型调用函数里打成本日志把 Bedrock 返回的 usage 字段和单价算好定期拿 CloudWatch 的指标去统计单任务平均成本。这个指标超了阈值就要去优化上下文截断策略或者换更便宜的小模型而不是等到月底卡账单爆了才意识到。8.3 后续演进流式体验、模型网关和评估集如果有人问我大半年跑下来最值得投入的下一个功能是什么我第一反应是流式输出。Agent 因为没有流式输出用户只能盯着一个转圈图标等十秒以上体验非常糟糕。加上流式之后流失率肉眼可见地下降。另一个值得投入的方向是评估集。给 Agent 准备一百个典型的用户问题每次改提示词或者换模型就整体跑一遍看通过率。这个机制做完之后这个 Agent 好像变笨了这种隐隐约约的体感问题就会变成有数据支撑的判断题。模型网关是更长远的规划。AWS 上跑 Agent长期最怕被单一模型供应商绑住Bedrock 本身已经做了一个跨模型的 API但你仍然需要在自己这一层保留 abstact layer把模型调用包装成内部接口。这样模型版本升级、价格调整、新模型发布的时候你只需要改配置项不用改编排逻辑。最后再分享一个我自己的最深体会Agent 架构的集成度非常高看起来每个模块都能独立演进实际上它们会被模型上下文这件事牢牢绑在一起。所以你改任何一层都要想清楚它会对别的层产生什么连锁反应。我在实际维护中已经把改模型提示词前先跑一遍回归用例写进了团队规范效果远超预期。如果你正在做类似的 Agent 项目建议也从第一天就把这件事纳入迭代流程。
返回列表