做AI工程这一年多,我发觉最难的地方不是“用哪个模型”,而是“从零开始怎么把一套东西串起来”。很多人以为AI工程就是调API、套LangChain,结果真上手才发现:需求边界模糊、Prompt改一版崩一版、Agent跑起来不可控、上线之后没人敢信输出结果。这篇东西不是概念科普,是我从零搭过知识库问答、自动化分析Agent、多Agent协作流程之后沉淀下来的一套完整路径——从怎么拆需求、怎么选技术栈,到Prompt具体怎么写、工作流怎么编排、上线后怎么排查,全部用真实项目经验讲,适合正在从“跑通Demo”走向“做成产品”的人参考。
1. 先搞清楚:AI工程到底在“工程”什么
1.1 传统软件和AI工程的本质差异
传统软件工程的核心是“确定性”:输入A走分支B得到输出C,每一步都可以单测,出bug了直接定位到哪一行。AI工程完全不是这套逻辑。模型是概率系统,同样的Prompt在不同时间调用,输出可能差很多。所以我刚开始做的时候犯过一个典型错误——用写后端接口的思路去写AI应用,结果整个系统像一坨无法预测的黑盒,出了问题不知道是该改代码还是该改Prompt。
AI工程真正要“工程”的,是把概率系统的不可控性用结构和约束包起来。具体来说有三层:第一层是数据层,决定模型能看到什么,包括知识库切片、向量化、检索质量;第二层是推理层,决定模型怎么思考,包括Prompt结构、工具调用逻辑、多步推理路径;第三层是应用层,决定用户怎么用,包括工作流编排、状态管理、结果校验、降级策略。这三层没有一层是靠“换个更强的模型”能解决的。
传统软件里,性能瓶颈通常在数据库或网络,AI工程里瓶颈往往是“上下文和信任度”。你在本地跑通一个Demo只需要5分钟,但把它变成能稳定服务100个用户的系统,要考虑的是:检索结果准不准、模型有没有幻觉、超时了怎么办、用户问到了知识库之外的东西怎么回应。这些才是工程的核心。
1.2 “从零开始”的常见误区
这几年我见过太多“从零开始”翻车的案例,无外乎三种:
第一种是“模型驱动陷阱”。上来就比参数、比跑分,GPT-5出了马上换。实际上换了模型之后Prompt要重新调,评测要重新跑,成本结构全变了。模型选型应该最后一个做,而不是第一个做。
第二种是“框架驱动陷阱”。看到GitHub上LangChain星标多,觉得用上就等于会了AI工程。我之前也被框架绑架过——为了用一个现成的Agent类,被迫接受它的Prompt模板和状态管理方式,出了问题查源码查了半天。后来我把大部分业务逻辑改成原生Python实现,只保留真正需要的抽象层,代码量少了30%,可调试性却翻倍。
第三种是“Demo驱动陷阱”。Demo和产品之间隔着一条巨大的河:评测、监控、成本控制、安全兜底、迭代流程。你给老板演示的时候模型表现很好,是因为你精心挑了五个问题。真实用户不会按你的脚本提问。做AI工程必须有“从失败中学习”的准备,第一版一定是不完美的,关键是建立一套迭代机制,让系统每周都比上周好一点。
2. 从零搭建一套AI工程的核心技术栈
2.1 模型选择:不是越大越好,而是越合适越好
模型选型我现在的思路是“按任务复杂度分层”。简单分类任务、实体提取这种,用中小参数模型(比如7B-14B的量化模型)就够,速度快成本低;复杂推理、多步工具调用,才需要上旗舰模型;如果涉及敏感数据不能出内网,还要考虑本地部署。
一个很实用的判断方法:拿你业务里最难的20个问题,分别用不同档位的模型跑一遍,人工打分,然后看“分数差”和“成本差”哪个更值得。我做过一次实测,某个内部文档总结任务,小模型90分,旗舰模型95分,但成本差了20倍。最后用了小模型加一层后处理校验,把准确率拉到了93分,综合性价比完胜。
另外一个容易忽略的点是上下文长度。参数上写的128K上下文,并不代表128K的内容效果都一样好。实测超过一定长度后,模型对中间部分的记忆会明显下降。所以设计系统时不要把“能塞多大的上下文”当卖点,而是尽量让检索模块只把最相关的内容送进Prompt,而不是一股脑全塞进去。
2.2 编排与Agent框架怎么选
关于框架,我的态度分三个阶段:初期可以用,中期要敢拆,后期只保留核心抽象。市面上主流框架提供的东西无非四类:模型调用封装、Prompt模板管理、工具注册与执行、状态与记忆管理。前两类其实用几十行原生代码就能实现,没必要引入重依赖;后两类才是框架真正有价值的地方——工具调用循环和记忆机制确实容易写错。
以Agent为例,核心循环其实就三步:观察(决定下一步做什么)→ 行动(调用工具)→ 反馈(把工具结果放回上下文)。自己用代码写这个循环并不难,反而是套框架时被它的抽象绕晕。我现在倾向于“轻框架重代码”:用框架的向量存储和模型调用接口,但业务编排逻辑全用自己写的Python控制,每个环节都能打断点调试。
工具注册这块有个关键设计,不要让Agent拿到“所有工具”。每多一个工具,模型的选择空间就大一分,出错的概率也升一分。我一般会做动态工具列表——根据用户问题先做一个粗分类,然后只向Agent暴露当前场景下可能用到的3-5个工具,大幅减少乱调用的情况。
2.3 记忆、检索与数据底座
记忆和检索决定了AI的“靠谱程度”。刚开始做知识库问答时,我天真的以为把文档全扔进向量库就完事了,结果发现检索质量惨不忍睹。后来才明白,RAG的瓶颈80%在“数据准备”,不是向量化。
切片是最容易被低估的环节。按固定字符数切片会让语义被切碎,比如一个表格被切成两半,检索出来就只剩半张表。我现在会先用结构解析把文档拆成标题块,再做语义边界调整,每个切片尽可能是一个完整的论点或操作步骤。切片之间的重叠(overlap)也必须有,否则边界处的语义会丢失。
向量模型的选择同样影响巨大。中英文混合场景用通用embedding模型效果一般,我会用领域微调过的向量模型,并且做“查询改写”——用户问“这个功能怎么排查”,实际检索时改写成“故障排查步骤”去匹配。评测过一组数据,查询改写后Top-5命中率从61%提到了78%。
3. 实操:从零构建一个知识库问答Agent
3.1 需求拆解与边界定义
直接讲我最近一次完整项目的实操过程。任务是做一个内部技术文档问答Agent,用户是几十名研发人员,他们想用自然语言查文档而不是翻Wiki。听起来需求很明确,但开工前我还是做了三轮需求澄清:
第一轮定义“能做”,范围限定在“已入库的技术文档”,越界问题明确告诉用户“没查到相关内容,建议去Wiki搜关键词XX”,绝不自己编。第二轮定义“不能做”,不回答代码评审、不诊断线上故障、不处理私人数据——这些是安全边界,必须靠系统指令和工具权限双重卡死。第三轮定义“好用的标准”,用户能接受的答案是“有结论、有步骤、有出处”,所以最终答案里必须包含引用来源。
这三轮做完,整个系统的评估指标就定了:检索命中率、答案完整度、引用准确率、和“拒答率”(该拒绝时没拒绝就是事故)。有了指标之后每一步改动都能量化,不再靠感觉判断“好像变好了”。
3.2 Prompt设计:从提示词开始的第一版
项目的第一版Prompt我保持了极简结构,核心逻辑不超过20行。系统提示词只做四件事:身份定义、任务边界、输出格式、行为红线。下面是这个项目用的简化版Python代码,用来说明结构而不是照搬:
system_prompt = """ 你是公司的技术文档助手。你只根据参考资料回答问题。 遵守以下规则: 1. 如果参考资料中找不到答案,直接说“文档库里没有相关内容”,不要推测。 2. 引用每个结论的来源,格式为[来源: 文档标题, 章节]。 3. 回答时先给结论,再给步骤,最后给参考链接。 4. 如果问题涉及代码评审、线上故障或私人数据,礼貌拒绝并说明范围。 参考资料: {retrieved_context} """这段Prompt看着简单,迭代过程中我发现几个坑。第一版没有“行为红线”,结果用户问“帮我看看这段代码有什么Bug”,模型真的根据上下文开始瞎分析,犯了越界错误。加上第4条之后,拒答行为终于可控。
输出格式约束也很重要。我遇到过一次离谱情况:模型在回答中间突然开始自言自语“让我想想这个问题”。后来我在Prompt里加了“直接输出答案,不要解释思考过程”,情况立刻消失。现在我会对输出格式做结构化约束,要求模型按固定JSON返回,工程上也好解析。
3.3 工作流与工具调用
项目没有直接用Agent框架的默认循环,而是自己定义了一条流水线:意图识别 → 查询改写 → 检索 → 重排 → 生成 → 校验。这里的关键设计是“先检索后生成”,而不是把检索本身包装成一个工具让模型自己决定要不要调。前者稳定可控,后者灵活但容易失控,生产环境我选稳定。
意图识别和查询改写我用的是一个带结构化输出的分类Prompt,把用户问题映射成几类意图,同时生成2-3个检索变体。这一步能让“查不到”的概率下降很多。重排环节我用的是轻量级交叉编码器,虽然比向量检索慢一点,但只对Top-20结果重排,耗时可以忽略,准确率提升却非常明显。
def agent_pipeline(question): intent = classify_intent(question) if intent == "out_of_scope": return "这个问题超出我能处理的范围,建议咨询相关负责团队。" queries = expand_queries(question) candidates = [retrieve(q, top_k=20) for q in queries] merged = merge_rank(candidates) top_docs = rerank(question, merged)[:5] answer = generate(question, top_docs) return validate_answer(answer, top_docs)校验这一步是生产环境最重要的兜底。我在代码里加了一个简单的检查器:如果答案中包含“根据我的经验”“我认为”“可能也许”这类主观词,系统会自动标记为“低置信度”,提醒用户谨慎参考。经验类AI项目可以不要这步,但工具型产品必须有,因为用户会拿你的答案去做决策。
3.4 部署与观测
部署层面我用的是FastAPI包一个服务接口,加内存缓存和限流。缓存是省钱利器——完全相同的用户问题一周内出现的比例通常超过15%,缓存命中一次就省一次模型调用。限流也必须做,尤其是内网服务,一个用户写个for循环就能把你的预算跑穿。
观测是我早期最不重视、后来最后悔的部分。现在每个请求我会记录五样东西:模型名称和版本、Prompt的最终形态(包括检索到的内容)、token用量、响应延迟、用户反馈。为什么记录Prompt完整形态?因为模型版本升级后行为会变,没有历史快照就没法排查“为什么这周效果突然差了”。
LangSmith这类工具可以用,但我后来发现自建一张日志表+简单的看板足够满足90%的需求。关键是“有数据”,而不是“工具多高级”。有了日志,后续做评测集、微调、Prompt优化才有底料。
4. 上线后最容易踩的坑与排查实录
4.1 上下文失控:又长又杂才是元凶
项目上线两周后,用户反馈“回答变笨了”。排查后发现问题出在Prompt里累积的会话历史——用户和Agent对话超过十轮后,历史消息全部被塞进上下文,干扰了模型对当前问题的判断。
这个问题的本质是:上下文越长,注意力越分散。模型不会因为你给了更多信息就变聪明,反而会被无关内容带偏。我的解决方案是做会话摘要压缩——每五轮对话之后,用一次轻量模型调用把历史总结成要点,只保留当前问题相关的部分。
另一个上下文失控的场景是工具返回结果太长。比如用户问“所有服务状态”,工具返回了200行原始数据,模型根本处理不过来。我给工具加了一层裁剪逻辑:根据当前问题的关键词,只保留相关行和摘要统计值,实在需要全量数据就写进临时文件让用户自己下载,不让模型消化。
4.2 幻觉与质量评估:没有尺子就没法迭代
幻觉是AI工程绕不开的话题。我的经验是:不能指望Prompt里写“不要编造”就解决幻觉,必须靠“引用约束”和“校验兜底”双管齐下。引用约束要求每个事实点必须带来源;校验兜底则是对答案做事实提取,回源检索文档验证一致性,不一致就拒绝输出或降级提示。
评测体系建议从第一天就建立。不需要一开始就搞几百条的复杂工程,最简单的做法是留出50条有标准答案的业务问题,每次改动后跑一遍,看三个指标:完全答对率、部分正确率、答错率。有了这个基础集,你就能判断“换模型到底有没有变好”“改了Prompt是真的进步还是碰巧”。
我在评测里还加了一个“边界问题集”——专门放那些不该回答的问题,比如“帮我预测股价”“你能修改我的代码吗”。如果系统开始过多回应这类问题,说明Prompt约束失效了,需要立刻处理。
4.3 成本与延迟:预算不失控的几条铁律
最后说下成本和延迟控制。第一条铁律是“大模型只做它必须做的事”。分类、改写、提取这些子任务,能用小模型就不用大模型;生成主答案才用旗舰模型。按这个策略,我项目的单次调用成本降低了将近一半,而用户感知到的质量没有明显变化。
第二条铁律是“设置合理的max_tokens”。很多开发者的模型调用都不写这个参数,导致模型跑满全速生成一大段废话。按业务场景分析,答案长度分布集中在300-500字,那我直接设600上限,既保质量又节约预算。
延迟优化上我做了两件事:一是检索和生成的部分环节并行化,让向量检索在做查询改写的同时就启动;二是对高频问题做答案缓存,缓存命中时延迟从3秒降到50毫秒。这两项加起来,线上P95延迟从4.8秒降到了1.9秒,体感是质的飞跃。
最后再分享一个个人体会:AI工程最迷人的地方是“永远没有标准答案”,最折磨人的也是这一点。我经历过把同一套系统推倒重写三次的夜晚,也有过为一个评测分数提升熬夜调Prompt到凌晨两点的时刻。现在我反而觉得,做AI工程最重要的一课是接受不确定性,然后用工程手段把不确定性一点点关进笼子里——评测集是你的尺子,日志是你的眼睛,兜底逻辑是你的安全网,这三样东西比任何模型都重要。如果你也正在从零搭建自己的AI工程,建议从一个小范围、高价值的场景开始,先跑通全链路再扩展,别一开始就想着做大而全的平台。