☰
AI工程从零到实战:Prompt、Agent与RAG的完整落地指南
2026/9/29 18:49:29 网站建设 项目流程

我最早接触 AI 工程的时候,心里想的很简单:把大模型的接口调通,传一段提示词进去,拿到结果,这不就完了吗。真正做完 "ai-engineering-from-scratch" 这条线之后我才意识到,跑通一个 demo 和做成一个"工程"之间,差着十万八千里。Prompt Engineering、Agent、RAG、评估、部署、成本控制,每一块都要单独吃透,而且互相咬合得很紧。这篇文章就是我在这条路上从零走到能独立交付 AI 应用的完整记录和思考。如果你也是刚起步,想搞清楚 AI 工程到底在做什么、怎么一步步搭建自己的技术栈和项目骨架,那这篇内容应该能帮你少走不少弯路。

1. 先弄清楚:AI 工程到底在工程化什么

很多人以为 AI 工程 = 调用大模型 API,这是个很危险的误解。API 只是其中最底层、最容易搞定的一环。真正难的是 API 之外的整套体系:怎么设计输入让模型稳定产出、怎么让模型和外部工具/数据协作、怎么衡量一次改动到底是变好了还是变坏了。所以我在从零开始的时候,做的第一件事不是写代码,而是把"AI 工程"拆成三个核心支柱,想清楚自己要补哪几块。

1.1 上下文工程:决定模型"看到什么"

第一个支柱是上下文工程。传统软件里,函数收到什么入参、得到什么返回值,逻辑是完全可控的;但在 AI 工程里,模型能接触到的信息完全取决于你在提示词里给了什么、从知识库里检索了什么、让 Agent 调了什么工具拿回了什么。这个"看到什么"的过程,决定了模型能产生什么水平的输出。

我见过太多失败的 AI 项目,问题不是模型不够聪明,而是上下文给得太粗糙。比如直接丢一句"帮我写个方案",模型没有背景、没有约束、没有示例,产出自然泛泛而谈。做上下文工程的核心,就是把模型从"一个什么都知道但什么都不精的通用大脑"变成"一个了解你业务背景、知道输出规范的专业助理"。这其中包括 prompt 设计、RAG 检索内容的选择、多轮对话历史的截断策略等,每一项背后都有大量细节。

1.2 调度与工具调用:决定模型"能做什么"

第二个支柱是任务编排。模型本身只是一个文本生成器,它不能主动去查数据库、不能发 HTTP 请求、不能操作文件。但靠 Function Calling 和 Agent 机制,可以把模型当作"决策大脑",让它决定调用哪个工具、按什么顺序执行,然后由程序去真正落地这些操作。

这一层解决的问题是:AI 从"聊天"变成"干活"。同样是"帮我查一下上个月的销售数据并生成分析报告",纯聊天只能得到一份编造的数据;而做好调度和工具调用之后,模型会先调用查询接口拿到真实数据,再基于数据生成分析。这一步能力跨越,才是 AI 工程和普通聊天机器人的分水岭。

1.3 评估与迭代:决定项目"能不能长期用"

第三个支柱是评估。这往往是新手最忽略、但决定项目生死的一环。传统软件每行代码的行为都是确定性的,测试很好写;而大模型的输出是概率性的,同一个提示词每次返回都可能不一样。如果没有一套评估机制,你根本不知道改了一版提示词之后效果是提升了还是下降了,不知道给知识库加了几篇文档会不会引入错误回答,"优化"就变成瞎撞运气。

我把这三个支柱想明白之后,整个学习路径立刻清晰了。后续所有的练手项目,都是在围绕这三个支柱做闭环。这也是我在这个系列里最先分享这段认知的原因——方向对了,后面做的每一步都有积累效应。

2. 从零起步:我的 AI 工程基础环境清单

搞 AI 工程不需要一步到位地搭一套重型系统,但也不建议什么工具都不用、全靠裸调 API。合理的起步组合,应该具备三个特征:足够轻、足够通用、能随项目成长。下面是我的基础环境,每一项都说明为什么这么选。

2.1 编程语言与项目环境

