我们团队这半年同时推进了三个面向不同行业的AI应用:一个做企业知识库问答,一个做自动化报表生成,另一个是客服工单分类。代码量都不大,真正让我们反复返工、开会吵到面红耳赤的,几乎全在架构设计阶段。模型选型、服务拆分、上下文怎么管、工具调用怎么约束、观测怎么做,每一步都是坑。所以我想把这段时间沉淀下来的AI应用架构设计经验整理成一篇长文,用一种"画图拆解"的方式,把从用户请求到模型响应这条链路上所有关键节点讲清楚。这篇文章不是教科书式的理论堆砌,而是基于真实上线场景的踩坑总结,适合正在做AI应用落地、或者准备从零搭建AI系统的团队参考。
1. 架构设计前必须想明白的三件事
1.1 单体优先还是服务化优先
很多团队一上来就按微服务拆:模型网关一个服务、向量检索一个服务、Agent编排一个服务、业务后端一个服务。框架选得倒是很齐全,结果联调了一个月还没跑通一个完整请求。我个人的建议是:AI应用架构设计的起步阶段,不要急着微服务化,先做"逻辑分层、物理单体"。
所谓逻辑分层,是在代码层面把模型调用、工具调用、业务逻辑分开;所谓物理单体,是先把所有模块放进一个服务里,保证端到端链路能跑通。原因很简单,AI应用最大的不确定性在模型输出质量,不在系统并发。如果连"模型能不能稳定返回合格答案"都还没验证,就去解决"每秒一万请求怎么分摊",这是在错误的时间解决错误的问题。
等链路跑通、业务逻辑稳定之后,再考虑把模型调用抽成独立网关服务,把向量检索抽成独立服务。而且哪怕到了这个阶段,我也建议"按故障域拆",而不是"按团队组织拆"——只有真正独立的资源消耗和扩缩容需求,才值得拆成独立服务。
1.2 模型选型不是选最好的,是选最不折腾的
模型选型这件事,我在实际项目中踩过最大的坑,是盲目追新。公司要求"我们必须用最新最强的模型",结果部署环境GPU显存吃紧,量化方案让推理速度掉了40%,最后还是退回了一个更老但更稳的版本。
架构设计角度看,模型选型需要考虑四个维度:
- 输出质量能不能满足业务场景,尤其是中文场景下的语义理解
- 推理延迟和吞吐能不能扛住真实流量,注意是P95不是平均值
- 是走API调用还是本地部署,API按量付费但数据要出内网,本地部署要买卡、要运维
- 生态兼容性,比如是否支持function calling、是否方便对接开源编排框架
这四个维度缺一不可。我见过一个团队因为模型不支持可靠的功能调用,硬生生在提示词里用"JSON格式输出工具调用结果"这种土办法,结果十个请求里有三四个JSON解析失败,最后整个工具调用机制被推倒重来。
1.3 非功能需求先量化再动手
架构设计最忌讳空谈"要高可用、要低延迟"。我在每个项目启动前都会先把非功能需求量化成具体数字,写进架构设计文档里:
- 端到端响应时间:普通问答场景P95小于2秒,需要走多轮工具调用的复杂场景P95可以放宽到5秒
- 并发规模:峰值QPS多少,平均QPS多少,这决定服务副本数和是否要引入异步任务队列
- 成本上限:单次请求的token成本预算、GPU资源预算
- 数据安全:哪些数据绝对不能出内网,哪些可以走第三方API
- 容错要求:模型超时多久要降级、降级到什么服务
没有这些数字,架构设计就是空中楼阁。比如"响应小于2秒"和"响应小于200毫秒"对应的架构方案完全不同,前者用SSE流式输出就够了,后者可能要考虑模型蒸馏和缓存策略。这些一定要在画架构图之前定下来。
2. 端到端链路拆解:一张图画清所有核心模块
2.1 请求进入:接入层与鉴权设计
任何AI应用的第一道入口都是接入层。这道层的核心职责是身份认证、访问控制、流量整形。不要小看这一层,我见过不少团队把API Key直接嵌在前端代码里,用户通过浏览器就能扒出来,然后拿着这个Key到你的模型接口上刷了十几万次调用,账单直接爆炸。
接入层的架构决策点包括:
- 鉴权方式:对内服务用服务间认证,对外API用JWT或OAuth2
- 限流策略:按用户维度限流还是按IP维度限流,AI应用通常还要加一个按token消耗量的限流
- 协议选型:多数AI应用走HTTP/SSE流式,如果要做实时语音交互,要考虑WebSocket
- 身份信息传递:网关把用户身份解析出来后,要在请求头里透传给下游的编排层
这块的架构原则很简单:接入层只做轻量转发和防护,绝对不塞业务逻辑。如果在这一层做了上下文管理或者嵌入了业务状态,后续扩容和迭代都会非常痛苦。
2.2 编排层:Agent调度与多Agent协作
编排层是整个AI应用架构设计里最核心、也最容易失控的一层。拿Agent场景举例,一个标准的Agent架构图里,编排层要做的事包括:解析用户意图、拆解任务、选择执行路径、调用工具、汇总结果、组织最终回复。
画架构图的时候,我会把编排层单独画一个方框,里面再画三个子模块:
- 规划器(Planner):负责把大任务拆成小步骤,这一步一般靠提示词驱动模型实现
- 工具注册表(Tool Registry):登记所有可用的工具函数,包括工具名称、参数Schema、调用限制
- 执行器(Executor):负责按计划调用外部工具并把结果反馈给模型
多Agent协作在这张图上的体现,是把"单Agent"的方框换成"N个Agent的池子",然后加一个调度组件。调度组件的核心挑战是Agent之间的上下文传递和任务分配。从工程角度,我不建议一开始就上多Agent——单Agent架构下如果任务拆解不够精准,多Agent只会把错误放大。多Agent协作适合目标明确、子任务边界清晰的场景,比如"先做检索、再做摘要、最后做翻译"这种流水线式的分工。
2.3 模型调用层:上下文管理与流式输出
模型调用层负责和远程模型API或本地推理服务通信。这一层在架构图里看起来只是简单的一排箭头,但实际实现里的细节非常多。
第一个核心问题是上下文管理。模型的上下文窗口是有限的,你不可能把整个历史对话记录全部塞进去。常用的策略是滑动窗口加摘要压缩:保留最近N轮完整对话,更早的内容让模型总结成一段摘要。架构设计时要把这个策略做成一个独立的上下文管理模块,而不是散落在业务代码里。
第二个核心问题是流式输出。想要端到端延迟体验好,必须用SSE把模型吐出来的token逐段推给前端。这也意味着编排层和后端服务之间的接口设计要考虑流式请求如何中转,包括流结束的标记、错误中断的处理、前端断连时的资源回收。
第三个核心问题是超时与重试。模型接口的延迟波动很大,慢的时候十几秒都不吐第一个token。架构上要做两件事:一是设置合理的连接超时和读取超时,二是超时之后走降级策略——返回兜底话术,或者切换到备用模型。
2.4 知识增强:RAG与向量检索模块
大多数企业级AI应用绕不开一个需求:让模型回答基于自有知识库。这就是RAG。RAG模块在架构图里通常包含三个子模块:文档解析与切分、向量化与存储、检索与重排序。
文档解析与切分是RAG里最容易被低估的环节。我见过的失败案例几乎都栽在这一步——直接把几千页的PDF整篇扔给Embedding模型,生成的向量里包含了大量噪音,检索出来的内容经常是答非所问。正确做法是把文档按照章节、段落语义切分成合理大小的块(chunk),每块之间保留一些重叠内容,防止"意思被切碎"。
向量化与存储的选择取决于数据规模。小规模场景(十万级文档块)用Postgres加pgvector就够了,不需要单独引入Milvus这类专业向量库。我之前为了图"技术先进",上来就部署了一套独立的向量数据库集群,加了两台机器、配了一堆监控告警,结果数据量连百万分之一的索引规模都不到。简化架构、按实际规模选型,这是我在RAG模块上最大的教训。
检索与重排序是决定RAG效果的上限。基础版是向量相似度Top-K召回,进阶版是召回的候选集再做一次重排序(rerank)。重排序模型一般比Embedding模型大,精度更高,但速度更慢,所以要放在召回的候选集上做,而不是全库扫描。
2.5 工具调用:Function Calling与权限边界
工具调用是Agent类AI应用和普通聊天应用最大的区别。架构图里,工具模块紧挨着编排层,表示工具是Agent的"手和脚"。
工具调用的架构设计要解决三个问题:
- 工具描述怎么写,模型才容易理解。每个工具函数需要清晰的描述、准确的参数Schema、必要的使用示例
- 工具调用结果如何反馈给模型。工具返回的结果需要做截断和格式化,防止把超长JSON直接塞进上下文把token烧光
- 工具的权限和控制边界。哪些工具是用户可以触发的,哪些工具是内部流程专用的,调用敏感工具时是否要二次确认
权限边界是我特别想强调的。很多团队开发的Agent工具里带着数据库查询能力,然后为了"用户体验",允许用户输入任意SQL去查询。这就是把生产数据库直接暴露给了大模型——Prompt注入攻击的风险极高。实际项目里,工具接口必须收窄,不让Agent直接对接原始数据库,而是只开放预定义的查询函数,输入输出都做严格校验。
2.6 输出管线:结构化输出与可观测性
模型输出的原始内容是一个token序列,直接扔给前端肯定不行——延迟高、难以排版、也不利于后续流程处理。所以架构图上需要在模型输出和最终响应之间加一根"输出管线"。
输出管线做的事情有:流式响应转发、结构化字段解析、内容安全过滤、格式模板拼接。以结构化输出为例,很多业务场景需要模型返回JSON格式的结果,但模型偶尔会返回带markdown代码块包裹的JSON、甚至夹带解释性文字。架构上要前置一个解析组件,出问题就触发自动重试或格式修正。
可观测性是输出管线里我认为价值最高也最容易被漏掉的模块。要追踪一次完整的AI请求链路,需要三类日志:请求日志(用户输入、参数、模型配置)、轨迹日志(Agent的每一步决策、调用了哪个工具、检索了什么文档)、性能指标(首token延迟、总耗时、token消耗量)。我建议用OpenTelemetry来串联这三类日志,给每个请求分配一个traceId,全链路一把梭。Debug模型"答非所问"的问题时,这个traceId能帮你省下好几个小时。
3. 部署与运维:从本机调试到稳定上线
3.1 环境搭建与本地开发体验
AI应用开发和传统后端开发有一个显著差异:本机开发需要能连上真实的模型服务。架构设计阶段就要想清楚,开发环境、测试环境、生产环境的模型接入方式是什么。
常见做法分三类:
- 全部走云端API:开发最省事,但模型输出结果和生产环境可能有差异(因为模型版本迭代)
- 本地起一个小模型供开发调试:隔离性好,响应快,但小模型能力不足,容易漏掉Prompt相关的问题
- 生产环境走云端API,开发环境走本地小模型,两套Prompt配置共存:用环境变量切换
我目前在实际项目中用的是第三种。大模型的选择和业务强相关,开发环境如果能用和线上一致的API服务,很多问题可以提前暴露。但本地也要留一个模型兜底,方便离线开发和语法调试,两边用同一个网关层,切换成本就低。
3.2 资源规划与容器化部署
资源规划的核心是估算单请求的GPU消耗。以7B参数模型为例,加载FP16权重大约需要14GB显存(7B乘2字节),实际运行还要算上KV Cache和中间激活值,单并发情况下20GB出头的显存比较稳。如果用4-bit量化,可以压到6GB以下,但推理质量会有轻微下降。
算清单请求消耗之后,再结合QPS预估总显存需求。我一般用这个粗粒度公式:
单实例支持并发 = 可用显存 / (权重显存 / 最大并发数 + 单请求激活显存)
这个计算不用非常精确,目的是快速得出"到底需要几张卡"的结论。比如你有两张24GB的卡,跑7B模型量化版,单卡能扛住5~8个并发,整体扛15个左右的并发请求问题不大。如果并发需求更高,优先加副本而不是加卡,再用负载均衡把流量分散到多个副本上。
容器化层面,我建议模型服务和业务服务一定分开部署。模型服务的扩缩容节奏和业务服务完全不一样:模型推理服务吃显存、启动时间长,业务服务吃CPU、可以秒级扩容。混在一起部署,凡是业务流量一波动,模型服务跟着重启,那个酸爽谁试谁知道。
3.3 性能优化:延迟与吞吐的权衡
性能优化的第一步是测量,测量两个指标:首token延迟(TTFT)和生成token速率(TPS)。一句话总结:首token延迟取决于预填充的速度,生成速率取决于解码阶段的速度。
架构设计阶段能做的优化手段包括:
- 流式输出:让用户先看到前面的token,感知延迟大降
- 缓存复用:命中缓存的请求跳过模型调用,直接返回结果
- 并发批处理:多个请求在推理引擎内部合并成一个批次,提高GPU利用率
- 语义缓存:对用户的相似问题做向量匹配,命中就直接返回答案,适合高频重复的客服问答场景
- 早期终止:设置了max_tokens范围,模型输出达到某个置信度阈值就提前结束生成
我踩过的坑是"盲目调并发导致超时成片"。一开始把单实例并发数调到16,结果服务端排队时间暴涨,P95延迟从2秒飙到8秒。后来把并发数降回8,配合请求队列限流,P95延迟反而降低了。这里的原则是:不要榨干单实例性能,留出20%的冗余给突发流量。
3.4 成本控制:Token消耗的两大杀手
AI应用的成本大头在于token。架构层面有两个控制成本的关键点:一是上下文管理,二是缓存策略。
上下文管理多啰嗦两句。很多初版的Agent实现里,每轮对话都把完整的历史记录塞给模型,结果用户聊了20轮之后,一次请求就要消耗五万token。我在架构设计里加了一个"对话压缩器":每轮对话结束后做一次token计数,超过阈值就触发摘要压缩,只保留最近三轮的原始上下文,其余全部压缩成结构化要点。
缓存策略要区分两层。第一层是结果缓存,完全相同的问题在缓存有效期内直接返回历史答案。第二层是组件缓存,RAG检索到的文档内容、工具调用结果这类中间产物,如果相关内容重复使用,可以像传统后端一样做缓存。这两层缓存叠加,实际项目中能把token成本压缩30%以上。
4. 常见问题与排查技巧实录
4.1 响应慢得离谱
最常见的原因不是模型推理慢,而是请求链路上多了意外环节。我排查过一次客户反馈的"响应要四十秒"问题,追踪发现请求从网关到编排服务再到模型网关竟然经历了八次序列化和反序列化。
排查建议是:画一遍实际链路图,对照设计架构图找出入点。重点排查三类问题:不必要的串行调用(并行调用可以省一半时间)、频繁的JSON序列化开销、模型网关层的排队等待。
4.2 上下文越滚越大导致出错率飙升
这个问题有两个典型症状:一是用户聊到后面模型开始"失忆",二是单次请求token数接近模型上限直接报错。
架构层面的解决思路在前面提到过:对话压缩器加滑动窗口。但有一个补充细节值得说——压缩策略要在请求侧设置兜底。当上下文压缩后仍然超过模型上限时,服务应该主动报错并建议用户开启新对话,而不是硬截断上下文导致模型回答质量直线下降。
4.3 工具调用的参数满天飞
模型生成的工具调用参数经常是幻觉的重灾区。比如用户问"帮我查一下张三的订单",模型生成的参数里getsUserId字段可能真的是"张三"而不是用户ID。
我的经验是三层防护:第一层在设计工具时尽量少用需要模型凭空生成的ID类参数,改成服务端根据用户身份自动注入;第二层是参数Schema要写清严格的枚举和格式约束,让模型生成时就有"边界感";第三层是工具执行前必须做程序化校验,不合法就拒绝执行并把错误信息返回给模型进行修正。
4.4 并发一上来就预算爆炸
流量大了之后最头疼的不是宕机,是账单超支。两个用户疯狂刷屏,token消耗量指数级上升,月底一算成本吓死人。
架构防御措施是预算配额(quota)系统。每个用户、每个租户、每个App分别设定token消耗限额和QPS限额。超限之后可以降级为普通基础模型的响应,或者限流返回错误码。配额系统最好在接入层就做好前置检查,别等请求打到模型调用层再拦,那样成本已经发生了。
4.5 调试Agent像在黑盒里猜
Agent一次决策链路可能涉及模型多次推理、多次工具调用,每次推理的结果都不一样(有随机性)。线上出问题,想靠复现来Debug基本不可能。
我在架构里给Agent系统加了"决策日志开关":Agent推理时记录下每轮的原始Prompt、模型输出原文(不光是最终结果)、工具调用的输入输出、token消耗量。线上收到用户反馈"回答不对",直接按traceId拉出Log,看模型在哪个步骤理解偏了。算是我做AI应用架构设计这半年认为最值得投资的功能之一。
5. 写在架构图之外的话
从做第一个Demo到稳定支撑线上流量,我体会到AI应用架构设计和传统后台系统最大的不同:传统系统里每个模块的行为是可预期的,AI应用每个环节都带着概率性。架构设计的核心目标,就是把这些概率性约束在可控范围内——模型输出不稳定,就用校验和重试兜底;工具调用容易幻觉,就在接口层收窄边界;上下文会膨胀,就用压缩和滑动窗口管理。
我个人的实操习惯是,每迭代一个版本就重新画一遍架构图,标注出每个模块的"确定性等级":哪些环节是确定性的(代码逻辑),哪些环节是概率性的(模型推理)。然后优先给概率性的环节加防护措施。这种分析方式帮我省掉了大量线上救火的时间。
另外一个私藏的小技巧是,给架构图上的每个模块都标上"如果不做会怎么样"。上下文管理不做,对话超过十轮就崩;工具权限边界不做,Prompt注入的漏洞就一直在;可观测性不做,线上出问题只能靠猜。这正是"图解"的意义所在——一张图画清楚系统全貌,很多时候问题在画图的那一刻就已经暴露了一半。