ARTICLE DETAIL

资讯详情

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

从API调用到RAG落地:跑通第一个检索增强生成程序

从API调用到RAG落地:跑通第一个检索增强生成程序 整整磨了一下午我才把RGA项目的第一个程序跑通。说出去有点丢人——前面几篇内容里我把概念框架、数据准备、模型选型聊得头头是道真到敲代码的时候反而被一个再简单不过的API请求卡住了。不是不会Python而是被“该调谁家的接口、base_url填什么、model名字写哪个、为什么照着文档抄都报错”这一串琐碎问题绕晕了。RGA是我手头这套检索增强生成小系统的代号很多朋友叫RAG一回事写到这里已经到了第四篇该动手了。这篇我就聚焦三件事先把RGA会用到API的整体地图画清楚让你知道嵌入接口、对话接口、辅助接口分别在项目里扮演什么角色然后给你一个可以直接跑的第一个程序从环境变量到输出结果逐行拆开讲最后把我真实调用API时踩过的鉴权、限流、上下文超限这些坑摊开说一遍。适合谁看打算用大模型API搭RAG、却卡在接口环节下不了手的开发者以及想从“API是什么”这个概念平滑过渡到“我能自己写代码调API”的朋友。1. 为什么第四篇先聊API而不是直接上核心逻辑1.1 从本地部署的幻想到接口调用现实一开始我也动摇过RGA项目不一定非要调外部API完全可以本地部署一个开源模型比如跑个Qwen或者LlamaEmbedding模型也用本地版本整套系统不联网也能跑。这个想法听起来很诱人但我算了一笔账就冷静了——一个7B量化模型大概需要6GB显存嵌入模型再吃2GB而我这台机器是普通开发机光是跑起来就够呛。就算勉强跑动每次生成答案的速度也只能用“倒杯水等结果”来形容还经常把脑子里的上下文忘干净。API这条路的优势不在于免费而在于把最贵的算力、最繁琐的部署、最痛苦的运维全部压缩成一个HTTP请求。对个人开发者、小团队和写自动化工具的人来说这是起步成本最低的路线。所以RGA的实操切入点很自然地落在了“调API”这件事上。这里忍不住先给完全零基础的朋友补一句API就是别人把服务封装成一个接口你发一个请求过去服务端处理完把结果返回给你。你用不着深入理解底层的HTTP协议细节只要知道“你要发什么、你能收到什么”就够了。后面所有代码本质上都在做这一件事。1.2 没有“接口思维”三步就会被劝退我觉得新手在API环节最典型的三步挫败值得单独拎出来说。第一步申请到Key直接复制到代码里请求发出去收到401或403。第二步查到需要配base_url加上之后又收到404因为base_url多了一个斜杠或者少了一个/v1。第三步好不容易能请求了结果把网站的全文都塞进上下文收到400上下文超限再一看账单还涨了一截。这三步看起来是三个不同的报错其实是同一个问题你还没有建立“接口”的心智模型。API调用本质上就是四件事——请求地址endpoint、身份验证auth、模型名字model、请求内容payload。后面我给的第一个程序说白了就是把这四件事老老实实落实一遍。想透这一点报错的时候你就不会慌因为你知道该去检查哪一个环节。2. API地图——RGA项目里会用到的三类接口一次认齐2.1 负责“看得懂资料”的嵌入模型API先把术语说透。嵌入模型做的事是把一段文字变成一串数字也就是向量。这串数字本身没有直观含义但语义上越接近的文字产生的向量在空间里的距离也越近。RAG为什么需要它因为我们要从一堆文档里找出和用户问题最相关的内容最可靠的办法之一就是把问题和候选文档都转成向量然后计算距离。你不需要会高数只需要记住一句话向量越近语义越近。选哪家的嵌入API我实际测试过几个列一张对照表给你。需要提醒的是这是我现在写这篇文章时的情况价格和模型名随时可能变动动手前务必以各家官网为准。服务商推荐模型向量维度计费方式备注智谱embedding-32048可下调按token计费新用户有免费额度国内直连稳定适合做主力硅基流动BAAI/bge-m31024有免费档和低价档支持多种开源模型切换方便本地Ollamabge-m3 / nomic-embed-text1024 / 768免费靠内存和CPU也能跑但速度一般OpenAItext-embedding-3-small1536按token计费质量稳适合已有账号的朋友这里有一条实实在在的经验DeepSeek的接口目前不提供embedding模型。我曾在它的文档里翻了大半天确认它只做对话和推理不做向量化。所以做RGA时嵌入这一环要么用智谱要么用本地bge模型要么通过硅基流动这类平台跑开源嵌入模型。这个小坑提前记下来省得你踩完再回头找原因。2.2 负责“生成答案”的大模型对话API对话接口是RGA输出的最后一环它负责把检索到的资料整理成通顺、自然、可信的中文回答。目前主流服务商几乎都提供OpenAI兼容接口这对开发者是个巨大的利好只要会调OpenAI的Python SDK把base_url和model换成目标服务商对应的值就能在DeepSeek、智谱、Kimi这些服务之间来回切换。所以我不建议给每家单独写一套调用逻辑统一走一套兼容接口是最省心的做法。我常用的一组配置如下同样会变化以官网文档为准服务商Base URL示例常用模型名亮点DeepSeekhttps://api.deepseek.com/v1deepseek-chat便宜中文质量好智谱https://open.bigmodel.cn/api/paas/v4glm-4-flashflash档免费额度大适合练手月之暗面https://api.moonshot.cn/v1moonshot-v1-8k长文本处理能力不错本地Ollamahttp://localhost:11434/v1qwen2.5:7b免费性能要求不高时可用关于“免费大模型API”我的建议是如果只是想先跑通流程优先用智谱的glm-4-flash或者硅基流动的免费模型。它们和收费模型走的是同一套接口逻辑等验证完再切正式模型就行代码一行都不用改。2.3 那些“可选但真香”的辅助API除了上面两个必须的接口一个完整的RGA项目还会陆续接触到三类辅助API。先说文档解析API把PDF、Word甚至扫描件变成干净的文本。这个环节我目前最常用的是MinerU它是开源项目也提供在线API能把排版复杂的PDF解析成结构完整的Markdown。我拿它解过一份几十页的资料文档效果明显好于传统PDF库图、表、段落层级都能保留下来。再看重排模型API。向量检索找到一批候选片段之后再用一个专门的重排模型把这些片段按相关性排一遍序。这一步不是第一版必需的但对答案质量有明显的提升——简单说向量检索负责“初筛”重排负责“精挑”。最后是向量数据库托管API。如果你不想自己维护Milvus或Chroma这类服务云厂商提供的托管向量数据库也是一个方向。但我给的建议很直接第一版程序完全用不上它先用文件或者内存里的临时方案等数据量到几万条以上再考虑托管服务。过早引入重型组件只会让你在第一版调试时分心。3. 第一个程序——20行代码让“检索-生成”转起来3.1 准备Key的时候我建议你多做一步先把API Key放在哪里这件事说清楚。最蠢的做法是把Key硬编码在代码里万一哪天你把这个文件传到GitHub上Key就彻底泄露了轻则被盗刷重则牵连整个账号。正确做法是放进环境变量。我习惯用python-dotenv这个库在项目目录创建一个.env文件记得把它加进.gitignoreRGA_API_KEYsk-xxxxx RGA_BASE_URLhttps://api.deepseek.com/v1 RGA_MODELdeepseek-chat RGA_EMBED_MODELembedding-3代码里这样加载import os from dotenv import load_dotenv load_dotenv() API_KEY os.getenv(RGA_API_KEY) BASE_URL os.getenv(RGA_BASE_URL) MODEL os.getenv(RGA_MODEL) EMBED_MODEL os.getenv(RGA_EMBED_MODEL)这么做的额外好处是后面想换服务商只需要改.env文件里的三个值代码完全不用动。我当时从智谱切到DeepSeek前后只花了一分钟。3.2 最小可运行代码先只调大模型API第一个程序不搞复杂的RAG先把“能不能成功发起一次对话请求”这件事搞定。安装依赖只需要一条命令pip install openai python-dotenv然后新建main.pyfrom openai import OpenAI client OpenAI( api_keyAPI_KEY, base_urlBASE_URL, ) resp client.chat.completions.create( modelMODEL, messages[ {role: system, content: 你是一个简洁的中文助手。}, {role: user, content: 请用一句话解释什么是RAG。}, ], temperature0.7, ) print(resp.choices[0].message.content)跑起来之后你会看到一行回答例如“RAG是一种结合检索和生成的技术先找到相关资料再让大模型生成回答”。这时候你的第一个程序就算真正跑通了别小看这一步它意味着你之后可以在任何大模型服务上自己动手搭东西。有两个参数值得解释一下。temperature0.7控制生成的随机度0表示每次结果基本一致1以上容易跑偏。RAG场景我建议设在0.3到0.7之间太低会显得机械太高会离题万里。system是角色设定它影响的不是这一次问答而是整段对话的风格和边界。空着也能跑但加了之后回答会更稳后续做RAG时你可以让模型扮演“严格基于资料的助手”它会更听话。3.3 把“检索”也加上一个50行的迷你RAG光会对话还不够RAG的核心是“先检索再生成”。我写了一个不需要向量数据库的迷你版本方便你在理解层面跑通全链路。思路是把资料切成小块对每一块算嵌入向量用户提问时把问题也转成向量然后取相似度最高的几块拼进上下文最后交给大模型回答。import numpy as np from openai import OpenAI client OpenAI(api_keyAPI_KEY, base_urlBASE_URL) def embed(texts): resp client.embeddings.create( modelEMBED_MODEL, inputtexts, ) return [item.embedding for item in resp.data] def cosine_sim(a, b): a, b np.array(a), np.array(b) return (a b) / (np.linalg.norm(a) * np.linalg.norm(b)) docs [ RAG是检索增强生成先从知识库检索相关资料再让大模型生成回答。, 嵌入模型把文本转成向量语义相近的文本向量距离更近。, API调用通常需要endpoint、身份验证、模型名和请求内容。, 好的提示词可以让大模型回答更准确但RAG的本质是提供外部知识。, ] chunk_vecs embed(docs) question 为什么RAG能减少大模型胡说八道 q_vec embed([question])[0] scores [cosine_sim(q_vec, c) for c in chunk_vecs] best_idx int(np.argmax(scores)) context docs[best_idx] final_prompt f基于以下资料回答问题。资料{context} resp client.chat.completions.create( modelMODEL, messages[ {role: system, content: 你只能基于资料内容回答资料里没有的内容就说不清楚。}, {role: user, content: final_prompt}, ], ) print(resp.choices[0].message.content)这段代码足够让你理解RAG的骨架切分、向量化、检索、拼装、生成。实际项目里要把这一步拆成多个模块管理但原理永远跑不出这几行逻辑。4. 我踩过的坑第一轮API调用基本都倒在这里4.1 401/403鉴权失败90%是Key或base_url的问题第一类坑是鉴权失败。我遇上过的具体原因包括复制Key时多带了一个空格旧项目的Key已经被删了但没更新base_url少写了/v1或者多写了一个斜杠。这些情况的报错都很相似大概率是一串401或者403光看报错根本看不出问题在哪。我的排查顺序固定三步。第一步先确认Key本身有效服务商通常提供网页端测试入口你在网页上能正常请求说明Key没问题。第二步打开官方文档把base_url和代码里的一字不差地核对尤其是结尾的路径段比如/v1、/v4。第三步把环境变量实际打印到控制台里看一眼。很多情况下问题不是配置错了而是.env文件根本没被程序读到——文件路径不对、没安装python-dotenv、或者变量名拼写不一致。这里有个很容易忽略的细节.env里写的是RGA_API_KEY代码里却读的是API_KEY那结果肯定对不上。我在这个低级错误上栽过不止一次。所以发现鉴权失败时第一件事永远是打印出代码实际拿到的base_url和key前缀而不是盯着报错看。4.2 429/400配额、限流和上下文超限第二类坑最让人头疼跑得好好的突然返回429。这大概率是触发了限流或者余额不足。这个问题最有效的预防方式是正式跑批之前先去后台看一眼当前API调用量和余额尤其别在半夜批量处理时才发现Key已经欠费。理想的情况是写一个完整的错误重试逻辑而不是只处理成功路径import time from openai import RateLimitError for attempt in range(3): try: resp client.chat.completions.create( modelMODEL, messagesmessages, ) break except RateLimitError: print(f触发限流第{attempt 1}次重试) time.sleep(2 ** attempt)还有一类400错误报错信息里会写明maximum context length。我见过有人把一整本电子书的文本全部塞进系统提示词结果连百万级token上下文的模型也被塞满了。RAG的正确姿势不是把资料全塞进去而是只把“检索出来的最相关的几段”拼进上下文。这正是第三节那个迷你RAG骨架存在的意义——它从一开始就帮你养成正确的信息筛选习惯。4.3 用一份代码切换多家API是我最推荐的姿势第四个建议跟报错无关但它能省掉一半调试时间把服务商相关的东西全部抽到配置层。前面那张表已经说清楚了绝大多数开放接口都兼容OpenAI协议差异只集中在base_url和model两个参数。所以在项目里做一个settings模块或者干脆用.env文件统一管理密钥、地址和模型名切换服务商就变成了一分钟的事。我当时是先借用智谱的免费模型跑通全流程再把同一份代码切到DeepSeek最后替换成带重排模块的完整方案。没有抽配置这一步光是改参数就得耗掉一下午。5. 跑通之后别急着加功能——按这个顺序往RAG上靠5.1 从测试文本换成你自己的真实资料程序能跑之后第一件事是把测试用的假数据换掉。挑一份真实文档用MinerU或你自己的解析脚本把PDF变成干净文本然后放进“切分-向量化-检索”的循环里。这时候你会接触到切块大小的调优。切块不是越大越好——切太大上下文容易被无信息量的内容污染切太小又可能截断一句完整的话。我一般先按章节和段落切发现效果不理想再往句子级或固定字符长度调整。在实际项目中这一步的影响不亚于模型选型。5.2 先加缓存再想优化大模型API是按调用量真实计费的所以我在跑通之后做的第一件事不是加功能而是加缓存。最简单的方案把问题和生成的答案存进一张本地表下次相同问题直接命中完全不消耗API。进阶一点可以用语义缓存思路和检索一样——计算当前问题的向量和缓存里的历史问题向量做相似度比较超过阈值就直接返回历史答案。这套逻辑几乎可以复用前面检索模块的代码性价比非常高。5.3 从numpy检索到正经向量库当文档量到了几千条以上全量计算相似度的方式就开始吃力了。这时候再考虑引入Chroma、pgvector或Milvus这类向量数据库。我的选择顺序是先用Chroma或pgvector这种轻量方案数据量继续涨再考虑Milvus不要一上来就搭分布式集群。选型标准无非三条数据量、并发量、团队会不会长期维护。如果只是个人项目Chroma跑在本地文件上就很舒服如果是云端服务pgvector能省掉一套新组件等数据量真的上到百万级再去看Milvus也不迟。顺便提醒一句如果你选择用Docker容器跑Ollama或Chroma偶尔会遇到permission denied while trying to connect to the docker api这种报错。这个问题本质是当前用户没有访问Docker引擎的权限解决方式是把当前用户加入docker用户组或者临时用sudo执行但注意不要在日常开发环境里长期用sudo跑容器不然后面权限问题会越滚越大。技术脉络到这里已经很清晰了API地图解决的是“调谁的接口”第一个程序解决的是“怎么跑起来”坑位清单解决的是“跑起来之后怎么不摔死”。我个人的体会是RGA系列最难的部分从来不是某个高深的算法而是把这些琐碎的工程细节一点点抠干净。如果你也被卡在API这一步按这个顺序试一遍应该能比我当年更快跑通。
返回列表