首选还是 Python。不是说别的语言不行,而是 AI 生态的工具链、示例代码、社区资料八成以上都围绕 Python 展开,碰到问题能搜到的解决方案最多。版本我直接用了 3.11,兼容性最好。项目管理上我用uv来管理依赖,比 pip + requirements.txt 舒服太多,锁依赖、建虚拟环境都是一条命令的事。

# 安装 uv(macOS/Linux) curl -LsSf https://astral.sh/uv/install.sh | sh # 初始化新项目 uv init ai-engineering-workspace cd ai-engineering-workspace uv add openai python-dotenv qdrant-client langchain-core

这里有个我踩过的坑:一开始不要急着加langchain全家桶。LangChain 抽象层很多,文档又散,新手直接上手很容易被各种概念绕晕。我更推荐先用openai官方 SDK 把核心链路跑通(因为绝大多数模型服务都兼容 OpenAI 协议),等你真的理解了底层机制之后,再根据实际痛点决定要不要引入框架。这是"先懂原理,再选工具"的原则。

2.2 模型 API 的统一抽象

我一开始同时接了好几个模型服务,比如 DeepSeek 和 OpenAI 的模型,发现如果每个服务分别写一套调用代码,会非常痛苦。解决方式很简单:因为大家都兼容 OpenAI 的 Chat Completions 协议,所以我只封装了一个统一的LLMClient,通过切换base_url和model字段来决定实际调用哪家模型。

from openai import OpenAI import os def get_llm_client(model_name: str = "deepseek-chat"): """按需切换模型服务商。""" if model_name.startswith("deepseek"): return OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com/v1" ), model_name else: return OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url="https://api.openai.com/v1" ), model_name client, model = get_llm_client() resp = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)

这种抽象带来一个特别实际的好处:模型服务商降价、出新模型、或者某个服务不稳定时,我只需要改一个环境变量就能切换,业务代码完全不动。做 AI 工程,一定要把"模型本身"当成可替换的组件,而不是把项目焊死在某一家服务上。

2.3 向量数据库与本地模型工具

做 RAG 类应用,向量数据库是刚需。我选的是 Qdrant,因为它是专门为向量检索设计的,支持过滤、持久化、性能好,而且有本地嵌入模式,单机项目用起来很方便。如果你只是学习验证,Chroma 更轻,装完就能跑;但一旦项目上了点规模,Qdrant 的稳定性优势就体现出来了。

本地模型工具我装了 Ollama。它解决的是两个问题:一是离线环境下的开发调试,二是模型隐私和数据合规问题。我在开发阶段经常先把请求切到本地小模型,业务逻辑调通了再切回云端大模型,这样能省掉大量 API 调试费用。Ollama 的接口也是兼容 OpenAI 协议的,所以上面那个get_llm_client函数同样适用,只要把base_url指到http://localhost:11434/v1就行。

这套环境清单不是一步到位的,而是我在不同阶段逐步补上的。先有 Python 和统一的模型客户端,能跑通最基础的"输入提示词→拿到结果";接着有向量库,能做知识库检索;最后有本地模型,能离线开发。你可以根据自己的项目需求决定先加哪一块,但底层思路是一致的:每一层都要可替换、可测试、可观测。

3. Prompt Engineering 不是写提示词,是设计上下文窗口

很多刚接触 AI 工程的人把 Prompt Engineering 等同于"把话说清楚一点",这远远低估了它的复杂度。我在实践里把它重新定义为:在有限的上下文窗口内,对模型要接收的信息做资源分配和结构设计。模型一次能处理的 token 是有限的,你要让它在有限的"视野"里,看到最相关的背景、最明确的任务、最严格的约束,才能拿到最稳定的输出。

3.1 一套我用了很久的 Prompt 结构模板

我从零开始打磨了很多版提示词,最后沉淀了一套通用的结构模板,适用于大多数任务型场景。它分为六层,顺序尽量不要乱:

  1. 角色定义:规定模型以什么身份、什么专业立场来回答。
  2. 背景信息:给模型提供业务上下文,让它"懂行"。
  3. 任务指令:用动词开头,明确要求模型做什么,必要时拆分步骤。
  4. 输入数据:把需要处理的内容和指令区隔开,避免混淆。
  5. 输出格式约束:告诉模型用什么样的结构输出,包括要不要 JSON。
  6. 边界与禁区:说明哪些事不能做、哪些内容必须忽略。

