拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI工程化从零开始:RAG与大模型落地的完整实践指南

AI工程化从零开始:RAG与大模型落地的完整实践指南

1. 从零开始做AI工程,到底在做一件什么事

这两年“AI工程化”这个词被反复提起,但真要说清楚它是什么,很多人的理解还是停留在“调接口、套Prompt”。我这个标题起的是ai-engineering-from-scratch,翻译过来就是“从零开始的AI工程”,说白了就是——不动手动写算法,不用复现论文,而是站在大模型这个巨人肩膀上,用工程手段把AI能力真正落到业务系统里。

那为什么“从零开始”值得单独拉出来讲?因为大部分开发者和技术管理者碰到的真实痛点,并不在于“模型效果不够好”,而在于“模型效果有了,怎么把它变成一个稳定、可维护、能对外提供服务的系统”。这里面的技术栈、设计模式和踩坑点,跟传统后端开发有很大差别,网上的资料又特别零散,很多人第一阶段就卡住了——不是不会写Python,而是不知道该从哪里下手把模型能力“工程化”起来。

这篇内容适合谁?三类人:

  • 传统后端/全栈开发者,想用大模型能力给现有系统加智能化功能;
  • 算法工程师,需要把实验脚本改造成对外API,交给其他团队使用;
  • 技术负责人,想了解AI工程化这条线到底要配什么技术栈、埋什么坑。

我写这篇文章的思路,就是模拟一次真实的从零落地:从技术选型开始,到目录搭建,再到跑通一个带检索增强(RAG)的问答API,最后把生产环境里最容易出问题的地方列一遍。整个过程我没有用任何云平台,也不需要花一分钱,全部在本机完成。

2. AI工程的核心技术栈与整体设计拆解

2.1 AI工程和传统后端开发的本质区别

传统后端开发的核心是“确定性”——你写一个函数,传入参数A,永远得到结果B。但AI应用最大的特征恰恰是不确定性:同样的Prompt,模型可能给出不同回答;同一个问题在不同上下文下,质量差异极大。

这个差异带来了一系列连锁反应。首先是代码架构的转变——传统业务逻辑是线性调用,AI应用则要围绕“模型调用”做大量的上下文组装、结果校验、异常兜底。其次是测试方式——传统后端断言输出等于期望值,AI应用没法这样测,你需要建立一套评估机制来衡量回答质量。第三是运维监控——传统接口观察QPS、延迟就够了,AI应用还得追踪Token消耗、模型幻觉频率、上下文漂移这些指标的组合变化。

我见过很多人拿着传统三层架构硬套AI应用,结果把Prompt胡乱塞进Service层,后来每次改提示词都要重新发版,追得运维苦不堪言。AI工程的合理设计,至少要把“模型交互层”和“业务逻辑层”彻底分离,让Prompt模板、知识库检索、模型参数配置都变成可独立调整的配置项。

2.2 技术选型:本地模型先跑通,再考虑API化

我刚做这个项目时的第一决策,就是选型模型加载方案。市面上主流的选择其实就三条路:

方案优点缺点适用场景
云端API(OpenAI等)部署简单,效果稳定数据出域、按量付费、有网络依赖快速验证、非敏感数据
本地模型(Ollama等)数据不出域、无网络依赖、可控性强受机器性能限制、效果弱于主流商用开发调试、私有化部署
混合模式灵活、可切换架构复杂度更高生产环境过度阶段

我的建议非常明确:先从本地模型起步。原因有三个:

  1. 开发期需要高频迭代Prompt和上下文逻辑,如果用云端API,每改一次配置就要等网络请求,Token费用也在无形消耗;
  2. 调试期经常需要反复换模型、改参数,本地模型可以毫秒级切换,云端API的版本兼容问题会在这个阶段浪费大量时间;
  3. 现在 Ollama、llama.cpp 这些工具已经做得相当成熟,一条命令就能启动支持OpenAI格式的本地API服务,开发体验和云端几乎一样。

我本机的配置是MacBook Pro M1 Pro(16GB内存),跑7B参数量化模型能稳定在每秒15-20 token的生成速度,做开发调试完全够用。如果你机器只有8GB内存,跑3B-7B的量化模型也没问题,生成速度会慢一些,但足够验证程序逻辑。

