从零搭建AI工程能力:分层架构、模型抽象与成本控制实战
1. 从零搭建AI工程能力为什么我劝你别一上来就调包这两年AI应用开发的门槛肉眼可见地降低了随便拉个框架、调个API就能跑出一个能对话的Demo。但我带过的新人里十个有八个卡在同一个地方Demo跑通了一上真实业务就崩。要么是响应慢得离谱要么是成本失控要么是数据一多就各种诡异报错。问题的根子不在模型本身而在于大多数人跳过了“AI工程”这一层直接站在了应用层。ai-engineering-from-scratch这个标题说的就是把这层被跳过的能力补回来。它不是教你从零训练一个大模型那既不现实也没必要它讲的是从零构建一套能支撑AI应用稳定运行的工程体系——数据怎么流转、推理怎么调度、成本怎么控制、效果怎么评估、线上怎么排障。这套东西才是把“能跑的Demo”变成“能赚钱的产品”之间的那道坎。这篇文章适合三类人一是刚转行做AI应用、只会调API的开发者二是有后端经验但没接触过AI系统、想补齐工程认知的工程师三是带团队做AI产品、需要一套可落地工程规范的技术负责人。我会按真实项目里的推进顺序把每个环节的选型逻辑、参数计算、踩坑经验都摊开讲你能直接抄作业也能理解每一步为什么这么做。2. 整体架构设计先想清楚数据怎么流再动手写代码2.1 为什么“先跑通再说”是最大的坑我见过太多项目第一周就把模型接进业务代码第二周开始加功能第三周发现要换模型结果发现模型调用散落在几十个文件里改一处漏三处。这就是典型的没有架构设计直接开干。AI工程和传统后端最大的区别在于模型是不稳定的。同一个输入不同版本、不同参数、不同负载下输出可能完全不同。传统后端你写个函数输入A永远输出BAI系统里输入A可能输出B、C、D取决于温度参数、上下文长度、甚至GPU的显存碎片。所以架构设计的第一原则是把模型当成一个“不可靠的外部依赖”来对待。就像你不会把数据库查询逻辑散落在业务代码里一样模型调用也必须收敛到统一的抽象层。这个抽象层要负责请求格式化、参数注入、重试与降级、结果解析、日志记录、成本统计。业务代码只跟这个抽象层打交道换模型、调参数、加缓存都只改这一层。2.2 分层架构的四个核心模块我推荐的分层是这样的从上到下依次是接入层处理用户请求、鉴权、限流、请求路由。这一层不碰模型只做流量治理。编排层决定一次请求要经过哪些步骤。比如先查缓存、再检索知识库、再调模型、最后做后处理。这一层是业务逻辑的核心但依然不直接调模型。模型抽象层统一封装所有模型调用。不管是本地部署的还是远程API都通过同一套接口访问。这一层负责重试、降级、超时控制、成本记录。基础设施层向量数据库、缓存、消息队列、监控告警。这些是支撑上面三层跑起来的地基。这么分的好处是每一层可以独立演进。比如你想从远程API切换到本地部署的小模型只改模型抽象层的实现编排层和接入层完全不用动。再比如你想加一个“先查缓存”的逻辑只在编排层加一个步骤不影响其他部分。2.3 一个具体的请求生命周期拿一个典型的“智能客服问答”场景来说一次请求的完整路径是这样的用户提问进入接入层鉴权通过后生成一个请求ID写入日志。编排层收到请求先拿问题去缓存里查有没有现成答案。缓存key是问题的语义哈希不是字符串精确匹配。缓存没命中编排层调用检索模块从向量数据库里找出最相关的几段知识。编排层把问题、检索结果、系统提示词组装成一个完整的prompt交给模型抽象层。模型抽象层根据当前配置选择模型比如优先用便宜的小模型失败再切大模型发起调用记录耗时和token消耗。模型返回结果编排层做后处理比如格式化、敏感词过滤再写回缓存。结果返回给用户同时把这次请求的完整链路信息写入监控系统。这个流程里每一步都有工程决策。比如缓存为什么用语义哈希而不是字符串匹配因为用户问“怎么退款”和“退款流程是什么”字符串不同但语义相同字符串匹配命中率极低。语义哈希需要把问题先转成向量再算相似度这又涉及到向量模型的选择和阈值设定。这些细节后面会逐个展开。3. 模型抽象层把不稳定的模型关进笼子里3.1 统一接口设计的三个关键决策模型抽象层的核心是定义一套统一的接口让上层不用关心底层是哪个模型。这套接口至少要包含这几个方法class ModelProvider: def generate(self, prompt: str, **kwargs) - ModelResponse: 生成文本返回统一格式的响应 pass def generate_stream(self, prompt: str, **kwargs) - Iterator[str]: 流式生成用于需要逐字返回的场景 pass def count_tokens(self, text: str) - int: 计算token数用于成本预估和上下文管理 pass def health_check(self) - bool: 健康检查用于故障转移 pass这里有几个设计决策值得展开。第一为什么要有count_tokens因为成本控制和上下文窗口管理都依赖它。不同模型的token计算方式不同必须在抽象层统一。第二为什么要有health_check因为线上模型服务可能因为各种原因不可用抽象层需要能感知并自动切换。第三generate_stream和generate分开是因为流式和非流式的错误处理、超时逻辑完全不同混在一起会很难维护。3.2 重试与降级的参数计算模型调用失败是常态不是异常。网络抖动、限流、服务过载都会导致失败。重试策略不能拍脑袋定要有计算依据。假设单次调用成功率是99%那么连续失败3次的概率是0.01³0.000001也就是百万分之一。但重试会增加延迟如果单次调用平均耗时2秒重试3次最坏情况就是6秒。所以重试次数和超时时间要配合设定单次超时根据P99延迟设定比如P99是3秒超时设5秒。最大重试次数2次总共3次尝试覆盖绝大多数瞬时故障。重试间隔指数退避第一次等0.5秒第二次等1秒避免加重服务端压力。总超时单次超时×最大尝试次数退避时间总和比如5×31.516.5秒超过这个时间直接降级。降级策略也要提前设计。比如主模型不可用时切到备用模型备用模型也不可用时返回缓存中的相似答案连缓存都没有返回一个友好的兜底话术。降级不是失败是保证系统始终有响应。3.3 成本控制的实操方法成本失控是AI应用最常见的死法。我见过一个项目上线第一周账单就超了预算的十倍原因是没有做token限制用户输入超长文本直接透传给模型。成本控制要在抽象层做三件事第一输入长度截断。每个模型都有上下文窗口限制比如8K、32K、128K。但你不能等到超了才截断要在调用前就检查。截断策略不是简单砍掉尾部而是优先保留系统提示词和最近几轮对话中间的历史对话做摘要压缩。第二输出长度限制。通过max_tokens参数强制限制输出长度。这个值根据业务场景定比如客服问答一般200token足够内容生成可能需要1000token。设太大浪费钱设太小回答不完整。第三成本实时统计。每次调用后记录token消耗和对应费用按请求ID、用户ID、业务模块三个维度聚合。这样你能清楚知道钱花在哪里哪个功能最烧钱哪个用户最费token。提示成本统计不要只记总数要记明细。我吃过亏月底发现账单暴涨但因为没有按模块拆分查了一天才定位到是一个测试接口忘了关。4. 数据流转与检索增强让模型用上你的私有数据4.1 为什么检索增强是AI工程的必修课模型本身的知识是训练时固化的它不知道你公司的产品文档、不知道昨天的会议纪要、不知道刚上线的功能。检索增强生成RAG就是解决这个问题的标准方案先把相关知识检索出来再连同问题一起交给模型生成答案。但RAG不是“把文档塞进向量库就完事”。我见过太多RAG项目效果差问题都出在数据流转的细节上。文档怎么切分、向量怎么生成、检索怎么排序、结果怎么组装每一步都有讲究。4.2 文档切分的参数选择文档切分是RAG的第一步也是最容易被忽视的一步。切分粒度太粗检索出来的内容包含大量无关信息浪费token还干扰模型切分太细单段信息不完整模型拼不出完整答案。我的经验值是中文文档按300-500字切分英文按200-300词切分。这个范围是基于两个考虑一是大多数嵌入模型的最佳输入长度在256-512token之间超出部分会被截断二是模型生成答案时3-5段相关内容的token总量在1500-2500之间加上问题和系统提示词刚好在常见模型的舒适区内。切分时还要注意重叠。相邻两段之间保留10%-20%的重叠内容避免一个完整句子被切断后两段都丢失关键信息。比如一段500字的文档切分成两段各300字中间重叠100字。def split_document(text, chunk_size400, overlap80): chunks [] start 0 while start len(text): end start chunk_size chunk text[start:end] chunks.append(chunk) start end - overlap return chunks这个简单的函数就是切分的核心逻辑。实际项目中还要处理标题、列表、表格等结构化内容但基本原理不变。4.3 向量检索的阈值设定向量检索返回的是相似度分数你需要设定一个阈值来决定哪些结果算“相关”。阈值设太高召回不足模型没有足够信息设太低引入噪声模型被误导。阈值不能拍脑袋要用真实数据测。方法是准备一批问题和对应的标准答案对每个问题检索出top-10结果人工标注哪些是真正相关的。然后看相关结果和不相关结果的分数分布取一个能最大化区分两者的值。通常这个值在0.7-0.85之间余弦相似度。如果检索结果分数普遍偏低说明嵌入模型不适合你的领域需要换模型或者做微调。如果分数普遍偏高但质量差说明文档切分有问题需要调整切分策略。4.4 检索结果的组装策略检索出相关文档后怎么组装进prompt也有讲究。我的做法是按相关性排序取top-3到top-5每段前面加一个来源标记比如[文档1]、[文档2]。这样模型在生成答案时可以引用来源也方便后续做溯源。组装格式大致如下你是一个客服助手请根据以下参考资料回答用户问题。 如果参考资料中没有相关信息请如实告知不要编造。 参考资料 [文档1] 退款流程用户可以在订单页面点击“申请退款”... [文档2] 退款时效退款申请提交后1-3个工作日到账... 用户问题退款要多久才能到账这个格式的关键是明确告诉模型“参考资料是什么”和“没有相关信息时怎么办”。很多RAG项目效果差就是因为没有明确这两点模型要么忽略参考资料自己编要么强行从无关资料里找答案。5. 效果评估与线上排障没有度量就没有优化5.1 离线评估的四个核心指标AI应用的效果评估比传统软件难因为输出是自然语言没有精确的“对错”。但也不是完全没法量化我常用的四个指标是相关性回答是否切题。用另一个模型来打分1-5分取平均。忠实度回答是否基于给定资料没有编造。同样用模型打分。完整性回答是否覆盖了问题的所有方面。人工抽样评估。格式合规率回答是否符合预期格式比如JSON、特定字段。这个可以程序自动检查。这四个指标里忠实度最重要。一个回答再流畅、再完整如果是编造的就是有害的。我通常把忠实度低于4分的case全部拉出来人工复核找出是检索问题还是生成问题。5.2 线上监控的关键指标离线评估通过不代表线上没问题。线上环境有真实用户的千奇百怪的输入有并发压力有网络波动。必须监控这些指标指标含义告警阈值P99延迟99%请求的响应时间超过5秒错误率调用失败的比例超过1%降级率触发降级的比例超过5%平均token消耗每次请求的token数突增50%缓存命中率缓存命中的比例低于30%这些指标要按分钟粒度采集按小时聚合。突增突降都要告警因为往往意味着上游出了问题。5.3 常见问题排查速查表线上出问题时快速定位比什么都重要。我整理了一份速查表覆盖了80%的常见故障现象可能原因排查方法解决方案响应突然变慢模型服务过载看模型端P99延迟切备用模型或限流回答质量下降检索结果变差看检索分数分布检查向量库是否更新成本突增输入变长看token消耗分布加输入截断错误率上升网络抖动看错误类型分布加重试或切线路缓存命中率下降问题分布变化看缓存key分布调整缓存策略这张表是我踩了无数坑总结出来的基本覆盖了日常运维会遇到的问题。建议打印出来贴在工位上。5.4 一个真实的排障案例有一次线上突然大量用户反馈“回答不相关”。我先看监控发现检索分数正常模型调用正常延迟正常。然后抽样看了几条具体请求发现检索出来的文档确实相关但模型生成时完全忽略了文档内容。进一步排查发现当天上午有人更新了系统提示词把“请根据参考资料回答”改成了“请回答用户问题”。少了“根据参考资料”这几个字模型就放飞自我了。改回来之后问题立刻消失。这个案例的教训是提示词的任何改动都要走变更流程不能随手改。提示词就是AI应用的代码改代码要测试改提示词也要测试。6. 从零到一的推进节奏我的实操时间线6.1 第一周搭骨架不碰模型第一周的目标是把分层架构搭起来用mock数据跑通全流程。接入层、编排层、模型抽象层、基础设施层每层都写最简实现。模型抽象层先用一个假的provider固定返回“这是测试回答”。这一周不碰真实模型是为了把工程骨架先立住。很多人反过来先接模型跑通再补架构结果补架构时发现到处都要改成本极高。先搭骨架再填肉是更稳妥的路径。6.2 第二周接模型跑通单条链路第二周接入真实模型但只跑通一条最简单的链路用户提问→直接调模型→返回结果。不加检索、不加缓存、不加降级。目的是验证模型抽象层的接口设计是否合理重试、超时、日志是否正常工作。这一周会暴露很多接口设计问题。比如你发现generate方法需要支持传入图片但接口里没有这个参数或者你发现流式和非流式的错误处理逻辑差异太大需要拆成两个方法。这些问题在只有一条链路时改起来很快等链路多了再改就麻烦了。6.3 第三周加检索调效果第三周加入检索增强。先把文档灌进向量库跑通检索→组装→生成的完整链路。然后开始调效果调整切分粒度、调整检索阈值、调整组装格式。这一周要反复做离线评估用真实问题测试看相关性、忠实度、完整性三个指标的变化。调效果是个体力活没有捷径。我的经验是每次只改一个变量改完测一批问题记录指标变化。同时改多个变量你根本不知道是哪个起了作用。6.4 第四周加缓存和降级准备上线第四周加入缓存和降级做压力测试。缓存要测命中率和一致性降级要测触发条件和恢复逻辑。压力测试用真实流量的1.5倍打看P99延迟、错误率、降级率是否在可接受范围内。这一周还要把监控告警配好。没有监控就上线等于闭着眼睛开车。至少要有延迟、错误率、成本三个维度的告警。6.5 上线后的持续迭代上线不是终点是起点。上线后每周看一次效果指标和成本报表每月做一次全量离线评估。发现效果下降就排查原因发现成本上升就优化调用策略。AI应用的效果会随着用户输入分布的变化而漂移不持续迭代就会慢慢变差。提示上线第一个月一定要每天看监控。我见过太多项目上线第一周没人管第二周发现账单爆了、效果崩了再救就来不及了。7. 几个我踩过的坑和对应的解法7.1 坑一向量库更新导致检索结果突变有一次我们更新了产品文档重新灌入向量库。结果更新后检索效果突然变差很多之前能答对的问题答错了。排查发现新文档的切分方式和旧文档不一致导致向量分布变了原来的检索阈值不再适用。解法向量库更新必须走完整流程——切分、嵌入、灌库、评估。评估不通过不能上线。而且新旧文档的切分策略要一致不能这次按300字切下次按500字切。7.2 坑二缓存key设计不当导致答案错乱早期我们用问题的MD5作为缓存key结果发现“怎么退款”和“如何退款”是两个不同的key缓存命中率极低。后来改成语义哈希命中率上去了但出现了新问题语义相近但意图相反的问题比如“怎么开通”和“怎么关闭”被当成同一个key返回了错误答案。解法语义哈希的相似度阈值要设得足够高比如0.95以上才认为是同一个问题。同时缓存里要存原始问题命中后做一次二次确认确保意图一致。7.3 坑三降级策略过于激进导致用户困惑有一次主模型服务抖动降级策略触发所有请求都返回了缓存中的“相似答案”。但很多问题在缓存里没有足够相似的答案返回的内容答非所问。用户以为系统坏了投诉量暴涨。解法降级要分级。一级降级切备用模型二级降级返回缓存答案但加提示“当前为缓存结果可能不准确”三级降级才返回兜底话术。不要一上来就返回兜底那等于直接告诉用户“我坏了”。7.4 坑四提示词版本管理缺失导致回滚困难前面提到的提示词改动导致效果下降的案例根本原因是提示词没有版本管理。改了就改了没有记录出问题不知道改了什么也没法回滚。解法提示词必须纳入版本控制每次改动记录改动人、改动内容、改动原因、评估结果。上线前用离线评估跑一遍通过才发布。发布后监控效果指标下降就回滚。8. 写给想认真做AI工程的人ai-engineering-from-scratch这个方向核心不是学某个框架或某个模型而是建立一套工程思维把模型当不可靠依赖、把数据流当一等公民、把评估当持续过程、把成本当硬约束。这套思维建立起来之后具体用什么框架、什么模型都是可以替换的细节。我个人的体会是AI工程最难的不是技术是克制。克制住“先跑通再说”的冲动克制住“加个功能试试”的欲望克制住“提示词随手改改”的随意。把工程规范立起来把评估闭环建起来把监控告警配起来剩下的就是持续迭代。这个过程不性感但能让你在别人Demo崩掉的时候系统还稳稳跑着。最后分享一个我一直在用的小技巧每次上线新功能前先问自己三个问题——效果怎么度量成本怎么控制出问题怎么回滚三个问题都有答案了再动手。这三个问题帮我省下了至少三次重大事故。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →