☰
从零开始学AI工程:从RAG到评估部署的完整落地指南
2026/10/1 18:41:30 网站建设 项目流程

“ai-engineering-from-scratch”这个标题,字面意思是“从零开始学 AI 工程”。我最早是在 GitHub 上看到这个仓库名,后来发现它更像一类学习路径的概括。很多人一上来就找现成的 RAG 模板、复制智能客服代码,部署半天 Demo 能跑,一换真实业务数据就崩。原因很简单:AI 工程的复杂度根本不在“调用模型”,而在模型之外那一整圈环节,包括数据处理、检索链路、上下文组装、效果评估、服务化部署和持续迭代。

这篇文章就用我自己的实操经历,聊聊从零起步搭建一套可用、可评估、可迭代的 AI 应用系统,到底要经过哪些关卡。适合想系统入门的开发者,也适合已经做了几个 AI 项目但总觉得很碎片、没有章法的同学。我不写概念堆砌,只写能落地的步骤、参数和避坑经验。

1. 先想清楚什么是“从零开始做 AI 工程”

1.1 AI 工程不是调接口,而是一条完整链路

我见过不少朋友把“AI 工程”理解成“调用大模型接口”。这个认知在小 Demo 里没问题,但一旦进入真实场景,问题就冒出来了:知识库在哪、怎么切分、怎么检索、怎么控制模型不乱说、怎么判断新版本比旧版本好、怎么保证接口延迟稳定、上下文一长成本怎么控制。这些才是工程问题,也是 ai-engineering 的核心。

一句话概括:AI 工程是把模型能力包装成可靠业务系统的过程。它包含数据采集与清洗、向量化与存储、检索排序、提示词组装、模型推理、结果评估、日志监控、成本治理这条完整链路。任何一个环节薄弱,整体效果都会垮。这就是为什么很多团队直接拿 LangChain 拼一个 Demo 很容易,可上线一两个月后就被各种边界问题折磨。

1.2 为什么我建议从最小闭环起步

刚开始做自己的 AI 项目时,我也犯过错。第一次做知识库问答系统,直接搬了一整套成熟框架,界面还没写好,先被框架里一堆抽象概念搞懵了。后来我换了个思路:不依赖重型框架,先用 Python 手写一个最小闭环,把“文档进、答案出”的每个中间步骤都拆开看清楚。

从零起步的真正价值,不是造轮子,而是逼你理解每一层在干什么。就拿最简单的检索增强生成(RAG)来说,你只有亲手算过余弦相似度,才会明白为什么切块大小影响效果;只有手动组装过上下文,才知道 token 是怎么被消耗的;只有自己写过评估脚本,才懂得为什么“看起来回答变好了”不一定是真的好。

所以我的建议是:第一版不要追求架构完整,追求链路完整。先跑通一条能够记录输入输出、能够改参数重新运行、能够做对比评估的极简链路,再逐步加复杂组件上去。

2. 最小可用的技术栈与环境搭建

2.1 我选型的原则:先能用,再优化

技术选型是最容易纠结的地方。今天有人推荐向量数据库,明天有人推荐编排框架,后天又有人说要上微服务。我的原则很简单:第一版能用最少工具跑通,所有组件都能被替换。

所谓能用,指的是代码量少、调试直观、依赖简单。如果你对 Python 有一定基础,那么 FastAPI、本地 embedding 模型和 NumPy 就够起步了。不需要一上来就上 Kubernetes,也不需要配十几个中间件。先让链路完整,再考虑性能、高可用和扩展性。

我自己的第一版甚至没有用向量数据库,只用了一个 JSON 文件加内存列表,几千条知识片段检索照样能跑。后面数据量上来了,再平滑迁移到专门的向量存储。这个迁移过程反而很顺畅,因为我当时的代码把“向量计算”和“向量存储”拆开了,替换存储不影响检索逻辑。