看一个实际例子。假设我要让大模型做技术方案评审,早期的提示词是:"请评估这个技术方案是否可行。"后期的提示词变成了这样:

你是一名有十年经验的系统架构师,擅长从可维护性、安全性、性能三个维度评审技术方案。 背景信息:当前团队维护一个日请求量百万级的订单系统,技术栈以 Python/FastAPI/PostgreSQL 为主,近期出现接口响应变慢的问题。 任务指令: 1. 阅读技术方案全文,识别方案中涉及的关键技术选型; 2. 分别从可维护性、安全性、性能三个维度给出评估; 3. 指出方案中风险最高的三个点,并给出替代建议。 输入数据(用 XML 标签包裹,区分于指令): <proposal> (技术方案原文) </proposal> 输出格式:使用 JSON 输出,字段包括 overall_score、dimension_scores、top_risks、suggestions。 边界:如果方案中缺少必要信息,请明确说明"信息不足",不要臆测。

改动前后的差异是巨大的。加了角色和背景之后,模型的输出明显更贴近场景;加了输出格式之后,结果可以直接被下游程序解析;加了边界之后,模型不会再硬编造缺失的信息。这就是 Prompt Engineering 的工程价值:不是让模型"更聪明",而是让模型"更可控"。

3.2 Few-shot 示例与思维链的实际用法

除了这套结构模板,还有两个技巧我几乎每个正式项目都会用:Few-shot 示例和思维链提示。

Few-shot 的核心逻辑是"让模型模仿"。如果你对输出格式有很强的要求,与其用文字描述"请按以下格式输出",不如直接给一两个栗子,比如:

以下是三个输入-输出对的示例,请模仿其逻辑和格式处理后续输入: 输入1:重构结算模块需要多长时间?输出1:{"estimate_days": 5, "risks": ["依赖第三方支付接口", "涉及优惠券兼容"]} 输入2:接入新的短信服务商评估一下?输出2:{"estimate_days": 3, "risks": ["对接文档不全", "需要测试环境"]}

模型看到这种示例之后,输出的格式稳定性会明显提升。原理不复杂,大模型本质上是"下一个词预测器",给它看到高质量的"范例",它就更倾向于沿着这套模式继续生成,比抽象规则的约束力强得多。

思维链(Chain-of-Thought)则是让模型一步步推理再给结论。对复杂的分析类任务,这招效果极其明显。最简单的用法就是加点提示,比如"请一步步推理,再给出最终结论",或者干脆要求模型输出时包含reasoning字段。大模型在被迫显式展开推理过程时,中间的错误会被逐步纠正,而不是一口气"跳"到最终答案,尤其对数学计算、逻辑判断类任务,正确率能提高不少。

3.3 把 Prompt 当代码管理,别当记事本

这是我在这个系列里最想强调的一点。很多人改 Prompt 像在聊天里改,改完就忘,回头对不上哪个版本效果好。我的做法是:每个正式项目的 Prompt 都有独立文件,按v1.md、v2.md这样命名,纳入 Git 管理。每次改动必须附带两条记录:改了什么,为什么改。这样项目出问题时,可以快速回滚到之前的好版本,也能从版本历史里复盘效果曲线的变化原因。

更进一步,一个合格的 AI 工程链路里,Prompt 不该只存在于"人写的一大段话"层面。复杂的业务场景往往要有多个子 Prompt 配合,比如一个 Agent 有系统 Prompt、工具调用 Prompt、输出整理 Prompt,它们之间互相衔接。把这些拆成独立模块、用代码去组装,才是最工程化的做法。

4. 从"对话"到"自动化":用 Agent 和工具调用打通业务流程

如果说 Prompt Engineering 解决的是"让模型回答得更准",那 Agent 和工具调用解决的是"让模型真正把事情办了"。这个跃迁是整个 AI 工程里最有意思的部分,也是项目价值感飙升的节点。我一开始只会做聊天机器人,拼了老命也只能回答得更好一点;直到学会 Function Calling 之后,才真的做出了能让同事省时间去用的工具。

4.1 Function Calling 的原理,一次讲透

