多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

AI应用架构设计:从模型接入到Agent编排的四层架构实践

AI应用架构设计:从模型接入到Agent编排的四层架构实践 1. 从一张架构图说起AI应用到底该怎么搭这两年我参与过不少AI应用项目的评审和落地发现一个特别普遍的现象很多人一上来就急着写Prompt、调API、接模型结果项目跑到一半发现整个系统像一团乱麻——模型换了要改几十处代码加个新工具要重写整个调用链多个Agent之间互相调用直接死锁。说到底问题出在动手之前没有把架构想清楚。“图解AI应用架构设计”这个主题核心就是解决一个问题当你要做一个AI应用时系统应该分成哪几层、每层负责什么、层与层之间怎么交互。它适合正在做AI应用开发的工程师、正在带团队做技术选型的架构师以及想从传统开发转向AI应用开发但不知道从哪里下手的程序员。不管你用的是LLM、Agent还是MCP架构设计的底层逻辑是相通的。我个人的习惯是在写第一行代码之前先在白板上画出一张架构图。这张图不需要多漂亮但必须回答几个关键问题模型层怎么抽象、工具层怎么注册、Agent之间怎么通信、状态怎么管理、异常怎么兜底。这篇文章我就把这张图拆开来讲从整体分层到每一层的实现细节再到实际踩过的坑尽量说透。2. AI应用架构的整体分层与设计思路2.1 为什么不能把LLM调用散落在业务代码里我见过最糟糕的一种写法是在Controller里直接写OpenAI的SDK调用然后在Service里又写一遍定时任务里再来一遍。这种代码在Demo阶段没问题一旦要换模型供应商、要加缓存、要做限流、要记录Token消耗你就得满世界找调用点。架构设计的第一原则就是关注点分离。LLM调用属于基础设施层面的事情不应该和业务逻辑混在一起。我的做法是抽象出一个Model Gateway层所有对LLM的请求都经过这一层。这一层负责的事情包括统一不同供应商的API差异、处理重试和降级、统计Token用量、做请求级别的缓存、管理API Key的轮转。这样做的好处很直接业务代码只关心“我要让模型做什么”不关心“模型是谁家的、怎么调”。换模型的时候只改Gateway的配置业务代码一行不动。这个思路和传统后端开发里把数据库访问抽象成Repository层是一模一样的道理。2.2 四层架构的划分逻辑基于多个项目的实践我把AI应用的架构划分为四层从下往上依次是模型接入层负责与各种LLM供应商对接统一接口协议处理认证、限流、重试、降级能力编排层负责Prompt模板管理、工具注册与调用、Agent的推理循环、MCP协议适配业务逻辑层具体的业务场景实现比如客服问答、代码生成、文档分析交互接入层对外暴露的API、WebSocket、SSE流式输出以及前端交互这四层的依赖关系是单向的上层依赖下层下层不知道上层的存在。模型接入层完全不知道业务逻辑层在做什么它只负责把请求发出去、把结果拿回来。这种单向依赖保证了每一层都可以独立替换和测试。注意不要为了“分层”而分层。如果你的应用只有一个简单的问答场景不需要Agent编排那能力编排层可以很薄甚至直接合并到业务逻辑层。架构是为了解决问题不是为了好看。2.3 Agent与LLM在架构中的位置关系很多人搞不清楚Agent和LLM在架构中的关系。用一句话说LLM是Agent的推理引擎Agent是LLM的能力扩展。LLM本身只能做文本生成它不能查数据库、不能调API、不能读文件。Agent做的事情是在LLM外面套一个循环让LLM决定下一步做什么然后执行这个动作把结果喂回给LLM再让它决定下一步。这个循环就是所谓的ReAct模式或者Function Calling模式。在架构图上Agent属于能力编排层。它依赖模型接入层提供的LLM能力同时依赖工具注册中心提供的工具能力。一个Agent可以调用多个工具一个应用也可以有多个Agent协同工作。MCP在这里的角色是工具注册和调用的一种标准化协议让不同来源的工具能够以统一的方式被Agent发现和使用。3. 模型接入层的核心细节与实操要点3.1 统一接口抽象的关键设计模型接入层的核心是定义一个统一的接口。不管你后面接的是哪家的模型业务层看到的都是同一个方法签名。我通常定义的接口大概长这样class ModelGateway: def chat(self, messages, toolsNone, streamFalse, **kwargs): ... def embed(self, texts): ...chat方法接收消息列表和可选的工具定义返回模型的响应。stream参数控制是否流式输出。embed方法用于文本向量化。这个接口看起来简单但要处理好几个细节。第一不同供应商的message格式不一样有的用role字段区分system/user/assistant有的用单独的字段。Gateway内部要做格式转换。第二工具调用的返回格式差异更大有的返回tool_calls数组有的返回function_call对象。Gateway要统一成一种格式。第三流式输出的chunk结构也不同要统一成一致的增量格式。3.2 多模型路由与降级策略实际生产环境中只用一个模型是不够的。原因很简单贵的不一定适合所有场景便宜的不一定能处理复杂任务。我的做法是在Gateway层实现一个路由策略场景类型首选模型降级模型路由依据简单分类/抽取小模型中模型请求复杂度评分复杂推理/规划大模型中模型任务类型标记高并发低延迟小模型队列缓冲实时负载代码生成代码专用模型通用大模型内容类型识别路由的依据可以来自请求的元数据也可以由业务层显式指定。降级策略要配合重试机制一起用当首选模型返回超时或错误时自动切换到降级模型同时记录降级事件用于后续分析。实操心得降级不是万能的。如果首选模型是因为内容审核被拒降级到另一个模型大概率也会被拒。所以降级策略要区分错误类型只对超时、限流、服务不可用这类错误做降级内容层面的错误直接返回给业务层处理。3.3 Token用量统计与成本控制Token统计这件事看起来简单做起来容易漏。因为流式输出的Token计数和非流式不一样工具调用的Token计算也有特殊规则。我的做法是在Gateway层统一拦截请求和响应从响应中提取usage字段。对于流式输出在最后一个chunk里通常会有usage信息如果没有就自己估算。成本控制方面我建议在Gateway层做三件事设置单次请求的Token上限、设置单用户的日Token配额、对超长上下文做自动截断或摘要。这三件事都不复杂但能避免很多意外账单。4. 能力编排层的实现要点4.1 Prompt模板管理不能硬编码Prompt是AI应用的核心资产之一但很多项目把Prompt直接写在代码里改一个标点都要重新部署。我的做法是把Prompt模板抽出来用独立的文件管理支持变量替换和版本控制。模板文件的结构大概是这样的name: customer_service_reply version: 3 variables: - user_name - order_info - knowledge_snippets template: | 你是一位专业的客服助手。当前用户是{{user_name}}。 以下是用户的相关订单信息{{order_info}} 以下是知识库中匹配到的内容{{knowledge_snippets}} 请根据以上信息用友好的语气回复用户的问题。这样做的好处是Prompt的修改不需要改代码运营人员也能参与优化。版本控制让每次修改都可追溯A/B测试也变得容易。4.2 工具注册与MCP协议适配工具是Agent的手和脚。在架构设计上工具应该有一个统一的注册中心每个工具声明自己的名称、描述、参数Schema和执行函数。Agent在推理时根据工具的描述来决定是否调用。MCP协议在这里的价值就体现出来了。它定义了一套标准的工具描述和调用格式让不同来源的工具能够以统一的方式接入。你可以把MCP理解成工具界的USB接口——不管什么设备插上就能用。在架构上我通常会在能力编排层放一个MCP Client负责与MCP Server通信。MCP Server可以是本地的也可以是远程的。本地Server适合文件操作、数据库查询这类需要低延迟的工具远程Server适合第三方服务集成。# 工具注册的简化示例 tool_registry {} def register_tool(name, description, parameters): def decorator(func): tool_registry[name] { name: name, description: description, parameters: parameters, func: func } return func return decorator register_tool( namequery_order, description根据订单号查询订单状态, parameters{order_id: {type: string, description: 订单编号}} ) def query_order(order_id): # 实际查询逻辑 return {status: shipped, eta: 2025-01-20}4.3 Agent推理循环的设计与超时控制Agent的核心是一个循环思考、行动、观察、再思考。这个循环必须有终止条件否则会无限执行下去。我通常设置三个终止条件达到最大迭代次数、模型输出最终答案、超过总超时时间。超时控制特别重要。我遇到过Agent在某个工具调用上卡住整个请求挂了五分钟的情况。后来我在每个工具调用外面包了一层超时控制单个工具调用超过10秒就中断返回超时错误让模型决定下一步。注意Agent的迭代次数不要设太大。一般3到5次就够了。超过5次还没得到答案大概率是工具描述有问题或者任务本身不适合用Agent解决。设太大只会浪费Token和时间。5. 业务逻辑层与交互层的落地实践5.1 业务逻辑层的边界划分业务逻辑层是最贴近具体场景的一层。我的原则是一个业务场景一个模块模块之间不直接调用。比如客服问答模块和文档分析模块它们各自独立如果需要协作通过能力编排层的Agent来协调。这样做的好处是每个模块可以独立开发、独立测试、独立部署。一个模块出问题不会影响其他模块。对于团队协作来说不同的人可以负责不同的业务模块只要接口约定好就行。业务逻辑层还需要处理一件事上下文管理。多轮对话中哪些历史消息要保留、哪些要丢弃、什么时候做摘要压缩这些策略应该在业务层定义而不是在模型层。因为不同业务对上下文的需求不一样客服场景可能需要保留最近10轮对话而代码生成场景可能只需要保留当前文件和最近一次修改。5.2 流式输出的架构考量流式输出是AI应用体验的关键。用户不需要等模型生成完整回答再看到内容而是一个字一个字地看到回答逐渐出现。在架构上流式输出意味着数据要从模型层一路透传到前端中间任何一层做了缓冲都会破坏流式效果。我的做法是在交互层使用SSEServer-Sent Events或WebSocket在业务层和编排层使用异步生成器在模型层使用流式API。每一层都支持逐块传递数据不做全量缓冲。但流式输出也带来一个问题如果生成到一半出错了怎么办我的处理方式是在流式输出的同时在服务端保留完整的生成内容。如果中途出错已经发送给前端的部分无法撤回但可以在流结束后发送一个错误事件让前端决定是保留已生成内容还是提示重试。5.3 并发场景下的架构调整AI应用扛并发和传统Web应用扛并发思路不太一样。传统应用瓶颈通常在数据库AI应用的瓶颈在模型推理。模型推理的延迟高、成本高、并发能力有限所以架构上要做针对性优化。我常用的几个手段请求队列把并发请求排队处理避免瞬时打爆模型服务结果缓存对相同或相似的请求缓存结果减少重复推理异步处理对不需要实时返回的任务用消息队列异步处理分级服务对延迟敏感的用户走快速通道对延迟不敏感的用户走批量通道。这些手段在架构上都需要在能力编排层和业务逻辑层之间增加相应的组件。具体用哪些取决于你的业务场景和成本预算。6. 常见问题与排查技巧实录6.1 模型返回格式不稳定怎么办这是最常见的问题之一。你让模型返回JSON它有时候返回纯JSON有时候在JSON外面包一层Markdown代码块有时候还加几句解释。我的处理方式是三层防护第一在Prompt里明确要求只返回JSON不要加任何其他内容第二在解析时先尝试直接解析失败则提取代码块内容再解析第三如果还失败用一个小模型做格式修复。但最根本的解决办法是使用支持结构化输出的模型API。现在很多模型都提供了JSON Mode或Function Calling模式能从API层面保证返回格式。如果你的场景对格式要求严格优先用这个。6.2 Agent陷入死循环的排查思路Agent死循环的表现是反复调用同一个工具或者在不同工具之间来回跳转。排查的时候我一般按这个顺序看排查项可能问题解决方法工具描述描述模糊导致模型误解补充使用场景和参数说明工具返回值返回内容太长或格式混乱精简返回值只保留关键信息系统Prompt缺少终止条件说明明确告知模型何时应该停止最大迭代数设置过大降到3-5次任务本身任务不适合Agent模式改用固定流程我踩过最坑的一次是工具返回了一个巨大的JSON模型每次都要花大量Token去理解返回值结果在同一个工具上反复调用。后来把返回值精简到只保留必要字段问题就解决了。6.3 MCP连接失败的常见原因MCP连接失败通常有几个原因Server没有启动、端口被占用、认证信息配置错误、协议版本不匹配。排查的时候先确认Server进程是否在运行然后检查网络连通性再看认证配置。实操心得MCP Server的日志一定要打开。很多连接问题在Server端日志里一目了然比如“收到不支持的协议版本”或者“认证Token已过期”。不要只盯着Client端的报错看。6.4 流式输出中断的排查流式输出中断的原因比较多网络超时、模型服务端主动断开、中间层缓冲导致连接被回收、前端处理不过来。排查的时候先确认是模型层断开还是网络层断开。如果是模型层断开看模型服务的日志如果是网络层断开检查Nginx或网关的超时配置。我遇到过一次是Nginx的proxy_read_timeout默认60秒而模型生成一个长回答超过了60秒Nginx主动断开了连接。把超时调到300秒就好了。这种问题不看Nginx日志根本想不到。7. 架构演进与扩展方向7.1 从单Agent到多Agent协作当业务复杂度上升单个Agent可能搞不定所有事情。这时候需要多个Agent分工协作。比如一个客服系统可以有意图识别Agent、知识检索Agent、回复生成Agent、质量审核Agent。每个Agent专注一件事通过消息传递来协作。多Agent架构的关键是通信协议和状态管理。Agent之间怎么传递消息、怎么共享上下文、怎么处理某个Agent失败的情况这些都需要在架构设计阶段想清楚。我的建议是先用简单的消息队列做Agent间通信不要一上来就搞复杂的黑板模型。7.2 可观测性建设AI应用的可观测性和传统应用不太一样。除了常规的QPS、延迟、错误率还需要关注Token消耗趋势、模型调用分布、Agent迭代次数分布、工具调用成功率、Prompt版本效果对比。这些指标需要在架构的每一层埋点。模型接入层记录Token和延迟能力编排层记录Agent迭代和工具调用业务逻辑层记录业务指标。所有数据汇总到一个可观测性平台方便排查问题和优化成本。7.3 安全与合规的架构考量Agent安全是最近被讨论很多的话题。从架构层面我建议做几件事工具调用白名单只允许Agent调用注册过的工具输入输出内容过滤防止Prompt注入和敏感信息泄露操作审计日志记录每个Agent的每一步决策和工具调用权限隔离不同Agent有不同的工具访问权限。这些措施不需要一开始就全部到位但在架构设计时要预留扩展点。比如工具注册中心要支持权限标记Agent执行器要支持审计日志接口。后期加上去比一开始设计好要麻烦得多。架构设计这件事说到底是在“够用”和“过度设计”之间找平衡。我见过太多项目在早期就搞了一套复杂的多Agent编排系统结果业务量根本撑不起来维护成本反而成了负担。也见过一些项目完全不做抽象模型调用散落各处后期想换个模型比登天还难。我的经验是先画出你当前能想到的最简单的架构图然后问自己三个问题换模型要改几处加工具要改几处扛不住并发时哪里是瓶颈如果这三个问题的答案都在可接受范围内那就够了。架构是演进来的不是设计出来的。
返回列表