2.2 最小技术栈清单与选型理由

模块我的选择为什么这么选
开发语言Python 3.10+AI 生态最成熟,调试速度快,写脚本和写服务都方便
服务框架FastAPI自带参数校验和接口文档,写一个 AI 接口非常快
向量化本地 embedding 模型(如 BGE 系列)或通用向量接口本地模型方便调试,接口方式适合快速验证
向量存储NumPy 数组 + JSON 文件起步数据量小时足够用,零运维成本
任务编排手写顺序逻辑 / 简单函数管线避免框架屏蔽细节,出现问题容易定位
容器化Docker Compose保证环境一致,后续迁移部署不踩坑

这套组合最大的优势是:任何一部分出问题,你都能直接看到代码、改代码、重跑结果。框架的抽象越少,排查问题的路径就越短。

2.3 环境搭建与项目结构建议

我建议从一开始就把项目结构按功能分目录,别把所有代码塞进一个 main.py。一个最小可维护的结构大概是这样:

ai-engineering-from-scratch/ ├── data/ │ ├── raw/ # 原始文档 │ ├── processed/ # 清洗后的文本 │ └── chunks/ # 切分后的知识片段 ├── src/ │ ├── embed.py # 向量化 │ ├── store.py # 存储与检索 │ ├── prompt.py # 提示词组装 │ ├── evaluate.py # 效果评估 │ └── api.py # 服务接口 ├── scripts/ │ ├── ingest.py # 数据处理流程 │ └── test_search.py ├── eval_set/ │ └── qa_pairs.jsonl └── requirements.txt

看起来简单,但结构背后是清晰的边界:数据管数据、向量管向量、提示词管提示词、评估管评估。这种边界感,是 AI 工程里最重要的习惯。

环境层面,我的经验是开发环境用 venv 就够了,项目级依赖用 requirements.txt 固定版本。部署环节用一个 Dockerfile 把环境和代码一起打包,避免“在我机器上是好的”这种尴尬。模型文件如果使用本地模型,注意挂载到容器外部目录,否则每次重新构建都要重新下载。

3. 核心环节实操拆解

3.1 数据准备:切块策略直接决定效果上限

数据准备是 AI 工程里最不性感但最影响效果的部分。我做过一个企业知识库项目,文档是几十份不同格式的培训材料,有 PDF、Word 和网页导出件。PDF 直接提取文本经常出现乱序和断行,尤其是有表格和页眉页脚的内容;Word 文档里有些“修订批注”也被提取出来,污染了知识库。

我最后花了两天时间处理数据格式:PDF 用解析库提取文本后做规则清理,把页眉页脚、水印、无用符号去掉;Word 先转成纯文本再按标题结构切分;网页内容只保留正文区域。这些脏数据如果不前置处理,后续检索和生成都会受到连带影响。

切块策略是另一个重点。块太小,单条信息不完整,检索到了但回答缺上下文;块太大,噪声太多,相似度被无关内容稀释。我常规的起步参数是:块大小 500 到 800 字,重叠 50 到 100 字。普通科普类文档用 600 字比较多,技术手册类条目化明显,可以压到 300 字左右。没有绝对标准,必须拿真实检索结果去调。

提示:切块时尽量保持段落和标题的完整性,不要在一句话中间硬切。简单的做法是优先按段落切,段落太长再按句子边界补充切分。这一条看着不起眼,实际很影响检索命中率。

3.2 检索增强生成的极简实现:不靠框架也能跑通

很多人问我要不要直接用 LangChain。我不排斥框架,但强烈建议先手写一遍核心检索逻辑,哪怕只是几百行。因为只有手写,你才能理解框架帮你做的事情是什么。

我第一版 RAG 的检索部分,甚至只有三个函数:向量化、相似度计算、取 Top-K。embedding 我用的是本地模型,把知识片段全部向量化之后存成 NumPy 矩阵。查询时把用户问题向量化,然后计算余弦相似度。

