图解AI应用架构设计:从LLM调用到Agent编排的六层工程实践

发布时间:2026/10/25 13:54:47
图解AI应用架构设计:从LLM调用到Agent编排的六层工程实践 1. 为什么“图解”才是AI应用架构的正确打开方式做AI应用开发这些年我最大的感受是架构图不是画给老板看的是画给自己保命的。一个LLM应用从Demo到上线中间隔着的不是代码量而是对架构层次的清晰认知。很多人一上来就写Prompt、调API结果做到第三周发现上下文管理混乱、Agent状态丢失、工具调用超时无法重试、多轮对话的Token成本失控。这些问题归根结底都是因为脑子里没有一张完整的架构图。所谓“图解AI应用架构设计”核心就是把一个AI应用拆解成可独立演进、可单独替换、可分别观测的若干层。它解决的不是“怎么调通一个模型”的问题而是“怎么让一个AI应用在真实流量下稳定跑三个月”的问题。这篇文章适合三类人一是刚接触LLM应用开发、想建立系统认知的工程师二是已经在做Agent项目、但架构越写越乱的开发者三是需要向团队解释AI系统设计思路的技术负责人。我会用图解的思路把AI应用架构从底到顶拆成六层每一层讲清楚它为什么存在、核心组件是什么、实际落地时怎么选型、踩过哪些坑。关键词会自然融入AI应用、架构设计、Agent、LLM、MCP以及当下热门的Agent开发、大模型应用、工具编排等话题。全文基于我实际做过的几个Agent项目的经验不是纸上谈兵。2. AI应用架构的整体分层设计思路2.1 从“调API”到“建系统”的认知跃迁大部分人接触LLM的第一行代码是这样的from openai import OpenAI client OpenAI() response client.chat.completions.create( modelgpt-4, messages[{role: user, content: 你好}] ) print(response.choices[0].message.content)这段代码能跑通但它不是一个AI应用它只是一个API调用示例。真正的AI应用需要回答一系列工程问题用户会话怎么管理模型返回的工具调用请求怎么执行执行失败了怎么重试多个Agent之间怎么协作上下文超长了怎么压缩这些问题的答案构成了架构设计的全部内容。我习惯把AI应用架构分成六层从下往上依次是模型接入层、上下文与记忆层、能力扩展层工具/MCP、Agent编排层、应用服务层、可观测与安全层。这个分层不是教科书上的标准答案而是我在实际项目中反复调整后觉得最顺手的一种切法。它的好处是每一层职责单一替换成本低——比如你想从GPT-4换到Claude只需要改模型接入层想加一个新工具只需要在能力扩展层注册。2.2 分层架构的核心原则依赖单向、接口稳定分层设计最容易犯的错误是“跨层调用”。比如在Agent编排层直接写HTTP请求去调外部API跳过了能力扩展层的统一封装。这样做短期省事长期就是灾难工具的超时策略、鉴权、日志、重试逻辑散落在各处改一个地方要翻十个文件。我的原则是上层依赖下层下层不知道上层的存在。Agent编排层只知道“我有一个工具叫search_flight”它不关心这个工具背后是HTTP调用、MCP协议还是本地函数。能力扩展层负责把各种异构的工具统一成标准接口。模型接入层负责把不同厂商的API差异抹平。这样每一层都可以独立测试、独立替换。另一个原则是接口稳定优先于实现优雅。工具调用的输入输出格式一旦定下来就不要轻易改。我见过一个项目因为中途把工具返回格式从JSON改成纯文本导致所有Agent的Prompt都要重写。接口稳定意味着你可以放心地在上面叠加新功能而不是每次改动都引发连锁反应。2.3 一张图看懂六层架构的职责边界层级核心职责典型组件替换频率模型接入层统一多厂商LLM调用API适配器、路由、降级低上下文与记忆层会话管理、上下文压缩、长期记忆对话历史、向量库、摘要器中能力扩展层工具注册、MCP协议、执行沙箱工具注册表、MCP Client高Agent编排层任务规划、多Agent协作、状态机编排引擎、Agent运行时中应用服务层业务逻辑、API网关、鉴权Web框架、消息队列低可观测与安全层日志、追踪、成本、内容安全Tracing、Guardrail持续这张表是我在多个项目里总结出来的替换频率这一列特别值得注意。能力扩展层替换频率最高因为业务需求变化最快今天要查天气明天要接内部工单系统。所以这一层一定要设计得足够灵活注册一个工具的成本要低到“写一个函数加一个装饰器”的程度。3. 模型接入层LLM调用的统一抽象与容错3.1 为什么不能直接调厂商SDK直接调厂商SDK的问题在于锁定。你今天用某家的SDK写了两百行代码明天老板说“换个模型试试效果”你就得改两百行。更麻烦的是不同厂商的API在参数命名、返回结构、错误码、流式输出格式上都有差异。比如有的用max_tokens有的用max_output_tokens有的工具调用返回tool_calls数组有的返回function_call对象。统一抽象层的做法是定义一个内部标准接口所有厂商的适配器都实现这个接口。核心方法就三个chat()、stream_chat()、embed()。输入是一个标准化的消息列表和参数对象输出是一个标准化的响应对象。适配器负责把标准请求翻译成厂商格式再把厂商响应翻译回标准格式。class LLMAdapter: def chat(self, messages: list[Message], tools: list[Tool] None, **kwargs) - Response: raise NotImplementedError def stream_chat(self, messages: list[Message], **kwargs) - Iterator[Chunk]: raise NotImplementedError这个抽象看起来简单但它是整个架构稳定的基石。有了它你可以在配置文件里切换模型可以在运行时根据任务类型路由到不同模型可以在某个厂商故障时自动降级到备用模型。3.2 模型路由与降级的实际策略模型路由不是简单地“哪个便宜用哪个”。我的策略是按任务复杂度分级简单分类、意图识别用便宜的小模型复杂推理、代码生成用大模型需要长上下文的用支持大窗口的模型。路由决策可以基于规则也可以基于一个轻量分类器。降级策略更关键。我遇到过某厂商API在高峰期返回503的情况如果没有降级整个应用就挂了。降级链一般配两到三级主模型失败后重试一次仍失败则切到备用厂商的同级别模型再失败则切到小模型并给用户提示“当前响应质量可能下降”。注意降级不是无脑切换。切换前要检查备用模型是否支持当前请求的特性比如工具调用、JSON模式、视觉输入。我见过因为备用模型不支持工具调用导致Agent直接卡死的案例。3.3 Token成本控制的第一道防线成本控制要从接入层就开始做。第一在适配器里统一记录每次调用的Token消耗包括输入和输出打上模型名、任务类型、用户ID的标签。第二设置单次请求的Token上限防止某个用户粘贴一篇长文导致成本爆炸。第三对输出长度做限制很多场景下模型会啰嗦地重复问题设置max_tokens能省不少钱。我实测过一个客服场景不加输出限制时平均每次响应消耗800 Token加了“回答不超过三句话”的约束后降到300 Token成本直接砍掉六成。这些约束应该写在接入层的默认参数里而不是指望每个调用方都记得加。4. 上下文与记忆层让LLM“记得住”的工程实现4.1 对话历史管理的三种模式上下文管理是AI应用最容易出问题的地方。最朴素的做法是把所有历史消息都塞进Prompt但Token很快就不够了。我总结出三种模式按场景选用全量模式保留完整对话历史适合短会话、客服工单这类轮次少的场景。实现最简单但超过模型窗口就崩。滑动窗口模式只保留最近N轮对话适合闲聊、问答。问题是会丢失早期的重要信息用户说“我前面提到的那个订单”时模型一脸茫然。摘要压缩模式把早期对话用LLM总结成一段摘要保留摘要加最近几轮原文。这是我最常用的模式兼顾了信息保留和Token控制。摘要的触发时机可以按轮次比如每10轮压缩一次或按Token数比如超过窗口70%时压缩。def manage_context(history, max_tokens4000): if count_tokens(history) max_tokens * 0.7: return history # 保留最近5轮其余压缩成摘要 recent history[-5:] older history[:-5] summary llm.summarize(older) return [SystemMessage(f之前的对话摘要{summary})] recent4.2 长期记忆向量库不是万能药长期记忆通常用向量库实现把用户的历史信息、偏好、事实存成向量需要时检索出来注入Prompt。但向量检索有个坑相似不等于相关。用户问“我的订单到哪了”向量检索可能召回“用户上次咨询过退货政策”这两者在语义上相似但当前场景无关。我的做法是混合检索向量相似度加关键词匹配加时间衰减。时间衰减很重要三个月前的偏好可能已经过时了。另外长期记忆的写入要克制不是什么信息都值得存。我一般只存三类用户明确表达的偏好、跨会话需要保持的事实、以及从交互中提炼的用户画像。提示向量库的维度选择和嵌入模型要匹配。换嵌入模型意味着所有存量向量都要重新计算这个成本要在项目初期就考虑进去。我一般选1536维或1024维兼顾效果和存储成本。4.3 上下文注入的顺序与优先级Prompt里信息的排列顺序会影响模型的表现。我的经验顺序是系统指令在最前然后是长期记忆摘要接着是当前任务相关的工具定义再是对话历史最后是用户当前输入。系统指令放最前是因为模型对开头的内容注意力更集中用户输入放最后是因为它最接近生成位置影响最直接。工具定义的位置有讲究。如果工具很多全部塞进去会占用大量Token且干扰模型判断。我的做法是先用一个轻量检索步骤根据用户输入筛选出最相关的5到8个工具只把这些工具的定义注入Prompt。这样既省Token又提高工具选择的准确率。5. 能力扩展层MCP与工具生态的工程化落地5.1 MCP到底是什么为什么它重要MCPModel Context Protocol是这两年AI应用开发领域最值得关注的变化之一。简单说它是一套标准协议让LLM应用能以统一的方式发现和调用外部工具、数据源和服务。在MCP之前每接一个工具就要写一套适配代码有了MCP工具提供方实现一个MCP Server应用方用MCP Client连接双方通过标准协议通信。MCP的核心价值在于解耦。工具的实现和工具的调用方不需要知道彼此的技术栈。一个用Python写的数据库查询工具和一个用TypeScript写的应用可以通过MCP无缝协作。这对Agent开发尤其重要因为Agent需要动态发现和组合工具标准协议让这种动态性成为可能。我实际用MCP接过几类工具文件系统操作、数据库查询、浏览器自动化比如Playwright MCP、以及内部API。体验下来MCP最大的好处是工具注册成本极低。以前接一个工具要写适配器、定义Schema、处理错误现在只要配置一个MCP Server地址工具列表自动发现。5.2 工具注册表的设计与工具选择策略不管用不用MCP能力扩展层都需要一个工具注册表。注册表的核心功能是注册工具、描述工具、执行工具、记录调用。工具的描述要足够清晰因为LLM是根据描述来决定用哪个工具的。描述写得好工具选择准确率能提升一大截。工具选择策略我试过几种。最简单的是全量注入工具少的时候没问题。工具多了之后我用两阶段选择先用一个分类模型或关键词匹配缩小候选范围再让主模型从候选中选。还有一种做法是给工具打标签按场景分组根据当前对话的意图加载对应组的工具。class ToolRegistry: def __init__(self): self.tools {} def register(self, name, description, schema, handler): self.tools[name] Tool(name, description, schema, handler) def get_relevant(self, query, top_k8): # 基于描述做语义检索返回最相关的工具 return semantic_search(query, self.tools.values(), top_k) def execute(self, name, args): tool self.tools[name] return tool.handler(**args)5.3 工具执行的沙箱与超时控制工具执行是AI应用里最容易出安全事故的环节。LLM生成的参数可能包含注入攻击、路径穿越、超大查询。我的做法是三层防护第一层是参数校验用JSON Schema严格校验类型和范围第二层是执行沙箱文件操作限制在指定目录数据库查询限制为只读且加LIMIT第三层是超时控制每个工具调用设置硬超时超时后返回错误让Agent决定下一步。超时时间怎么定我的经验是查询类工具5到10秒写入类工具15到30秒浏览器自动化类30到60秒。超时后不要直接抛异常给用户而是返回一个结构化的错误信息给Agent让Agent有机会重试或换工具。我见过因为一个工具超时导致整个Agent崩溃的情况就是因为没有做超时后的优雅降级。注意工具执行结果要截断。一个数据库查询可能返回几万行全部塞回Prompt会直接撑爆上下文。我一般在工具层就做截断比如最多返回50行或2000字符并在结果里注明“已截断”。6. Agent编排层从单Agent到多Agent协作6.1 Agent的核心循环感知、规划、执行、反思一个Agent的本质是一个循环接收输入规划下一步动作执行动作观察结果再规划。这个循环听起来简单但工程实现上有大量细节。规划环节模型可能输出无效的动作格式执行环节工具可能失败反思环节模型可能陷入死循环。我的做法是给循环加上明确的终止条件和最大步数。一般设置最大10到15步超过就强制返回当前结果并提示“任务未完全完成”。终止条件包括模型输出了最终答案、达到最大步数、连续两次动作相同说明卡住了、或者累计Token超过预算。def agent_loop(task, max_steps12): history [SystemMessage(AGENT_SYSTEM_PROMPT), UserMessage(task)] for step in range(max_steps): response llm.chat(history, toolsregistry.get_all()) if response.is_final_answer: return response.content if response.has_tool_calls: for call in response.tool_calls: result registry.execute(call.name, call.args) history.append(ToolResultMessage(call.id, result)) if is_stuck(history): break return 任务处理达到步数上限请补充信息后重试6.2 多Agent协作的三种拓扑结构单Agent能力有限复杂任务需要多Agent协作。我实践过三种拓扑主管- worker模式一个主管Agent负责拆解任务和分配多个worker Agent各司其职。适合任务可以清晰分解的场景比如“调研报告”可以拆成搜索、分析、写作三个worker。流水线模式Agent按顺序处理前一个的输出是后一个的输入。适合有明确阶段的任务比如“数据清洗→分析→可视化→报告”。辩论模式多个Agent对同一问题给出方案互相评审最后综合。适合需要高质量决策的场景但Token消耗大一般只在关键决策点用。选哪种拓扑取决于任务的可分解性和对质量的要求。我的经验是大部分业务场景用主管-worker模式就够了辩论模式成本太高除非是特别重要的决策。6.3 Agent状态管理与断点恢复Agent执行过程中可能因为各种原因中断工具超时、模型限流、用户关闭页面。如果没有状态管理用户回来就得从头开始。我的做法是把Agent的每一步状态持久化到数据库或Redis包括当前步数、历史消息、待执行的工具调用。断点恢复的关键是幂等性。恢复时不能重复执行已经成功的工具调用否则可能产生副作用比如重复下单。我在工具执行记录里标记状态pending、executing、success、failed。恢复时只重新执行pending和failed的success的直接用缓存结果。提示状态持久化的粒度要适中。每一步都存会增加IO开销存太少又恢复不了。我一般每完成一个“规划-执行”循环存一次工具调用内部的状态不单独存。7. 可观测与安全层AI应用上线的最后一道防线7.1 追踪与日志看见Agent的每一步AI应用的可观测性和传统应用很不一样。传统应用看QPS、延迟、错误率就够了AI应用还要看每次调用的Token消耗、工具调用的成功率、Agent的步数分布、上下文长度变化、模型输出的质量指标。我用OpenTelemetry做基础追踪每个用户请求生成一个Trace每个LLM调用和工具调用是一个Span。Span上记录模型名、Token数、延迟、是否命中缓存。这样出问题时可以快速定位是模型慢、工具慢还是编排逻辑有问题。日志要记录完整的Prompt和响应但要注意脱敏。用户输入里可能有手机号、身份证号这些在落盘前要过滤。我一般用正则做基础脱敏敏感字段用占位符替换。7.2 内容安全与Guardrail的工程实现内容安全是AI应用不能回避的问题。我的做法是在三个位置加Guardrail输入侧、工具执行侧、输出侧。输入侧检查用户输入是否包含注入攻击、越权指令。工具执行侧检查LLM生成的工具参数是否合法比如文件路径是否在允许目录内。输出侧检查模型生成的内容是否符合规范比如是否泄露了系统Prompt、是否包含不当内容。Guardrail的实现可以是规则引擎也可以是另一个LLM调用。规则引擎快但覆盖有限LLM判断准但增加延迟和成本。我的策略是规则引擎做第一道过滤拦截明显的违规LLM做第二道处理规则覆盖不到的边缘情况。两道都通过才返回给用户。7.3 成本监控与预算告警成本是AI应用的生命线。我在接入层记录每次调用的Token消耗按用户、按任务类型、按模型聚合。设置日预算和周预算超过80%时告警超过100%时自动降级到便宜模型或限制请求频率。成本优化有几个实操技巧第一缓存高频请求的结果比如常见问题的回答第二对长文档做预处理提取关键段落再送给模型而不是全文塞入第三用流式输出让用户感知更快同时可以在用户中断时提前停止生成节省Token。我实测过一个场景加了结果缓存后重复问题的成本降为零整体成本下降约35%。缓存的关键是设计好缓存键一般用“用户意图关键实体”做键而不是原始输入文本这样语义相同但表述不同的请求也能命中。8. 常见问题与排查技巧实录8.1 Agent不调用工具或调用错误工具怎么办这是最高频的问题。排查顺序是先看工具描述是否清晰描述模糊是首要原因再看工具数量是否过多超过15个工具时模型选择准确率明显下降然后看Prompt里工具调用的示例是否充分给一两个few-shot示例能显著提升准确率。如果工具描述没问题但还是选错可以试试在系统Prompt里加一句“在调用工具前先说明你为什么选择这个工具”。这个“思考链”能迫使模型更谨慎地选择。我实测下来加了这句话后工具选择准确率从七成提升到九成左右。8.2 上下文超长导致模型“失忆”上下文超长的典型表现是模型忘记了前面的指令或信息。排查时先确认实际Token数用tiktoken之类的工具精确计算不要凭感觉。然后检查是否有不必要的长文本被塞入比如完整的工具返回结果、重复的系统指令。解决方案按优先级先做上下文压缩把早期对话摘要化再优化工具返回截断长结果最后考虑换更大窗口的模型。但换模型是最后手段因为成本和延迟都会增加。我一般优先在前两层解决。8.3 工具调用超时与重试策略工具超时的排查要看是工具本身慢还是网络问题。如果是工具本身慢考虑加缓存或异步化如果是网络问题加重试。重试策略我一般用指数退避第一次等1秒第二次2秒第三次4秒最多重试3次。重试时要区分错误类型参数错误重试没用网络超时重试有意义。注意写操作的重试要特别小心。查询可以随便重试但下单、发消息这类写操作重试可能导致重复。我的做法是给写操作加幂等键服务端根据幂等键去重。8.4 常见问题速查表问题现象可能原因排查动作解决方案Agent不调工具工具描述模糊检查描述文本补充使用场景和示例工具选错工具数量过多统计工具总数分组加载或语义筛选上下文失忆Token超限精确计算Token摘要压缩结果截断工具超时工具慢或网络差看工具耗时分布缓存指数退避重试成本飙升输出过长或缓存缺失看Token消耗分布限制输出加缓存死循环无终止条件看步数分布加最大步数重复检测这张表是我从实际项目中积累的每次遇到新问题就往里加一行。建议你也维护一份自己的速查表比翻文档快得多。9. 我在实际项目中的几点体会做AI应用架构这几年最大的体会是架构的价值不在于多先进而在于多稳定。我见过太多项目追求最新的框架、最炫的Agent模式结果上线后各种边界情况处理不了。反而是那些分层清晰、接口稳定、有完善降级和监控的系统能扛住真实流量的考验。另一个体会是MCP这类标准协议值得尽早接入。虽然初期看起来多了一层抽象但当你需要接第五个、第十个工具时标准协议的优势就体现出来了。工具生态的复用性会随着接入的工具数量增加而指数级提升。最后分享一个小技巧在Agent的系统Prompt里加一句“如果不确定先问用户而不是猜”。这句话能减少大量因为模型臆测导致的错误。很多Agent项目的问题不是能力不够而是太自信。让模型学会说“我不确定”比让它多会一个工具更有价值。这个架构后续还可以扩展的方向包括多模态输入的统一处理、Agent之间的标准化通信协议、以及基于反馈的自动Prompt优化。但这些都是锦上添花把上面六层的基础打牢才是大多数AI应用项目最该做的事。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询