1. 从一张架构图说起:AI应用到底由哪些层组成
很多人第一次接触AI应用开发,脑子里冒出来的第一个问题就是:这东西到底怎么搭?跟传统后端开发有什么区别?我刚开始做AI应用的时候也有这个困惑,翻了不少资料,发现大部分文章要么讲得太虚——满篇“大模型赋能业务”之类的空话,要么一上来就甩代码,缺少一张能把全局串起来的架构图。
这篇内容就是想把AI应用架构设计这件事,用图解的思路讲清楚。所谓“图解”,不是真的画几张图就完事,而是用结构化的方式把AI应用的各个层次拆开,让你看完之后脑子里能形成一张清晰的架构地图。不管你是刚转行做AI应用开发的程序员,还是已经在大模型领域摸爬滚打了一段时间的工程师,都能从中找到对自己有用的东西。
先给一个最粗的框架。一个典型的AI应用,从下往上大致可以分成五层:模型层、能力层、编排层、应用层、交互层。这个分层不是教科书上的标准答案,而是我在实际项目中总结出来的一套理解方式,好处是每一层的职责边界比较清晰,出了问题也容易定位是哪一层的锅。
模型层就是最底下的大模型本身,包括LLM、多模态模型、Embedding模型等等。能力层是在模型之上封装出来的各种原子能力,比如RAG检索、工具调用、记忆管理。编排层负责把这些能力按照业务逻辑串起来,Agent的核心逻辑就在这一层。应用层是面向具体业务场景的,比如客服机器人、代码助手、数据分析Agent。交互层则是用户直接接触的部分,包括对话界面、API接口、流式输出等等。
你可能会问,为什么要把能力层和编排层分开?直接用模型加Prompt不就行了吗?这个问题我在早期项目中也纠结过。后来发现,当你的AI应用只有一两个功能的时候,确实不需要分这么细。但一旦功能多起来——比如既要检索知识库,又要调用外部API,还要维护多轮对话的上下文——如果不把能力层抽象出来,代码会变得非常混乱,每加一个功能就要改一遍主流程。把能力层独立出来之后,每个能力就是一个可插拔的模块,编排层只负责决定什么时候调用哪个能力,维护起来轻松很多。
提示:分层不是目的,而是手段。如果你的项目规模很小,不需要强行套五层架构,两三层也能跑得很好。架构设计永远要服务于实际需求,不要为了架构而架构。
理解了整体分层之后,接下来逐个拆解每一层的设计要点和常见坑。
2. 模型层选型:别一上来就盯着最大的那个
模型层是整个AI应用的地基,选错了模型,后面的架构设计再漂亮也白搭。但很多人在选模型的时候容易走两个极端:要么无脑选最大的那个,觉得参数越多效果越好;要么只看价格,哪个便宜用哪个。这两种做法都有问题。
2.1 不同任务对模型能力的需求差异
先搞清楚你的应用要做什么类型的任务。不同任务对模型能力的要求差别很大,不是所有场景都需要顶级模型。
| 任务类型 | 核心能力要求 | 推荐模型量级 | 典型场景 |
|---|---|---|---|
| 文本分类/意图识别 | 理解能力、指令遵循 | 小模型即可 | 客服工单分类、路由分发 |
| 知识问答(RAG) | 理解能力、忠实度 | 中等模型 | 企业知识库、文档问答 |
| 复杂推理/规划 | 推理能力、长上下文 | 大模型 | Agent任务规划、代码生成 |
| 多模态理解 | 视觉理解、跨模态对齐 | 多模态大模型 | 图片分析、文档解析 |
| 内容生成 | 创造力、语言流畅度 | 中等偏大 | 文案撰写、创意辅助 |
我踩过的一个坑是:早期做一个工单自动分类的功能,上来就用了一个很大的模型,效果确实好,但成本高得离谱,而且延迟很大。后来换了一个小模型,针对分类任务做了少量微调,效果几乎没降,成本直接降了一个数量级。所以选模型的第一步,是明确你的任务到底需要什么级别的能力。
2.2 模型选型的四个实际考量维度
除了能力匹配之外,还有几个实际因素必须考虑:
延迟。用户能接受的响应时间是多少?如果是实时对话场景,首Token延迟最好控制在1秒以内。大模型虽然效果好,但推理速度慢,有时候不得不用小模型或者做模型蒸馏。
成本。按Token计费的模式下,输入输出长度直接决定成本。一个日活几千的应用,如果每次请求都带上完整的对话历史和知识库检索结果,Token消耗会非常惊人。这时候就需要做上下文压缩、缓存策略等优化。
部署方式。是调用云端API还是本地部署?云端API省事,但数据要出你的服务器;本地部署可控性强,但对硬件有要求。这个选择取决于你的数据敏感程度和预算。
上下文窗口。你的应用需要处理多长的输入?做长文档分析可能需要128K甚至更长的上下文窗口,而简单的对话场景8K就够用了。上下文窗口越大,通常成本也越高。
注意:不要只看模型排行榜上的分数。排行榜上的评测集和你的实际业务场景差别可能很大。最靠谱的做法是拿你自己的真实数据做A/B测试,用实际效果说话。
2.3 多模型策略:一个应用不一定只用一个模型
在实际项目中,我越来越倾向于多模型策略。什么意思呢?就是同一个AI应用里,不同环节用不同的模型。比如意图识别用一个小模型快速判断用户想干什么,然后根据意图路由到不同的处理流程,复杂推理用大模型,简单问答用中等模型。这样整体成本和延迟都能优化。
这种策略的实现需要在架构上做一个模型路由层,根据任务类型、复杂度、当前负载等条件动态选择模型。路由层可以很简单——就是几个if-else规则;也可以很复杂——用一个分类模型来判断。具体怎么选,取决于你的业务复杂度。
3. 能力层拆解:RAG、工具调用与记忆管理
能力层是AI应用架构中最“厚”的一层,也是最容易出问题的一层。这一层的核心任务是把模型本身不具备的能力——比如获取外部知识、操作外部系统、记住历史信息——封装成可复用的模块。
3.1 RAG检索增强生成:不只是“搜一下再回答”
RAG(Retrieval-Augmented Generation)是AI应用中最常见的能力之一。表面上看很简单:用户问一个问题,你去知识库里搜相关内容,然后把搜索结果和问题一起塞给模型,让模型基于搜索结果回答。但实际做起来,坑非常多。
第一个坑是切分策略。文档怎么切?按固定长度切还是按语义切?切多大?这些参数直接影响检索效果。我试过按512个Token固定切分,结果经常把一句完整的话切成两半,检索出来的内容断断续续。后来改成按段落切分,再配合重叠窗口,效果好很多。
第二个坑是检索策略。纯向量检索在处理精确匹配的时候效果不好,比如用户问一个具体的产品型号,向量检索可能返回一堆语义相似但型号不对的内容。这时候需要混合检索——向量检索加关键词检索,两路结果合并后重排序。
第三个坑是上下文组装。检索出来一堆文档片段,不能直接全塞给模型。一是Token有限,二是无关内容会干扰模型判断。需要做重排序(Rerank),把最相关的排在前面,然后根据Token预算截断。
# 一个简化的RAG流程示例 def rag_pipeline(query, knowledge_base, model, top_k=5): # 1. 混合检索:向量 + 关键词 vector_results = vector_search(query, knowledge_base, top_k=top_k*2) keyword_results = keyword_search(query, knowledge_base, top_k=top_k*2) # 2. 合并去重 merged = merge_and_dedup(vector_results, keyword_results) # 3. 重排序 reranked = rerank(query, merged, top_k=top_k) # 4. 组装上下文 context = assemble_context(reranked, max_tokens=2000) # 5. 生成回答 prompt = f"基于以下资料回答问题:\n{context}\n\n问题:{query}" return model.generate(prompt)这段代码看起来简单,但每一步都有调优空间。比如重排序模型的选择、上下文组装时怎么处理冲突信息、检索不到相关内容时怎么兜底,这些细节决定了RAG系统的实际可用性。
3.2 工具调用:让模型能“动手”
工具调用(Tool Use / Function Calling)是Agent的基础能力。模型本身只能生成文本,但通过工具调用,它可以触发外部API、查询数据库、发送邮件等等。
工具调用的架构设计有几个关键点:
工具描述的设计。模型是根据工具的名称和描述来决定调用哪个工具的。描述写得好不好,直接决定调用准确率。我见过很多工具调用失败的情况,不是模型能力不行,而是工具描述写得太模糊。比如一个查询订单的工具,描述只写“查询订单”,模型根本不知道什么时候该用它。改成“根据订单号查询订单状态和物流信息,适用于用户询问订单进度、快递位置等场景”,调用准确率立刻上去了。
参数校验与错误处理。模型生成的参数不一定合法,可能缺字段、类型不对、值超出范围。必须在工具执行前做校验,校验失败时把错误信息返回给模型,让它重新生成。这个重试机制是工具调用稳定性的关键。
并行调用与串行调用。有些工具之间没有依赖关系,可以并行调用以提高效率;有些工具需要前一个的输出作为后一个的输入,必须串行。编排层需要能识别这两种情况。
3.3 记忆管理:短期记忆与长期记忆
记忆管理是很多AI应用容易忽略的部分。没有记忆管理,每次对话都是“失忆”状态,用户体验很差。
短期记忆就是当前对话的上下文。实现方式很简单——把历史消息拼接到Prompt里。但问题是Token有限,对话轮次多了之后,早期消息会被挤掉。常见的优化策略包括:滑动窗口(只保留最近N轮)、摘要压缩(把早期对话总结成一段话)、关键信息提取(只保留实体和意图)。
长期记忆则是跨会话的信息持久化。比如用户上次说过自己的偏好,下次对话时应该能记住。实现方式通常是把关键信息存入数据库或向量库,需要的时候检索出来注入到Prompt中。
提示:记忆管理最容易出的问题是“记了不该记的”。比如把用户的临时指令当成长期偏好,或者把敏感信息持久化存储。设计记忆系统时一定要考虑什么该记、什么不该记、记多久。
4. 编排层设计:Agent的核心逻辑在哪里
编排层是AI应用架构中最能体现“设计功力”的一层。模型层和能力层都有比较成熟的方案可以参考,但编排层的设计高度依赖具体业务场景,没有万能模板。
4.1 从固定流程到自主Agent的频谱
编排层的设计不是非黑即白的。一端是固定流程——开发者预先定义好每一步做什么,模型只在特定环节发挥作用;另一端是自主Agent——模型自己决定下一步做什么,开发者只给出目标和可用工具。
| 编排模式 | 控制权 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 固定流程 | 开发者 | 流程明确的业务 | 可控性强、稳定 | 灵活性差 |
| 有限分支 | 开发者+模型 | 多意图场景 | 平衡可控与灵活 | 分支设计复杂 |
| 自主Agent | 模型 | 开放式任务 | 灵活、适应性强 | 不可控、成本高 |
我个人的经验是:大部分生产环境的应用应该落在中间地带。完全固定的流程太死板,用户稍微换个问法就处理不了;完全自主的Agent太不可控,容易跑偏、死循环、烧Token。比较好的做法是“框架固定、细节灵活”——整体流程由开发者定义,但每个环节内部给模型一定的自主决策空间。
4.2 ReAct模式与Plan-Execute模式的取舍
在Agent编排中,两种最常见的模式是ReAct和Plan-Execute。
ReAct是“推理-行动”循环:模型先思考当前该做什么,然后执行一个动作,观察结果,再思考下一步。这个模式适合步骤不多、需要根据中间结果动态调整的场景。
Plan-Execute是先制定完整计划再逐步执行:模型先分析任务,列出所有步骤,然后按顺序执行。这个模式适合步骤明确、可以提前规划的场景,好处是执行效率高,坏处是计划可能跟不上变化。
实际项目中,我经常把两者结合:先用Plan-Execute做整体规划,然后在每个步骤内部用ReAct做细节调整。这样既有全局视野,又有局部灵活性。
4.3 状态管理与错误恢复
编排层还有一个容易被忽视的职责:状态管理。Agent执行多步任务时,需要维护当前进度、中间结果、已尝试的方案等信息。如果某一步失败了,需要能回退到之前的状态,或者换一种方式重试。
错误恢复策略包括:重试(同样的操作再试一次)、降级(换一个更简单的方案)、跳过(跳过这一步继续往下)、终止(放弃整个任务并告知用户)。选择哪种策略取决于具体场景和错误的性质。
# 一个简化的Agent编排示例 class AgentOrchestrator: def __init__(self, model, tools, max_steps=10): self.model = model self.tools = tools self.max_steps = max_steps self.state = {"history": [], "current_step": 0} def run(self, task): plan = self.model.plan(task, self.tools) for step in plan.steps: if self.state["current_step"] >= self.max_steps: return self.handle_max_steps() try: result = self.execute_step(step) self.state["history"].append(result) except ToolError as e: recovery = self.decide_recovery(e, step) if recovery == "retry": result = self.execute_step(step) elif recovery == "skip": continue else: return self.handle_failure(e) return self.synthesize_result()这段代码省略了很多细节,但核心思路是:编排层要能规划、执行、监控、恢复。每一步都要有明确的成功/失败判断,失败后要有恢复策略。
5. 应用层与交互层:用户真正接触到的部分
前面几层都是“幕后”的,应用层和交互层才是用户直接感知的。这两层设计得好不好,直接决定用户愿不愿意继续用你的产品。
5.1 流式输出与用户体验
流式输出是AI应用交互层的基本要求。用户等一个完整的回答可能要好几秒甚至十几秒,但如果能看到文字一个字一个字地蹦出来,感知上的等待时间会短很多。
实现流式输出需要在架构上做全链路的支持:模型层要支持流式生成,编排层要能处理流式数据,交互层要能实时渲染。任何一环不支持流式,整个体验就会打折扣。
注意:流式输出和工具调用有时候会冲突。如果模型在生成过程中决定调用工具,流式输出可能需要暂停,等工具返回结果后再继续。这个交互细节需要在设计时考虑清楚。
5.2 多轮对话的上下文管理
多轮对话不是简单地把历史消息拼起来就行。需要考虑的问题包括:什么时候开启新话题、什么时候引用历史信息、怎么处理指代消解(“它”、“那个”指什么)、怎么处理话题切换。
一个实用的技巧是维护一个“对话状态”对象,记录当前话题、涉及的实体、用户的偏好等信息。每次新消息进来时,先更新对话状态,再根据状态决定怎么组装上下文。
5.3 反馈闭环与持续优化
AI应用上线不是终点,而是起点。用户的实际使用数据是最宝贵的优化资源。需要建立反馈闭环:收集用户反馈(显式的点赞点踩、隐式的采纳率)、分析失败案例、迭代优化Prompt和工具描述、必要时微调模型。
这个闭环的架构设计包括:日志记录(记录每次请求的输入输出和中间状态)、反馈收集(用户界面上的反馈入口)、数据分析(定期分析失败模式和优化机会)、版本管理(Prompt和模型的版本控制)。
6. 那些架构图上看不到的坑
架构图画得再漂亮,实际落地时还是会遇到各种意想不到的问题。这一章分享几个我在实际项目中踩过的坑,以及后来总结出来的应对策略。
6.1 并发场景下的Agent状态隔离
单用户测试的时候一切正常,一上并发就出问题——这是很多AI应用的常见病。Agent在执行多步任务时是有状态的,如果多个请求共享同一个Agent实例,状态就会串。
解决方案是每个请求创建独立的Agent实例,或者用请求级别的状态存储。但这样会带来资源开销,需要在架构上做权衡。我的做法是用对象池管理Agent实例,每个实例处理完一个请求后重置状态再复用。
6.2 Token预算的精细化管理
Token就是钱。一个没有做Token优化的AI应用,成本可能是优化后的好几倍。Token优化的手段包括:精简Prompt(去掉冗余的指令和示例)、压缩上下文(摘要代替全文)、缓存常用结果(相同问题不重复调用模型)、选择合适的模型(简单任务不用大模型)。
我建议在架构设计阶段就把Token预算作为一个核心指标来考虑。每个环节的Token消耗都要有预期,超出预算时要有降级策略。
6.3 模型输出格式的稳定性
让模型输出JSON格式的数据,大部分时候没问题,但偶尔会多一个逗号、少一个引号、或者加一段解释文字。这些格式问题会导致下游解析失败。
应对策略包括:使用模型的结构化输出功能(如果支持)、在Prompt中明确格式要求并给出示例、在解析层做容错处理(尝试修复常见的格式问题)、解析失败时重试并加强格式约束。
6.4 安全边界与权限控制
AI应用的安全问题比传统应用更复杂,因为模型的输出是不确定的。需要防范的风险包括:Prompt注入(用户通过精心构造的输入让模型执行非预期操作)、数据泄露(模型在回答中泄露了不该泄露的信息)、工具滥用(模型调用了不该调用的工具)。
架构上的应对措施包括:输入过滤(检测并拦截可疑的Prompt注入)、输出审查(检查模型输出是否包含敏感信息)、工具权限控制(不同用户可用的工具不同)、操作确认(敏感操作需要用户二次确认)。
7. 从架构图到可运行系统:我的落地建议
聊了这么多架构层面的东西,最后分享一些落地的实操建议。这些建议不一定适用于所有场景,但至少是我自己验证过、觉得有用的。
先从最简单的方案开始。不要一上来就设计一个五层架构、支持多模型、带自主Agent的系统。先用最简单的方案跑通核心流程——一个模型、一个Prompt、一个API接口。跑通之后再根据实际遇到的问题逐步优化架构。
把可观测性做在前面。AI应用的不确定性比传统应用大得多,没有完善的日志和监控,出了问题根本不知道从哪里查。建议在项目初期就把请求日志、Token消耗、延迟、错误率等指标记录下来。
为失败设计。模型会出错、工具会超时、检索会返回无关内容。每个环节都要有失败处理策略,不能让一个环节的失败导致整个系统崩溃。
持续迭代Prompt和工具描述。这两样东西对效果的影响非常大,而且不需要改代码就能优化。建议建立一套Prompt管理和版本控制机制,方便快速迭代和回滚。
关注成本但不唯成本论。成本很重要,但用户体验更重要。为了省Token而牺牲回答质量,最终会失去用户。找到成本和质量的平衡点,才是架构设计的核心挑战。
架构设计这件事,纸上谈兵容易,真正落地时才会发现各种约束和取舍。希望这篇内容能帮你少走一些弯路,在实际项目中搭出既稳定又灵活的AI应用架构。