import numpy as np def cosine_similarity(vec_a, vec_b): dot = np.dot(vec_a, vec_b) norm_a = np.linalg.norm(vec_a) norm_b = np.linalg.norm(vec_b) if norm_a == 0 or norm_b == 0: return 0.0 return dot / (norm_a * norm_b) def search(query_vector, chunk_vectors, top_k=3): scores = [cosine_similarity(query_vector, vec) for vec in chunk_vectors] top_indices = np.argsort(scores)[-top_k:][::-1] return top_indices, [scores[i] for i in top_indices]

这段代码本身很简单,但跑通以后你会自然理解几个关键点:向量维度是多少、全表遍历有什么瓶颈、Top-K 的 K 取多少合适、为什么同一问题换个写法检索结果会不同。这些都是直接用框架时很难感知的。

在实际项目中,我后来把检索从纯向量召回升级成了“向量召回 + 关键词召回 + 重排序”。原因是很多专业名词、缩写、产品编号,在向量空间里未必能通过语义关联到;而关键词检索在精确匹配方面更强。把两种结果合并再去掉重复,已经能解决大部分召回不足的问题。还可以再接一个重排序模型,用小模型对候选结果做二次打分,进一步优化排序质量。

3.3 提示词与上下文组装:让模型稳定输出的关键

很多人在提示词上疯狂调试,其实真正的问题出在上下文组织上。我把上下文组装分成三层:系统提示、参考材料、用户问题。

系统提示负责设定角色和行为边界,我常用的写法是明确告诉模型“只根据提供的参考材料回答,如果材料中没有答案,直接说明不知道”这类约束。参考材料是检索回来的知识片段,需要按相关度排序拼接,并在每段前面标注来源编号。用户问题保持原样传入,除非需要改写后再检索。

一个容易被忽略的细节:参考材料里的噪声会直接干扰生成质量。如果检索结果中有明显不相关的片段,不要硬塞进上下文。我在评估阶段发现,加入两条无关片段后,模型答错的概率明显上升。所以宁可只喂一条高质量片段,也不要为了显得信息丰富而堆入大量无关内容。

另一个细节是 token 消耗。每次请求都会把系统提示、参考材料、历史对话全部算进去。如果上下文越堆越长,不只是成本上升,模型反而会因为信息过载而“忽略”关键内容。我后来给上下文加了一个上限:最多放 5 条片段,每条最多 400 字。超出部分直接截断,优先保留最相关的片段。这个简单策略在线开销和回答质量之间取得了不错的平衡。

3.4 效果评估:没有评价指标的 AI 工程都是玄学

这一点是我最想强调的。如果每次改完提示词,只靠人工看两三个例子判断“好像更好了”,这个项目永远不会稳定。AI 工程必须引入评估集和量化指标。

我建议准备一份黄金评估集,格式非常简单,每行是一条 JSON 记录,包含输入问题、期望的知识点关键词、可选的标准答案。规模不用很大,一百到两百条覆盖主要场景就够了。每次改动后,在同一个评估集上跑一遍,记录三个指标:检索命中率、生成答案的相关性、答案正确率。

检索命中率看的是 Top-K 结果里有没有出现正确片段;相关性可以人工打分,也可以用另一个更强的模型来打分;正确率用于判断最终回答是否满足要求。我实际使用中,字符串匹配这种简单方式只适合检测“是否包含某个关键词”,真正的语义正确性必须靠人工或模型评测。

# eval_set 示例 {"question": "产品的试用期是多久?", "keywords": ["30天", "免费试用"], "reference_chunk_id": "chunk_0042"}

注意:用模型评测另一个模型时,要定义明确的评价标准和打分区间,比如 1 到 5 分分别代表什么。同一个问题最好采样两到三次取平均,否则随机性会让评估结果失真。