切入到工程视角还有一个关键点:模型加载方式必须做成可配置的。我建议配置项里写MODEL_BACKEND=ollama或者MODEL_BACKEND=openai,程序内部统一走 OpenAI SDK 的接口格式,这样将来换模型服务商时只需改配置、不用动业务代码。Ollama 本身就提供了 OpenAI 兼容端点(默认http://localhost:11434/v1),这个迁移成本几乎为零,相当于你从一开始就在为将来保留退路。

2.3 向量数据库与检索增强的整体设计

做AI应用,绕不开RAG(检索增强生成)。这个名称听着学术,落地到工程上的理解其实很直接:

  • 用户提问时,先从知识库/资料库里找出与问题最相关的几段文本;
  • 把这几个片段连同用户问题一起组装成上下文,交给模型生成回答。

RAG解决的是大模型“不知道你专属业务信息”的困境。大模型的训练数据是公开互联网上采集的,它不了解你公司的内部流程、你产品的使用方法、你历史订单的细节。而RAG本质上是给模型一个“参考资料的抽屉”,让它根据问题现场把相关的几页纸抽出来读一遍,再回答。

我对RAG在2025年的一个最新认知是——它已经从“锦上添花”变成了“AI工程的骨架”。纯粹靠模型本身的知识去回答问题,在产品上是走不通的(用户问一个私域问题就露馅),RAG是让AI应用具备“内部知识主权”的最低成本方案,也是当前性价比最高的方案,它的整体架构可以拆成三个环节:

  • 文档入库阶段:把PDF、Word、Markdown、网页抓取数据切块,向量化后存入向量数据库;
  • 查询召回阶段:用户提问时把问题向量化,在库里找语义最接近的Top-K个片段;
  • 生成组装阶段:把召回的片段压进Prompt模板,连同问题一起送给模型生成答案。

这里的核心难点在于切块策略和召回质量。切块太大会把无关信息混进上下文,太小则可能切断完整语义,这两个问题直接决定回答质量。我通常从800-1000个字符的块大小起步,重叠控制在块大小的15%左右,再根据实际检索效果微调。

向量数据库选型方面,我的建议是开发阶段用 Chroma——它是纯本地的,一个命令就能跑起来,Python接口直接操作;如果将来数据量到了百万级向量,再切换 Milvus 或 Qdrant 也不迟,因为通过统一的向量存储接口(比如LangChain的VectorStore抽象)来做这件事,成本并不高。在这个阶段没必要过度设计,跑通了链路比什么都有说服力。

2.4 项目目录结构设计

好架构不是写出来的,是从第一天就把骨架立对了。下面这个目录结构是我实操验证过、且会持续沿用下去的模板,每一步都有它存在的理由:

ai-engineering-from-scratch/ ├── app/ # 核心应用代码 │ ├── main.py # FastAPI入口 │ ├── config.py # 全局配置(模型、向量库、Prompt路径) │ ├── api/ # API路由层 │ │ └── chat.py # 问答接口 │ ├── core/ # 核心逻辑层 │ │ ├── llm.py # 模型调用封装(支持本地/云端切换) │ │ ├── chains.py # LangChain链式编排 │ │ └── retriever.py # 检索模块 │ ├── prompts/ # Prompt模板(独立管理) │ │ └── chat.txt │ └── schemas/ # Pydantic入参出参模型 ├── data/ │ ├── documents/ # 原始文档 │ └── vector_store/ # Chroma本地向量数据 ├── scripts/ │ ├── ingest.py # 文档入库脚本 │ └── test_chain.py # 链路自测 ├── tests/ # 单元测试 ├── requirements.txt ├── README.md └── .env.example # 环境变量示例

这个结构的好处是边界清晰:api/只负责HTTP层,core/负责核心逻辑,Prompt模板作为独立文件放在prompts/下面——这意味着产品同事改了话术,直接改文件就行,不用动代码。scripts/里放一次性任务脚本,比如首次入库清洗,这样它们不会污染应用代码。

我见过太多人把RAG流程全部揉进一个main.py里,前期确实爽,但迭代到第三周改Prompt时需要四处找字符串拼接的位置,那时候再来重构的代价就大了。

3. 核心细节解析与实操要点

3.1 文档入库链路怎么实现最稳

文档入库是RAG系统的“地基”。地基没打好,后面检索和回答的质量都会受影响。

入库链路的实操要点我拆成了四步:

第一步:确定文档来源,建议把资料统一放到data/documents/目录下。第一步先别追求格式丰富,就从Markdown和纯文本开始,因为它们的切块最干净。

第二步:文档加载器,LangChain仓库中有对应加载器,它能把文档读成统一的Document对象,这个对象的page_content存文本、metadata存来源等信息。

实际建议:加载文档后,先把metadata里的source信息补上。这一条在后续回答溯源里极其重要——你至少要能回答“这个回答是模型编的,还是来自哪个文档的哪一段”。没有溯源能力的回答,在生产环境没人敢信。

第三步:切块策略选择。这一块是最多新手翻车的地方。我做过一组对照实验,直接用固定字符数切块的话,如果块大小设800字符,很多语义完整的段落会被拦腰切断;设1500字符,上下文信息冗余,容易把无关内容捎带进来,模型的注意力被稀释。

实操中用RecursiveCharacterTextSplitter来切更稳,它的切法是这样的:按优先级用分隔符尝试切分,先尝试用章节标题这种大分隔符切,如果块太大再往下用段落、句号、字符号逐级细切,而不是粗暴地按固定长度截。

from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.document_loaders import DirectoryLoader # 1. 加载文档 loader = DirectoryLoader("data/documents/", glob="**/*.md") documents = loader.load() # 2. 切块:chunk_size=1000,overlap=150 splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=150, separators=["\n\n", "\n", "。", ";", ",", " ", ""] ) chunks = splitter.split_documents(documents) print(f"文档切块完成,共 {len(chunks)} 个片段")

chunk_overlap这个参数很多人不理解,我解释一下:它是让相邻片段保留一小段重复文本。比如前一块的末尾是“公司成立于2010年”,后一块的开头是“,总部在上海”,如果没有重叠,前一块丢失了逗号后半句,后一块开头就是无头之尾,两块的语义都残了。有了重叠,检索时无论命中哪一块,都能找到相对完整的语义边界。

第四步:向量化入库。用Embedding模型把每个切块转成向量数组,再把向量写入Chroma。这里有个容易被忽略的性能细节:Embedding模型加载后,要对几千个块逐条向量化,不用循环里每条都调用模型,效率太低。

from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma # 用nomic-embed-text做向量化 embeddings = OllamaEmbeddings(model="nomic-embed-text") vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="data/vector_store" ) print("向量化入库完成")

Chroma.from_documents会批量处理整个文档列表,而不是逐条处理。完成这一步,你的知识库就算建立起来了。

3.2 检索与Prompt组装:回答质量的“胜负手”

向量库里存好了数据,接下来要解决“怎么查”和“怎么用查到的内容”这两个问题。

检索函数的核心代码很简洁,但它在整个链路里的地位极高。我给出的检索函数如下:

def retrieve_context(query: str, k: int = 4) -> list: """检索最相关的K个文档片段""" docs = vectorstore.similarity_search_with_score(query, k=k) results = [] for doc, score in docs: results.append({ "content": doc.page_content, "source": doc.metadata.get("source", "未知"), "score": float(score) }) return results

这段代码的similarity_search_with_score返回相似度分数。在实际工程使用中,我给每段召回结果都打上分数是用来做“防幻觉”的一道闸门:只有召回分数达到阈值的内容才允许拼进Prompt,否则就干脆告诉用户“知识库中没有找到相关内容”。

我之前遇到过的情况是,无论用户问什么,系统都会硬从库里捞几段相关性最低的文本拼进上下文,模型就基于这些无关内容强行编造答案。这个问题一旦出现,靠换Prompt根本解决不了,只能在检索端加质量闸门。

接下来是Prompt组装,这是一个值得单独占位为独立服务的环节:

def build_prompt(query: str, context_docs: list) -> str: """组装Prompt,带引用来源""" context_text = "\n\n---\n\n".join( f"[来源: {d['source']}]\n{d['content']}" for d in context_docs ) return f""" 你是公司的智能助理。请基于以下参考资料回答问题。 参考资料: {context_text} 用户问题:{query} 要求: 1. 如果参考资料中没有相关信息,直接说“根据我目前掌握的资料,无法回答这个问题”。 2. 回答尽量简洁,控制在200字以内。 3. 在回答最后用"#来源"标注引用到的文档名称。 """

为什么Prompt模板必须单独放文件?因为这玩意儿在业务迭代时会高频调整,产品听说“改一下Prompt就能提升效果”之后,大概率会每天提修改需求。如果Prompt拼接逻辑散落在代码里,每次改动都要走代码发布流程,效率极低。独立文件化管理之后,想怎么调就怎么调,还可以做A/B对照。

3.3 API服务层:把调用链暴露成标准接口

前面这些逻辑最终都要暴露成HTTP接口,给前端、机器人或其他服务调用。我用FastAPI来实现这个服务层,它自带Pydantic类型校验、OpenAPI文档,对AI应用这种需要调试的场景极其友好。

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class ChatRequest(BaseModel): query: str model: str | None = None # 可选,覆盖默认模型 temperature: float | None = 0.7 # 可选,覆盖默认温度 class ChatResponse(BaseModel): answer: str sources: list[dict] model: str @app.post("/chat", response_model=ChatResponse) async def chat(req: ChatRequest): retrieved = retrieve_context(req.query) prompt = build_prompt(req.query, retrieved) answer = call_llm(prompt) return ChatResponse(answer=answer, sources=retrieved, model="qwen2.5:7b")

这里有个需要强调的细节:回答里的“来源引用”清单必须独立给前端展示。用户看到“这个回答是基于哪些文档给出的”,信任感会完全不同。不要小看这个设计,一个AI应用能不能在一个团队里被持续使用,很多时候靠的是这份“透明度”。

调用模型的方式,建议封装在core/llm.py里,用配置项控制后端是走Ollama还是OpenAI:

from langchain_openai import ChatOpenAI def get_llm(): return ChatOpenAI( base_url=os.getenv("LLM_BASE_URL", "http://localhost:11434/v1"), api_key=os.getenv("LLM_API_KEY", "no-key-needed"), model=os.getenv("LLM_MODEL", "qwen2.5:7b"), temperature=float(os.getenv("LLM_TEMPERATURE", "0.7")) )

这套封装的价值在于:开发期用本地Ollama,将来业务量上来了或需要更强模型时,把.env里的LLM_BASE_URL改成云端的服务地址、填上真实API Key即可,业务代码一行都不用动。这种“可迁移性”就是工程化区别于纯脚本玩法的关键差异。

4. 实操过程与核心环节实现

4.1 环境准备:从零把依赖装齐

这一节我按步骤走一遍,让刚了解AI工程的新手也能不出错地搭起环境。

第一步,为项目建立独立的虚拟环境。这一步不可跳过——如果不隔离,你会被全局Python环境搞疯:

python3 -m venv .venv source .venv/bin/activate

第二步,安装依赖。我把requirements.txt整理成了下面这个最小可用集:

fastapi==0.115.6 uvicorn==0.34.0 langchain==0.3.14 langchain-community==0.3.14 langchain-openai==0.3.14 langchain-text-splitters==0.3.4 chromadb==0.5.23 pydantic==2.10.4 python-dotenv==1.0.1

安装命令:

pip install -r requirements.txt

这里我特意锁了版本号。LangChain是迭代极快的库,不同小版本的API差异很大,网上很多教程的代码跑不通,原因就是版本不一致。建议后期逐步放开版本限制。

第三步,安装并启动Ollama。Ollama官网一键安装,然后拉取两个模型:

ollama pull qwen2.5:7b # 对话生成模型 ollama pull nomic-embed-text # 向量化模型

拉取qwen2.5:7b之前建议先ollama list看下有没有旧版本残留。我第一次拉模型时,没注意已经下过一个旧版本,重复下了一遍占用了好几GB磁盘。

第四步,验证模型服务正常。启动服务后,请求:

curl http://localhost:11434/api/version

能返回类似{"version":"0.x.x"}就说明Ollama已经在后台待机了。首次调用模型时会有一段模型加载时间,后面就快了。

4.2 小数据集端到端跑通验证

完整链路验证我用了一个很小的知识集——我把自己博客里几篇关于“个人知识管理”的文章存成Markdown放进data/documents/,然后跑入库脚本:

python scripts/ingest.py

输出:

文档加载完成,共 3 篇文章 切块完成,共 42 个片段 向量化入库完成,耗时 3.2 秒

然后启动API服务:

uvicorn app.main:app --reload --port 8000

用一个请求验证完整的RAG链路:

curl -s http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"query": "如何建立知识管理流程?"}'

