AI工程入门:从Prompt、RAG到本地部署的完整实践路径

发布时间:2026/11/4 5:40:46
AI工程入门:从Prompt、RAG到本地部署的完整实践路径

我做AI工程这条路,是从一次非常狼狈的演示开始的。当时领导要求把一个开源大模型接到内部工单系统上,我整整调了三天提示词,结果演示时模型回复了一长串道歉话术,场面极其尴尬。那之后就逼着自己把"调用模型API"这件事从野路子玩成了体系化工程。如果你现在也想从零走进AI Engineering(AI工程)这条赛道,或者已经踩过几个坑还想继续走,这篇文章会把我走过的完整路径、关键选型逻辑、实操细节还有踩坑记录,一次性讲清楚。我会尽量说人话,不搞那种"从入门到放弃"的教程,而是给你一条真的能走通的路。

适合看这篇文章的人:有基本Python基础、想做大模型应用但还没系统地建立"AI工程"知识框架的开发者;后端想转AI应用方向的工程师;还在用"调Prompt碰运气"方式写应用、希望把流程做得更可靠的产品技术同学。内容会从概念梳理讲到代码实现,再讲到本地部署和排查,你完全可以顺着章节往下看,把每一步落到你的机器上跑一遍。

1. 先搞清楚:AI Engineering到底在解决什么问题

1.1 它不是"调API",而是"驯服不确定性"

很多人在刚接触AI工程时会有一个误解,觉得"我会requests.post调一下OpenAI接口,会写Prompt,就是AI工程师了"。说实话,这种水平做个小玩具没问题,但离"工程"两个字还差得很远。我在实际项目里体会最深的一点是:传统软件工程的逻辑是确定性的——你输入A,代码输出B,一切可预测、可测试。但大模型应用是概率性的——你输入A,模型可能输出B,也可能输出C,甚至输出一段自带格式错误的文本。

所以AI工程的核心,不是把模型接进来,而是把这种"不确定性"给治理住。你要设计一套完整的流程,让模型在绝大多数情况下都能稳定输出你要的东西;输出不理想时,系统有降级策略;模型抽风时,有兜底逻辑。这些技术点串起来,才叫AI工程。举一个我常用的类比:传统开发像是盖砖混结构的房子,每块砖的尺寸你都清楚;AI工程更像是浇筑混凝土——材料本身是流动的,你需要靠模板(提示词结构、流程约束)把它定型,而模板的搭法,恰恰是这个领域最值钱的经验。

从热搜词里你也能看出大家关心什么:AI Agent(智能体)、Prompt Engineering(提示工程)、RAG(检索增强生成)、本地部署。这些都不是孤立的技术点,它们是围绕"如何把大模型稳定地嵌入业务系统"展开的。AI工程要解决的,就是把这些点串成一条端到端的流水线。

1.2 它和传统后端开发、数据科学的边界怎么划

我做这个方向之后,经常有人问:这到底是后端活还是算法活?我理解是这样:

方向关注点交付物
传统后端接口、数据库、事务、并发稳定运行的Web服务
数据科学/算法模型效果、指标、训练流程高精度的模型或分析结果
AI工程模型能力如何被业务稳定调用可维护、可观测的AI应用系统

数据科学更关心"模型的准确率怎么提上去",AI工程更关心"模型效果还行的情况下,系统能不能扛住线上流量、能不能做流式输出、上下文一长会不会崩"。后端开发关心通用的中间件和架构,而AI工程要在通用架构之上,额外处理"模型输入输出本身带来的问题"——比如用户的提问怎么改写才能命中知识库、检索出来的片段怎么组装模型才不会胡编、模型的返回怎么解析才能保证JSON格式不错乱。

这个边界感很重要。我见过不少人转行AI工程,一上来就死磕模型训练和微调,方向就偏了。AI工程的主战场在"应用层"和"推理层",它的核心技术栈是提示工程、工具调用、检索管线、输出解析、评估和部署优化。知道自己在哪个层干活,才能把时间花在刀刃上。

2. 从零开始的完整学习路径:按这个顺序走,三个月见效

2.1 先把基础技术栈补牢

别急着上大模型,先确认你手上有这几个工具:

  • Python基础:至少要看得懂类、装饰器、类型注解、yield生成器。AI应用很常见的一种写法是用yield做流式输出,你不懂生成器,连流式响应的代码都会读不懂。
  • OpenAI SDK的调用方式:不只是OpenAI,现在很多国产模型也兼容这个SDK风格,会一个就通吃一大片。
  • Git和虚拟环境:用python -m venv .venv建环境,用git init管理代码,这是基本的工程素养,不然代码改着改着就成一坨烂摊子。
  • FastAPI基础:只需要会定义路由、用Pydantic模型做请求体校验、能跑起一个简单的服务。FastAPI是目前AI应用层最主流的选择,异步支持和类型提示跟AI生态配合得很好。

