你有没有过这样的体验——用ChatGPT写几段代码、让它帮你润色一封邮件,觉得AI也不过如此?但当你接到一个真正的任务,比如“给公司做一个AI客服助手”“把工单系统接入大模型自动分类”,你会发现事情完全不一样了。模型吐出来的内容时好时坏,回答经常带幻觉,上下文一长就开始“失忆”,改一个Prompt可能牵一发动全身。这时候你面对的不再是某个模型,而是一整套工程。
这就是“AI工程”这个词真正想表达的东西。从零开始做AI应用,不是“调一个API”这么简单,而是一条涉及需求拆解、上下文管理、Prompt设计、Agent编排、评估与回归测试的完整链路。很多团队在Demo阶段跑得很欢,一上生产就翻车,根子就在于他们只做了“AI调用”,没做“AI工程”。这篇内容就是围绕“ai-engineering-from-scratch”这个主题,把从零搭建AI应用的核心链路、实操方法、踩坑经验一次性讲透。适合那些已经会写代码、但对AI工程化还处在“会用但没系统做过”阶段的后端开发者、测试工程师、独立开发者,以及正在带队做AI落地的技术管理者。
1. AI工程从零开始到底学什么——先明确边界再动手
1.1 别急着追新模型:AI工程的四根支柱
很多人一提到AI工程,第一反应是去追最新的大模型、看各种新框架的发布会。这个方向不能说错,但至少是偏了。我自己带过几个AI落地项目,最深的一个体会是:模型能力只是整个链路里“最不稀缺”的一环,真正决定项目成败的,是模型之外那套工程体系。
AI工程,我自己的理解是四个支柱:需求定义、数据与上下文、模型与提示词、评估与交付。
需求定义解决的是“这个AI功能到底要解决什么问题”。举个例子,你说“要给工单系统做个AI助手”,这个需求太模糊了——是帮客服写回复?还是帮管理员自动归类?还是帮用户自助查进度?这三个需求的Prompt设计、数据来源、链路复杂度完全不一样。
数据与上下文解决的是“模型从哪里知道它该知道的事”。大模型的训练数据是通用的,它不知道你公司的退款政策、不知道你的产品型号命名规则、不知道你最近的促销活动。你必须在调用模型之前,把“它该知道的”准备好,并且用工程手段高效地塞给模型。
模型与提示词就是大家最熟悉的Prompt Engineering层。但这里我多说一句:Prompt Engineering在AI工程里占的比重,是随着模型能力升级而变化的。2023年你可能需要花大量时间调Prompt才能让模型输出稳定JSON,2024年之后很多模型原生就支持结构化输出。如果你现在还停留在“反复调Prompt”的阶段,说明你的工程思维还没跟上。
评估与交付是最容易被砍掉、但最不能砍的一环。AI应用和传统软件最大的区别是:传统软件的逻辑是确定的,输入一样输出一定一样;AI应用的输出是概率性的,同一个Prompt十条里可能有一条是坏的。你要建立评估集、跑回归、盯指标,否则你根本不知道一次Prompt调整是变好了还是变坏了。
1.2 为什么“从零开始”不是从模型训练开始
“ai-engineering-from-scratch”最容易让人误解的地方在于“from scratch”这个表述。大多数人会以为要从Transformer架构学起,要先懂Attention机制,甚至要会微调模型。我见过的真实情况正好相反。
如果你不是研究型团队,而是要在业务里用AI解决实际问题,那么你完全不需要碰模型训练。你需要的“from scratch”,是从一条最朴素的链路开始:需求 → 数据准备 → Prompt → 功能实现 → 评估 → 上线。整个过程中最重要的编程能力其实是“怎么处理和拼接数据”“怎么设计可维护的调用链路”“怎么把AI能力包成可以被业务方消费的接口”。
我打个比方。你不需要懂得发动机的燃烧原理才能把车开好,但你一定要懂“油表亮了要去加油”“水温过高要停车检查”“刹车异响可能是什么问题”。AI工程里的“常识”,就是模型调用、上下文管理、输出校验、异常兜底、效果评估这一整套“驾驶常识”。
所以我在这篇内容里反复强调的“从零开始”,指的是从无到有建立一条可维护的AI应用链路,而不是从学术原理开始啃。如果你想系统学AI工程,我建议的顺序是:先用现成大模型做几个完整的小功能(哪怕是很简单的文本分类),跑通一条链路,再回头补理论知识,效率会高非常多。
1.3 不同角色怎么切入这个工程体系
AI工程不像传统岗位那样边界清晰。后端说要写接口,算法说要调模型,产品说要定需求——在AI项目里这些角色是高度交叉的。我自己总结过几种角色的切入方式,你可以对号入座。
后端开发者:你的优势在系统设计和接口能力,切入AI工程时重点学“怎么把大模型调用封装成服务”,包括限流、重试、缓存、流式输出、结构化解析。这部分是最接近你本职技能的地方,学起来最快。
前端或全栈开发者:你最容易出成果的方向是“AI功能的产品化”,比如给Ai应用做对话交互界面、做流式打字效果、做多轮对话的状态管理。同时建议接触一些后端知识,因为独立的AI应用大概率需要一个薄后端来管理API密钥和调用日志。
测试或质量保障工程师:你的切入价值在于“评估与回归”。AI应用的评测体系目前还很不成熟,谁先建立起一套“能跑回归的评测流程”,谁就是团队里不可替代的人。这个方向非常值得深耕。
产品经理或业务方:你的切入点是“需求定义”和“场景拆解”。AI工程最怕的就是“需求一句话、实现两行泪”。你如果能把业务需求拆成“输入是什么、输出是什么、验收标准是什么”,就已经超过大半同行。
我个人强烈不建议做的事情是:一上来就买课学深度学习原理。除非你日后想做算法研究员,否则那笔时间投资在AI工程实战中的回报率极低。先把一条链路跑通,让一个真实的业务问题被AI解决掉,这会给你最大的正反馈。
2. 核心技术点拆解:从Prompt到AI Agent的一条完整链路
2.1 Prompt Engineering从玄学到工程化
我见过很多开发者对Prompt Engineering的态度走极端:要么觉得这玩意儿没有规律全靠试,要么背了一堆“角色扮演”“思维链”技巧就开始套。两种都不对。Prompt Engineering确实有一定的经验性,但完全可以工程化,关键是把它当代码来管理。
工程化第一步是结构化。别把Prompt写成一坨自然语言堆在代码里,要拆分出系统指令、用户消息、参考材料、输出格式约束几个模块。我自己常用的结构是:
SYSTEM_PROMPT = """ 你是一名【角色定义】。 你的任务目标是【一句话说清楚要完成什么】。 你必须遵守以下规则: 1.【规则一:格式要求】 2.【规则二:信息边界】 3.【规则三:兜底行为——不确定时就明确说不确定】 """ USER_TEMPLATE = """ 【用户提交的原始内容】 {user_input} 【参考信息】 {context} 请根据以上内容,完成【具体任务描述】。 输出格式要求:{output_schema} """工程化第二步是可测试。每一次修改Prompt,都要能对应到评估集里的指标变化。我在实践中的做法是:把每个Prompt版本记上版本号,评估跑完之后对比指标,留下效果好的版本。如果你改Prompt从来不跑评估,那基本等于闭着眼睛开车。
工程化第三步是降耦合。不要让Prompt承担太多职责。如果你的Prompt里充斥着大量的“如果…就…”分支,说明业务逻辑写得太复杂了,应该把部分判断放到代码里做。比如“判断用户输入是否包含退款相关关键词”这种事,正则就能做,没必要让模型去做。
还有一个小点:结构化输出从第一天就要用起来。无论是要求模型输出JSON还是Markdown,一定要在Prompt里明确schema,并且配合代码做解析兜底。我见过太多项目死在“解析模型输出”这一步——模型偶尔多输出一段解释文字,你的json.loads()就崩了。工程上的解法是让模型只输出纯JSON、禁用markdown代码块包裹,解析失败时做重试,重试还失败就降级为默认结果。
2.2 上下文工程:RAG不是插件,是基础设施
AI工程里最值钱、也最容易被忽视的部分,其实是“给模型喂什么”。大模型训练完就像一位读书很多的顾问,知识面很广,但他不了解你公司的内部情况。想让模型说出行话、给出符合你业务逻辑的回答,就必须在调用时把相关信息放进去。
这就是RAG(检索增强生成)发挥作用的地方。RAG的基本思路是:用户问题来了,先从知识库里检索出最相关的片段,拼进Prompt里,再让模型基于这些片段生成答案。听起来不复杂,但工程细节极多。
核心环节之一是切分策略。把长文档切成小块存进向量库时,切太大,检索结果不精准,而且占用上下文;切太小,语义不完整,模型看不懂。我自己试下来,中文场景里按300到500个字符切分,同时保留段落边界和标题信息,效果比较稳。你也可以试“父子切分”——小块用于检索、大块用于给模型补充上下文,效果更好但实现成本高一些。
核心环节之二是Embedding模型与向量库选型。国内环境里,常见的选择是BAAI/bge系列的Embedding模型,向量库可以用轻量的chromadb、sqlite-vec,也可以用生产级的milvus、elasticsearch。初期做原型阶段,我建议直接用最简单的方案,把向量库当“有搜索功能的字典”用就够了,别一上来就上分布式。
核心环节之三是检索策略与重排。只做一次向量相似度检索往往不够精准,工程上常见的做法是先召回Top20,再用交叉编码器做重排,取Top5拼进上下文。这样虽然多了一步计算,但回答质量提升非常明显。如果你的业务对实时性要求高,也可以跳过重排,改用关键词与向量混合检索。
有一个我反复强调的观点:RAG不是“加一个插件”,而是AI应用的底层基础设施。你的知识库更新机制、权限控制、版本管理,都要围绕它来做。很多团队前期图省事直接把文档一次性全量灌进向量库,业务更新后向量库里还是旧数据,模型用过期信息一本正经胡说,用户就会开始不信任这个产品。
2.3 AI Agent:从单次问答到多步执行
RAG解决的是“模型知道什么”的问题,AI Agent解决的是“模型能做什么”的问题。一个AI Agent系统看起来复杂,核心的工程模式其实我可以拆给你看:大模型做大脑,工具注册表做四肢,控制循环做神经系统。
最基础的控制循环就是大家常说的ReAct模式——推理、行动、观察、再推理:
def agent_run(task, tools, max_steps=5): messages = [{"role": "system", "content": "你是一个可以调用外部工具解决问题的AI助手。"}] context = {"task": task} for step in range(max_steps): response = llm_call(messages + [{"role": "user", "content": format_context(context)}]) action = parse_action(response) # 解析模型返回的动作指令 if action["type"] == "final_answer": return action["content"] if action["type"] == "call_tool": tool_result = tools.execute(action["tool_name"], **action["arguments"]) context[f"tool_result_{step}"] = tool_result messages.append({"role": "assistant", "content": response}) messages.append({"role": "tool", "content": f"工具返回结果: {tool_result}"}) return "达到最大执行步数,返回当前最佳结果"这段代码把Agent的核心逻辑说清楚了:模型不是直接回答你,而是先判断“我应该调用哪个工具、传什么参数”,拿到工具结果后,再决定下一步是继续调用还是给出最终答案。
工程上,Agent系统的难点不在循环本身,而在工具注册与参数校验。你要为每个工具写清楚description,让模型知道什么情况下该调用它、参数是什么格式。还要对工具返回结果做清洗——模型拿到的工具结果如果很脏,后续推理质量就崩。我常用一个做法:把工具结果的JSON截断到一定长度再喂给模型,避免无用信息占上下文。
Agent场景里还有一个很实际的问题:多步骤任务的稳定性。每一步都有可能出错,而且错误会累积。所以工程上要做“步骤检查点”,每一步都校验模型输出的合法性,非法就重试;超过重试次数就终止并告知用户“任务未能完成”,而不是硬着头皮往下走。很多Agent“越跑越偏”的问题,就是因为缺少这种“刹车机制”。
近段时间社区里还有一个概念叫“Harness Engineering”,意思是给Agent加上更严格的执行框架和边界约束,把模型的自由度限制在可控范围内。我的理解是,这本质上就是给模型套上“工程缰绳”——工具白名单、参数校验、步骤上限、权限隔离,全都要在代码层面定义好。你越依赖模型“自由发挥”,系统越不稳定;反而把自由度收窄,效果更可控。这也是Agent工程化的大方向。
2.4 评估闭环:AI工程成立的判决书
很多做AI应用的人,花80%的时间在写Prompt和调模型,只留20%时间给测试,甚至完全没有评测集。这是本末倒置。我自己经历过的教训是:不计评估的AI项目,就像在流沙上盖楼,Demo跑得再漂亮,一改需求或者一换模型就整栋塌掉。
评估闭环怎么搭?核心是把“效果好”这个主观感受,转成可量化的指标。常见的指标有这么几类:任务成功率(比如分类准确率、提取正确率)、回答质量(相关性、忠实度,通常是人工或大模型打分)、工程指标(调用延迟、Token消耗、兜底次数)。
实践中最实用的做法是先搭评估集,再写代码。把你的业务场景整理出100条有代表性的输入,手工标注好期望输出,这就是评估集。模型一版、Prompt一版、RAG参数一版,都要在这100条上过一遍。虽然标注100条数据是件枯燥的事,但它的价值会在之后的每一次迭代里放大。
评估跑完后要看“坏case”。我在团队里的习惯是:每次评估完,召集大家把失败样本逐条看一遍,判断是哪一类问题——是检索没找到对的文档?是模型理解偏了?是Prompt约束不够?然后带着对坏case的理解去改进。这比盯着一个平均得分反复调Prompt有用得多。
3. 从零搭建一个AI工程案例:售后工单智能分类与自动摘要
前面章节把核心概念讲完了,这一节我们直接动手,走一遍完整的实操。我选的案例是“售后工单智能分类与自动摘要”,这个场景足够典型:有分类需求(结构化输出)、有知识库需求(参考历史处理方案)、有多步执行需求(查订单、查政策、给方案),非常适合用来演示AI工程的完整链路。而且这个案例脱胎于真实业务,你做完之后稍微改改就能用到自己的场景里。
3.1 为什么选这个场景练手
选“工单处理”而不是选“写诗”或“聊天”做练手项目,是因为它覆盖了AI工程的所有关键环节:
第一,它有不折不扣的结构化输出需求。工单分类要输出类目+置信度+处理建议,这就逼着你用JSON Schema,逼着你做模型输出解析与校验。
第二,它有明确的上下文依赖。客服需要查订单状态、查退换货政策,这些数据在知识库和业务系统里。要实现这个功能,RAG和工具调用绕不开。
第三,它有清晰的可量化指标。分类结果比照人工标注,准确率一目了然。能做评估闭环,不会像聊天机器人那样“感觉不错但说不出哪里好”。
第四,它的业务价值一眼可见。这个功能真正上线能帮客服省时间,你做出来会很有成就感。
所以这个案例不是玩具,是一个可演进的MVP。
3.2 工程目录与基础设施设计
动手之前先设计目录。我习惯把AI工程的代码按“数据层、逻辑层、接口层”三层拆开,这样后期维护容易。下面是一份我常用的结构,你可以直接抄:
ticket-agent/ ├── app/ │ ├── main.py # FastAPI入口,提供HTTP接口 │ ├── config.py # 全局配置(模型名、API Key、参数) │ └── services/ │ ├── llm.py # 大模型调用封装(统一入口、重试、超时) │ ├── retriever.py # 知识库检索服务 │ ├── agent.py # Agent控制循环 │ └── tools.py # 工具注册表(查订单、查政策、算运费等) ├── prompts/ │ ├── classify.yaml # 工单分类Prompt │ ├── summarize.yaml # 工单摘要Prompt │ └── agent_system.txt # Agent系统提示词 ├── data/ │ ├── eval_cases.json # 评估集(人工标注) │ └── knowledge_policy/ # 知识库原始文档 ├── scripts/ │ └── build_vector_db.py # 构建向量库脚本 └── tests/ └── test_eval.py # 自动化评测脚本这个目录的精髓在于:Prompt单独放、知识库单独放、服务按职责拆开。哪怕你后续要换模型、换向量库,也不需要改业务代码。
基础设施层面,初期你只需要三样东西:一个大模型的API(国内可用通义、豆包、DeepSeek、智谱等,各有免费额度)、一个向量库(先用ChromaDB,单机开发够用)、一个轻量服务框架(FastAPI)。不要一开始就上复杂架构,能用最简单的工具跑通,就是最好的开始。
3.3 核心代码落地:Prompt模板、RAG、Agent、接口
基础设施准备好了,开始写核心代码。先写打通流程的最小版本,再逐步增加细节。第一步是大模型调用封装。所有AI工程的第一步,都是统一LLM调用入口,把重试、超时、Token上限这些细节都收编到一个函数里。
# app/services/llm.py import os, json, time from openai import OpenAI client = OpenAI( api_key=os.getenv("LLM_API_KEY"), base_url=os.getenv("LLM_BASE_URL"), # 兼容OpenAI格式 ) def llm_call(messages, temperature=0.2, max_tokens=2000, retries=2): for attempt in range(retries): try: resp = client.chat.completions.create( model=os.getenv("LLM_MODEL", "qwen-plus"), messages=messages, temperature=temperature, max_tokens=max_tokens, response_format={"type": "json_object"}, # 强制JSON输出,关键! ) return resp.choices[0].message.content except Exception as e: if attempt == retries - 1: raise time.sleep(2 ** attempt) # 指数退避重试注意response_format这个参数,现在的模型普遍支持强制JSON输出,这是AI工程里最值得用准的一条能力。它意味着“模型输出不合法”这类问题从根本上减少了一大半。
第二步是知识库构建与检索。把知识文档切块、向量化、存进ChromaDB,查询时按相似度召回。
# scripts/build_vector_db.py from langchain_text_splitters import RecursiveCharacterTextSplitter import chromadb from chromadb.utils import embedding_functions text_splitter = RecursiveCharacterTextSplitter( chunk_size=400, chunk_overlap=100, separators=["\n\n", "\n", "。", ";", " ", ""] # 中文场景注意分隔符 ) chunks = text_splitter.split_text(raw_doc) client = chromadb.PersistentClient(path="./data/chroma_db") collection = client.get_or_create_collection( name="ticket_knowledge", embedding_function=embedding_functions.DefaultEmbeddingFunction() ) collection.add(documents=chunks, ids=[f"chunk_{i}" for i in range(len(chunks))])检索函数就简单了:拿到用户问题,向量化后查TopK,拼成上下文。
# app/services/retriever.py def retrieve(query, top_k=5): results = collection.query(query_texts=[query], n_results=top_k) return "\n\n".join(results["documents"][0])第三步是工单分类的Prompt模板。用YAML管理Prompt是为了让业务同学也能参与修改,不用碰代码。
# prompts/classify.yaml system: | 你是一位售后工单分类专家。请根据工单内容,将其归类到以下类目之一: - 退换货 - 物流查询 - 产品质量 - 支付问题 - 账号安全 - 其他咨询 你必须输出JSON,格式如下: {"category": "分类名", "confidence": 0-1之间的小数, "summary": "一句话摘要", "suggestion": "建议处理方式"} 注意: 1. 不要输出JSON以外的内容。 2. 如果信息不足,category输出"其他咨询",并在suggestion里说明需要进一步追问的内容。 user: | 工单内容: {ticket_text} 参考政策信息: {context}第四步是Agent控制循环。这里处理的是“用户工单需要查订单”的场景:模型先判断订单号信息是否齐全,不齐就请求工具查询。为了让这个案例可控,我设计一个简化版的工具:查询运单状态。
# app/services/tools.py TOOL_SCHEMA = [{ "name": "query_shipping_status", "description": "根据订单号查询物流状态,返回当前物流节点和预计送达时间", "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]} }] def call_tool(name, args): if name == "query_shipping_status": order_id = args["order_id"] # 真实环境从这里查询业务系统;演示环境直接返回模拟数据 return {"order_id": order_id, "status": "运输中", "location": "杭州转运中心", "eta": "预计2天内送达"} raise ValueError(f"未知工具: {name}")Agent循环代码,见2.3小节的示例,把它移植到工单场景即可。这里我特别强调一个工程细节:工具结果返回后必须写进messages里,让模型“看得见”工具的结果,否则它会在下一步里瞎猜。
第五步是FastAPI接口。让前端或IM系统可以通过HTTP接口消费这个AI能力。
# app/main.py from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class TicketQuery(BaseModel): ticket_text: str @app.post("/api/ticket/process") def process_ticket(query: TicketQuery): context = retrieve(query.ticket_text, top_k=5) # 第一轮:直接尝试分类 result_1 = llm_call([ {"role": "system", "content": classify_system}, {"role": "user", "content": classify_user.format(ticket_text=query.ticket_text, context=context)} ]) parsed = json.loads(result_1) # 如果是物流查询,走Agent流程查真实状态 if parsed.get("category") == "物流查询": agent_result = run_agent(query.ticket_text) parsed["tracking"] = agent_result return parsed跑通这个接口之后,你的AI工程“从零到一”的骨架就已经建立了。后续的工作就是不断丰富工具、优化Prompt、扩充知识库、完善评估,把它从“能跑”打磨成“好用”。
3.4 评估结果与第一轮迭代
代码跑通之后,我先用模拟工单做了3组实验,对照人工标注结果看效果。
第一组是普通工单分类。比如“我昨天买的手机到了,发现屏幕有划痕,想问一下能不能换”,模型正确输出退换货,置信度0.92,摘要准确。这一类本身简单,模型表现稳定。
第二组是信息不完整的情况。“我这个订单怎么还没送到?我朋友昨天买的都到了”,工单里没有订单号。模型第一步分类为物流查询,随后尝试查物流时因为缺少订单号,通过工具校验提示了“缺少必要参数”。测试结果是:分类正确,但后续Agent链路没有启动追问机制,只是返回了一个缺参数的错误。这属于“闭环没闭合”的问题。我还观察到,这类工单在真实场景下应该触发的是“追问用户补充订单号”的对话,而不是简单报错。
第三组是模糊场景。“你们客服电话怎么打不通,我在你们这里买的东西坏了,急死人了”,这单里既有服务投诉又有产品质量问题。模型分类输出为产品质量,但建议里没有包含“先安抚用户情绪再引导到售后流程”的动作——这在业务上是必要的。说明这个Prompt在“多意图识别与优先级排序”上还需要优化。
基于这次评估,我做了两轮迭代。第一轮,在分类Prompt里增加“多意图处理”说明,明确当工单包含多个问题时,按业务严重程度排序,输出主要分类并附带次要注意事项。第二轮,给Agent循环增加了追问机制——当工具调用缺少必要参数时,不直接报错,而是生成“需要补充的信息”返回给调用方。这两处改动看起来不大,但线上体验提升非常明显。
这个案例想告诉你的是:AI工程的迭代不是“换个更好的模型”就完事了。评估集、坏case分析、链路补全才是主线。模型只是这条链路里的一个组件,组件可以换,但链路必须稳。
4. 常见问题与排查技巧实录
这个版块,我把自己在实际项目里踩过、也带别人填过的坑集中整理一下。这些都是常规文档里不会告诉你、但实战里几乎必然碰上的问题。
4.1 输出不稳定和格式不听话
症状是明明天天写“输出JSON”,模型偶尔还是会夹带私货,比如在JSON外面包一层markdown代码块,或者键名是中文、值是数字类型错乱。
这里我最大的建议是:不要指望模型自觉,要假设它会犯每一种错,并在代码里兜底。具体手段有三层:第一层,Prompt里写清schema并给示例;第二层,API层面启用response_format={"type": "json_object"}强制JSON模式;第三层,解析时做容错处理——先剥离markdown尖括号,再提取最外层花括号,解析失败就重试一次,再失败就进人工兜底队列。三层都上了之后,格式问题基本能压到1%以内。
4.2 上下文既不能太长也不能太短
“太长”的表现是模型开始忽略中段信息,回答质量下降,Token消耗也高;“太短”的表现是模型缺少背景知识,回答变得泛泛而谈。
排查时要先看检索环节。如果TopK的文档片段和问题相关性低,那说明切分或Embedding选型有问题;如果相关文档已经召回但模型没用到,那说明拼接位置或上下文量有设计问题。我一般把“参考信息”放在Prompt中段偏后,紧贴任务指令,因为模型对Prompt末尾和开头的注意力最强。另外一个实用技巧是限定参考材料的总字符数,比如检索召回后按字数截断,宁可少给也不给噪声。
4.3 Agent工具调用时参数传错
Agent系统里最常见的问题:模型明明注册了一个query_shipping_status工具,但在调用时把order_id传成了订单号这个自然语言短语,或者把多个工具名混在一起调。
我的排查经验是:先检查工具schema是不是够“傻大白”。工具描述要和模型“讲人话”:比如“order_id是用户订单号,通常是10位字母数字组合。”参数描述越具体,模型传错的比例越低。还要在工具调用环节加一次“参数格式校验”,不合法就自动触发澄清追问,而不是傻乎乎地拿脏数据去查业务系统。这一步在工程上是防止脏数据污染整个链路的关键闸门。
4.4 测试集全过但线上效果打折扣
这是最让人头疼的情况。离线评估结果一片大好,上线后却被业务方反馈“答案质量不稳定”。
根因往往有两个。第一个是测试集和真实分布的偏差——你精心设计的100条测试用例,和线上真实进线的用户提问在风格、长度、复杂度上完全不同。解法是定期从线上抽真实工单加入评估集。第二个是“上下文污染”——真实场景中,RAG有概率召回不相关内容,模型会用这些噪声信息编出错误答案;而测试集里往往假设检索结果完美。解法是给RAG接上“相关性阈值”,分数低于阈值的片段不往Prompt里塞,宁可信息少也别喂垃圾。
4.5 各层问题排查优先级速查表
| 现象 | 优先排查环节 | 常用手段 |
|---|---|---|
| 回答与事实不符 | 数据层/检索层 | 检查召回文档相关性,检查知识库版本与更新机制,必要时加相关性阈值 |
| 输出格式混乱 | 模型层 | 开启JSON模式,增加解析兜底,降低temperature |
| 回答泛泛无重点 | 上下文层/评估层 | 检查上下文是否含关键业务信息,调整切分窗口与TopK |
| 分类准但摘要差 | Prompt层 | 把摘要指令单独成段,明确“讲人话、不超过X字” |
| Agent步骤突然跑偏 | 控制循环层 | 加步骤上限、加参数校验、给模型更严格的工具描述 |
| 线上偶发变差 | 评估层/监控层 | 建立线上抽样回测流程,把坏case回流进评估集 |
有一个绕不开的底层认知:你不可能用一个万能Prompt解决所有问题。AI应用的问题定位,必须基于“分层排查”的思维——模型只是链路里的一环,数据问题、检索问题、编排问题都可能是主角。把这个认知刻在脑子里,你的排查效率会高很多。
写在最后的一点个人体会
带过几个AI项目之后,我最大的感受是:AI工程真正难的地方,不是那些炫酷的模型,而是一个个不起眼的工程细节。上下文怎么管理、输出坏了怎么兜、效果怎么评估、线上怎么回归,这些问题没有一个会让Demo变得好看,但每一个都决定产品能不能真正活下来。如果你正在从零学这门工程,我的建议是别贪多、别追新。用最朴素的工具,把一个真实的业务场景跑通,再一轮轮地迭代和补齐。踩过的坑、补上的细节,到最后都会变成你自己头脑里那套“工程直觉”——这是任何课程都没法直接教你的东西。