我实际跑出来的回答摘录(已经能看到引用来源说明):

要建立知识管理流程,核心是三步: 1. 定期收集分散的信息,统一存放在一处; 2. 每周做一次整理,把信息归类并补充自己的理解; 3. 定期回顾,把知识变成可执行的行动。 #来源: data/documents/knowledge-management.md

这个输出的意义在于:从文档入库到检索、组装、生成、溯源,整条链路通了。接下来的工作就是在这个骨架上演进迭代。

4.3 性能优化:首次真正把这些模块调配到位后的感受

当这条链路真正跑通之后,才开始进入“工程优化”环节。这个体验我想特别说明一下,因为很多教程到“跑通”就戛然而止,但实际项目中最重要的功力恰恰在后面。

第一处优化是启动慢。import chromadb这个库在启动时会做一堆初始化检查,第一次启动可能耗掉3-5秒。但FastAPI的--reload模式会在源码变更时自动重启,控制台不断刷出“Running on http//localhost:8000”这行字——如果你忍受不了这种等待,可以把Chroma的初始化封装成懒加载模式,只在首个请求进来时才加载。这也是小细节,但正是一个个小细节叠加出工程感的。

第二处优化是上下文裁剪。我上面组装Prompt时把k=4设成固定值,但实际收到的查询中,有些问题本身很短(比如“您好”),有些问题很长很具体。一个k=4无法适配所有情况。我的实用做法是:先从k=3入手,根据Query长度动态调整:短Query加一个兜底的“系统提问”改写环节,长Query则重点保障相关性。