这些基础如果你完全从零开始,花两三周扫一遍就够了,不用啃大厚书,遇到不懂的语法直接查文档。关键是不要停在"看",要动手把每一个示例自己在本地跑出来。我在带人入门的时候反复强调:AI工程是"泡"出来的手艺,不是"看"出来的知识。你亲手跑通一遍API调用,比看十篇教程都有用。

2.2 Prompt Engineering:这是你和大模型沟通的底层语言

很多人对提示工程的印象是一句"你很聪明"就能把模型调教好,那就大错特错了。真实场景里,我需要把提示词当成一套结构化协议来写。一个可靠的生产级Prompt,通常包含这些模块:

  • 角色定义:让模型明确"你是谁"("你是某公司的售后客服助手")。
  • 任务目标:让模型知道"你要干什么"("根据提供的知识片段,回答用户问题")。
  • 约束条件:让模型知道"哪些事绝对不能做"("如果知识片段中没有相关信息,明确回答'知识库中未找到',禁止编造")。
  • 输入数据格式:明确什么位置开始是用户问题,什么位置是检索到的知识片段。
  • 输出格式:有结构要求的,直接要求输出JSON并给出字段说明。

再配合few-shot(给一两个示例)和Chain of Thought(让模型先思考再回答),一次调用效果就能提升一大截。还有一点,Prompt也要有版本管理。我现在的项目里,每个Prompt都单独放在一个目录下,用Git来管理版本,改了什么一目了然。你想想,如果Prompt全写在代码里,每次微调之后线上效果变了,你不知道是代码导致的还是提示词导致的,排查起来会非常痛苦。

写Prompt还有一个避坑技巧:不要只在"一个完美的示例"上测试,要多准备几个边界案例。比如空输入、超长输入、包含恶意指令的输入、包含与主题无关内容的输入。大模型应用一个很常见的隐患就是提示词注入——用户可能故意在问题里加上"忽略之前的指令"之类的话来绕过系统约束。把这类边界案例写进测试集,每次改Prompt都跑一遍,比什么防护都实在。

2.3 RAG:让模型拥有"私有知识"

当你要做的应用必须回答包含公司内部文档、产品规格、专业资料的问题时,纯粹靠Prompt去塞上下文是不行的。这里就涉及RAG(Retrieval-Augmented Generation,检索增强生成)。它在AI工程里的地位,几乎等同于"数据库"在后端里的地位。

RAG的基本流程不复杂:把文档切块 → 通过Embedding模型把每个块变成向量 → 用户提问时把问题也变成向量 → 在向量库里做相似度检索 → 把命中的文档块和问题一起交给大模型回答。但这个流程每一步都有讲究:

  • 切块切多大?我建议按内容语义来切,而不是死板地按固定字符数。优先保留章节结构,比如按Markdown标题、按段落切,每个chunk控制在300~500个字左右。切小了检索时缺乏上下文,切大了容易混入不相关内容、还浪费token。
  • Embedding模型怎么选?中英文混合场景建议用bge-m3这类支持多语言的模型;纯英文场景选择面更广。不要盲目追求"最强",要看你部署环境的硬件能否跑得动。
  • 检索不只要向量。纯向量检索容易漏掉关键词命中但语义距离稍远的文档。更可靠的做法是混合检索:BM25(关键词)+向量检索各取一批,再通过重排序模型(比如bge-reranker)把合并后的结果重新打分,取最靠前的top-k。

RAG工程里还有一个很容易被忽略的点:上下文组装格式。检索到的文档块与用户问题的界限要清晰,否则模型分不清哪些是参考资料哪些是闲聊内容。我常用的一套格式是:

请根据以下资料回答用户问题。 资料开始: [1] ... [2] ... 资料结束。 用户问题:... 请基于资料回答问题,如果资料中没有依据,请如实说明。

2.4 AI Agent:从"一问一答"到"让模型干活"

如果RAG是AI工程里的"数据库",那Agent就是"业务逻辑"。它的核心思想,是让模型在回答过程中调用外部工具(查天气、查订单、发邮件、执行代码),再根据工具返回的结果决定下一步动作。这个方向是目前ChatBot走向生产力工具的关键。

