1. 项目概述:为什么我从零开始搭建AI工程链路
我是在一次内部工具开发中意识到这个问题的。团队里所有人都能跑通大模型API、都能写出一段还不错的Prompt,但一旦涉及“这个功能能不能上线”“效果怎么评估”“模型换版本了会不会崩”,整个讨论就开始含糊。市面上不缺AI教程,缺的是从零到一、按工程标准把AI应用搭起来的方法论。这也是我做“ai-engineering-from-scratch”这个项目的初衷——不依赖任何封装好的低代码平台,不跳过任何一个关键环节,用最朴素的工程手段,把一套完整的大模型应用从零搭出来。
这个项目能解决什么问题?它解决的是很多人“只会调接口、不会做系统”的尴尬。举个具体场景:你想做一个智能客服,不只需要接一个大模型的API,还需要考虑知识库怎么切分、用户问题怎么改写、模型回答怎么裁剪、超时和成本怎么控制、效果怎么回归验证。这些全都是工程问题,而不是模型问题。项目从需求分析、技术方案选型、数据准备、提示词设计、评估体系、部署监控一路走下来,覆盖的正是这条完整链路。
适合谁来看?两类人最适合。一类是刚接触大模型应用开发、想建立正确工程观的技术人员,你可以照着项目把流程跑一遍,形成自己的框架;另一类是已经在做AI应用但总感觉“差一口气”的人,你缺的往往不是模型能力,而是工程化手段,这个项目里踩坑和补课的记录正好能对上你的困惑。
我选择“from scratch”这个角度,还有个深层原因:封装好的框架确实快,但会让你失去对细节的掌控感。你用完LangChain,可能依然说不清Chain内部到底做了什么、为什么会失败。自己从零写一遍,哪怕只实现最基础的功能,你对整个系统的理解都会完全不一样。这个认知差异,在真正的生产环境中会直接体现在你排障的速度和方案设计的质量上。
2. 核心思路拆解:AI工程不是堆模型,而是做系统
2.1 先搞清楚AI工程和传统软件工程的差异
传统软件工程的输入输出是确定的,一个函数传进去什么参数就返回什么结果,逻辑可以被测试用例完整覆盖。而AI应用的核心推理环节是一个概率系统,同样的输入可能产生不同的输出,甚至可能产生错误输出但表现形式非常“像样”。这个本质差异决定了你没法用传统的测试方式来验证AI应用。
我现在做项目规划时,会把AI系统拆成三个层次来看:
- 基础设施层:模型API、向量数据库、缓存、对象存储,这些是相对确定的组件;
- 编排层:提示词模板、工具调用、上下文管理、知识库检索,这层开始出现不确定性;
- 交互层:用户输入、流式输出、反馈收集、人工介入,这层的不确定性最大。
每一层都要有独立的容错策略。比如基础设施层要考虑模型服务不可用时的降级方案;编排层要考虑检索结果为空时怎么回复;交互层要考虑用户连续追问时怎么维护上下文状态。传统软件工程关注的是“逻辑正确性”,AI工程还得额外关注“输出安全性”“成本合理性”“体验一致性”。
2.2 技术选型背后的决策逻辑
这个项目里我做的第一个重要选型决策是:不直接使用重型编排框架。很多人在项目一开始就引入LangChain或LlamaIndex,我建议你先hold住。
不用的原因不是这些框架不好,而是它们把太多细节藏起来了。你拿LangChain搭一个RAG链路,可能二十分钟就搞定,但之后出了问题你很难定位是Embedding的问题、还是向量检索参数的问题、还是Prompt拼接的问题。我见过太多团队把时间浪费在“框架行为猜测”上,而不是花在“业务效果优化”上。
我的做法是用原生代码做一层薄的封装。核心链路就几个函数:文档加载、文本切分、向量化、检索、拼接上下文、调用模型、解析输出。每个函数都可以单独测试、单独调优、单独加日志。等你把这条路跑通了,再去对比LangChain的实现,你会瞬间看懂它替你做了什么,也更容易判断什么时候该用框架、什么时候该自己写。
模型选型方面,我遵循的原则是“任务复杂度匹配模型规模”。能做分类的就不做生成,能用小模型解决的就不上大模型。比如用户意图识别,如果类别有限,我倾向用embedding加规则匹配,而不是每次都调用一个千人规模的模型去“理解”。延迟和成本在生产环境中是实打实的指标,不能只盯着效果看。
2.3 从需求到AI方案的路径规划
很多人拿到需求就直接开始写Prompt,这是最大的误区。我现在的流程是四步走:
第一步,把需求翻译成“输入-输出-约束”三元组。输入是什么格式、可能有哪些变化?输出是什么结构、给谁看?约束有哪些——实时性要求、成本上限、合规要求?
第二步,判断这个问题是否真的需要大模型。如果传统规则能覆盖80%的场景,就先做一套兜底规则,再用大模型处理剩下的长尾。这个思路在真实项目中非常实用,因为规则系统稳定且便宜,大模型处理的是规则覆盖不了的灵活场景。
第三步,定义评估指标。这一步必须在写代码之前做,而不是之后补。我会先把“好回答”和“坏回答”的样子定义出来,用一批真实样例固定下来,作为后续迭代的基准。
第四步,才是方案设计和编码。如果你前三步做扎实了,到这一步通常是水到渠成的事。
这个路径帮我避过很多坑。最典型的是:需求方一开始说“做一个智能问答”,你如果不往深了问“答错了会怎样”“用户问的问题大概在什么范围”“回答的格式有没有要求”,你做出什么他们都可能不满意。需求澄清的过程,在AI项目里比在传统项目里更重要,因为模型可调的方向太多了,方向不对的努力全白费。
3. 核心环节实操:RAG链路、提示词工程与Agent设计
3.1 知识库构建:切分策略决定检索质量
RAG(检索增强生成)是当前AI工程中最常用也最容易被做砸的模式。我见过不少团队,向量数据库换上最贵的、Embedding模型也选最新的,但回答效果依然不理想。问题往往出在文本切分这个最不起眼的环节。
文本切分的目标不是“切成固定长度”,而是“切出语义完整的单元”。我用过固定chunk_size=500的切法,结果一句话被从中间截断,检索出来了也看不懂。后来改成按段落边界加滑动窗口的混合策略,情况好了很多。
我目前在项目中用的切分规则是:
- 优先按标题结构切(Markdown的##、###,HTML的h1-h3),保留标题信息作为上下文;
- 段落过长时再按句子边界二次切分,单块大小控制在300到800字之间;
- 相邻块之间保留20%的重叠,避免关键信息正好落在切缝上;
- 每块都附带来源信息、标题路径,便于回答时引用。
这套策略我用一万多字的内部文档测过,检索出来的内容相关性明显提升。切分这个活看起来简单,其实值得反复调。你如果拿PDF测试,还要考虑表格、页眉页脚、多栏布局的影响,往往需要单独做解析和清洗。
向量化也是一步讲究的工程。选择Embedding模型时,我建议拿你自己领域的语料做个小规模检索测试,看结果在top-k中的命中率,不要只看模型榜单上的分数。榜单分数高不代表在你的领域里表现就好,尤其是专业术语多、表达方式独特的场景。我踩过的坑是:用通用模型向量化医疗术语文档,检索“空腹血糖”完全匹配不上“FPG”这种缩写表达,后来加了同义词扩展才解决。
3.2 提示词设计:结构化输出的工程化技巧
提示词工程在这个项目里占了很重要的篇幅,因为它是你与模型交互的主要界面。我总结了一套自己的写法,核心思路是:把提示词当成接口协议来设计,而不是当成聊天文案来写。
具体来说,一个生产级提示词至少包含五个部分:
- 角色与目标:模型以什么身份、完成什么目标;
- 输入数据说明:输入是什么结构、可能有哪些边界情况;
- 输出格式约束:JSON结构、字段说明、枚举值范围;
- 限制条件:什么不许做、超纲怎么回应;
- 示例:少量高质量示例,尤其是反例。
我写过一个客服场景的提示词,输出要求是严格的JSON。如果没有格式约束,模型偶尔会输出纯文本或者Markdown,下游解析就会崩。加了JSON Schema描述和示例之后,解析成功率从91%提到了接近100%。注意这里不是要求模型“严格遵守”,而是你需要在提示词里把结构描述得足够清楚,还要在代码层做二次校验和修复。
另外一个关键工程手段是给模型“不知道”的权利。很多Prompt写得不好,会强迫模型在不确定时强行编造。我在实际项目中会明确加上一句“如果你不确定,请直接说明不知道,不要猜测”。这句话看起来简单,却能大幅度降低幻觉问题。配合知识库检索的置信度判断,我做过一个测试,回答准确率从78%提升到了86%。
提示词迭代要遵循“一次只改一个变量”的原则。我见过有人一次改了角色描述、输出格式、示例、限制条件四个地方,效果差了却不知道是哪个改动导致的。正确的做法是用版本号管理提示词,每次只动一个维度,用固定的测试集跑效果对比。这个习惯养成之后,提示词优化就不再是“玄学”,而是可追踪、可回退的工程过程。
3.3 Agent设计:工具调用的状态机思维
Agent(智能体)是AI工程中更有挑战性的一块,因为它从“单次问答”扩展到了“多步任务执行”。我设计Agent时,最核心的理念是:不要让模型自由发挥,而是给它铺一条轨道。
我实现了一个轻量级Agent框架,核心组件包括:
- 任务规划器:接收用户请求,拆解为子任务列表;
- 工具注册表:维护可用工具的描述、入参结构、调用方式;
- 执行引擎:按规划器输出逐步执行,每步把结果反馈给模型;
- 状态管理器:维护上下文历史、中间结果、重试次数。
最容易出问题的是“让模型自己决定下一步做什么”这个环节。模型可能会调用一个不存在的工具名、传错参数类型、或者在已经拿到答案之后还在循环调用工具。我的解法是在工具调用环节加一层校验器:模型输出的工具调用先过一遍JSON Schema校验,不合法就反馈错误信息让模型重新生成;执行结果也会被截断和格式化后再交回模型上下文。
这里我强烈建议用状态机的思维来管理Agent生命周期。我把Agent的状态定义为:初始化、规划中、执行中、等待用户输入、完成、失败。每个状态都有明确的进入条件和退出条件,有全局超时控制,有最大执行步数限制。这样即使模型行为不可控,整个流程还是在工程可控的范围之内。
工具调用测试要格外用心。我在项目中给每个工具都做了单独的模拟器和真实的沙箱环境,先离线测通再去真实环境跑。工具越复杂(比如改数据库、发邮件),越要警惕不可逆操作。我的经验是:在生产环境禁用具有写权限的工具,或者至少增加确认机制。
3.4 上下文管理:窗口有限,但信息要够
大模型的上下文窗口虽然是越来越大了,但“能容纳”和“用得对”是两回事。上下文窗口中每加一段信息,模型的处理时间、成本、注意力分散风险都在增加。上下文管理的核心是:把对当前任务最有用的信息放在最重要的位置,把不相关的信息果断丢掉。
我实现了一个分层的上下文管理策略:
- 短时记忆:当前轮次的对话历史和中间结果,始终保留;
- 工作记忆:与当前任务直接相关的检索结果、工具输出,有容量上限;
- 长时记忆:用户画像、历史偏好、领域知识,按需摘要加载;
- 总结记忆:当对话历史超过阈值时,用模型做摘要压缩。
用这种分层方式,我测试过一个多轮对话场景:用户连续修改需求五次,前四次的细节不可能全部放进窗口,但通过逐步摘要,Agent依然能准确记住用户的最终意图,不会因为早期信息的丢弃而出错。做这个功能要舍得花摘要的API调用费用,它比你配置更大的上下文窗口便宜得多。
4. 效果评估与性能调优:拿数据说话,不靠感觉
4.1 建立离线评估集:AI工程的“单元测试”
在传统软件开发中,没有单元测试你不敢重构;在AI工程中,没有评估集你不敢改Prompt、不敢换模型、不敢动检索参数。我建评估集的原则是“真实优先、反馈闭环”。
我从用户的真实日志中抽样,覆盖以下类型:
- 常见问题(高频问题,必须是多数)
- 边界问题(空输入、超长输入、多语言混杂)
- 困难问题(需要多步推理、跨文档查询)
- 拒答问题(不该回答的内容,测试模型是否会越界)
每个样本要标注标准答案和关键评分点。评分不只用“对/错”,而是多维度的:正确性(信息是否准确)、完整性(是否漏了要点)、相关性(是否答非所问)、格式合规性(是否是要求的输出结构)、安全性(是否产生不当内容)。
评估的方式可以用模型打分,也可以用人工评审,我建议两者结合:模型打分做日常回归,每周抽一批做人工盲评。盲评很关键,因为模型打分偏好和人类真实感受有偏差,你不定期校准就会跑偏。
4.2 在线监控指标:准确率之外的三座大山
到了线上环境,单纯看回答质量已经不够,你需要一套可量化的监控体系。除了常规的响应时延和错误率,我在项目中重点监控以下三类指标:
第一是成本指标。大模型应用的成本是可变的,随用户输入长度、输出长度、检索次数波动。我按“每千次会话成本”来做预算管理,这个指标能直观反映你的系统是否被恶意刷量、是否某些用户的请求路径异常复杂。
第二是空回答率与兜底率。系统有多少比例的请求没找到知识库内容?有多少比例的回复是“我不确定”?这些指标上升通常意味着知识库覆盖不足或检索质量下降,需要及时补充资料。
第三是用户反馈数据。线上要埋点收集“赞/踩”和“追问/离开”行为。用户如果追问,往往代表第一轮回答没能解决他的问题,这是比显式差评更丰富的信号。把这些信号回填到评估集里,就形成了一个持续优化的闭环。
4.3 性能调优:从“能用”到“好用”的路径
在项目进入到打磨阶段后,性能调优主要围绕三件事:延迟、成功率和效果一致性。
延迟优化方面,我推荐三个手段:流式输出优先,用户首字延迟大幅降低;对复杂请求做任务并行,比如先并发检索多个知识库再合并结果;模型选择降级,简单问题走小模型快速通道,复杂问题才路由到大模型。这套组合拳一般能把体感延迟降低一半以上。
成功率方面,要重点做输入校验和异常恢复。输入超长就截断或者摘要,输出格式不对就自动修正一次,模型超时就重试且切换备用模型。每加一道防护,线上成功率都能提高零点几个百分点。
效果一致性是我特别想强调的。相同的问题不同时间问,回答风格可能飘,一会儿详细一会儿简略,一会儿用列表一会儿用段落。我的做法是在提示词中固定回答风格偏好(比如“回答控制在200字以内,先给出结论,再补充理由”),同时在输出后做规范校验,不符合就直接重生成一次。一致性直接影响用户信任感,这一块值得扣细节。
5. 部署上线与踩坑实录:那些文档里不会写的事
5.1 从开发到生产:配置管理与安全基线
AI应用部署上线,与传统服务相比多了一层麻烦:你不仅要管理代码版本和配置,还要管理提示词版本、模型版本、Embedding版本。这三个版本的组合不一致,系统行为就会千差万别。
我在生产环境中采用了一套管理方案:每一个线上请求都在日志中记录当时的提示词版本号和模型版本号。这样就算模型厂商改了底层行为(这种事并不少见),你也能精确定位到是“哪一次更新导致效果波动”。需要注意,模型版本这东西不是你自己能锁定的,在线API随时可能悄悄升级,所以要做效果基线监控,每周跑一遍标准测试集,发现指标波动及时跟进。
安全方面有几个容易被忽视的细节:
- 系统提示词可能被用户通过Prompt注入套取。我在用户输入进入上下文之前做一轮转义和截断,并且提示词中明确标注“以下内容来自用户,可能不可信”;
- 日志中不能记录用户的敏感信息。我在开发环境可以随意打印上下文,但生产环境必须对输入输出做脱敏处理,尤其是身份证、手机号、地址这类数据;
- API密钥必须使用独立的密钥管理服务,不能出现在代码库中,哪怕是私有仓库也保不准哪天泄露。
5.2 我踩过的五个坑和排查路径
这个项目从零到一,遇到的坑比预期多得多。我整理了最有代表性的五个,每个都花了不少时间排查:
第一个坑是向量检索的“方言问题”。用户提问用词与文档用词不一致,Embedding匹配不上。排查了很久才发现不是代码问题,是语料覆盖问题。解决办法是在切分阶段加入领域同义词表,同时增加一条扩写链路:把用户问题先做一次改写再检索。
第二个坑是上下文被“毒化”。系统跑了一段时间后,用户多轮对话的中间内容中混入了错误信息,导致后续回答全部走偏。排查后发现是上下文管理只加不减,旧信息在窗口里堆着,干扰了模型注意力。修复方式是引入了对话回合级别的相关性过滤和过期淘汰机制。
第三个坑是模型输出格式的“幽灵空格”。模型返回的JSON有时会被Markdown代码块包裹,有时在字段名里多出空格,下游解析偶尔失败。排查后发现不能用简单字符串匹配来解析,后面改成先提取代码块、再修复JSON语法、最后用Schema校验三层处理,这个组合方案上线后解析失败率几乎降为零。
第四个坑是超时重试引发的重复写入。一次工具调用超时,客户端重试,结果工具实际上执行成功了,导致数据库里出现重复记录。这个问题很经典,需要在工具层实现幂等控制,用请求ID做去重。排查过程中我还顺带发现了流式输出场景下的“半截回答”问题——用户页面显示了一半就断掉,后来加上断线重连和补偿机制才解决。
第五个坑是评估集和线上数据分布不一致。离线评估集的指标很好,上线后用户实际体验却一般。原因是评估集是团队成员写的,用词正式、意图明确,线上用户提问随意、口语化严重。解决方法是增加线上日志回流到评估集的机制,保证评估数据分布和真实流量靠拢,这个机制建立后离线指标和线上体验才开始对齐。
5.3 成本控制与模型降级的实践经验
大模型应用跑在真实流量上,成本会以你想象不到的速度攀升。我在项目中有几个屡试不爽的成本控制手段:
第一是缓存策略。相同的用户问题在短时间内重复出现,直接命中缓存,不调用模型。我的实现是用Embedding近邻匹配做语义缓存,设置24小时过期,缓存命中率在客服场景能到15%左右,对应成本能省下一成多。
第二是Prompt瘦身。生产环境下在每轮对话里都塞长篇系统提示词,成本和时间都在隐性地增加。我把一些“一次性说明”挪到首次交互时以用户可见的方式说明,尽量保持系统提示词精简。实验下来,提示词从1200字减到600字,对回答质量几乎没有影响,但token消耗少了将近三成。
第三是任务拆分时的模型分级。一个复杂的分析任务,我拆成多个子步骤,简单子步骤用小型模型,只有最终综合和深度推理才用大模型。这个策略在生产环境跑下来,总计成本降了40%多,效果基本不受影响。
6. 经验总结与可复用的行动清单
这个项目做下来,我最大的感受是:AI工程的核心竞争力不在“会调模型”,而在“能控制不确定性”。模型行为有随机性,但你的工程手段可以把随机性限制在可控范围里。提示词可以写得很灵活,但必须有版本、有测试、有回滚机制;检索可以做得很快,但必须有评估指标和监控告警;Agent可以做得很智能,但必须有状态管理和权限边界。
另外一个很深的体会是:AI工程的成效取决于“反馈闭环”的运转速度。你改了一版提示词,需要评估集告诉你效果变好还是变差;你换了一种切分策略,需要检索命中率告诉你值不值得;你上了新模型版本,需要线上监控告诉你有没有回归。闭环越快,迭代越快,系统的成熟度也就越高。那些“跑起来就不管了”的应用,过了三个月往往已经悄悄变得不可用,原因就是反馈闭环断了。
如果你想从这个项目里带走最关键的三件事,我会总结为:先用离线评估集锁定基线再动手优化,任何改动要有版本记录和回滚方案,线上和离线的效果指标必须打通形成闭环。这三件事听起来不酷,但它们才是AI应用真正能落地的基石。
最后再分享一个小技巧:在你开始写代码之前,先手工模拟一遍完整的用户旅程。拿你准备做给用户的AI应用,自己扮演用户,手动在纸上走一遍流程,记录每一步需要的输入、期待的返回结果、可能的失败点。这个动作看似原始,但在项目前期能帮你发现大量需求理解偏差。AI工程和传统工程一样,需求理解的偏差在后期修正的成本是最高的,前期的“笨功夫”是最划算的投资。