从“能调 API”到“能做大模型项目”,中间到底隔着什么?
很多同学学大模型应用,路径大概是这样的:先花半天把 LangChain 装好,再调用一次大模型 API,成功输出一句“你好,我是 AI 助手”,然后就开始找不到下一步了。遇到真正的业务问题时,仍然不知道该先写提示词、还是先建知识库、还是先设计工具函数。甚至会把“调用大模型”和“做大模型应用”画上等号。
这个差距的本质,不是模型能力不够,而是工程链路没有建立起来。一次真正可用的 AI 功能,背后是一整条链路:用户输入进入系统后,要经过提示词约束、检索增强、上下文管理、工具调用、Agent 决策等多个环节,最后才生成回答。任何一个环节不稳定,最终结果都会变得不可用。
这篇文章会带你把这条链路完整走一遍:从环境搭建入手,依次打通提示词工程、Embedding、向量数据库、RAG 检索增强、Agent 工具调用,最终跑通一个“企业知识库问答 + 工具调用”的典型案例。你不仅能得到一份可以照着敲的代码,还会知道每一步为什么这样做、出现问题该从哪里查起。读完你会有一个明确判断:大模型项目的核心难点,不在于“选哪个模型”,而在于“如何把检索、上下文和工具稳定地组织在一起”。
1. 这篇文章真正要解决的问题
1.1 三道坎:提示词失控、知识不落地、工具链路断裂
把大模型接进业务系统,你大概率会遇到三类问题。
第一类是提示词失控。同一个问题,换个句式问,答案质量就明显下降;加了限制条件,它还是“自由发挥”。这说明你还没有把提示词当成代码来管理,也没有用结构化模板把模型的行为边界约束住。
第二类是知识不落地。模型没有真正“知道”你们公司的内部文档。你训练不了它,也不应该训练它;最合理的办法是把文档检索出来,作为上下文喂给它。这个过程中,文档怎么切、向量怎么算、数据库怎么存、相关片段怎么召回,都会直接影响回答质量。
第三类是工具链路断裂。你希望 AI 能查天气、查订单、操作内部系统,而不是只能聊天。但当模型开始调用工具时,你会发现它并不总能按正确参数调用,也不一定能根据工具返回结果继续推进任务。稍微复杂一点的流程,它就“掉链子”。
这三道坎,就是本文要逐步解决的问题。
1.2 本文的项目场景与读者收益
为了不让概念悬空,我们设定一个贯穿全文的项目:企业内部知识库问答助手。它的需求是,员工可以像聊天一样询问制度、项目规范、产品文档,系统需要基于内部文档给出有依据的回答;同时,助手还能调用一个查询订单状态的外部工具。这个项目同时覆盖了 RAG 和 Agent 工具调用,是学习大模型应用全链路最典型的一个切入点。
如果你是后端、全栈工程师,或者刚接触大模型方向的学生,这篇文章的价值在于:把散落在各个文档里的技术点串成一条可运行的主线。你不会再纠结“LangChain 到底用来干什么”“向量数据库是不是必须的”“Tool Calling 和 Agent 是什么关系”,而是直接看到一个完整答案。
2. LangChain、RAG、Agent、向量数据库全景图
2.1 先建立一张概念地图
在写代码之前,先把几个关键术语的关系理清楚。它们不是互相替代,而是在一条链路里各司其职。
| 概念 | 通俗解释 | 它在链路中的角色 |
|---|---|---|
| 大模型 API | 一个“能力很强但记性差”的文本生成器 | 最终负责理解与生成回答 |
| 提示词工程 | 给这个生成器写的“任务说明书” | 控制输出的格式、风格与边界 |
| Embedding | 把文本变成数字向量 | 让计算机可以计算“哪两段话语义相近” |
| 向量数据库 | 专门存储、检索向量的仓库 | 从大量文档中快速找“最相关的片段” |
| RAG | 先检索、再生成的整体方案 | 让模型在回答前先看到限定资料 |
| 工具调用 | 让模型输出“调用哪个函数、参数是什么” | 把说话能力变成执行能力 |
| Agent | 基于大模型的自主执行框架 | 负责拆解目标、调度工具、观察结果 |
| LangChain | 大模型应用的编排工具库 | 把上面这些组件“拼装”起来 |
| LangGraph | 基于状态图的编排框架 | 在复杂 Agent 流程中管理状态和节点 |
一句话概括:RAG 解决“模型基于什么回答”的问题;工具调用解决“模型能做什么”的问题;Agent 解决“谁来编排执行流程”的问题;向量数据库和 Embedding 是让 RAG 真正可用的底层基础设施;LangChain 则是把这些环节用 Python 组装起来的脚手架。
2.2 LangChain 和 LangGraph 到底有什么区别
这是很多初学者会问的问题。从热度看,LangGraph 已经越来越被推荐用于生产级 Agent。它们的关系可以这样理解:
- LangChain 是一套集成工具集,核心价值是提供了大量现成的封装:文档加载器、文本分割器、向量存储封装、Prompt 模板、模型接口封装。它适合快速拼出 RAG 链路和简单的 Agent 流程。
- LangGraph 是一个更底层的编排框架,它把执行过程建模为“图 + 状态”。每个节点做一件事,状态在不同节点之间流转,开发者可以精细控制何时调用工具、何时返回给用户。它更适合复杂、多步骤、需要人类确认或分支判断的 Agent。
初学者先用 LangChain 跑通全链路,理解每个组件的作用;当 Agent 流程变复杂,再迁移到 LangGraph 管理状态。这是比较稳妥的学习路径。
2.3 全链路数据流
整个系统运行时的数据流向是:
用户提问 -> 系统判断:是检索知识库,还是调用工具,还是直接回答 -> 如果走 RAG:文档分块 + Embedding -> 向量库检索 -> 拼装上下文 -> 如果走 Tool Calling:模型输出工具名和参数 -> 执行工具 -> 将结果回填 -> 最终拼装 Prompt -> 大模型生成回答 -> 返回给用户后面的实战环节会照着这条链路逐段实现。
3. 环境准备与前置条件
3.1 运行环境
建议使用 Python 3.10 或更高版本。大模型的 SDK 生态更新很快,较新的 Python 版本往往对类型注解和异步支持更好。
建议创建一个干净的虚拟环境:
mkdir ai-project cd ai-project python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate3.2 安装依赖
本文使用 LangChain 生态演示,核心依赖如下:
pip install langchain pip install langchain-openai pip install langchain-community pip install chromadb pip install python-dotenv注:LangChain 的接口演进速度比较快,不同小版本的 API 可能存在差异。本文代码以“理解主流程”为目标,你安装时以官方最新文档为准。如果某个类的导入路径报错,通常搜索报错信息就能找到新路径。
3.3 模型服务的选型
模型 API 的理想选择是“兼容 OpenAI 接口”的服务。这样你可以用同一套代码,切换不同模型提供商。国内很多大模型厂商都提供兼容接口,只要配置 Base URL 和 API Key 即可。如果你想完全本地运行,也可以部署 Ollama,通过本地接口接入。
关键配置用.env文件管理,不要硬编码在代码里:
# 文件路径:.env OPENAI_API_KEY=your-api-key OPENAI_BASE_URL=https://your-model-provider.com/v1 EMBEDDING_MODEL=text-embedding-3-small LLM_MODEL=gpt-4o-mini如果使用的是国内模型服务,把上面 Base URL 和模型名换成服务商提供的参数即可。还要注意:API Key 是敏感信息,不要把.env提交到 Git 仓库,生产环境建议使用密钥管理服务。
4. 提示词工程:让模型输出稳定可用的第一步
4.1 提示词为什么值得当作代码来写
很多初学者把提示词当成“一句问话”,觉得“能说清楚就行”。但实际项目中,提示词的稳定性直接决定功能可用性。比如你在知识库问答里要求“只能基于给定资料回答”,模型可能第一次遵守,第二次就自行发挥。问题通常出在:提示词没有结构化,也没有用模板统一管理。
好的提示词一般包含四部分:
- 角色定义:让模型知道它在完成什么任务。
- 任务规则:明确能做什么、不能做什么。
- 输入数据:把检索出来的资料作为 Context 拼接进去。
- 输出约束:要求格式、长度、是否引用来源等。
4.2 结构化提示词模板示例
# 文件路径:src/prompt_template.py from langchain_core.prompts import ChatPromptTemplate qa_system_prompt = """你是一个严谨的企业知识库问答助手。 请严格遵循以下规则: 1. 只能根据【参考资料】中的内容回答,不得编造事实。 2. 如果参考资料不足以回答用户问题,直接回复“资料库中未找到相关信息”。 3. 回答时用简洁的中文,分点列出关键内容。 4. 不要输出与问题无关的背景信息。 【参考资料】 {context} """ qa_prompt = ChatPromptTemplate.from_messages( [ ("system", qa_system_prompt), ("human", "用户问题:{question}"), ] )这段代码最重要的不是格式花哨,而是把“禁止幻觉”的规则显式写进了 System Prompt。变量{context}会在运行时被检索到的文档片段填充,{question}来自用户输入。用模板管理提示词,你才能在不同版本间对比效果,也方便上线后观测模型行为。
4.3 新手最容易踩的提示词坑
- 提示词过长但信息冗余。系统给模型的“约束”应该是精准指令,而不是大段描述。
- 把保密要求放在提示词里,就以为模型“真的保密”。提示词只是行为约束,不是安全机制,不能依赖它处理敏感数据。
- 不做版本管理。提示词改一次效果变一次,没有记录就无法回归。建议在代码仓库里用单独目录管理。
5. Embedding 与向量数据库实战
5.1 Embedding 解决的是什么问题
先看一个场景。员工提问“年假能休几天”,文档里的原话是“员工每年可享受的带薪休假天数见下表”。关键词并不重合,传统数据库按关键词搜索大概率找不到这一段。Embedding 的解法是:把“年假”和“带薪休假”分别转成向量,这两个向量在数学空间里的距离很近,于是系统能理解它们语义相似。
Embedding 本身就是一个模型,它把一段文本映射为几百上千维的浮点数数组。有了这个数组,我们就可以用“向量距离”来衡量语义相关度。
5.2 向量数据库选型对比
| 数据库 | 部署形态 | 适合场景 |
|---|---|---|
| Chroma | 本地嵌入式 | 学习、原型验证、中小规模知识库 |
| FAISS | 本地库 | 高性能向量检索,需要自己管理存储 |
| Milvus | 分布式服务 | 生产级大规模向量检索 |
| pgvector | PostgreSQL 插件 | 与业务数据共存,适合已用 PG 的团队 |
| Qdrant | 服务化 | 高可用向量检索,功能完善 |
学习阶段推荐 Chroma,因为它零服务器、自动持久化到本地文件,几行代码就能跑起来。生产阶段,按团队已有技术栈和数据量选型即可。
5.3 文档加载、分块与向量化完整代码
下面这段代码演示:加载一个本地 Markdown 文档,按章节拆分,生成向量,存入 Chroma,并执行一次检索。
# 文件路径:src/build_vector_store.py from dotenv import load_dotenv from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma load_dotenv() # 1. 加载文档 loader = TextLoader("docs/employee_handbook.md", encoding="utf-8") documents = loader.load() # 2. 文本分块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=80, separators=["\n## ", "\n### ", "\n", "。", "!"], ) chunks = text_splitter.split_documents(documents) print(f"文档已切分为 {len(chunks)} 个片段") # 3. 初始化 Embedding 模型 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") # 4. 构建向量数据库并持久化到本地目录 vector_store = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", ) print("向量数据库构建完成,已保存到 ./chroma_db") # 5. 检索验证 retriever = vector_store.as_retriever(search_kwargs={"k": 3}) results = retriever.invoke("年假能休几天") for i, doc in enumerate(results, 1): print(f"--- 第 {i} 个检索结果 ---") print(doc.page_content[:200])代码说明:
RecursiveCharacterTextSplitter是 LangChain 的分块工具,它会优先按段落标题切分,再按标点切分,避免把语义完整的一句话拦腰截断。chunk_size=500, chunk_overlap=80表示每个片段约 500 个字符,相邻片段重叠 80 个字符,用重叠来避免关键信息正好落在两块边界而丢失。Chroma.from_documents会自动完成向量化并持久化,persist_directory参数指定存储目录。- 检索时
k=3表示召回最相关的 3 个片段。
5.4 分块策略是工程质量的关键
很多项目效果差,不是 Embedding 模型差,而是文档切得太随意。如果块太大,向量包含太多无关信息,检索精度下降;如果块太小,语义不完整,模型无法理解上下文。合理做法是根据文档结构切分,例如一个 Markdown 的##标题作为一个逻辑单元,遇到超长章节再继续拆。这个策略需要根据你实际文档类型反复调,不要一上来就追求完美。
6. RAG 检索增强生成全链路实现
6.1 为什么需要 RAG,而不是微调
企业内部知识变化频繁,如果用微调更新新制度,每次都要准备数据集、训练、评测和上线,时间成本极高。RAG 的思路是:把知识放在外部库里,回答前先检索相关内容,塞进上下文,让模型“带着资料回答问题”。优点是知识更新只需更新文档库,不用重新训练模型,也便于追溯来源。
RAG 也有自身的边界:它适合“知识密集型问答”,不适合“要求模型学会新推理能力”的场景。常见误解是把 RAG 当成万能药,遇到失败一味加更多资料。实际上,检索召回质量、提示词约束、文档切分策略都会影响最终效果。
6.2 把检索和生成串起来
下面的代码把上一章的向量库和第四章的提示词模板组合成完整的 RAG 链路:
# 文件路径:src/rag_chain.py from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings from langchain_core.runnables import RunnablePassthrough from prompt_template import qa_prompt load_dotenv() # 1. 加载已有向量库 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vector_store = Chroma( persist_directory="./chroma_db", embedding_function=embeddings, ) retriever = vector_store.as_retriever(search_kwargs={"k": 4}) # 2. 定义大模型 llm = ChatOpenAI( model="gpt-4o-mini", temperature=0.2, ) # 3. 格式化检索结果 def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) # 4. 组合 RAG 链路 rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | qa_prompt | llm ) # 5. 提问 question = "员工每年可以享受多少天带薪年假?" response = rag_chain.invoke(question) print(response.content)关键逻辑说明:
retriever | format_docs会把检索到的文档片段拼成一个大字符串,作为{context}传给提示词模板。RunnablePassthrough()把用户问题直接传下去,省去手动组装字典的代码。temperature=0.2降低生成随机性,让回答更稳定,适合知识库问答这类对准确性要求高的场景。
运行前确保docs/employee_handbook.md中存在相关内容。运行成功后,你应该能看到模型基于检索到的片段给出回答,而不是凭空编造。
一个直接有效的验证方法是:故意问一个文档里没有的问题,正常情况下模型应回复“资料库中未找到相关信息”。如果它依然长篇大论,说明 System Prompt 里的规则没有被严格执行,优先检查提示词约束,而不是换模型。
6.3 RAG 知识库指标怎么理解
热搜里经常出现“RAG 知识库指标有哪些”。这确实是上生产前绕不开的问题。业界常用 RAGAS 等框架评测,核心指标包括:
| 指标 | 面向环节 | 含义 | 通俗理解 |
|---|---|---|---|
| 忠实度 | 生成 | 回答是否严格基于检索片段,没有幻觉 | 模型有没有“照着材料说话” |
| 答案相关性 | 生成 | 答案是否切题,没有答非所问 | 回答是不是用户想要的 |
| 上下文相关性 | 检索 | 召回片段与问题的相关度 | 系统有没有捞回有用的资料 |
| 召回率 | 检索 | 应该召回的正确答案,实际召回多少 | 有没有漏掉关键片段 |
| 精确率 | 检索 | 召回的片段里,多少是真正有用的 | 有没有混入大量噪声 |
上线之前,建议准备一批标准问题,人工标注期望的答案片段,跑一遍评测,记录指标。后续每次修改文档切分策略、提示词或模型,都重新评测。没有评测体系的 RAG 项目,上线后基本靠运气。
7. Agent 智能体与工具调用实战
7.1 从“聊天”到“做事”:Agent 的核心价值
大模型直接接入业务,形态是“你问一句,它答一句”。Agent 改变了这个模式:你提出目标,模型负责拆解步骤、决定调用哪些工具、观察工具结果、再决定下一步。以查订单为例,普通聊天模型只能回答“我无法查询订单”;接入工具后,模型可以调用订单查询函数,拿到真实数据,再组织成自然语言回答。
初学者容易混淆:工具调用(Tool Calling)和 Agent 不是一回事。工具调用是让模型在输出中声明“我要调用哪个函数、传什么参数”;Agent 是用 Prompt 和编排逻辑决定“何时调用、调用后怎么处理”。工具调用是 Agent 的重要零件,但 Agent 包含更多调度逻辑。
7.2 定义一个可被模型调用的工具函数
下面演示一个“查询订单状态”的工具函数,并把它包装成模型可以理解的工具描述。
# 文件路径:src/tools.py from datetime import datetime from langchain_core.tools import tool @tool def get_order_status(order_id: str) -> str: """根据订单号查询订单状态。 参数 order_id 是用户在问题中提到的订单编号。 """ # 这里对接真实订单系统,当前为示例逻辑 if order_id.startswith("A"): return f"{order_id} 已发货,预计 3 天内送达" return f"{order_id} 正在处理中,请稍后查询"这段代码的核心在两个地方:
- 函数注释写得非常详细。LangChain 会把函数名、参数说明、docstring 一并发送给模型,模型根据这份“说明书”决定何时调用、如何传参。
@tool装饰器自动把 Python 函数转换为模型可调用的工具描述。不需要手写 JSON Schema,这是 LangChain 比较方便的地方。
7.3 用 LangChain 让模型自动决定调用工具
接下来把工具挂到模型上,构造一个最小 Agent:
# 文件路径:src/agent_demo.py from dotenv import load_dotenv from langchain_openai import ChatOpenAI from tools import get_order_status load_dotenv() llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # 绑定工具 llm_with_tools = llm.bind_tools([get_order_status]) messages = [ ("system", "你是一个订单查询助手。如果用户提供了订单号,请调用工具查询订单状态。"), ("human", "我的订单 A10086 到哪里了?"), ] response = llm_with_tools.invoke(messages) print(response) if response.tool_calls: for call in response.tool_calls: tool_name = call["name"] args = call["args"] print(f"模型决定调用工具:{tool_name}") print(f"工具参数:{args}") # 执行工具函数 if tool_name == "get_order_status": result = get_order_status.invoke(args["order_id"]) print(f"工具返回:{result}")运行后,模型应当输出一个tool_calls结构,里面包含工具名get_order_status和参数{"order_id": "A10086"}。注意,模型此时只是“声明要调用工具”,真正执行工具需要我们自己调用函数。执行完工具之后,通常还需要把工具结果回传给模型,让模型基于真实数据给出最终答案。
这个“模型声明调用 -> 程序执行 -> 结果回填 -> 模型继续生成”的循环,就是 Agent 最底层的形态。复杂 Agent 框架只是在上面增加了更多调度策略。
7.4 复杂流程用什么:LangChain Agent 还是 LangGraph
简单的单次工具调用,用 LangChain 的bind_tools就够。当流程变成“先查订单,再判断是否退款,再查库存,最后生成报告”,每一步之间都有状态依赖时,建议改用 LangGraph。
LangGraph 的价值在于:把流程建模成图,节点可以是模型,也可以是普通函数;状态在节点之间显式传递;支持条件分支、循环、人工介入。它的调试体验比 LangChain 原生 Agent 好很多,尤其适合生产级应用。
学习路径建议:先用本文的bind_tools理解工具调用机制,再用 LangGraph 重写同一个流程,体会状态管理带来的差异。
8. 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 安装 chromadb 时 sqlite3 版本报错 | Windows 环境自带 sqlite3 版本过低,Chroma 依赖高版本 | 运行import sqlite3; print(sqlite3.sqlite_version)检查版本 | 安装pysqlite3-binary并在导入 Chroma 前替换sqlite3模块 |
| 报错提示 Embedding 维度不一致 | 建库时用的 Embedding 模型和查询时用的模型不同 | 查看向量库创建时的模型名和当前模型名 | 统一使用同一个 Embedding 模型,重新构建向量库 |
| 检索结果与问题无关,答非所问 | 文档切块方式不合理或k值太小 | 打印检索到的片段内容,逐条检查相关性 | 调整分块策略、增大k、尝试不同 Embedding 模型 |
| 模型没有遵循“只能基于资料回答”规则 | 提示词约束不明确,或上下文占比较高时被模型忽略 | 单独测试 System Prompt,做几组对照 | 加强指令措辞,先回“未找到”再结束;必要时用编程逻辑强制判断 |
| Agent 总是不调用工具,或参数传错 | 工具函数说明不够明确,模型不知道何时调用 | 检查工具函数 docstring 是否说清楚了触发条件和参数含义 | 重写工具描述,给出具体示例,例如“当用户提到订单号时调用” |
| Agent 陷入多轮循环,不结束 | 缺少停止条件或最大迭代次数限制 | 观察调用日志,看是哪一步反复执行 | 设置最大执行轮次、增加超时、在流程中增加“结束”判断 |
| API 返回 token 超限 | 一次放入上下文的文档片段太多 | 计算每次请求的 token 消耗 | 降低k、增加分块过滤规则、对大文档做摘要 |
| 大模型返回内容的格式不稳定 | 没有做输出解析,完全依赖提示词 | 检查返回结果的 JSON 结构 | 使用 LangChain 的with_structured_output等方法做结构化输出解析 |
9. 最佳实践与工程建议
9.1 把提示词、工具、评估都纳入版本管理
大模型应用和传统后端应用有一个显著区别:影响行为的因素不止代码,还有提示词、工具描述、文档内容。这些都应该纳入 Git 管理,每次改动都走代码评审。不要把提示词偷偷改在线上调试窗口里,更不要用一段无记录的 Prompt 支撑生产功能。
9.2 建立日志与全链路追踪
生产环境里,模型回答错了,你很难直接知道是检索错了还是生成错了。建议至少记录以下信息:
- 用户原始输入
- 最终发给模型的完整 Prompt
- 检索到的文档片段及其来源
- 模型返回的原始输出
- 工具调用名、参数、返回结果
有了这些日志,问题才能快速定位。更进一步,可以接入 LangSmith 或 OpenTelemetry 这类可观测平台,实现完整链路追踪。
9.3 工具权限遵循最小化原则
工具调用意味着模型获得了“执行能力”。如果工具可以查询数据库,提示词注入就可能诱导模型执行危险操作。实际项目里,给模型的工具应该是受限的只读接口,不提供删除、覆盖等高危操作;即使需要写操作,也应该增加人工确认环节。工具名和参数要做白名单校验,不要盲目信任模型输出。
9.4 性能与成本平衡
RAG 链路的主要性能瓶颈是向量检索和 LLM 生成。向量检索如果数据量大,可以考虑加一层粗排,或者用基于关键词的 BM25 和向量检索做混合召回。LLM 成本方面,可以用更小的模型处理简单问题,只有复杂问题才路由到更大模型;同时做好结果缓存,相同或相似问题直接从缓存读取。
9.5 先跑通再优化,先评测再上线
整条链路涉及的变量非常多:模型、Embedding、分块、检索、提示词、工具描述,任何一项改动都可能影响效果。正确做法是:先用小规模文档跑通端到端,再建立评测集,再持续优化。不要一上来就追求复杂架构。
10. 总结与后续学习方向
到这一步,你已经走完了一条完整的大模型项目主线:用提示词工程约束模型行为,用 Embedding 和向量数据库解决知识检索,用 RAG 让回答有据可依,用工具调用和 Agent 让 AI 具备执行能力,最后靠 LangChain 这类框架把这些环节串起来。
接下来的进阶方向可以按这个顺序走:先深入研究 LangGraph,把 Agent 流程从简单调用升级为有状态、可分支的编排;再深入学习 RAG 评测,建立你自己的评估集,确保每一次改动都有数据支撑;然后了解混合检索、重排序、Agent 记忆等进阶手段。生产环境的挑战永远比教程复杂,但核心链路搞清楚了,后续遇到的问题都会变成可以定位、可以解决的工程问题。
建议先把本文的代码跑通,再用自己的公司文档替换 Employee Handbook,最后把工具函数换成真实业务接口。动手做一遍,比收藏十篇教程更有价值。