做Agent绕不开Function Calling(函数调用)。简单说,就是在调用大模型时,你把可用的工具以JSON Schema的形式告诉模型,模型会在输出里返回"应该调用哪个函数、参数是什么",你的程序再负责真正执行这个函数,把结果返回给模型,让模型生成最终回答。这个机制非常实用,但工程化的时候有几个坑:

  • 工具描述要写清楚,尤其参数的格式。模型是根据描述来选函数的,描述模糊了,它就选错。比如"获取天气"要写明参数是city还是location,省份、城市要不要分开。
  • 必须有循环上限。模型可能会在工具之间反复横跳,或者一次对话触发十几次调用。一定要在代码里设置最大迭代次数(比如5轮),超出就主动终止,不然你的token账单会很难看。
  • 工具调用的结果要经过处理再给模型。原始返回可能是超长JSON,直接塞进上下文会稀释模型的注意力。我一般会在工具函数里先做一次提炼,只保留真正需要让模型知道的关键字段。

3. 动手做一个完整项目:内部知识库问答助手

3.1 项目选型:为什么首选"问答机器人"而不是"智能助手"

对于从零起步的人来说,我最推荐的项目不是AI Agent,而是一个"知识库问答助手"。原因很简单:Agent涉及的工具调用和循环控制对新人来说状态管理太复杂,容易死于半途。而知识库问答助手的技术栈恰好在"够用"和"有深度"之间:它需要你做文档处理、Embedding、向量检索、Prompt优化、流式输出、评估,这些全是AI工程的核心基本功,做完这个项目,你可以直接横向扩展去做Agent。

我建议的项目目标:做一个本地运行的Web服务,用户输入问题后,它能基于一份给定的技术文档精准回答,并且在回答末尾列出"参考了哪几个文档片段"。不要小看这个需求,它已经覆盖了AI应用产品化的80%关键路径。

3.2 整体架构与关键依赖

这个项目的架构分成两条主线:

  1. 预处理线(离线):把PDF/Markdown文档加载进来 → 清洗 → 切块 → 向量化 → 存入向量数据库。这条线只在文档更新时跑。
  2. 问答线(在线):用户提问 → 改写/直接向量化 → 检索 → 重排序 → 组装Prompt → 调用大模型 → 流式输出。

技术选型上我推荐这一套,主要看重生态成熟和部署简单:

  • 向量数据库:用chromadb或lancedb,本地文件型,不需要单独起服务,对新手最友好。
  • Embedding:初学本地跑可以先用sentence-transformers里的bge-small-zh-v1.5,效果够用,显存占用低。
  • LLM:如果本地显卡允许,7B~14B量的量化模型足够;没有GPU的就调云端的兼容OpenAI接口服务。
  • 主框架:FastAPI + LangChain(或者纯手动组装,不依赖框架)。这里我多说一句:学习阶段强烈建议先纯手写一遍RAG流程,不要直接上框架。等你手动组装过一遍"向量化→检索→组装Prompt"整个流程,再去用LangChain之类的东西,你才能真正看懂它帮你做了什么,出了问题也好排查。

3.3 核心代码逐段拆解

文档切块阶段,我的一个核心思路是"按结构切"而不是"按字符硬切"。我会先用正则把Markdown文档按标题分块,再对超过长度上限的块做滑动窗口补充。切完的每个块要保留来源标记(比如"来自第2章第3节"),这样回答引用出处时才有据可依。

import re from typing import List def split_markdown_by_heading(text: str, max_chunk_size: int = 500): # 按markdown标题切分 sections = re.split(r'\n(?=#{1,3} )', text) chunks = [] for sec in sections: if len(sec) <= max_chunk_size: chunks.append(sec.strip()) else: # 超长的块再按段落切 paragraphs = sec.split('\n\n') buffer = "" for para in paragraphs: if len(buffer) + len(para) > max_chunk_size and buffer: chunks.append(buffer.strip()) buffer = para else: buffer += "\n\n" + para if buffer.strip(): chunks.append(buffer.strip()) return chunks

向量化和检索的代码,我会把Embedding模型和向量库封装成独立的模块。这一段的关键点是召回数量(top_k)和重排序后取多少条(rerank_top_n)要分开控制。比如向量检索先召回20条,重排序后再取前5条进Prompt,这样既保证候选覆盖面,又不会把上下文撑爆。

