构建LangChain应用程序的示例代码:58、用 Nomic 嵌入模型 + LangServe 部署 RAG 服务:config.toml 与 settings.json 配置骨架

发布时间:2026/9/29 3:53:13
构建LangChain应用程序的示例代码:58、用 Nomic 嵌入模型 + LangServe 部署 RAG 服务:config.toml 与 settings.json 配置骨架
1. 为什么本地跑 RAG 总在“最后一公里”翻车如果你正在用 LangChain 搭检索增强问答大概率经历过这个流程文档加载、切块、嵌入、存向量库、拼 prompt、调模型本地chain.invoke()跑得挺顺结果一换到“给别人调用”就卡住——要么是同事想用你的检索服务要么是前端要接一个 HTTP 接口要么是你自己想把这条链路固定成一个可复用的服务。这时候 LangServe 就派上用场了它能把一条 LangChain 链直接暴露成/invoke、/batch、/stream这些标准接口。而嵌入模型这块Nomic 的nomic-embed-text-v1是个很实用的选择8k 上下文窗口对长文档切块友好检索语义也稳。问题在于很多教程只给你一段 Python 代码真到部署时config.toml和settings.json这两个配置文件怎么写、Key 放哪、模型名怎么填、LangServe 路由怎么挂全靠猜。这篇就按“能直接复制去跑”的标准把 Nomic 嵌入 LangServe 部署 RAG 的配置骨架和验证动作讲清楚面向的是想在本机快速验证检索增强问答的开发者。核心检索词先摆出来LangChain 负责编排链路Nomic 提供嵌入模型RAG 是最终形态LangServe 负责把服务暴露成 APIconfig.toml与settings.json是配置骨架。适合谁适合已经会写基础 LangChain 链、但被部署配置卡住的人。下面从统一 Key 通道开始再给可复制配置最后用/invoke验证整条检索与生成链路。2. TaoToken 前置统一 Key 与 API 通道怎么接在本地验证 RAG 时最烦的不是代码是 Key 管理。嵌入模型一个 Key、对话模型一个 Key、追踪平台又一个 Key环境变量散落在各个 shell 里换台机器就得重配。我习惯用一个统一的 API 通道来收敛这件事TaoToken 就是干这个的它提供一个统一的 Key 和 API 入口LangChain 里通过base_url指向它就能把对话模型的调用统一走一条通道。官网入口在这里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置里直接写这个就行。具体到操作你需要先拿到 Key。进入控制台创建 API Key路径是 consolehttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 api-keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建完复制那串sk-开头的 Key后面写进settings.json或环境变量。这里要区分两件事Nomic 的嵌入模型走的是 Nomic 自己的接口需要 Nomic 的 token而对话模型比如 GPT-4 或本地 Mistral可以走 TaoToken 的统一通道。所以你的配置里会有两类凭证别混在一起。如果你只是想先验证检索链路嵌入用 Nomic、生成用 TaoToken 通道的对话模型是最省事的组合。提示Key 不要硬编码进chain.py统一放settings.json或.env再用os.environ读取。这样换机器只改一个文件。3. 可复制配置config.toml 与 settings.json 骨架LangServe 项目用langchain app new创建后目录里会有pyproject.toml、app/server.py、app/chain.py。但真正决定“服务怎么起、Key 从哪读、模型名是什么”的是我们自己补的两个配置文件。下面给的是骨架字段名你可以按项目习惯微调但结构建议保留。先看config.toml它管的是服务级参数监听地址、端口、路由前缀、日志级别。# config.toml [server] host 0.0.0.0 port 8000 route_prefix /nomic-rag log_level info playground true [embedding] provider nomic model nomic-embed-text-v1 # Nomic 嵌入维度v1 为 768 dimensions 768 # 长上下文切块参数 chunk_size 7500 chunk_overlap 100 [vectorstore] type chroma collection_name rag-chroma persist_directory ./chroma_db [llm] provider openai-compatible model gpt-4-1106-preview temperature 0.0 base_url https://taotoken.net/api再看settings.json它管的是凭证和运行时开关。注意这里我把对话模型的 Key 和 Nomic 的 token 分开写避免混淆。{ nomic: { api_token: 你的_nomic_token, login_cmd: nomic login }, llm: { api_key: sk-你的_taotoken_key, base_url: https://taotoken.net/api, model: gpt-4-1106-preview, temperature: 0.0 }, langsmith: { tracing: false, endpoint: https://api.smith.langchain.com, api_key: }, runtime: { retriever_k: 4, max_context_tokens: 7500 } }两个文件的分工要清楚config.toml是“服务长什么样”settings.json是“用什么凭证、开什么开关”。这样你在chain.py里读配置时逻辑就很干净。下面这段是读取配置并构建链的核心代码可以直接放进app/chain.py。# app/chain.py import os import json import tomllib from langchain_community.vectorstores import Chroma from langchain_community.document_loaders import WebBaseLoader from langchain_text_splitters import CharacterTextSplitter from langchain_nomic import NomicEmbeddings from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI # 读取配置 with open(config.toml, rb) as f: cfg tomllib.load(f) with open(settings.json, r, encodingutf-8) as f: settings json.load(f) # 注入环境变量 os.environ[NOMIC_API_TOKEN] settings[nomic][api_token] os.environ[OPENAI_API_KEY] settings[llm][api_key] os.environ[OPENAI_BASE_URL] settings[llm][base_url] # 文档加载与切块 urls [ https://lilianweng.github.io/posts/2023-06-23-agent/, https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/, ] docs [WebBaseLoader(url).load() for url in urls] docs_list [item for sublist in docs for item in sublist] splitter CharacterTextSplitter.from_tiktoken_encoder( chunk_sizecfg[embedding][chunk_size], chunk_overlapcfg[embedding][chunk_overlap], ) doc_splits splitter.split_documents(docs_list) # 向量库 Nomic 嵌入 vectorstore Chroma.from_documents( documentsdoc_splits, collection_namecfg[vectorstore][collection_name], embeddingNomicEmbeddings(modelcfg[embedding][model]), persist_directorycfg[vectorstore][persist_directory], ) retriever vectorstore.as_retriever( search_kwargs{k: settings[runtime][retriever_k]} ) # 提示模板 template 根据以下上下文回答问题 {context} 问题{question} prompt ChatPromptTemplate.from_template(template) # 对话模型走统一通道 model ChatOpenAI( temperaturecfg[llm][temperature], modelcfg[llm][model], base_urlcfg[llm][base_url], ) # RAG 链 chain ( {context: retriever, question: RunnablePassthrough()} | prompt | model | StrOutputParser() )这段代码里有两个关键点NomicEmbeddings读的是NOMIC_API_TOKEN而ChatOpenAI的base_url指向 TaoToken 的 API 地址Key 用统一通道的 Key。这样嵌入和生成各走各的路互不干扰。4. 挂载 LangServe 路由并启动服务链写好了接下来把它挂到 LangServe 上。打开app/server.py加上路由注册。# app/server.py from fastapi import FastAPI from langserve import add_routes from app.chain import chain as nomic_chain app FastAPI( titleNomic RAG Service, version1.0, descriptionNomic 嵌入 LangServe 部署的 RAG 服务, ) add_routes(app, nomic_chain, path/nomic-rag) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)依赖安装这块用 poetry 管理的话在项目根目录执行poetry add langchain-nomic langchain_community tiktoken langchain-openai chromadb langchain langserve poetry install如果你不用 poetry直接 pip 也行pip install -U langchain-nomic langchain_community tiktoken langchain-openai chromadb langchain langserve[all]启动服务poetry run langchain serve或者直接跑 uvicornuvicorn app.server:app --host 0.0.0.0 --port 8000 --reload启动成功后终端会打印监听地址。默认情况下 LangServe 会暴露几个标准端点/nomic-rag/invoke、/nomic-rag/batch、/nomic-rag/stream、/nomic-rag/playground。其中 playground 是个网页调试界面适合快速点着玩/invoke是给程序调用的。这里有个容易忽略的点config.toml里的route_prefix和add_routes的path要保持一致否则你按配置去请求会 404。我一般让path直接读配置避免两处手改。5. 验证请求用 /invoke 跑通检索与生成服务起来后先别急着写前端用 curl 打一发/invoke确认检索和生成都通。curl -X POST http://127.0.0.1:8000/nomic-rag/invoke \ -H Content-Type: application/json \ -d {input: What are the types of agent memory?}正常返回是一个 JSON结构大致是{ output: 根据上下文agent memory 主要分为……, metadata: { run_id: ..., feedback_tokens: [] } }如果你看到output里有基于文档内容的回答说明整条链路通了Nomic 把文档切块嵌入进了 Chromaretriever 召回了相关片段prompt 拼好上下文对话模型通过统一通道生成答案。如果output是空的或者报错先看服务端日志通常是 Key 没读到或者模型名写错。再验证一下流式输出LangServe 的/stream端点适合做打字机效果curl -X POST http://127.0.0.1:8000/nomic-rag/stream \ -H Content-Type: application/json \ -d {input: What are the types of agent memory?}你会看到一串data:开头的 SSE 事件每个事件里带一小段 token。前端接这个端点就能实现逐字显示。如果你想在 Python 里直接调这个服务用RemoteRunnable最方便from langserve import RemoteRunnable remote_chain RemoteRunnable(http://127.0.0.1:8000/nomic-rag/) result remote_chain.invoke({input: What are the types of agent memory?}) print(result)实测下来/invoke返回时间主要花在嵌入检索和模型生成两段。如果检索慢检查 Chroma 的persist_directory是否生效避免每次启动都重新嵌入如果生成慢看对话模型的通道是否稳定。6. 本篇常见错排查配置和启动过程中有几个坑几乎每次都会遇到提前列出来。第一个是NomicEmbeddings报 401。原因通常是NOMIC_API_TOKEN没注入或者nomic login没执行。检查settings.json里的api_token是否填了真实值以及chain.py里是否在创建NomicEmbeddings之前就设置了环境变量。顺序错了嵌入模型初始化时读不到 token。第二个是ChatOpenAI报base_url相关错误。如果你用的是统一通道base_url要写成https://taotoken.net/api不要带多余的路径后缀。另外OPENAI_API_KEY要设成 TaoToken 的 Key不是 Nomic 的 token。两个 Key 混用是高频错误。第三个是 LangServe 启动后访问/nomic-rag/invoke返回 404。先确认add_routes的path和请求路径一致注意path是/nomic-rag请求时是/nomic-rag/invoke。如果用了route_prefix配置检查有没有重复拼接。第四个是 Chroma 每次启动都重新嵌入导致启动特别慢。这是因为persist_directory没配或者目录被清空。确认config.toml里persist_directory指向一个固定目录并且该目录有写权限。第一次嵌入后后续启动会直接加载已有集合。第五个是长文档检索召回不准。Nomic 的 8k 窗口虽然大但切块策略还是关键。chunk_size7500配合chunk_overlap100是个起点如果你的文档段落特别长可以适当调小chunk_size让每个块语义更集中。检索的k值也别设太大4 到 6 之间通常够用太大反而会稀释上下文。注意如果你在settings.json里开了 LangSmith tracing记得把api_key填对否则链执行时会因为追踪上报失败而报错。本地验证阶段可以先关掉。7. 下一步把这条链固定成可复用服务到这里一条 Nomic 嵌入 LangServe 部署的 RAG 服务已经能在本地跑通/invoke和/stream都能正常返回。接下来你可以做两件事一是把config.toml和settings.json纳入版本管理但把真实 Key 抽到.env里避免泄露二是把这条链接到你的前端或 Agent 里用RemoteRunnable当远程工具调用。如果你后面要做长期编码或 Agent 类项目可以考虑用 Coding Plan 来管理调用额度入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。想直接在网页里试模型对话效果用模型对话页https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。接入文档在 dochttps://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite Claude Code 相关配置在 ClaudeCodeAnthropichttps://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 。最后留一个实用技巧把chain.py里的文档加载部分改成从本地目录读比如DirectoryLoader(./docs)这样你换自己的文档时不用改代码只改路径。检索增强问答的价值在于“你的数据 通用模型”配置骨架搭好之后剩下的就是往里灌文档了。