
1. 为什么现在必须重新思考系统架构过去两年我参与过三个从零起步的 AI 项目也接手过两个“传统系统加挂大模型接口”的改造项目。这两类项目的体验差异非常大前者从第一天就把模型当作系统的一等公民迭代速度快、功能边界清晰后者则像是在老房子里改水电每加一个 AI 能力都要跟原有的数据库事务、缓存策略、权限模型打架。这个对比让我意识到AI Native 架构不是一个营销词汇而是一套需要从地基开始重新设计的工程方法。所谓 AI Native核心含义是系统的核心决策路径由模型驱动而不是把模型当作一个外挂的“智能模块”。传统架构里业务逻辑是确定性的 if-else 和状态机模型只是锦上添花AI Native 架构里模型输出是系统状态的一部分不确定性被当作一等公民来管理。这个转变听起来抽象但落到代码和部署上会直接影响你选什么框架、怎么切分服务、怎么设计数据流。这篇文章适合三类人正在规划 AI 产品的技术负责人、需要把现有系统往 AI 方向迁移的架构师、以及想理解 Agent 和 LLM 工程化落地细节的开发者。我会从整体设计思路讲到具体实现包括参数选择、并发处理、常见故障排查尽量把踩过的坑和验证过的方案都摊开来说。2. 整体架构设计与核心思路拆解2.1 从“模型调用”到“模型驱动”的思维转变很多团队做 AI 功能时习惯性地把模型封装成一个 service业务代码调用它、拿到结果、继续走原来的流程。这种模式在简单场景下没问题比如给文章自动生成摘要。但一旦涉及多轮决策、工具调用、状态保持这种“调用式”架构就会暴露出三个问题。第一上下文管理混乱。模型需要的历史信息散落在各个业务模块里每次调用都要重新拼装 prompt容易遗漏关键状态。第二错误处理困难。模型输出不符合预期时业务层不知道该重试、降级还是走人工兜底。第三扩展性差。想加一个新的工具或能力要改动多个调用点而不是在一个统一的编排层里完成。AI Native 的思路是把模型放在决策中心业务逻辑围绕“模型需要什么”来组织。具体来说系统维护一个显式的上下文状态模型每次决策都基于这个状态输出的动作再反过来更新状态。这其实就是 Agent 架构的基本形态感知、决策、执行、反馈形成一个闭环。2.2 分层设计接入层、编排层、能力层、状态层我在实际项目中采用的分层方案是这样的接入层负责协议转换和流量控制把来自不同客户端的请求统一成内部消息格式。这一层不碰模型只做鉴权、限流、请求路由。编排层是核心包含 Agent 的决策循环。它决定什么时候调用模型、传什么上下文、拿到结果后走哪条分支。这一层通常用状态机或工作流引擎来实现保证每一步都可追踪、可回放。能力层是模型和工具的实际执行者。模型服务、检索服务、外部 API 调用都放在这里。每个能力有独立的超时、重试和降级策略。状态层保存会话历史、中间结果和长期记忆。可以是关系数据库、向量库也可以是两者的组合。这个分层的价值在于编排层的变化不会影响能力层的实现能力层的替换也不会打乱编排逻辑。比如从 GPT 系列换到其他模型只需要改能力层的适配器编排层的 prompt 模板和状态机基本不动。2.3 为什么选择“有状态编排”而不是“无状态函数”有人会问为什么不把每个 AI 功能做成无状态的云函数简单又弹性我的经验是对于单轮任务确实可以但 Agent 类应用天然是有状态的。多轮对话需要记住历史工具调用需要传递中间结果长任务需要断点续传。如果强行无状态每次调用都要把完整上下文传一遍不仅浪费 token还容易超出上下文窗口。有状态编排的代价是需要管理会话生命周期和并发访问。我的做法是给每个会话分配一个唯一 ID状态存在 Redis 或数据库中编排层通过 ID 读写。并发冲突用乐观锁或队列来解后面会详细讲。3. 核心组件细节与实操要点3.1 LLM 接入层的参数选择与适配接入大模型时有几个参数直接决定系统的稳定性和成本。温度temperature控制输出的随机性Agent 场景下我一般设 0.1 到 0.3因为需要模型稳定地选择工具和参数而不是天马行空。最大输出长度要根据任务类型设工具调用通常很短设 512 够用生成报告类任务可能需要 4096 以上。超时设置是最容易被忽视的。模型响应时间波动很大我一般设 30 秒作为单次调用上限超过就触发重试或降级。重试次数不要超过 2 次否则用户等待时间太长。降级策略可以是返回缓存结果、走规则引擎或者直接告诉用户“当前繁忙”。还有一个关键点是流式输出。对于面向用户的场景流式返回能显著提升体验用户看到文字逐渐出现感知等待时间更短。但流式输出和工具调用结合时要小心因为工具调用的参数需要完整解析后才能执行不能边流边执行。3.2 Agent 编排的状态机设计Agent 的核心是一个循环观察当前状态决定下一步动作执行动作更新状态直到任务完成或达到终止条件。用状态机来实现这个循环比用 while 循环加一堆 if-else 要清晰得多。我通常定义这几个状态IDLE等待输入、THINKING模型推理中、TOOL_CALL执行工具、WAITING等待外部事件、DONE任务完成、ERROR异常。每个状态有明确的进入条件和退出条件状态转移由模型输出或系统事件触发。这里有个实操心得给状态机加最大步数限制。我见过 Agent 陷入死循环反复调用同一个工具烧掉大量 token。设一个上限比如 20 步超过就强制终止并返回当前结果同时记录日志供后续分析。3.3 上下文管理与 Token 预算上下文窗口是有限资源怎么用好它是个技术活。我的策略是分三层管理固定层放系统提示词和工具定义这部分每次都要传但内容稳定可以利用模型的 prompt caching 能力降低成本。滑动层放最近的对话历史通常保留最近 10 到 20 轮。超过的部分做摘要压缩用模型生成一段简短回顾替代原始对话。检索层放长期记忆和知识库内容通过向量检索按需注入。不是每次都传而是根据当前 query 的相关性动态选择。Token 预算要提前算好。假设模型上下文窗口是 128K系统提示词占 2K工具定义占 3K检索内容占 10K那么留给对话历史的空间大约是 113K。按每轮对话平均 500 token 算能放 200 多轮但实际上为了控制成本和延迟我不会放这么多通常控制在 30 轮以内。3.4 工具调用的设计与安全边界Agent 的能力边界由它能调用的工具决定。设计工具时有几个原则单一职责一个工具只做一件事参数尽量少。比如“查询订单”和“修改订单”应该是两个工具而不是一个带 action 参数的工具。幂等性读操作天然幂等写操作要设计成可重试的。比如“创建任务”可以带一个客户端生成的唯一 ID重复调用返回同一个结果。权限校验工具执行前必须校验当前用户是否有权限。这个校验放在能力层不能依赖模型判断。超时和熔断每个工具设置独立超时连续失败达到阈值就熔断避免拖垮整个 Agent。注意不要让模型直接生成 SQL 或 shell 命令去执行。模型输出不可控必须经过参数校验和转义。我见过因为模型生成了带删除条件的 SQL 导致数据丢失的案例。4. 实操过程与核心环节实现4.1 环境准备与技术选型假设我们要从零搭建一个 AI Native 的客服助手系统。技术栈选择如下编排层Python FastAPI状态机用transitions库或自己实现一个轻量级版本模型服务通过统一接口对接多个模型提供商方便切换和对比状态存储Redis 存会话状态PostgreSQL 存长期记录向量检索用开源向量库配合嵌入模型做知识库检索消息队列处理异步任务和削峰填谷选 Python 是因为生态成熟LLM 相关的库最丰富。FastAPI 的异步支持好适合 IO 密集的模型调用场景。Redis 做会话状态是因为读写快支持过期自动清理。4.2 核心编排循环的代码实现下面是一个简化的 Agent 循环实现展示核心逻辑import asyncio from enum import Enum class AgentState(Enum): IDLE idle THINKING thinking TOOL_CALL tool_call DONE done ERROR error class Agent: def __init__(self, llm_client, tools, max_steps20): self.llm llm_client self.tools {t.name: t for t in tools} self.max_steps max_steps async def run(self, session_id, user_input): state self.load_state(session_id) state.messages.append({role: user, content: user_input}) for step in range(self.max_steps): # 1. 调用模型决策 response await self.llm.chat( messagesstate.messages, tools[t.schema for t in self.tools.values()], temperature0.2, timeout30 ) # 2. 判断是否有工具调用 if response.tool_calls: for call in response.tool_calls: tool self.tools.get(call.name) if not tool: state.messages.append({ role: tool, content: f未知工具: {call.name} }) continue # 权限校验 if not tool.check_permission(state.user): state.messages.append({ role: tool, content: 权限不足 }) continue # 执行工具 try: result await asyncio.wait_for( tool.execute(**call.arguments), timeouttool.timeout ) state.messages.append({ role: tool, content: str(result) }) except asyncio.TimeoutError: state.messages.append({ role: tool, content: 工具执行超时 }) else: # 没有工具调用说明模型给出了最终回复 state.messages.append({ role: assistant, content: response.content }) self.save_state(session_id, state) return response.content # 超过最大步数 self.save_state(session_id, state) return 任务处理步骤过多已终止。请简化您的请求。这段代码的关键点每一步都有超时保护工具调用前做权限校验超过最大步数强制终止。实际生产中还要加上日志记录、指标上报和异常捕获。4.3 并发处理与状态一致性Agent 服务面临的并发压力主要来自两个方面多个用户同时请求以及单个用户的多个请求。前者靠水平扩展解决后者需要会话级别的串行化。我的做法是给每个会话加一个分布式锁。请求进来时先尝试获取锁拿到锁才处理处理完释放。锁的过期时间设得比最大处理时间长一些比如 60 秒。这样即使用户快速连点也只有一个请求在处理其他请求排队或直接返回“处理中”。对于高并发场景可以用消息队列做削峰。请求先入队后台 worker 按会话 ID 分区消费保证同一会话的消息被同一个 worker 顺序处理。这样既保证了顺序性又实现了并行处理不同会话。4.4 效果验证与迭代方法系统上线后怎么验证效果我通常从三个维度看任务完成率用户请求中Agent 成功完成的比例。这个指标反映整体能力。平均步数完成一个任务平均需要多少轮模型调用。步数越少说明编排越高效。工具调用准确率模型选择的工具和参数是否正确。这个需要人工标注一部分样本做评估。迭代时我会把失败的案例收集起来分析是 prompt 问题、工具设计问题还是模型能力问题。如果是 prompt 问题调整系统提示词如果是工具问题优化工具描述和参数设计如果是模型能力问题考虑换模型或增加 few-shot 示例。5. 常见问题与排查技巧实录5.1 模型输出格式不稳定的处理这是最常见的问题。模型有时候返回 JSON有时候返回带 markdown 代码块的 JSON有时候干脆返回一段自然语言。我的处理策略是首先在 prompt 里明确要求输出格式并给出示例。其次用结构化输出功能如果模型支持强制返回 JSON schema。最后在代码里做容错解析先尝试直接解析失败后提取代码块内容再解析再失败就用正则提取关键字段。如果还是不稳定可以考虑用一个小模型做后处理把自由文本转成结构化数据。或者干脆用 function calling 机制让模型通过工具调用来输出结构化参数这比让它生成 JSON 字符串要可靠得多。5.2 上下文超长的截断策略当对话历史超过上下文窗口时需要截断。简单的做法是保留最近 N 轮但这样会丢失早期的重要信息。更好的做法是保留系统提示词和工具定义必须保留最近 5 轮完整对话对更早的对话做摘要用模型生成一段 200 字以内的回顾如果有关键信息如用户 ID、订单号提取出来放在固定位置摘要的 prompt 可以这样写“请用不超过 200 字总结以下对话的关键信息包括用户身份、已确认的事实、未解决的问题。”这样生成的摘要信息密度高占用 token 少。5.3 工具调用失败的排查思路工具调用失败的原因很多我整理了一个排查表现象可能原因排查方法解决措施模型不调用工具工具描述不清检查工具 schema 的 description补充使用场景和参数说明调用参数错误参数类型不匹配查看模型输出的 arguments在 schema 里明确类型和枚举值工具执行超时下游服务慢查看工具执行日志加超时、重试、降级权限校验失败用户无权限检查权限配置返回明确提示引导用户重复调用同一工具模型陷入循环查看调用历史加步数限制检测重复调用实操心得给工具描述加上“什么时候用”和“什么时候不用”比只写“这个工具做什么”效果好很多。模型需要知道边界而不仅仅是功能。5.4 成本控制的几个实用手段AI Native 系统的成本主要来自模型调用。控制成本的手段有缓存相同的输入直接返回缓存结果。对于 FAQ 类场景命中率可以做到 30% 以上。模型分级简单任务用小模型复杂任务用大模型。可以用一个分类器先判断任务难度再路由到不同模型。Prompt 压缩精简系统提示词去掉冗余描述。工具定义只保留必要的字段说明。批量处理非实时任务攒一批一起处理减少调用次数。监控告警设置每日成本上限超过就告警或限流。我实测下来通过缓存和模型分级成本可以降低 40% 到 60%而效果下降不明显。5.5 会话状态丢失的恢复机制Redis 虽然快但也不是绝对可靠。如果会话状态丢失用户体验会很差。我的做法是每次状态更新时同时写 Redis 和 PostgreSQL。Redis 作为热存储PostgreSQL 作为冷备份。读取时优先读 Redis读不到就从 PostgreSQL 恢复并回填 Redis。另外关键操作要记流水日志。比如工具调用的输入输出、模型的决策结果都存到数据库。这样即使状态丢失也能通过回放日志重建。6. 架构演进与扩展方向6.1 从单 Agent 到多 Agent 协作当任务复杂度增加时单个 Agent 可能力不从心。比如一个客服系统需要同时处理订单查询、退换货、投诉建议每个领域的知识和工具都不同。这时候可以拆成多个专职 Agent由一个协调者 Agent 来分派任务。多 Agent 架构的关键是通信协议和任务分解。我通常用一个共享的任务队列协调者把子任务放进去专职 Agent 消费并返回结果。协调者根据结果决定下一步直到任务完成。这种架构的挑战在于错误传播和状态同步。一个子任务失败可能影响整个流程需要设计好回滚和补偿机制。6.2 引入长期记忆与个性化短期记忆靠会话状态长期记忆需要额外的存储和检索机制。我的方案是用向量库存用户的历史交互摘要每次新请求进来时检索最相关的几条记忆注入上下文。个性化则是在系统提示词里加入用户画像比如“该用户是 VIP偏好简洁回复”。这些信息可以来自用户配置也可以从历史行为中推断。6.3 可观测性建设AI Native 系统的可观测性比传统系统更重要因为不确定性更高。我通常会采集这些指标每次模型调用的延迟、token 消耗、成功率每个工具的调用次数、成功率、平均耗时Agent 完成任务的平均步数、成功率用户满意度反馈如果有这些指标用 Prometheus 采集Grafana 展示。异常时通过告警通知到值班人员。日志方面每次会话的完整轨迹都要记录包括模型输入输出、工具调用参数和结果。这些日志是排查问题和优化 prompt 的基础。6.4 安全与合规的工程实现安全方面除了前面提到的权限校验和参数转义还要注意输入过滤用户输入可能包含注入攻击需要在进入模型前做清洗。输出审核模型输出可能包含不当内容需要过一遍审核服务再返回给用户。数据隔离不同用户的数据要严格隔离向量检索时要带用户 ID 过滤。审计日志所有敏感操作都要记录满足合规要求。这些措施会增加一些延迟但安全是底线不能妥协。我的做法是把审核做成异步的先返回结果审核发现问题再撤回或修正。7. 一些踩坑后的个人体会做 AI Native 架构这两年最大的体会是不要试图让模型做它不擅长的事。模型擅长理解和生成自然语言不擅长精确计算和严格逻辑。把计算交给代码把逻辑交给状态机把理解和生成交给模型各司其职系统才稳定。另一个体会是prompt 工程是持续迭代的过程不是一次性的任务。上线只是开始后面要根据真实数据不断调整。我习惯每周 review 一次失败案例看看有没有共性问题然后针对性地优化 prompt 或工具设计。最后不要过度设计。我见过一些团队一开始就搞复杂的多 Agent 协作、长期记忆、自动学习结果基础的单轮对话都没做好。我的建议是先从最简单的单 Agent 加几个工具开始跑通闭环再逐步增加复杂度。每一步都要有数据支撑证明增加的复杂度确实带来了收益。这个领域变化很快新的模型、新的框架、新的模式层出不穷。但底层的工程原则是不变的清晰的边界、可观测的状态、可控的失败、持续的迭代。把这些做好无论技术怎么变系统都能保持稳定和可维护。