import chromadb from sentence_transformers import SentenceTransformer class VectorStore: def __init__(self, model_name: str = "BAAI/bge-small-zh-v1.5"): self.encoder = SentenceTransformer(model_name) self.client = chromadb.PersistentClient(path="./chroma_db") self.collection = self.client.get_or_create_collection( name="docs", metadata={"hnsw:space": "cosine"} ) def add_documents(self, chunks: List[str], metadatas: List[dict]): embeddings = self.encoder.encode(chunks, normalize_embeddings=True).tolist() ids = [f"doc_{i}" for i in range(len(chunks))] self.collection.add(ids=ids, documents=chunks, embeddings=embeddings, metadatas=metadatas) def query(self, question: str, top_k: int = 20): emb = self.encoder.encode([question], normalize_embeddings=True).tolist() res = self.collection.query(query_embeddings=emb, n_results=top_k) return list(zip(res["documents"][0], res["metadatas"][0]))

组装Prompt时,我通常用一个模板函数,把检索到的片段逐一编好序号塞进去。注意,只有重排序之后真正要发给模型的内容,才占用最终的上下文窗口。这个意识越早建立越好,它决定你的应用能不能处理超长文档。我习惯的做法是:检索20条→重排序取5条→每条最多500字,那上下文里检索内容部分最多2500字左右,给模型输出留足空间。

3.4 从"能跑"到"能上线":评估这套系统

项目跑通之后,千万不要说"看起来回答得还行"就收工。你要建一个最小的评估集:准备20~30个问题,覆盖简单问答、跨章节组合问答、文档里找不到答案的问题、带攻击性的提示词注入这几类。然后拿一个参考答案的评分模板,逐条检查模型的回答是否引用了正确的文档片段、是否如实说"不知道"。

我推荐的做法是:把每个测试问题的"预期行为"写成结构化的检查项,比如"回答中必须出现文档原文中的指标数字""当问题超出文档范围时,必须拒绝回答",然后用大模型当裁判或者规则脚本去批量判断。这个评估集就是你的安全网。以后不管你换模型、改Prompt、调切块参数,都先跑一遍评估集,用分数说话,而不是"感觉变好了"。没有评估体系的AI项目,就是在裸奔。

4. 本地部署AI:把学习变成可复现的实践

4.1 怎么选本地模型和推理框架

很多人在做完知识库助手之后,会想把模型也部署到本地,彻底摆脱对外部API的依赖。这是很好的进阶练习,但一上来就会碰到一道坎:显存。我给你的选型原则是:显存不够,量化来凑;参数不在大,跑得动才算数。

以大众常见的消费级显卡为例,做几档参考:

显卡显存可选模型档位说明
8GB7B模型4bit量化日常问答够用,速度尚可
12GB14B模型4bit量化效果明显更好,接近可用
24GB32B模型4bit量化综合效果强,适合追求质量
48GB以上70B模型量化或全精度小模型属于重度玩家

数值估算方法也很简单:模型显存约占参数量的2倍(半精度FP16),也就是说一个7B模型半精度大约占14GB,4bit量化后约4~6GB,再加上KV Cache和中间计算缓冲,预留多2~4GB。所以8GB卡跑7B量化版是比较稳妥的组合。

推理框架方面,我的经验是这样:

  • Ollama:最推荐入门用。一行命令就能拉起模型服务,自带的OpenAI兼容接口让你的代码不用改就能用。
  • llama.cpp:适合需要极致控制和部署在CPU机器上的场景。纯CPU跑7B量化模型也能出结果,只是慢一些。
  • vLLM:适合有一定并发需求的线上服务,它对吞吐量的优化很猛,但显存占用也相对高,配置复杂一点。
  • LM Studio:想要GUI图形界面,不想敲命令行的话可以用它。本地跑实验、测试模型效果非常顺手。

4.2 推理速度和显存的工程级优化

本地部署最常见的三个痛点是:慢、爆显存、吐着吐着停了。我逐个说解法。

慢的解法:首要是调整上下文长度。默认的上下文窗口往往拉得很长,但你的真实应用可能用不到那么多,把num_ctx改小能让显存占用明显下降、速度变快。其次是使用并发请求时,小模型用Ollama自带的请求排队就行,但如果你用的是vLLM,可以用continuous batching(连续批处理)机制让吞吐量成倍提升。还有一点经常被忽略:输入长度越长,生成越慢。所以Prompt里只保留必要信息,别把一堆没用的检索片段塞进去。

爆显存的解法:除了量化之外,要关注KV Cache。它的大小和你的上下文长度成正比。如果你开一个8K上下文,KV Cache可能也要吃好几个GB。一般的处理是在部署配置里限制最大上下文长度,给生成留余量。另外,模型加载时用gpu_layers参数控制层数在GPU和CPU之间分配,别一股脑全塞进去。