def retrieve_with_length_k(query: str) -> list: base_k = 3 if len(query) > 150: # 问题很长时多召回一些上下文 base_k = 5 return retrieve_context(query, k=base_k)

这种处理原则很像后端开发里“根据请求参数选择默认策略”的思路,借过来降低了AI环节特有的不确定感。

第三处优化是缓存。如果两个用户在短时间内问完全一样的问题(这在企业内部知识问答里很常见),重复做一遍向量检索+模型生成毫无意义。我用了一层简单的Redis缓存:以Query的哈希为Key,命中直接返回之前的结果,未命中才走完整链路。这个优化能把热点问题响应时间从3-5秒降到毫秒级。

上面这三点优化做完,这个小项目才算得上“工程化”而不是“能跑的脚本”。它揭示的规律是:AI工程里90%的复杂度不在模型参数,而在系统集成中那些细节性的性能、成本和可维护性问题。

5. 常见问题与排查技巧实录

AI工程从零起步最大的问题是:报错真的多,而且报错信息往往不直观。我把实训阶段最常碰到的问题整理成一张速查表:

现象可能原因排查思路解决方案
ollama连接被拒绝Ollama服务没启动curl http://localhost:11434/api/version启动Ollama服务
模型生成中文乱码/吞字量化模型表达能力弱换更大的模型用qwen2.5:7b-instruct替代3b
检索结果完全无关Embedding模型用错检查向量库中的Embedding模型是否与查询时一致ollama list查看,统一模型
回答指代不清、不引用资料上下文组装有问题打印最终Prompt检查调整上下文拼接格式、加“引用格式”要求
API启动后 import 报错依赖版本冲突pip list查看版本对着requirements.txt锁版本
首次调用模型很慢模型从磁盘加载到内存等待几秒首次请求前可预热:调用一次空对话
Token消耗过快(云端API)没有控制上下文长度打印Prompt字节数给上下文设置最大长度截断