这个环节还有一个额外好处:评估集就是一个回归测试集。当你指向一个新的 prompt 优化方向,发现整体分数下降时,就能立刻回滚,不用靠感觉。

3.5 部署与服务化:从脚本到可用的 API

本地跑通脚本和对外提供稳定服务之间,还差一层封装。我用 FastAPI 做一个极简接口,把检索和生成串起来,暴露成标准的 POST 接口。

from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() class QueryRequest(BaseModel): query: str top_k: int = 3 class QueryResponse(BaseModel): answer: str sources: list[str] @app.post("/query", response_model=QueryResponse) def query_endpoint(request: QueryRequest): try: result = run_pipeline(request.query, top_k=request.top_k) return QueryResponse(answer=result["answer"], sources=result["sources"]) except Exception as e: raise HTTPException(status_code=500, detail=str(e))

这里有一个非常实用的经验:流式输出。如果大模型推理时间较长,用户等待固定响应会很难受。改成 SSE 流式输出后,首字延迟从几秒降到几百毫秒的体感,能极大改善用户体验。虽然代码多了一些,但绝对值得。

并发控制也不能忽略。每个请求都要占用显存或带宽资源,如果不对并发做限制,一个热门查询就能把服务打挂。我的做法是给模型推理服务加一个请求队列,控制最大并发数为 4 或 8,超出部分排队处理。再加一层简单的缓存,相同或相似问题直接返回历史答案,效果非常明显。

4. 实操过程中最常见的六个坑

4.1 数据解析阶段:格式与编码的隐形杀手

第一个坑出现在数据解析阶段。PDF 表格内容提取出来经常是乱序文本,指望它直接进入知识库等于埋雷。另一个坑是字符编码,某些老系统导出的 Word 或 TXT 文件是 GBK 编码,直接按 UTF-8 读取会出现一堆乱码。我的处理顺序是:先统一转成 UTF-8,再按文件类型分别解析,最后做一轮正则清理,把多余空白、页眉页脚、特殊符号全部过滤掉。

如果知识库涉及用户上传的文档,还要考虑文件名、路径里的中文编码问题。我遇到过 Linux 部署环境下,文件名中文乱码导致读取失败的情况,后来统一规范成 hash 文件名,原文件名只存元数据,彻底避免了这个坑。

4.2 检索阶段:向量检索命中低、结果不稳定

检索阶段最常见的坑是向量化模型选型和数据领域不匹配。通用embedding模型对垂直领域术语的理解可能不够,这时候要么换领域微调的 embedding 模型,要么用混合检索补足。我做过一个法律文档问答,发现纯向量检索对法条编号的匹配极不稳定,改成“关键词完全匹配权重调高 + 向量语义召回”之后,命中率明显提升。

还有一个低级的坑:向量没有做归一化就直接算余弦相似度。其实很多向量接口输出的向量默认不是单位向量,手动归一化后再存入矩阵,计算逻辑更稳,也方便后续直接不用再除模长,能省一点计算时间。

4.3 生成阶段:模型“太有主见”与“太听话”

生成阶段的坑有两端。一端是模型太有主见,参考材料里没有的内容它也自行补全,然后在答案里出现编造信息。另一端是模型太听话,参考材料里哪怕有明显的错别字或错误数据,它也照抄。我常用的对策是:在系统提示里加一句“根据参考材料回答,材料未提及的信息请明确说明”,同时在生成后加一轮简单的规则校验,检查是否引用了参考材料中的关键句。

如果你用的是开放对话模型,还要注意模型可能把历史对话中的信息当成新知识。我会在每次请求时控制是否有历史记录,如果没有历史上下文,就明确告诉模型不要编造用户信息。

4.4 服务化阶段:并发上不去、响应太慢

