Agent系统架构实战:从ReAct循环到企业级私有可控底座 1. 从单体脚本到系统底座为什么Agent需要一套架构很多人第一次接触Agent这个概念脑子里浮现的是一段Python脚本调一次大模型接口拿到返回解析出要调用的工具执行再把结果塞回去循环几次。这确实能跑通一个Demo但一旦把它放进真实业务里问题会像潮水一样涌上来——工具调用失败怎么重试、多轮对话的上下文怎么裁剪、模型突然返回了不符合格式的内容怎么办、多个Agent之间怎么协作、权限怎么隔离、成本怎么控制、出了问题怎么追溯。这些问题的本质是你写的不是一个Agent而是一个没有操作系统的裸机程序。就像早期的计算机没有操作系统每个程序都要自己管理内存、自己调度CPU、自己处理中断写起来痛苦且不可复用。Agent发展到今天同样需要一层“Agent OS”来把那些共性的、重复的、容易出错的事情收敛掉让开发者专注于业务逻辑本身。我理解的Agent OS不是某个具体产品而是一套分层架构约定底层是模型和工具的接入层中间是运行时负责循环、状态、调度上层是编排与治理负责多Agent协作、权限、可观测性。这套架构要解决的核心矛盾只有一个——让Agent的行为在可控的前提下尽可能自主。完全自主会失控完全受控就退化成工作流引擎失去了Agent的意义。这篇文章我会从五个核心组件讲起拆解ReAct循环的真实运行机制再聊企业级场景下“私有可控底座”到底要控什么、怎么控。内容偏架构和落地适合已经写过Demo、准备把Agent推向生产环境的开发者也适合正在做技术选型的架构师。如果你还停留在“调通接口就完事”的阶段这篇可能会有点重但迟早用得上。2. 五组件拆解一个Agent系统的骨架长什么样2.1 组件一模型接入层——不只是换个API Key那么简单模型接入层最容易被低估。很多人觉得这就是封装一个HTTP请求把API Key和Base URL做成配置项就完事了。真到生产环境你会发现这一层要处理的东西远超想象。首先是多模型路由。企业里往往同时存在多个模型来源有的任务需要强推理能力有的任务只需要快速分类有的场景对延迟极度敏感有的场景对成本极度敏感。接入层要能根据任务类型、Token预算、当前负载动态选择模型。我见过一个团队的做法是维护一张“模型能力矩阵”横轴是任务类型抽取、推理、生成、分类纵轴是模型格子里填的是实测的成功率和平均延迟路由时查表决定。这个做法很土但很有效。其次是统一的消息格式。不同模型的输入格式差异很大有的用system/user/assistant三角色有的把system单独拎出来有的对tool call的字段命名完全不同。接入层必须做一层归一化把上层传下来的标准消息结构翻译成各家模型能懂的格式再把返回翻译回标准结构。这层翻译做不好上层逻辑就会被模型差异污染换一个模型就要改一堆代码。第三是流式与非流式的统一。流式输出对用户体验很重要但流式场景下工具调用的解析会变得复杂——你需要在流式返回的过程中判断这是普通文本还是工具调用参数还要处理工具调用参数被分片返回的情况。接入层要把这两种模式对上层屏蔽掉让上层用同一套接口处理。实操心得接入层一定要做请求级别的超时和熔断。模型服务偶尔抽风是常态没有熔断的话一个慢请求会拖垮整个Agent循环。我一般设置单次模型调用超时30秒连续3次失败就熔断该模型5分钟自动切到备用模型。2.2 组件二工具执行层——Agent的手脚也是最危险的地方工具是Agent和外部世界交互的唯一通道。工具执行层要解决三个问题注册、调用、隔离。注册方面我推荐用装饰器或Schema声明的方式定义工具把工具的名称、描述、参数结构、返回值结构都显式声明出来。描述特别重要模型就是靠描述来决定调不调这个工具的。描述写得太简略模型不知道该用写得太啰嗦浪费Token还容易误导。我的经验是描述里要包含“什么时候用”和“什么时候不用”比如“查询订单状态。当用户询问订单进度、物流信息时使用。不要用于查询商品库存”。调用方面核心是参数校验和错误处理。模型生成的参数经常有各种问题类型不对、必填项缺失、枚举值超出范围。工具执行层必须在真正执行前做严格校验校验失败要把清晰的错误信息返回给模型让它重新生成。这里有个技巧错误信息要具体不要只说“参数错误”要说“参数order_id必须是字符串你传的是数字12345”。模型看到具体错误后修正的成功率会高很多。隔离方面这是企业级场景的重中之重。工具执行可能涉及数据库操作、文件读写、外部API调用必须做权限控制。我的做法是给每个工具打上权限标签Agent在调用前要检查自己是否有该权限。更严格的做法是把工具执行放在沙箱里限制它能访问的资源。比如文件操作工具只能访问指定目录网络请求工具只能访问白名单域名。工具类型典型风险隔离手段数据库查询SQL注入、全表扫描参数化查询、查询超时、行数限制文件操作越权读写、路径穿越目录白名单、路径规范化校验外部API敏感信息泄露、SSRF域名白名单、请求体审计代码执行任意代码执行容器沙箱、资源限额、超时强杀2.3 组件三记忆与状态层——Agent的“记性”决定它的上限Agent的记忆分短期和长期。短期记忆就是当前对话的上下文长期记忆是跨会话的知识沉淀。这两者的管理策略完全不同。短期记忆的核心矛盾是上下文窗口有限但对话可能很长。粗暴的做法是截断把最早的消息丢掉但这会导致Agent“失忆”忘记之前达成的共识。好一点的做法是做摘要把早期对话压缩成一段摘要保留。更好的做法是分层记忆最近N轮保留原文N轮之前的做摘要再之前的只保留关键实体和结论。我实测下来分层记忆在长对话场景下比简单截断的效果好很多Token消耗也能控制在合理范围。长期记忆的核心是存什么和怎么取。不是所有信息都值得存我一般只存三类用户偏好比如“这个用户喜欢简洁的回答”、事实性知识比如“用户的订单号是XXX”、任务结论比如“上次分析得出的结论是XXX”。存储用向量数据库做语义检索但检索时要加时间衰减越久远的记忆权重越低。状态层则是管理Agent执行过程中的中间状态。ReAct循环里每一步的思考、行动、观察都需要被记录一方面是为了下一轮循环能用到另一方面是为了可追溯。我习惯把状态设计成不可变的结构每一步产生一个新的状态快照这样出问题可以回放整个执行链路。2.4 组件四编排调度层——单Agent是玩具多Agent才是系统单个Agent能做的事情有限复杂任务需要多个Agent协作。编排调度层要解决的是谁来做、做什么、什么时候做、做完给谁。最基础的编排是串行流水线Agent A的输出作为Agent B的输入依次传递。这种模式简单可控适合步骤明确的流程。但它的问题是前一步的错误会累积放大而且无法并行。进阶一点的是主管-工人模式一个主管Agent负责拆解任务、分配任务、汇总结果多个工人Agent负责执行具体子任务。这种模式灵活但主管Agent的提示词设计很关键它要能准确判断任务该拆成几步、每步该给谁。再复杂的是辩论模式多个Agent对同一问题给出不同答案然后互相批判最终收敛到一个结论。这种模式在需要高可靠性的场景下有用但成本高、延迟大不适合实时场景。我个人的经验是能用串行就别用多Agent。多Agent带来的复杂度和不确定性是成倍增加的很多团队一上来就搞多Agent协作结果调试到崩溃。先把单Agent的循环跑稳确实遇到单Agent搞不定的任务再考虑拆分。2.5 组件五可观测与治理层——没有这层Agent就是黑盒这是最容易被忽略但企业级场景最不能少的一层。Agent的执行过程本质是一个概率性的决策过程同样的输入可能产生不同的执行路径。没有可观测性出了问题你根本不知道是哪一步错了。可观测性要记录的东西包括每次模型调用的输入输出和Token消耗、每次工具调用的参数和结果、每一步的耗时、整个任务的执行链路。这些数据要能关联起来形成一个完整的Trace。我一般用Trace ID贯穿整个执行过程任何一步出问题都能顺着Trace ID查到全貌。治理层则是在可观测的基础上做控制成本控制单个任务Token上限、单日总消耗上限、质量监控工具调用成功率、任务完成率、异常率、安全审计敏感操作记录、权限变更记录。这些指标要能实时告警比如工具调用成功率突然下降可能是某个外部服务挂了要能及时发现。注意可观测性数据本身也有隐私风险。记录模型输入输出时要做敏感信息脱敏比如手机号、身份证号、银行卡号这些要打码后再存储。3. ReAct循环Agent的心跳也是最容易出问题的地方3.1 ReAct到底在循环什么ReAct是Reasoning Acting的缩写核心思想是让模型在每一步都先“想”再“做”。一个标准的ReAct循环是这样的模型接收当前上下文包括历史步骤输出一段思考Thought基于思考模型决定调用某个工具Action并给出参数系统执行工具拿到结果Observation把Observation追加到上下文回到第1步直到模型认为任务完成输出最终答案Final Answer这个循环看起来简单但每一步都有坑。我见过太多团队Demo跑得飞起一上生产就各种问题根源都在循环的细节里。3.2 循环终止条件什么时候该停最常见的错误是没有明确的终止条件。模型可能陷入死循环反复调用同一个工具或者一直在“思考”不输出最终答案。必须设置硬性终止条件最大循环次数一般设10-15次超过就强制终止并返回当前最佳结果重复检测如果连续两次调用的工具和参数完全相同判定为死循环终止无进展检测如果连续几步的Observation没有带来新信息终止Token预算累计Token超过预算就终止这些条件要组合使用不能只靠一个。我一般把最大循环次数设为主保险其他作为辅助。3.3 思考与行动的格式约束模型输出的格式稳定性是ReAct循环的生命线。如果模型输出的格式不对解析就会失败循环就断了。我的做法是用结构化输出强制约束格式比如要求模型输出JSON包含thought、action、action_input三个字段。如果模型不支持结构化输出就用少样本示例引导并在解析时做容错——比如模型输出了一段自然语言而不是JSON尝试用正则提取关键信息。这里有个经验格式约束越严格模型的灵活性越低但稳定性越高。在需要高可靠性的场景宁可牺牲一点灵活性也要保证格式稳定。我一般会在系统提示词里明确写出格式要求并给出正例和反例。3.4 观察结果的注入方式工具执行的结果怎么塞回上下文也有讲究。直接塞原始结果可能太长占满上下文窗口。我的做法是对结果做预处理如果是结构化数据提取关键字段如果是长文本做摘要如果是错误信息保留完整的错误码和错误描述。预处理后的结果要标注清楚来源比如“工具search_order的返回结果...”这样模型能准确理解这是哪个工具的结果。还有一个细节错误结果也要注入。工具调用失败时不要把错误吞掉要把错误信息返回给模型让它决定是重试、换工具还是放弃。很多团队在这里处理不当工具失败了直接抛异常终止循环其实模型完全有能力根据错误信息做出合理决策。3.5 一个完整的ReAct执行示例假设用户问“帮我查一下订单A12345的物流状态如果还没发货就取消它”。一个健康的ReAct循环应该是这样的第一步模型思考用户要查订单状态我需要调用查询订单的工具。行动query_order参数{order_id: A12345}。观察订单状态为“待发货”。第二步模型思考订单还没发货用户说没发货就取消我需要调用取消订单的工具。行动cancel_order参数{order_id: A12345}。观察取消成功。第三步模型思考任务完成可以给用户最终答复了。最终答案订单A12345还未发货已为您取消。这个例子里模型做了两次工具调用每次都有明确的思考和行动。如果中间查询订单失败模型应该看到错误信息后决定是重试还是告知用户。整个过程的每一步都被记录在状态里可以追溯。4. 企业级Agent OS私有可控底座到底要控什么4.1 为什么企业场景必须“私有可控”个人开发者用Agent追求的是效果和速度数据隐私、成本控制、合规审计这些可以往后放。但企业场景完全反过来效果可以打折扣但可控性是底线。我接触过的企业需求里排在前三的顾虑永远是数据会不会泄露、成本会不会失控、出了问题能不能追溯。这三个顾虑决定了企业级Agent OS必须是“私有可控”的——模型可以私有部署数据不出内网每一步操作都有审计日志成本有硬性上限。4.2 模型私有化不是所有场景都需要但关键场景必须有模型私有化部署的成本很高不是所有企业都值得做。我的判断标准是如果Agent处理的数据包含核心商业机密或大量个人敏感信息就必须私有化。否则可以用公有模型但要做好数据脱敏。私有化部署的模型能力通常比公有模型弱这是现实。应对策略是用架构弥补模型能力的不足把复杂任务拆成多个简单步骤每一步用私有模型做通过流程设计来保证整体效果。比如一个需要强推理的任务可以拆成“信息抽取-逻辑判断-结论生成”三步每步对模型能力的要求都不高但组合起来能完成复杂任务。4.3 数据不出域从接入层到存储层的全链路管控数据不出域不是一句口号要落实到每一层接入层所有模型调用走内网网关禁止直连外部服务工具层工具执行在隔离环境里禁止访问外部网络除非白名单存储层向量数据库、日志存储都在内网加密存储传输层内部通信加密敏感字段额外加密这里有个容易忽略的点日志里可能包含敏感数据。模型输入输出、工具调用参数和结果都可能包含敏感信息。日志存储前必须做脱敏而且脱敏要在写入前做不能写完再脱否则中间有个窗口期数据是裸的。4.4 权限与审计每一步都要有迹可循企业级Agent的权限模型要比个人版精细得多。我的做法是三级权限控制第一级是Agent级权限每个Agent有自己的身份只能访问被授权的工具和数据源。第二级是用户级权限Agent代表用户执行操作时要继承用户的权限不能越权。第三级是操作级权限敏感操作比如删除、转账需要额外审批或二次确认。审计日志要记录谁哪个用户、通过哪个Agent、在什么时间、调用了什么工具、传了什么参数、得到了什么结果。这些日志要不可篡改最好用追加写入的方式存储。4.5 成本控制Token是要花钱的企业级场景下Token消耗是实打实的成本。一个不加控制的Agent可能一次任务就烧掉几块钱。控制手段包括单任务Token上限超过就终止返回部分结果单用户日消耗上限防止某个用户滥用模型分级简单任务用便宜模型复杂任务才用贵模型缓存相同或相似的请求复用结果上下文压缩定期压缩历史上下文减少Token占用我一般会在接入层做Token计数每次模型调用后累加超过阈值就触发降级或终止。这个计数要精确到每个用户、每个Agent、每个任务方便后续分析成本构成。5. 落地实操从零搭一个最小可用的Agent OS5.1 技术选型别一上来就追求大而全搭Agent OS最容易犯的错是过度设计。一上来就想搞多Agent协作、长期记忆、复杂编排结果三个月过去了连个能跑的单Agent都没有。我的建议是从最小可用版本开始逐步演进。最小可用版本只需要一个模型接入层支持一个模型就行、一个工具执行层支持几个核心工具、一个简单的ReAct循环、基础的日志记录。记忆层可以先不做用完整的对话历史代替。编排层也可以先不做单Agent跑通再说。技术栈方面Python生态最成熟LangChain、LlamaIndex这些框架可以用但我不建议深度绑定。框架抽象层太厚出问题很难调试。我的做法是用框架做原型验证生产环境自己实现核心循环只借用框架的工具定义和模型接入部分。5.2 核心循环的代码骨架下面是一个简化的ReAct循环骨架用伪代码展示核心逻辑def react_loop(task, tools, model, max_steps10): context build_initial_context(task) state {steps: [], token_used: 0} for step in range(max_steps): # 1. 调用模型获取思考和行动 response model.generate(context) state[token_used] response.token_count # 2. 检查Token预算 if state[token_used] TOKEN_BUDGET: return build_partial_result(state) # 3. 解析模型输出 parsed parse_response(response) if parsed.is_final_answer: return parsed.answer # 4. 校验工具和参数 if not validate_tool_call(parsed.action, parsed.action_input): context.append(build_error_message(工具或参数无效)) continue # 5. 执行工具 try: observation execute_tool(parsed.action, parsed.action_input) except Exception as e: observation build_error_message(str(e)) # 6. 记录状态并更新上下文 state[steps].append({ thought: parsed.thought, action: parsed.action, input: parsed.action_input, observation: observation }) context.append(build_observation_message(observation)) return build_partial_result(state)这个骨架里每一步都有明确的职责调用模型、检查预算、解析输出、校验、执行、记录。实际生产环境还要加上重试、熔断、日志、监控等但核心逻辑就是这些。5.3 工具定义的规范工具定义的质量直接决定Agent的能力上限。我总结了一个工具定义的检查清单名称要动词开头见名知意比如query_order而不是order描述要包含使用场景和排除场景参数要明确类型、是否必填、取值范围返回值要结构化避免返回大段自然语言错误要分类区分参数错误、权限错误、系统错误一个反面例子是工具描述写“查询订单”模型不知道什么时候该用。正面例子是“根据订单号查询订单的详细信息和物流状态。当用户询问订单进度、物流信息、预计送达时间时使用。不适用于查询商品信息或用户信息。”5.4 提示词工程系统提示词决定Agent的性格系统提示词是Agent的“人格设定”要包含角色定义、能力边界、行为规范、输出格式。我一般会写清楚“你是一个XX助手你能做XX不能做XX遇到XX情况要XX”。特别重要的是边界情况的处理。比如用户问了超出Agent能力范围的问题Agent应该明确说“这个我处理不了”而不是硬编一个答案。再比如工具调用失败Agent应该根据错误类型决定重试还是放弃。这些都要在系统提示词里写清楚。5.5 测试与迭代怎么知道Agent好不好Agent的测试比传统软件难因为它的行为是概率性的。我的做法是建一个测试集包含正常场景和边界场景每次改动后跑一遍看通过率。测试集要覆盖正常任务、参数缺失、工具失败、多轮对话、超长输入、恶意输入。除了自动化测试还要做人工评估。自动化测试只能验证格式和基本逻辑回答质量、推理合理性这些需要人工看。我一般会抽样看执行Trace重点看模型的思考过程是否合理有没有绕弯路。6. 常见问题与排查技巧实录6.1 模型不调用工具直接编答案这是最常见的问题。模型明明有工具可用却直接凭记忆回答。原因通常是工具描述不够清晰或者系统提示词没有强调“必须用工具获取信息”。解决办法在系统提示词里明确“对于需要实时数据的问题必须先调用工具不能凭记忆回答”并在工具描述里写清楚适用场景。6.2 工具调用参数格式错误模型生成的参数经常有格式问题比如该传字符串传了数字该传数组传了对象。解决办法在工具定义里用JSON Schema严格约束参数类型并在系统提示词里给出参数示例。解析时做类型转换和校验校验失败把具体错误返回给模型。6.3 循环停不下来模型反复调用同一个工具或者一直在思考不输出最终答案。解决办法设置最大循环次数、重复检测、无进展检测。另外检查系统提示词是否明确说了“任务完成后要输出最终答案”。6.4 上下文超长对话轮次多了上下文超过模型窗口。解决办法分层记忆最近N轮保留原文更早的做摘要。摘要要保留关键实体和结论丢弃寒暄和重复内容。6.5 工具执行超时某个工具执行太慢拖垮整个循环。解决办法给每个工具设置超时时间超时后返回超时错误让模型决定是否重试。同时做熔断连续超时就暂时禁用该工具。问题现象可能原因排查方向解决手段不调用工具描述不清、提示词未强调检查工具描述和系统提示词补充使用场景说明参数格式错Schema不严、缺示例检查工具定义加JSON Schema和示例循环不停无终止条件检查循环控制逻辑加最大次数和重复检测上下文超长无压缩机制检查记忆管理分层记忆加摘要工具超时无超时设置检查工具执行层加超时和熔断6.6 一个真实的排查案例之前遇到过一个情况Agent在查询订单时总是失败但手动调工具是好的。排查发现模型生成的order_id带了引号比如A12345而工具期望的是不带引号的A12345。原因是系统提示词里的示例带了引号模型照抄了。把示例改成不带引号后问题解决。这个坑很小但排查花了不少时间因为错误信息只说“订单不存在”没说是格式问题。后来我在工具里加了参数格式的详细校验错误信息里明确说“order_id不应包含引号”类似问题就少多了。7. 架构演进从单Agent到Agent OS的路线图7.1 第一阶段单Agent跑通核心场景这个阶段的目标是验证Agent在核心场景下能不能用。选一个高频、边界清晰的场景比如客服问答、订单查询把单Agent跑通。重点是把ReAct循环、工具调用、错误处理做扎实。这个阶段不要追求多Agent、长期记忆这些高级特性。7.2 第二阶段补齐可观测和治理单Agent跑通后马上要补的是可观测性。没有日志和Trace出了问题就是黑盒。这个阶段要加上完整的执行日志、Token统计、工具调用成功率监控。同时开始做成本控制设置Token上限。7.3 第三阶段引入记忆和编排当单Agent的能力遇到瓶颈比如需要跨会话记住用户偏好或者需要多个Agent协作完成复杂任务时再引入记忆层和编排层。记忆层先做简单的向量检索编排层先做串行流水线够用就行。7.4 第四阶段私有化与合规如果业务涉及敏感数据这个阶段要做模型私有化部署、数据加密、权限精细化、审计日志。这个阶段的投入很大但对企业场景是必须的。7.5 演进中的注意事项每个阶段都要有明确的退出标准不要为了技术而技术。比如单Agent的任务完成率达到90%以上再考虑多Agent。记忆层的引入要有明确的场景驱动比如“用户抱怨每次都要重新说一遍偏好”而不是“别人都有记忆层我们也要有”。我在实际推进中最大的体会是Agent OS的复杂度应该由业务需求驱动而不是由技术可能性驱动。技术上有无数种做法但真正值得做的只有那些能解决实际问题的。很多团队在架构上过度投入结果业务价值还没验证团队已经疲了。先把一个场景做深做透比铺开十个半成品场景有价值得多。最后分享一个小技巧每次架构演进前先问自己“当前架构在哪个具体场景下不够用了”如果答不上来说明还没到演进的时候。架构是解决问题的不是用来炫技的。