先聊个比较实在的话题:现在到处都在说 AI 工程、AI 应用开发,打开招聘软件满屏都是“大模型工程师”“AI Agent 开发”这类岗位,可真正能把一条 AI 应用从零到一、从代码到线上跑通的人,其实比想象中少很多。
原因很简单,市面上绝大多数教程只教你调 API、套模板,最多教你招一个聊天机器人,但真实工程里你遇到的问题往往是:回答质量不稳定怎么办、检索出来的东西不对怎么办、模型响应太慢怎么办、上线之后怎么观察它有没有退化。
我把“ai-engineering-from-scratch”这条路线从头到尾走了一遍,从对一个语言模型只知道“输入一句话就能返回一句话”,到做出带知识库的 RAG 问答、带工具调用的 Agent,再把它容器化部署上线做监控评估。这篇文章就是把这条路线上的核心知识点、选型逻辑、实操步骤和踩过的坑一次性讲清楚,希望能帮你少走几个月的弯路。
1. 先想清楚:AI 工程到底在解决什么问题
1.1 它和“调 API”不是一回事
很多人有个误解:AI 工程就是拿大模型 API 拼聊天窗口。如果真这么简单,那这个岗位就不会开出这么高的薪资了。
我理解的 AI 工程,是把一个大模型从一个“试用阶段的东西”变成“稳定生产系统”的全部工作。它至少包含五个板块:
- 提示工程:把业务需求翻译成模型能理解、能稳定执行的输入。
- 检索增强(RAG):把外部知识、私有数据交给模型,让它回答得准而不是瞎编。
- Agent 能力:让模型不只是“说”,而是能调工具、执行动作、完成多步任务。
- 部署与推理优化:把模型放到服务里,控制延迟、吞吐和成本。
- 评估与监控:用数据说话,保证每次改动不会让效果变差。
这五块不是割裂的,是一条完整链路。从零开始学的时候,千万别光抓着“提示词”这一个点死磕,提示词是基本功,但不是全部。
1.2 从零开始的正确路径是什么
我见过太多人一上来就啃 Transformer 论文、从头复现 LLaMA,结果被数学公式劝退,然后就没有然后了。
作为一个过来人,我给的建议是**“应用驱动,倒推知识”**:
- 先跑通一个最简单的调用。
- 做一个带知识库的问答系统,理解 RAG。
- 再做带工具调用的 Agent,理解模型怎么和外部世界交互。
- 然后把它部署成服务,理解并发、延迟、成本。
- 最后回来补基础,比如注意力机制、token 原理、采样参数,这时候你再看理论,会觉得全都对得上。
这条路最大的好处是:每个阶段都有看得见的成果,人不容易放弃。
2. 入门技术栈与工具选型:别被“全家桶”忽悠了
2.1 提示工程先打底,这是性价比最高的投入
我至今觉得,提示工程是 AI 工程里“投入产出比”最高的一项技能。不需要显卡,不需要买课,不需要复杂的代码,把模型的行为逻辑理解透就行。
核心要掌握的四个方法:
- 角色设定:给模型一个明确的身份和行为边界。
- 少样本示例(Few-shot):与其解释一堆抽象规则,不如直接给 2-3 个输入输出例子。
- 思维链(Chain-of-thought):让模型“先分析再输出”,对复杂推理任务效果提升非常明显。
- 结构化输出:用 JSON Schema 或 XML 标签约束输出格式,方便后续代码解析。
我自己写提示词有个习惯:先写“你是做什么的、你能做什么、不能做什么”,然后给 2 个示例,再进入正式输入。不要写“请”“亲爱的”这些废话,模型不需要礼貌,需要的是清晰。
2.2 应用框架怎么选:别一上来就上框架
现在主流的框架有 LangChain、LlamaIndex,还有 Spring AI 这类偏向 Java 生态的(如果你的团队是 Java 技术栈,Spring AI 很值得关注)。
但我的建议是:前两周亲自用裸 Python 代码调模型,不要碰框架。因为你只有先用纯代码体验过“把文本切开、转成向量、塞进向量库、搜出来、拼给模型”这个过程,你才能真正理解框架帮你省了什么工作量。不然框架对你来说就是一个黑盒,出问题你完全不知道从哪排查。
之后再用框架,也不是全盘照抄。LangChain 我常用到的其实就那几个:DocumentLoader、TextSplitter、Embedding、VectorStore、Retriever、ChatPromptTemplate。其余概念网上吹得天花乱坠,实际生产里用得很少。
2.3 模型部署和推理工具链是分水岭
很多人做 demo 很流畅,一到部署就卡壳。这里我给你一条可以直接照抄的选型路线:
| 场景 | 工具 | 说明 |
|---|---|---|
| 快速实验 / 个人项目 | 云端模型 API | 成本低、上手快,适合验证想法 |
| 私有化部署 / 数据敏感 | vLLM 或 TGI | 高吞吐推理引擎,兼容 OpenAI 接口协议 |
| 中小团队推理服务 | vLLM + FastAPI | 部署灵活,可控性强 |
| 大规模集群 | KServe + K8s | 自动伸缩、多模型管理,运维成本高 |
这里我特别推荐 vLLM,它在吞吐优化上做得非常激进,连续批处理、PagedAttention 这些技术能让同样一张显卡跑出大几倍的请求量。后文我会专门演示怎么用它。
3. 实操:从零搭一个 RAG 知识问答系统
3.1 整体设计思路
假设咱们要做一个“公司内部规章制度问答机器人”。传统的做法是把文档放在某个目录里,让人自己翻,现在我们要让模型基于文档内容来回答。
整体流程拆开就三步:灌知识(索引)、搜知识(检索)、生成答案(生成)。每一步都有对应的工程决策。
我画一个最简链路给你看:
- 读入 PDF / Word / 网页文本
- 按语义切分成长度合适的片段
- 每个片段用 Embedding 模型转成向量
- 向量存入向量数据库
- 用户提问时,把问题转成向量,做相似度检索
- 把检索到的片段和问题拼接成 Prompt
- 模型综合上下文给出答案,并标注来源
3.2 文档切分:细节决定 RAG 的上限
说实话,RAG 系统效果烂,八成问题出在切分上,而不是模型上。切分太粗,片段里包含大量噪声,检索召回不精确;切分太细,单个片段语义不完整,模型也答不到点上。
我在实际项目里常用的策略是**“按结构优先、按长度兜底”**:
- 如果是 Markdown 或带标题的文档,先按标题层级切,尽量保证一个二级标题下的内容作为一个整体。
- 如果没有结构,用固定长度切,中文一般 300-500 字一个 chunk,重叠 50-100 字。
- 不要小看重叠部分,它能避免句子被拦腰截断导致语义丢失。
一个简单的切分代码骨架:
from langchain_text_splitters import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=400, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " "], ) chunks = splitter.split_text(long_text) print(len(chunks))这里的 separator 顺序很重要:先按段落,再按句子,最后按单词。这个顺序能最大程度保留语义完整性。
3.3 Embedding 与向量库选型
Embedding 模型负责把文本变成向量。选型标准有三个:维度、中文效果、部署成本。
- 目前中文场景比较常用的是 BGE、M3E 系列的 Embedding 模型,效果不错而且可以在本地跑。
- 如果你的数据以英文为主,OpenAI 的 text-embedding-3-small 也够用。
- 注意一个坑:检索用的 Embedding 模型要和生成模型区别对待,它不追求“会说话”,只追求“语义距离准确”。
向量数据库的选择,我从三个档位给你参考:
| 档位 | 方案 | 适用场景 |
|---|---|---|
| 入门 | Chroma / FAISS | 个人项目、文档量百万以下 |
| 生产 | Milvus / Qdrant | 需要高并发、复杂过滤 |
| 轻量运维 | PostgreSQL + pgvector | 团队已有 PG,不想引新组件 |
我个人最喜欢 pgvector。因为它不需要单独维护一套数据库,业务数据也在 PG 里,直接就能做 SQL 级别的过滤,比如“只检索部门 A 的文档”,非常方便。
3.4 检索:不是搜出来就完事
检索之后要不要重排序?我的答案是:如果你的数据量超过 1 万条,强烈建议加。
第一次向量检索召回 Top 20,再用一个轻量的 Cross-Encoder 模型对这 20 条做精确打分,取 Top 5 给到模型。这样做的好处是,第一阶段向量检索负责“宽进”,把候选尽量捞全,第二阶段精排负责“精选”,把真正相关的排到前面。
这里附一个完整 RAG 检索端代码:
from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams from bge_embedding import BGEEncoder # 伪代码,按你的模型导入 encoder = BGEEncoder() client = QdrantClient(host="localhost", port=6333) # 建集合 client.create_collection( collection_name="company_docs", vectors_config=VectorParams(size=768, distance=Distance.COSINE), ) # 写入向量 points = [ {"id": i, "vector": encoder.encode(chunk), "payload": {"text": chunk}} for i, chunk in enumerate(chunks) ] client.upsert(collection_name="company_docs", points=points) # 查询 query_vec = encoder.encode("请假流程是什么") results = client.search( collection_name="company_docs", query_vector=query_vec, limit=20, )3.5 生成环节:把 Prompt 拼对,答案质量翻倍
很多人以为生成环节就一段用户问题、一段检索内容拼一起就行了,实际上你把顺序和结构控制好,效果差异非常大。
我的 Prompt 模板长这样:
你是一家公司的行政助手,请根据【资料】中的内容回答用户的问题。 要求: 1. 只能基于资料回答,不要编造。 2. 如果资料里没有答案,明确说“资料中未找到相关信息”。 3. 用简洁的中文回答,可以分点。 4. 最后一行附上你参考的资料编号。 【资料】 [1] 请假三天以内,需部门主管审批... [2] 请假三天以上,需分管副总审批... 【用户问题】我要请一周假,应该找谁审批?这句话其实是 RAG 组合的正确姿势,把检索到的内容放在用户问题之前。实测下来,上下文放在前面比放在后面,模型的回答稳定性更高。
4. 再进一步:把 RAG 升级成 Agent
4.1 为什么 RAG 还不够
RAG 能解决“知识型问答”,但它有个明显的天花板:它只会查,不会做。
比如用户问“帮我查一下这个月的服务器账单,超过预算就发邮件给财务”。这里涉及到数据库查询、判断逻辑、发邮件这三个动作,这不是简单的检索生成能搞定的,你需要 Agent。
Agent 的核心思想叫ReAct:模型交替进行“思考(Reasoning)”和“行动(Acting)”。模型可以请求调用工具,工具返回结果后再决定下一步,直到任务完成。
用一句话说:RAG 是让模型“知道得多”,Agent 是让模型“做得了”。
4.2 一个最小 Agent 的长什么样
以 OpenAI Function Calling 或通用工具调用协议为例,模型输入不再是纯文本,而是带有一组“工具说明”。模型在回答时会生成一个结构化的调用请求,我们解析这个请求,执行对应代码,把结果返回给模型。
一个工具调用的最小实现长这样:
import json from openai import OpenAI client = OpenAI() tools = [ { "type": "function", "function": { "name": "get_server_bill", "description": "获取指定月份的服务器账单金额", "parameters": { "type": "object", "properties": { "month": {"type": "string", "description": "月份,如 2026-01"}, }, "required": ["month"], }, }, } ] messages = [{"role": "user", "content": "一月份账单是多少?"}] response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, ) # 看模型是否想调用工具 print(response.choices[0].message.tool_calls)经典的坑是:模型说出“我要调用 get_server_bill 工具”但代码里根本没有对应的执行逻辑,或者工具名称写错匹配不上。你需要在工具层做一层注册表,把函数名和实际函数绑定,并加超时和异常处理。
4.3 规划、记忆与人工兜底
Agent 在真实生产环境里,比 RAG 难得多。核心难点在三个地方:
- 规划失败:模型在复杂任务上容易漏步骤,或者步骤顺序不对。我建议把复杂任务拆成子任务,别指望模型一次规划到底。
- 记忆混淆:多轮对话中,模型容易把当前轮次和之前轮次的信息混在一起。每个 Agent 会话要有独立的记忆空间,按轮次组织。
- 不可逆操作:涉及发邮件、删除数据、扣款这类高风险动作,一定要设计“二次确认”。我的做法是:工具调用先进入“待确认”队列,由用户在前端点击确认后才真正执行。
Agent 不是玩具,它需要你比做 RAG 的时候更谨慎。
5. 部署、评估与监控:让应用真正能上线
5.1 模型推理服务化
如果你用的是云端 API,这部分很简单,就是常规的 Web 服务开发。但如果你的场景要求私有化部署,那就绕不开推理引擎。
我强烈建议用 vLLM 部署开源模型。它极其适合中文场景,也支持 OpenAI 兼容接口,意味着你本地起一个服务后,代码里只需要改一个 base_url 就能把 API 调用平滑迁移到本地模型。
启动一个 vLLM 服务很简单:
pip install vllm vllm serve Qwen/Qwen2.5-7B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192启动后,你的代码里只需要:
from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY") resp = client.chat.completions.create( model="Qwen/Qwen2.5-7B-Instruct", messages=[{"role": "user", "content": "你好"}], )前后端代码一行不改就能切换模型,这个统一接口标准的好处你越用越能体会到。
5.2 评估:没有指标,改动就是开盲盒
我一直有个观点:没有评估体系的 AI 项目,等于没有刹车系统的车。你今天换了一个 Prompt,感觉回答“好像变好了”,但你不知道是不是只是这一条变好了,另外十条变差了。
我初期踩过最大的坑就是靠感觉调优,今天调调这个明天换换那个,效果时好时坏。后来我学乖了,花一天时间建了一个 100 条问题的评测集,每条问题包含:
- 标准问题
- 参考答案关键词
- 期望来源文档
之后每次改动,跑一遍评测集,统计几个关键指标:
| 指标 | 含义 | 衡量方式 |
|---|---|---|
| 命中率 | 回答里是否包含参考答案的关键信息 | 关键词匹配或 LLM 评分 |
| 幻觉率 | 回答里是否有材料外的编造内容 | 人工抽检 + 引用溯源 |
| 检索召回 | 正确文档是否被检索到 | 计算命中比例 |
跑评测集这个过程很枯燥,但是它能帮你守住底线:每次改动至少不会让整体效果倒退。
5.3 线上监控:日志比你想的重要
上线第一天,你觉得一切完美。第三天,有用户反馈回答开始出错。你查了半天,发现是文档库被误更新了。这种事故我见过不止一次。
所以,一个 AI 应用上线前,必须把日志做全。我自己的日志体系分成三层:
- 请求日志:用户问题、完整 Prompt、模型输出、耗时、token 数。
- 链路日志:检索了哪些文档、每个文档的分数、重排序结果。
- 反馈日志:用户是否有点赞/点踩、是否复制了回答、是否有二次提问。
有了这些数据,你才能做三件事:定位问题、分析用户真实需求、持续迭代。否则它就是个黑盒。
6. 常见问题与避坑实录
6.1 高频问题速查表
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
| 回答内容跟给的材料无关 | 检索失败,向量召回质量差 | 检查 Embedding 模型、增加重排序、调整切分粒度 |
| 回答还没材料就结束了 | Prompt 对资料引用约束太弱 | 强化“只能依据资料”的指令,并限制输出长度 |
| 同一问题多次回答不一致 | 采样温度太高 | 把 temperature 调到 0-0.3,或固定 seed |
| 模型开始瞎编 | 知识库没覆盖该问题 | 加入“未知即未知”的 Prompt 兜底,并完善知识库 |
| 服务并发一高就超时 | 推理引擎配置不足 | 上 vLLM,开启连续批处理,或加节点 |
6.2 几个我反复踩过的坑
第一个坑:盲目追求大模型。我一开始直接用 72B 的模型,效果确实好,但成本爆炸。后来换成 7B + 做好的 RAG 和 Prompt,业务效果几乎没差。工程上,先优化数据,再优化 Prompt,最后才考虑换大模型,这个顺序别搞反。
第二个坑:把 Embedding 模型和业务模型混用。有的团队为了省事,直接用同一个模型做 Embedding 又做生成。这是彻头彻尾的错误,Embedding 模型追求表示学习,生成模型追求文本生成,两者任务完全不同,硬混用效果一定差。
第三个坑:对检索结果不加过滤。一开始我不管相似度分数多低,都塞给模型。结果就是明明没有相关内容,模型还要硬答。后来加了一个相似度最低阈值,低于阈值就提示“知识库暂无相关内容”,反而用户满意度提高了。
第四个坑:没有注意 token 消耗。检索回来的文档片段是越多越好吗?不是。3 个片段能讲清楚的,不要喂 10 个片段。因为上下文拉长以后,模型容易注意力发散,回答质量下降,还费钱。我的习惯是从少到多调,先给 3 个片段,不够再加。
6.3 给新人的几条建议
如果你也是从零开始,我很想告诉你三件事:
- 不要跳过基础。至少要搞懂 Token 是什么、嵌入向量维度意味着什么、相似度是怎么算的、温度参数影响的是什么。这些基础不牢,后面每一步都像踩在棉花上。
- 一定要亲手写一遍 RAG。不要复制。从读文件、切分、Embedding、向量检索,到拼 Prompt,跟着代码逐行走一遍,这个过程的价值远超刷十遍教程。
- 多跟行业的人交流。AI 工程更新迭代太快,一个人闷头学容易走偏。找到同路的人互换经验,比自己闭门造车高效得多。
我自己刚入门那阵,也经常因为跑不通一个效果沮丧到想放弃。后面慢慢明白,AI 工程跟传统软件开发最大的不同是:它带有一层“不确定性”。你不能像写业务代码那样期望一次就对,你要学会用数据、日志和评估去逼近一个“稳定可用”的状态。
这条路没有终点,但每一步积累的能力都是真实的。希望这篇长文能帮你把 roadmap 看清楚,少踩几个我踩过的坑。动手永远是第一步,跑起来再说。