这些坑看起来零散,背后其实是一个共性问题:**链路里的任意一环出错,最终表现都会“翻译”成回答质量问题或API异常,而不是直接告诉你哪里出了问题。**所以排查时的起点总是链条任一端的日志,尤其是Prompt组装前后的完整日志。建议在开发期加一个环境开关,能打印出每次请求最终送给模型的Prompt以及检索到的片段,这是最快定位问题的手段。

一个独家技巧:我给这个项目加了个“检索联调检查模式”——启动时设置环境变量DEBUG_LANGCHAIN=1,LangChain会自动输出链路上每一个步骤的执行日志,包括召回片段、分数、耗时。上生产时关掉它就行,它在开发期几乎能替代所有手动断点。这个功能是LangChain自带的,但大量教程都不会特意提它。

再补充一个需要特别提醒的点:任何AI应用上线前,都要确认模型的输出经过了合规审查。如果你做的是面向公众的服务,回答内容中不含敏感、违法或风险信息是底线。技术上的做法是在Prompt里明确约束回答范围,流程上则是设置人工抽检机制。这里是AI工程最严肃的一环,没有商量的余地。

6. 项目延伸:从demo到产线的三个关键升级

如果你把前面的骨架跑通了,我想额外展开几点“从demo到产线”的经验,因为这三件事是让项目从“能用”跨到“好用、敢用”的分水岭。

