
一个半路出家的后端工程师决定认真搞AI工程之后我把过去三个月踩过的坑、走过的弯路、以及最终沉淀下来的那条“最短可行路径”整理成了一份从零到一的工程手记。先说结论AI工程不等于“训练模型”更不等于“调API”。它是一套把模型能力变成稳定业务服务的系统工程。如果你是后端工程师、全栈开发者或者刚转行想做AI应用落地的人这篇文章能帮你避开我当初浪费掉的那几周时间——直接告诉你该学什么、该做什么、该按什么顺序做。1. 为什么“跑通一个模型Demo”和“AI工程”之间隔着一条鸿沟如果你只打算在笔记本里跑通一个OpenAI的接口调用或者本地加载一个开源模型随便聊两句那你其实不需要看这篇文章。AI工程真正复杂的地方在于当你需要让模型在真实业务里稳定服务、持续迭代、可观测、可回滚、成本可控的时候问题就全来了。1.1 我最初的理解错在哪里我最早以为AI工程就是把训练好的模型包装成一个HTTP服务然后调一调就完事了。但实际做下来发现一个能上生产的AI系统大概是这样的结构模型服务层处理推理请求、控制并发和超时数据管道层负责知识的采集、清洗、切分、向量化、增量更新评估层持续衡量模型输出质量防止悄悄变差成本控制层Token消耗追踪、缓存策略、模型路由可观测层日志、链路追踪、输出抽样审计这五层任何一层做不好系统都会在真实流量下暴露问题。我在第一个项目里光是把调用超时从10秒调到30秒就引出连锁问题——下游任务队列堆积、重试风暴、账单翻倍这些都是Demo阶段根本不会遇到的。1.2 传统后端思维在AI项目里会失效的地方后端工程师做AI工程有个天然优势懂系统设计、懂可靠性。但有几处传统思维会害了你模型输出不是确定性的。传统接口返回的是结构化数据模型返回的是概率分布。同样的输入两次结果可能不同这就要求你做输出校验和兜底。Token就是钱。传统后端优化关注CPU和内存AI项目里除了这些还要关注Token消耗优化Prompt往往比优化代码更能省钱。不可解释性。模型链路越长出问题时越难定位必须从一开始就设计好日志和追踪。提示如果你从零开始别先学模型训练先学会怎么把模型“用”好。工程能力只有在使用真实模型处理真实需求时才能积累下来。2. 环境与工具链选型我如何搭建一套从零开始的AI工程底座从零开始的第一步不是写代码而是把工具链选对。这一步决定了你后面所有操作的顺畅程度选错了后面光是折腾环境就能消耗掉一半精力。2.1 语言和框架Python不是唯一选择但确实是最省心的我知道你可能是个Java程序员或者Go程序员觉得Python性能差、环境乱。但AI生态决定了Python仍然是工程集成效率最高的语言原因很实在所有AI框架PyTorch、Transformers、LangChain、LlamaIndex的一等公民支持基本都是Python数据处理的生态最完整Pandas、NumPy、PyArrow这些库你绕不开业界几乎所有AI工程的参考实现都是Python写的抄作业成本最低我的建议是别把整个系统都用Python写而是把Python作为AI编排层把业务系统留在你熟悉的语言里两者通过API通信。这样架构清晰又不用被Python的性能短板卡脖子。2.2 模型选型什么时候用API什么时候用开源模型这是每个AI工程新人面临的第一个关键决策。我当时的判断逻辑是业务需要快速验证、对数据隐私要求不敏感 → 用API模型推理快、效果稳定、不用管基础设施业务有数据合规要求、需要离线部署、成本敏感 → 用开源模型通过Ollama或vLLM自托管两者混用 → 冷热分流日常请求走API批量离线任务走开源模型我第一个项目因为业务涉及用户隐私数据最终选择了开源模型自托管的方式。用Ollama跑Qwen系列模型起步后来又用vLLM做了生产部署推理速度和吞吐量比Ollama高出很多但配置复杂度也相应增加。从0到1用Ollama从1到100用vLLM这个节奏是对的。2.3 向量数据库选型越晚决策越好这个建议可能有点反直觉——你会看到很多项目一开始就选定向量数据库。但我建议把它当作“最后一个决策”因为向量数据库的选型高度依赖你的数据量和查询模式而这些在项目初期完全不清楚。我个人的经验路径数据量低于100万条向量 → 先用SQLite加一个向量扩展如sqlite-vec或者直接用PostgreSQL的pgvector数据量达到千万级 → 再考虑专门的向量数据库如Milvus、Qdrant等还在创业探索期 → 优先用托管服务别自己运维节省精力我在第一个版本里就用的是pgvector因为PostgreSQL本来就是业务库少一个组件少一个运维负担。事实也证明前期数据量不大时pgvector的表现完全够用。3. 第一行代码前的“为什么”拆解一个真实AI项目的必备组件说实话我最初也想跳过设计直接写代码但等我真开始写的时候发现哪里都要改最后索性推倒重来。如果你准备从零开始做一个AI项目先花两天时间想清楚下面这些组件比写代码值钱得多。3.1 系统架构不是“调一个模型”而是“编排一条链路”拿一个典型的企业知识库问答系统举例表面上看起来就是一个“用户提问-返回答案”的接口但内部链路是这样的输入处理查询改写把口语化问题转成适合检索的形式、意图识别检索阶段知识库召回、重排序、结果过滤增强生成组装Prompt系统指令加检索内容加用户问题、约束输出格式输出后处理格式校验、引用来源标记、兜底回答这套链路里任何一个环节做不好最后的质量都会受影响。最常见的问题是把所有逻辑写在一个巨型Python文件里改一处崩一路。我建议从一开始就按一条Pipeline拆成不同模块每个模块有明确的输入输出这样才能定位问题和迭代。3.2 数据先行没有高质量的知识库什么模型都救不了你说句残酷的话模型决定的是下限数据决定的是上限。我见过不少团队折腾选型调参半个月结果知识库里的文档还是杂乱的PDF和扫描件问答效果自然一塌糊涂。数据工程至少要处理这几件事格式统一把PDF、Word、HTML全部转成纯文本保留结构信息清洗去重去掉页眉页脚、导航栏、重复段落切分策略按标题和段落切分而不是无脑按字数切分元数据保留文档来源、更新时间、所属部门这些在结果溯源时极其重要我踩过一个坑把一个技术手册按固定512字符切分结果把表格和代码块拦腰截断后面检索出来的内容乱七八糟。后来改成基于Markdown标题结构切分效果立竿见影。3.3 评估体系没有衡量标准优化就是玄学这是我最想强调的一点。从零做AI项目必须第一天就建立评估集哪怕只有30个问题。没有评估集你会陷入“改了Prompt感觉变好了/变坏了”的玄学循环。我建立的评估维度很简单准确率回答是否与知识库内容一致完整性关键信息是否全部覆盖引用正确性引用的来源是否确实支持该结论兜底率该说不确定的时候是否诚实说不确定还是强行编造这些评估维度一开始可以用人工打分跑通之后再用LLM作为裁判LLM-as-a-Judge批量评估。但注意LLM裁判本身也有偏差最好的方法是人工标注一小批基准答案再让LLM裁判对齐人工的判断标准。4. 从零落地一个端到端的RAG系统完整复现我的实操过程前面讲了这么多设计层面的东西这一节回到实操把一个最小可用的RAG系统一步步带出来。我会用最通用的技术栈让你跟着做就能跑起来。4.1 为什么选RAG作为第一个项目RAG检索增强生成是AI工程里复杂度适中、价值落点清晰、可扩展性强的项目类型。它能让你一次接触到数据管道、向量检索、Prompt工程、模型调用、评估闭环的全部环节。做一遍RAG等于把AI工程的地基打了一遍。4.2 数据准备和向量化从原始文档到检索库我先准备了一份产品手册PDF目标是让AI能回答手册里提到的问题。整个准备流程如下第一步提取文本。我用了PyMuPDFfitz来提取PDF的文本内容保留原始的章节结构import fitz doc fitz.open(product_manual.pdf) for page in doc: text page.get_text() # 简单处理写入到文本文件后续再做清洗第二步清洗和结构化。这一步我手工检查了几页输出发现页眉页脚和页码混进来了写了一个简单脚本过滤掉它们。同时按章节标记切分每段保留来源页码import re def clean_text(text, page_num): # 去掉页眉页脚和页码 lines text.split(\n) cleaned [] for line in lines: if re.search(r第\s*\d\s*页, line): continue if 保密文件 in line or 内部资料 in line: continue cleaned.append(line) return \n.join(cleaned)第三步切分策略。我对比了固定长度切分和按结构切分的效果最终选了按Markdown风格的标题层级切分。如果原始文档没有清晰结构就先用正则识别标题如果连标题都没有就退回固定窗口切分但会设置重叠窗口避免切散语义。这里用LangChain的递归切分器就能实现from langchain.text_splitter import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n## , \n### , \n, 。, ], ) chunks splitter.split_text(manual_text)这个切分器会优先尝试按“二级标题”切分切不动就降级到段落和句子最终保证每个块内有完整的语义单元。第四步向量化入库。我用的是开源嵌入模型把每段文本转成向量写入PostgreSQL的pgvector表。在实际项目里不建议自己实现向量化入库逻辑直接用框架封装会更稳from pgvector.sqlalchemy import Vector from sqlalchemy import Column, String, Integer class DocumentChunk(Base): __tablename__ document_chunks id Column(Integer, primary_keyTrue) content Column(String) source_page Column(Integer) embedding Column(Vector(1024)) # 维度要和嵌入模型一致4.3 检索和重排序为什么“最相似的”不一定是最合适的这一步是我踩坑最多的地方。向量检索返回的结果单纯按余弦相似度排序往往不理想。原因在于语义相似不等于答案可用。比如用户问“退货政策是什么”知识库里有一段提到了“退货”但主要的其实是“换货”只做向量检索就会把这段排到前面。解决方法是引入重排序模型。简单说向量检索负责“海选”召回Top20重排序模型负责“精选”精排Top5。虽然多了一步计算但对最终回答质量的影响是决定性的。我用的是一个轻量的重排序模型通过API调用每次查询多花几毫秒但效果立竿见影from sentence_transformers import CrossEncoder reranker CrossEncoder(BAAI/bge-reranker-base) def retrieve(query, top_k10): # 先向量检索召回 20 条 candidates vector_search(query, top_k20) # 再用重排序模型精排 scores reranker.predict([(query, c.content) for c in candidates]) sorted_results [c for _, c in sorted(zip(scores, candidates), reverseTrue)] return sorted_results[:top_k]提示如果只是快速跑通Demo可以不加重排序。但是只要你想用RAG做真实产品重排序是必做的坑。它不是可选项是必备项。4.4 Prompt组装和输出约束让模型“按规矩说话”检索到了内容之后关键的环节是把内容组装成Prompt交给模型。这里有个很重要的经验Prompt里的指令格式要固定、模板化每次只替换变量。我在项目初期经常手工拼字符串后来改成结构化模板稳定性和可维护性都大幅提高。下面是我用的一个基础模板你是企业知识库助手。请严格根据提供的【资料】回答用户问题。 如果【资料】中没有相关信息请直接回答“根据现有资料无法回答该问题”不要编造。 【资料】 {context} 【用户问题】 {query} 请用简洁的语言回答并在回答末尾列出引用的资料编号如 [1]、[2]。除了Prompt还要约束输出格式。我的做法是用模型的工具调用能力或结构化输出能力让模型返回一个JSON对象里面包含“answer”和“citations”两个字段。这样下游解析就非常简单不需要正则去抠答案completion client.chat.completions.create( modelyour-model, messages[{role: system, content: system_prompt}, {role: user, content: user_query}], response_format{type: json_object}, # 开启结构化输出 ) result json.loads(completion.choices[0].message.content) answer result[answer] citations result[citations]这一步做对之后后续的日志审计、前端渲染、人工审核都变得毫不费力。4.5 评估闭环用10个问题发现80%的问题我搭建完第一版RAG系统之后第一件事就是拿测试集来评估。这里建议准备一份10到20个问题的小数据集每道题包含正确答案和期望引用来源。用这套测试集跑了一遍我发现了几个之前完全没意识到的问题知识库里有多个文档都涉及同一主题模型有时选错了来源检索阶段返回了很多跟问题无关的内容导致模型跟着跑偏系统提示词不够严格有些问题模型会选择猜测而不是承认不知道这些都是通过评估集才暴露出来的。没有评估我可能一直以为自己写的系统效果不错。后来每改一次Prompt或者检索逻辑我就跑一遍测试集看打分变化效果好坏心里有数。5. AI工程的性能瓶颈排查与生产化落地系统能跑通是一回事跑到生产环境被真实用户用又是另一回事。这一节讲讲我上线前后遇到的最典型的性能瓶颈和踩坑经历。5.1 第一个瓶颈慢得离谱的检索上线第一个版本后有用户反馈响应太慢。我先看了日志发现检索阶段要500毫秒模型生成要2秒整体将近3秒。排查过程是这样的先定位到检索阶段——单个查询在PostgreSQL里做向量检索本身只要几十毫秒但因为我用了全表扫描没走索引数据量一上来就慢。解决方法是给向量列建IVFFlat索引CREATE INDEX idx_chunk_embedding ON document_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists 100);建完索引检索从500毫秒降到了80毫秒。这一步带来的提升比我优化Prompt还要明显。5.2 第二个瓶颈Token消耗高得吓人上线后账单超出预期。我查了一下发现每个请求携带的资料块太多默认把Top5全部塞进Prompt每块500字就是2500字加上系统提示词和对话历史一次请求动辄三千多Token。优化的策略并不是无脑减少资料块数量而是做取舍检索时先判断相似度分数低于阈值的直接丢弃对对话历史做摘要压缩而不是无限塞入模型路由简单问题走便宜的小模型复杂问题才走大模型做完这几项调整单次请求平均Token消耗下降了40%而回答质量评估分数基本没有下降。5.3 生产化的其他注意事项最后说一下生产环境下的其他细节都是我实测踩过的熔断与降级模型服务偶发超时需要设置合理的超时时间和重试次数超过阈值就降级到兜底话术缓存相似问题在短时间内大量出现时走了缓存能省下一大笔模型调用费数据版本管理知识库更新后旧版本还能回滚不然一次错误的数据发布就可能让全站回答质量崩溃日志追踪每个请求分配一个request_id贯穿检索、Prompt组装、模型调用、输出校验全过程出了问题能三分钟定位到具体环节6. 陷阱清单为什么别人的方案在你这里总会失效项目推进过程中有一类麻烦最消磨精力照着社区里的成功方案部署却总是得不到预期效果。有些坑必须单独拎出来说。6.1 模型版本悄悄变更导致的结果漂移我在项目里遇到一个诡异的现象同一个测试集早上跑评分0.85下午再跑变成了0.78。一开始我怀疑是随机性后来发现是供应商那边模型版本更新了但接口文档没怎么提真值分布悄悄变了。这件事给我两个教训第一凡是调用外部模型API必须在日志里记录请求时间和模型版本号第二维护自己的测试集并且定期跑分一旦分数波动就要排查是Prompt变了、数据变了、还是模型变了。6.2 开源项目最新版不一定比旧版适合你搞AI工程的一大诱惑是追新框架出了新版本赶紧升级模型出了新发布赶紧替换。但升级带来的风险往往被低估。我的一次经历是LangChain从0.0.x升到0.1.x接口大面积不兼容我的代码改了一整天才恢复正常。从那以后我养成了一个习惯项目锁定版本升级前先读Changelog并且用评估集对比升级前后的效果分数低于预期就降级等待。在新版本发布时先观望是低谷期最有价值的操作之一。6.3 知识库数据越加越多效果却越来越差这是RAG项目最反直觉的一个现象知识库越全回答效果越差。原因在于检索阶段容易召回到更多弱相关片段而这些弱相关片段会把模型“带跑偏”。解决办法不是减少知识量而是一是优化检索——加重排序、提高相似度阈值二是把知识库分域管理——不同业务域单独建索引查询时先路由到对应域再在该域内检索。这种方法在实际落地中比单纯调参管用得多。7. 下一步还能做什么从RAG项目延伸出去的几条进阶路径如果你已经从一个零基础状态成功把上面这套RAG系统跑通并上线那么恭喜你你已经不再是AI工程的新手了。接下来的进阶方向取决于你手头业务的真实需求。7.1 从“单点助手”走向“Agent系统”如果业务要求AI不仅能回答问题还能执行操作比如发邮件、查库存、自动改订单状态那RAG的方向就演变成了Agent系统。核心是把大模型拆成“规划器”“调用器”“验证器”等多个角色每一步都通过工具调用获取结果再决定下一步行动。这条路径上的工程难点从调用质量转向了流程可靠性——一个链路的每一步都可能出错每一步都需要校验和重试。7.2 从“离线评估”走向“在线监控”离线评估集说到底覆盖不了生产流量的复杂性。真正上线后模型会在你完全没预料到的输入上犯错。这时需要构建在线监控体系对线上真实对话进行定时抽样、识别错误模式、追踪修复闭环。这个方向最花精力的不是技术而是反馈链路——你要让使用端的用户或运营人员养成“标记错误答案”的习惯并把反馈数据回流到评估集中形成越用越准的正向循环。7.3 从“一套知识库”走向“多源数据干线”如果你的业务涉及多来源数据连续更新如钉钉文档、飞书知识库、内部Wiki、工单系统等那么关注点就变成了数据同步与增量更新机制怎么感知源端变化、怎么做增量切分入库、怎么避免重复向量导致的检索污染。这是数据工程与AI工程的交叉地带也是短期内不太容易被替代的技能方向。我自己目前就在往这个方向深挖——把RAG系统做成企业知识的中枢干线而不是一个孤立的问答机器人。这条路线越往下走越有意思也越能体现一个工程人的综合积累。