Function Calling 并不玄乎。常规对话时,你传 messages 给模型,模型返回文本;开功能函数调用时,你除了传 messages,还要附上一组"可用工具"的声明(函数名、参数描述、参数类型)。模型不会真的执行函数,它只是在生成回复时,如果判断应该调用某个工具,就会返回一个结构化的"调用意图",通常是这样:

{ "tool_name": "search_order", "arguments": { "order_id": "A123456" } }

你的程序拿到这个 JSON 后,自己执行真实的search_order函数,再把执行结果作为一条新的消息传回给模型,模型才会基于真实结果生成最终回答。这个过程叫"工具调用循环"。

def call_model_with_tools(messages, tools): resp = client.chat.completions.create( model=model, messages=messages, tools=tools # 核心:告诉模型有哪些工具可用 ) return resp.choices[0].message def run_agent(user_query): messages = [{"role": "user", "content": user_query}] tools = [{ "type": "function", "function": { "name": "search_order", "description": "根据订单号查询订单状态和金额", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单号"} }, "required": ["order_id"] } } }] # 最多循环 5 轮,防止模型陷入死循环 for _ in range(5): msg = call_model_with_tools(messages, tools) if msg.tool_calls: for tool_call in msg.tool_calls: args = json.loads(tool_call.function.arguments) result = search_order(args["order_id"]) # 真正执行工具 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result) }) continue return msg.content

这段代码其实就是 Agent 最核心的骨架。关键是"循环上限"这个细节:模型在工具调用链条里,偶尔会因为中间结果不符合预期而反复调用同一个工具,如果没有轮数上限,会把 API 费用烧穿。所以我后来固定所有 Agent 循环都带max_rounds参数,这是我花了不少 API 费用才换来的教训。

4.2 多步任务拆解:从"一步调用"到"自主规划"

最简单的 Function Calling 是单轮:模型决定调哪个工具、拿结果、回答。但真实业务往往要连续调用多个工具。比如用户问:"对比一下最近两周每天的订单量和退款量,给我一个趋势总结。"这个任务至少要:取订单数据、取退款数据、分别按天聚合、再综合生成总结。如果让模型自己规划,就是一个典型的多步 Agent 场景。

我做这个项目的思路不是一上来就追求"全自动自主规划",而是先给它设计好"可执行的子任务模板"。Agent 的"规划能力"一半来自模型,一半来自你给的工具设计。工具设计得好,模型自然能组合出正确的调用链。这里有三个工具设计原则:

  • 单一职责:一个工具只做一件事。不要把"获取订单数据"和"生成报告"塞进一个函数。
  • 参数描述要写清楚:模型靠 description 理解工具的用途,写得太糊,它就会乱调。description 要包含参数含义、典型用法、返回结构。
  • 提供能自纠错的工具:常见业务里,单查一个订单号可能查不到,这时提供"列表搜索"类工具会比只提供精确查询更实用,模型在找不到时能自动降级。

4.3 要不要直接上 LangChain / LangGraph

我遇到最多的问题就是:既然有 LangChain/LangGraph 这么好的 Agent 框架,为什么还要自己写循环?我的建议是分层看待。如果你完全不懂底层,直接上框架会遇到灾难性的调试体验。因为框架把工具调用、记忆管理、任务规划都封装了起来,一旦某一步出问题,你根本不知道是模型傻了,是工具定义有问题,还是框架的状态管理出了 bug。

我自己的路径是:先手工实现一两版"裸循环",把 tool_calls、tool 消息回传、多轮上下文这些概念彻底吃透;然后再接入框架,让框架帮我处理复杂状态流(比如 LangGraph 的分支和持久化)。走到这一步,框架就成了效率工具,而不是黑盒。具体到选型,小项目我用 LangChain 的create_agent快速搭建,大项目或者需要复杂状态流转时用 LangGraph,自己控制StateGraph。

5. 一次走通:从零做一个内部技术文档问答系统

聊了这么多理论,我拿一个我实际从头做过、也认为最适合从零练手的项目作为完整示例:内部技术文档问答系统。为什么选它?因为它一次性覆盖了 AI 工程的三大支柱——Prompt 设计、RAG 检索、Agent 工具调用,而且业务逻辑足够简单,任何团队都能复现。这个项目就是从零开始练手 AI 工程的最佳样例。

5.1 需求定义和整体架构

需求很简单:同事们总在问"部署流程怎么写""这个服务的接口规范在哪",与其到处翻文档,不如做一个机器人,用自然语言提问,它基于内部文档回答,并且必须提供引用来源。

整体上分成两个阶段。离线的"知识入库"阶段:把散落的 Markdown/PDF 文档切成小块,做向量化,存进 Qdrant。在线的"问答"阶段:用户提问,系统先从向量库检索相关片段,再把片段拼进 Prompt,交给大模型生成答案。整个链路如下:

  1. 文档加载 → 2. 文本切块(chunking)→ 3. 向量化嵌入 → 4. 存入向量库
  2. 用户提问 → 6. 检索 Top-K 相关片段 → 7. 组装带上下文的 Prompt → 8. 生成答案并附带引用

这个架构的价值在于:不会涉及任何复杂微调,但能真真切切体验到 AI 工程里"数据质量决定模型质量"的深水区规律。

5.2 文本切块和向量库写入的关键细节

文本切块是 RAG 项目里最容易被低估的环节。切得太小,语义不完整;切得太大,冗余信息太多,还容易撑爆上下文。我实测下来,对技术文档这种结构化文本,按 Markdown 标题层级切分的效果,远好于按固定字符数硬切。原因很简单:标题本身就是天然的语义边界。我的切块策略是:

  • 优先按## / ###标题切分;
  • 每个切片控制在 300-800 个字符,按段落自然截断;
  • 同一标题下内容过长就再按段落拆分,并在每条切片前保留所属的标题路径,作为检索时的上下文前缀。

这个"保留标题路径"的做法是我实验后的得意之处。它相当于给每个向量片段加了"路径信息",检索时匹配到的片段自带章节上下文,大模型的回答质量会有肉眼可见的提升。

from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct client = QdrantClient(path="./qdrant_data") # 本地持久化模式 client.recreate_collection( collection_name="tech_docs", vectors_config=VectorParams(size=1024, distance=Distance.COSINE), ) # 假设 chunks 是切好的文本切片列表, embeddings 是向量数组 points = [ PointStruct(id=i, vector=embeddings[i], payload={"text": chunks[i], "source": "deploy.md"}) for i in range(len(chunks)) ] client.upsert(collection_name="tech_docs", points=points)

向量维度取决于你用的嵌入模型,我用的模型是 1024 维,这里要保持一致。检索的时候,我通常取 Top-5 的片段,但不会直接全部塞给模型,而是先用简单的相关性过滤和去重,去掉明显不匹配的噪音片段,这一小步会让最终回答的精确率显著提升。

5.3 Prompt 组装与引用溯源设计

检索到相关片段之后,Prompt 的组装方式直接决定了答案质量。我早期的做法是简单把所有片段拼一块,结果模型经常答非所问。后来按这个结构重写,效果稳定了很多:

你是公司的技术文档助手。请根据以下文档片段回答问题。 如果文档片段中没有相关信息,请直接回复"文档中没有找到相关内容",不要自行发挥。 回答时标注信息对应的文档来源编号,格式如 [1][2]。 文档片段: [1](来源:deploy.md,内容:……) [2](来源:api-spec.md,内容:……) 用户问题:……

这里做了一次很关键的"边界收紧":明确要求模型在信息不足时直接承认不知道。这听起来简单,却让我这个项目的"幻觉率"降了一大截。大模型天生有"有问必答"的倾向,你必须在 Prompt 里给它一个可以说"不知道"的安全出口,它才敢不硬编。

引用溯源是这类系统能被业务方接受的硬性条件。我的做法不复杂:给每个文档片段在检索时带上 source,在组装 Prompt 时给每个来源编号,模型输出时我要求它引用编号。因为编号和真实文档对应关系由代码维护,我可以在后处理时把编号替换成文档链接,展示给用户。这套机制让同事用起来有安全感,因为每个答案都能追到源头。

5.4 这个项目教会我的三个经验

做完这个项目之后,我总结了三条通用经验,可以说适用于绝大多数从零开始做 AI 应用的人:

第一条,数据清洗比模型选型更重要。我中途换过一次嵌入模型,效果变化不大;但当我花了半天时间把源文档里的过期命令、错误表格修掉之后,回答质量的提升立刻就能感受到。模型是通用能力,数据是领域知识,后者才是差异点。

第二条,没有评估就没有优化。在接入评估之前,我改检索参数全靠感觉——加 window_size,去掉一个过滤器,看似合理,但不知道是变好还是变坏。后来我建了 30 条测试问题集,每次改动跑一遍,用准确率和"引用命中率"两个指标来衡量,优化才真正走上了正轨。

第三条,先跑通最小闭环,再谈复杂优化。这个项目第一个可运行版本只用了不到一百行代码,效果已经能应付日常使用。后来我加的缓存、查询改写、多路召回,都是建立在"用户已经在用、反馈真实"的基础上。不要一开始就陷入完美主义,要从一个能用的版本开始演变。

6. 本地部署与云端 API:这笔账要算清楚再选型

做 AI 工程,模型部署方式是个绕不开的决策点。我刚起步时倾向于什么都用云端 API,方便是方便,但心里总不踏实;后来接触本地部署之后,发现这两种方式根本不是替代关系,而是服务于不同场景。搞清楚它们的成本和优缺点,可以帮助你省下一大笔冤枉钱。

6.1 两者之间的核心差异,我列了一张实测对比表

我同时用过云端 API(DeepSeek、OpenAI 这类)和本地部署(Ollama 跑开源模型),从几个关键维度做了对比。这张表是我自己试出来的感受,不代表所有情况,但对选型很有参考价值:

对比维度云端 API本地部署(Ollama + 开源模型)
起步门槛极低,注册拿 Key 就能调需要配置环境、下载模型,略麻烦
模型能力大模型多,指令跟随和推理能力强可用开源模型多,顶尖能力稍弱
单次响应质量稳定取决于模型参数规模和量化等级
隐私与合规数据经过服务商,需谨慎评估数据不出本机,合规压力小
成本结构按 token 计费,用越多越贵主要是一次性硬件成本 + 电费
离线可用不支持完全离线可用
运维复杂度不用管需要管理模型存储、显存、更新

重点解释一下"成本结构"这一行。云端 API 用熟了之后,最容易失控的不是单次价格,而是积少成多的总消耗。我自己有个小项目,每天约 200 次问答,每次平均消耗 1500 token 输入、500 token 输出。按我当时用的模型价格粗算:输入每百万 token 约 2 元,输出每百万 token 约 8 元,一天的模型费用大概是(200×1500/1e6×2) + (200×500/1e6×8) = 0.6 + 0.8 = 1.4元,一个月也就四五十块钱。听着不贵对吧?但同样的量如果换成价格高好几倍的模型,或者让 Agent 频繁调用工具导致一次问答消耗上万 token,月费用就可能破千。所以做生产项目之前,一定要先估算 token 消耗量,再确定模型选型。

6.2 本地部署的显存计算与量化选择

本地部署最大的门槛是显存。以 llama 系模型为例,一个 7B 参数模型,FP16 精度下权重约占 14GB 显存,加上 KV Cache 和运行时开销,实际需要至少 16GB 显存的显卡才能顺畅运行。7B 模型的量化版本,比如 Q4_K_M 量化后权重约占 4-5GB,8GB 显存的显卡也能跑,只是速度会慢一些。

所以我给不同需求的配置建议是这样的:

  • 只是开发调试、跑通逻辑:随便一台带 8GB 显存的机器,Ollama 跑 qwen2.5:7b 这类量化模型就够。
  • 生产级小流量应用、需要私有化部署:建议 24GB 显存及以上,跑 13B 或 32B 量化模型,能兼顾效果和合规。
  • 追求顶尖效果:本地部署的成本会变得很高,这时反而应该回到云端 API,用顶级模型处理少数高价值请求。

你在 Ollama 里换量化级别也很简单,比如ollama run qwen2.5:7b-q4_K_M这种标签区分精度,实际跑完对比一下 BLEU、相关性和延迟,就知道哪个档位适合自己。

6.3 我现在的混合部署策略

踩过一轮坑之后,我现在的策略是"混合部署":大多数场景走云端 API,享受顶级模型的能力;涉及敏感数据或离线场景时切到本地模型;日常开发调试时优先用本地模型,节省 API 费用。这个策略得益于第一章那套统一客户端抽象,切换部署方式只是改环境变量的事。

如果你也是一个人做项目或者小团队起步,我不建议一开始就买大显卡。先用云端 API 快速验证产品价值,等真正跑出用户量、也积累出离线部署的需求,再上本地部署的硬件。买硬件永远要等需求明确之后,AI 硬件迭代太快,早买必然亏。

7. AI 工程另一半江山:测试、评估与持续迭代

很多教程讲到"项目能跑"就结束了,但真实的 AI 工程里,上线只是开始。模型输出的概率性意味着每一次改 Prompt、调参数、换模型都可能导致行为漂移,不建立可靠的评估体系,你很快就不知道自己改出来的是好是坏。这一块我把它称为"AI 工程的另一半江山",没有它,前面所有工作都建立在流沙上。

7.1 建立回归测试集:把模型输出"钉"住

我第一次意识到评估的重要性,是在一次惨痛经历之后:我调了一个 Prompt 的措辞,自认为只是"优化表达",结果线上问答的正确率掉了 15%,用户立刻来抱怨。那时我没有任何测试,完全发现不了问题。后来我建立了回归测试集,才算真正治好了这种"盲改"的毛病。

做法很简单,但很有效:针对你要稳定的每类任务,准备 20-50 条典型输入,标注期望行为。期望行为不一定是完整精确答案,可以是检查点。比如:

测试用例期望检查点
"部署文档里步骤1是什么?"答案包含"拉取代码";引用来源为 deploy.md
"订单超时怎么办?"答案提到限流与重试机制;不得编造支付渠道
"推荐一下前端框架"答案基于文档内容,不出现文档外的品牌推荐

每次改动后,把整批测试集跑一遍,统计每条是否通过,对比基线。如果一次改动让用例通过数上升,那就是真优化;如果下降,立刻回滚。这套流程让我的 Prompt 迭代从"感觉好用"进化到了"数据证明好用"。

7.2 LLM-as-Judge:让大模型评估大模型

除了人工标注的规则检查点,还有一个高级技巧:让一个强模型当"裁判",去评估另一个模型的回答质量。比如评估两个版本的回答哪个更好,可以把两个回答连同用户问题、参考文档一起丢给裁判模型,要求它从相关性、完整性、忠实度三个维度打分。

def evaluate_answer(question, answer_a, answer_b, context): judge_prompt = f""" 你是一个严格的 AI 评估员。请根据提供的资料,从相关性、完整性、忠实度三个维度比较以下两个回答。 资料背景:{context} 用户问题:{question} 回答A:{answer_a} 回答B:{answer_b} 请输出 JSON,格式: {{"scores": {"A": 0-10, "B": 0-10}, "better": "A或B", "reason": "简要理由"}} """ resp = judge_client.chat.completions.create( model="gpt-4o-mini", # 裁判模型 messages=[{"role": "user", "content": judge_prompt}], response_format={"type": "json_object"} ) return json.loads(resp.choices[0].message.content)

这种方式的优势是省人工、可扩展。但要注意一个坑:裁判模型也会有偏好,比如偏好更长的回答或更喜欢"一本正经"的语气。所以裁判 Prompt 必须明确要求"不要因为长度差异而偏向回答,只看信息质量",而且最好让裁判看到资料原文而不是只听回答自说自话,这样才能评估"忠实度"而不只是"流畅度"。

7.3 指标分层:业务指标优先于模型指标

在评估体系搭建过程中,我最大的认知转变是:模型技术指标不等于业务价值指标。比如 RAG 系统的召回率提升了 5%,听起来是好事,但用户是否觉得好用,取决于他有没有更快找到答案、答案是否让他少跑一趟。所以我在监控体系里分了两层:

  • 模型层指标:检索命中率、答案相关度、幻觉率、格式合规率、平均延迟。
  • 业务层指标:用户问题解决率、平均对话轮数(越少解决越好)、用户反馈评分、功能启用率。

两层指标要一起看。曾经我优化了一个 Agent 的调度逻辑,模型层指标全面变好,但业务层"用户放弃率"反而升高了——因为多轮工具调用让响应时间变长,用户没耐心等。这就是只盯模型指标会踩的坑。AI 工程要以最终用户体验为准绳,而不是以某个漂亮的模型指标为准绳。

7.4 版本管理与线上灰度

最后是发布层面的稳定手段。AI 项目的 Prompt 和模型配置必须像代码一样进入版本管理,我是在 Git 仓库里维护一套prompts/和configs/目录,所有改动都是 MR 流程。发布时使用"金丝雀发布"策略:先在 10% 的流量上跑新版本配置,对比旧版本的业务指标,确认没问题后再全量切换。

这套机制初看有点重,但对任何一个要长期维护的 AI 应用都是必要的。因为模型服务商会更新模型、你的知识库会增删文档、业务会调整需求,三样东西都在变,评估和版本管理是你唯一能握住的"锚"。

8. 这条路上我踩过的坑,和总结出的几条实战规律

最后一部分,我不按教程体系来讲了,纯粹分享一下从零做到现在,我积累的几条接地气的实战规律。这些规律不是从文档里读来的,都是真金白银的 API 费用和深夜排查换来的,希望你能直接复制。

8.1 不要过度设计:先跑通一个"丑但能用"的版本

我最初犯的最大错误,就是什么都想做到完美:第一版就想做多 Agent 协作、加记忆、做用户反馈循环。结果开发周期拉得很长,代码复杂到我自己都难以调试,最后核心体验反而不如一个简单的问答效果好。后来我强制自己遵循"最小可用闭环"原则:任何功能,先做一个不需要完美但能用的版本,让真实用户使用,再根据反馈迭代。

不要误会,我不是让你交付劣质产品,而是让你把"探索和学习成本"降到最低。一个只有 100 行代码的 RAG 问答机器人,和一个 2000 行的多 Agent 系统,前者的用户反馈更清晰,迭代路径更明确。复杂永远应该建立在简单被验证之后,而不是之前。

8.2 Prompt 和代码一样需要重构

我在维护了几个月的 AI 项目之后发现,最初的完整 Prompt 变得臃肿不堪,各种历史需求堆叠出来的约束让模型反而变得迟钝。这就像传统软件里没有重构的代码累积技术债一样,AI 项目同样会累积"提示词债"。

我的规律是:每个季度系统性做一次 Prompt 清理。删掉已经满足历史需求的约束,把重叠的指令合并,把一段长 Prompt 按职责拆成"系统角色 + 业务规则 + 输出模板"三个独立模块,再重新跑一遍回归测试集。几次重构之后,我发现模型的表现通常比"带病运行"时好不少,token 消耗也跟着降下来。

8.3 工具调用链越长,错误越会累积,所以永远要有兜底

做 Agent 项目后,我彻底理解了"链式调用会放大错误"这回事。第一轮工具返回了错误字段,第二轮模型基于错误字段推理,第三轮输出就会歪到离谱。所以我给所有 Agent 系统都加了两条兜底:

第一条,工具结果必须做校验,不能直接把原始返回值塞给模型。比如工具返回空值、超时、异常时,我给模型补一句"调用失败,原因如下,请重试或告知用户稍后再试",这让系统在面对异常时不会硬编。

第二条,所有 Agent 必须有"逃生通道"。当模型连续两轮找不到工具调用方向,或者超过最大循环次数,应该直接停止并返回"这个问题我需要更多信息来帮你处理",而不是无限空转。看似不起眼,这两条兜底让系统的可用性提升了一个数量级。

8.4 从零开始的心态:把它当成一套方法,而非一门手艺

最后说说心态。很多人问我要不要学微调、要不要啃机器学习理论,我的答案是:先不用。做 AI 工程的第一步,是学会把已有的强大模型"接入"到你的业务语境里,这更像一种系统集成能力,而不是从零训练模型的能力。你需要掌握的,是如何定义问题、如何组织数据、如何设计上下文、如何评估结果、如何控制成本,这套方法比任何一个具体模型都更重要。

也正因为模型层更新迭代太快,今天学会的具体 API 调用方式可能半年后就变了,但这套工程方法论是相对稳定的。我建议你把注意力放在"问题定义、数据准备、评估闭环、系统设计"这些不会过时的能力上。这条路不是看几篇文章就能走完的,但每走一步,后面能做的事都会上一个台阶。希望这套从零开始的经验,能陪你走好最开始那段路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询