服务化阶段的问题往往不是模型本身,而是架构。第一次上线时,我把模型加载、向量检索、生成逻辑全部放在同一个进程里,结果两个并发请求一进来,CPU 和显存飙满,接口大面积超时。后来拆成两部分:入口服务和推理服务分离,推理服务单独管理并发。入口服务负责检索、组装上下文,再通过内部接口调用推理服务。这样任何一部分抖动,都能单独定位和扩容。

流式输出这里再提一次,不用流式时用户看到的总响应时间等于完整生成时间;用流式后,用户感受到的等待时间大大缩短。配合前端打字机效果,体验提升非常明显。

4.5 成本与性能的平衡:能用小模型不上大模型

成本失控是最容易被忽视的工程问题。我见过一个项目,每天调用量不大,但每次都把长篇参考文档塞给大模型,一个月的 token 费用高得离谱。优化思路有几个:优先用小模型处理简单任务,只有复杂推理才调用大模型;缓存高频查询;压缩上下文长度。我把这三板斧用上后,成本降到了之前的五分之一,效果基本没变。

4.6 评估与回归的坑:人工验证掩盖了真实波动

最后一个坑是评估样本太少。只看三五个例子就宣布某个 prompt 更好,这是自欺欺人。模型输出有随机性,同一个 prompt 连续问两次都可能不同。我的建议是:评估集至少五十条,每次修改跑完整评估,并且对关键结果做多次采样取多数答案。这不是浪费时间,这是在给项目建立依赖的数据基线。

5. 常见问题速查表与避坑清单

现象常见原因排查与解法
检索结果完全不相关切块过大、向量模型不匹配调整块大小,启用混合检索,可选换模型
答案包含编造信息缺少约束提示、上下文不足强化系统提示,要求“无据不答”,增加片段来源
同一个问题答案不稳定温度过高、未设置随机种子降低 temperature,多次采样取多数
接口响应太慢同步生成、并发未控制改流式输出,拆分推理服务,加缓存
费用快速上涨上下文过长、高频调用未缓存截断上下文,复用结果,小模型分流
知识点更新后不生效嵌入库未重建、缓存未清理重建向量索引,设置缓存失效策略
特定格式文档解析乱码编码问题、PDF表格乱序统一 UTF-8,分类型解析,规则清理

这份速查表不是一次性生成的,而是我在多个项目里不断积累的。遇到新问题,第一反应应该是把它记录下来并归因到链路的具体环节,而不是头痛医头地乱调参数。

6. 最后的经验:把系统拆到足够细,才能走得足够远

写到这里,我最有感触的一点是:从零开始做 AI 工程,最难的不是学会某一个工具,而是建立“拆解黑暗”的能力。你面对一个不能稳定输出的系统时,如果只能看到“模型回答不对”,那就无从下手;而当你把链路拆成数据、切块、向量化、检索、排序、上下文、提示词、生成、评估九段,每一段都能单独输入、单独输出、单独测指标,那问题就变成查表定位,剩下的只是耐心调优。

我在实际项目中养成了一个习惯:每次改动只改一个变量。改 prompt 就别动切块参数,动了切块参数就不要同时换 embedding 模型。否则结果变好了不知道是哪一步起了作用,变差了也不知道该回滚哪一层。这个习惯看起来保守,但在 AI 系统的混沌和随机性面前,它反而是最稳妥的推进方式。

另外,建议尽早把评估集建立起来,哪怕只有二十条。我前面几个项目都是先写功能后补评估,导致后期优化时没有基线,很多改动没法确认是否真的有效。等你在 AI 工程这条路上走了一段,你会发现自己最值钱的资产不是某一个模型或框架,而是那套完整的实验流程和数据基线。

这套方法后续还可以继续扩展:接入用户反馈自动标记难例,把难例吸收回评估集,逐步构建更高质量的训练数据,甚至做更细粒度的 prompt 自适应调整。但这一切的前提都一样:基础链路清晰、评估指标可信、迭代过程可控。从零开始,恰恰是建立这些前提最扎实的路。

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

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

立即咨询