6.1 数据权限隔离怎么做

企业内部AI应用几乎必然面临权限问题:销售部的AI不应该能查到财务部的数据。RAG架构天然具备这个能力,但需要你在入库时把权限标签写进metadata,查询时把用户身份一起传入检索条件:

vectorstore.similarity_search_with_score( query, k=k, filter={"permission_group": user_group} # Chroma支持按metadata过滤 )

在Chroma这种轻量级向量库里做权限过滤,原理是在向量相似度检索之外加一层过滤条件,通俗理解就是:先按相似度粗筛出一批候选片段,再通过filter把没有权限标签的片段剔除。如果数据量大或权限模型复杂,就需要换到支持权限感知的更重型解决方案,但这个思路是通用的。

6.2 模型评估与回归测试怎么做

传统应用上线前跑测试用例,AI应用同样需要——只是断言方式改为质量评估。我建议在下线新Prompt版本时,建立一个包含50-100条问答对的小评估集,每条记录包含“问题、参考回答、期望引用来源”。改完Prompt跑一遍:

  • 如果回答能对应上参考内容的关键点,记1分;
  • 如果引用了错误文档,扣1分;
  • 全部跑完后算平均分,跟上一版对比,分数掉了就不发布。

做法再简单一点,可以把评估集直接写道一个Excel里,由同事人工打分。这个流程也许不“高级”,但它让Prompt优化变得有据可依,而不是“感觉效果更好了”。

6.3 成本与延迟的持续追踪

AI应用的成本跟传统应用不在一个维度上。调用云端API时,一次问答消耗的Token费用是动态变化的,不监控会失控。我的做法的core/llm.py里记录每次调用的输入Token数、输出Token数和总耗时,写进日志:

{"event":"llm_call","model":"qwen2.5:7b","prompt_tokens":642,"completion_tokens":183,"total_seconds":4.2}

一周汇总后才看得出:哪些查询占了70%的Token消耗?是不是可以把那些被频繁查询的高频知识块直接缓存或者做成预置回答?这套数据显示出来后,优化方向自然就清楚了,不用靠感觉去做优化。

这三件事做下来,这个从零搭建的AI工程项目就真正具备了上线的自信。落地的过程其实是一个把不确定性不断收敛为确定性的过程,而工程化的全部功夫,都在这份确定性的收敛里。

7. 踩坑后的经验小结

整个从零搭建的过程跑下来,我最想留给后来人的经验就三条。

第一,别迷信“效果”,先跑通链路。我见过太多新手花了一周调Prompt,试图把回答效果调到完美,结果发现检索环节的相似度阈值设置错了,Prompt再好也白搭。开局阶段的目标不是效果,而是把整条链路每个环节的最小可用版本跑通。

第二,版本和锁依赖要舍得花时间。如果所有代码都写好了才发现LangChain大版本升级把API改得面目全非,重改一遍的代价远大于最初花半小时锁定版本。建议在requirements.txt里锁住版本号,网上教程里出现的、实测报错的,绝大多数是依赖版本不一致引起的。

第三,从第一天就把可观测性考虑进去。日志输出、Prompt组装预览、召回片段展示、Token计数——这些在开发阶段就要配上。哪怕丑一点,也别等上了生产再补。有多少次,我差点放弃一个看起来奇慢无比的RAG系统,最后打开日志才发现痛苦的根源是某一步在循环里反复重建向量连接。

这套ai-engineering-from-scratch的骨架,是完全可以作为公司内部智能化项目的启动模板来用的。上手跑通它之后,无论再把架构往哪个方向演进,你对AI工程的“手感”已经建立起来了。好的开始是成功的百分之八十,这句话放在AI工程里同样成立——只要你选择从正确的起点开始。

返回列表