生成中断的解法:最常见的原因是输出超长或触发了上下文窗口上限。我一般会做两层防护:第一,代码层面设置max_tokens,控制单次生成的最大长度;第二,部署层面设置更保守的上下文上限,给Prompt和输出之间留出缓冲。还有一个很隐蔽的问题:模型生成的结束符不一定是EOS,有的模型会输出一段"感谢"之类的废话后才停。这时候你在代码里定义一个"停止标识列表",匹配到就截断,能省掉大量时间和token。

4.3 本地部署的常见报错速查表

我把自己踩过的坑整理成一张速查表,遇到同样问题可以直接对号入座:

报错或现象原因解法
CUDA out of memory显存不足换更小量化模型,减小上下文长度,关闭其他占显存的程序
模型加载后首次生成极慢prompt处理阶段在跑长文本缩减检索片段数量,启用Flash Attention(如果框架支持)
回答到一半停止触发了上下文上限调大max_tokens或减小上下文长度预留空间
CPU模式速度只有几token/s内存带宽瓶颈考虑换GGUF量化等级更低的模型,或接受它作为离线预览用途
API请求超时本地推理太慢被网关断开调大请求超时时间,用流式输出代替一次性等待

这里再提醒一句:本地部署不要追求"一步到位"的高难度框架。Ollama真能解决你80%的需求,先把服务跑起来,后面再慢慢研究vLLM的--quantization参数那些进阶选项。工程这东西,跑通是第一优先级,优化是后续的事。

5. 给新手的实用建议与避坑指南

5.1 学习过程中最容易犯的三个错

第一个错误,是一上来就扎进模型训练和微调。我见过太多人学AI工程,第一个月就在看LoRA微调教程,结果对Prompt工程和RAG还一知半解。不是说微调没用,而是微调解决的是"模型能力"问题,而AI工程日常面对的多是"怎么稳定调用模型"的问题。你先得学会用模型,再去考虑改模型。

第二个错误,是不重视评估,全凭感觉调系统。我早期写RAG,每次调完检索参数,都觉得"好像变好了",结果一上线被用户投诉"回答变差了"。后来老老实实建了评估集,才发现直觉根本不靠谱。所有改动的合理性,都要靠测试数据来判断。从第一天做项目开始,就顺手把测试问题和期望行为记录下来,这段积累会在你后面迭代时发挥巨大作用。

第三个错误,是不愿意看"原始输出"。调试AI应用时,真正的线索藏在模型的完整响应里。很多人在Web界面看到"回答正确"就以为万事大吉,但一旦做流式、做结构化输出,模型返回里的那些"思考过程"“兜底话术"才是破坏格式的元凶。我调任何问题,第一步永远是打印完整的原始响应JSON,从来不看美化后的展示结果。

5.2 如果你工作忙,怎么保持学习节奏

这个方向不是看几篇文章就能掌握的,它需要持续的动手时间。我给自己的节奏是"小步高频":每天至少写半小时代码,哪怕只是改一个Prompt、调试一个小函数。不要等周末攒出大块时间再冲刺,那样很容易因为一次卡住就放弃。

再推荐一个"以教代学"的办法:每完成一个小项目,把过程写成一篇简短的经验笔记,不需要公开发布,就当作是给未来的自己看的操作手册。我现在的很多生产经验,其实都是从自己整理的笔记里快速翻出来的。写作的过程,会逼你把"模糊知道"变成"确切知道"。

5.3 最后分享一个我反复用的小技巧

不管你是做RAG还是Agent,所有发往大模型的请求和所有模型返回的响应,都建议留一份带时间戳的日志。手动调试时,这个日志目录就是你排查问题最好的朋友——它记录着每一次Prompt的长相、检索到了什么、模型回了什么。比起在终端里翻历史记录,看着几十个带时间戳的JSON文件,思路会清晰得多。

我刚开始做AI工程时,也曾经在"这模型怎么不听话"的抱怨里消耗了大量时间。后来我慢慢意识到,问题通常不在模型,而在工程链路里某个不起眼的环节——可能是Prompt里少了边界约束,可能是检索结果没有排好序,也可能是上下文被无关内容塞满了。把每个环节都做成可以观测、可以控制、可以测试的模块,你就能真正驾驭模型,而不是被模型的随机性牵着走。

这条路没有想象中的那么玄,它的知识边界很明确,方法也是可以复制的。从一个知识库问答助手开始,一步步把RAG、评估、本地部署、Agent这些能力叠加上去,你会发现自己已经能做出很多以前不敢想的应用。希望这篇梳理清晰的路径笔记,能帮你少走一些我走过的那种弯路。