LangChain与Ollama本地大模型结合:快速构建可部署AI微服务
1. 项目概述当LangChain遇见本地大模型如果你正在探索如何将LangChain的强大应用编排能力与本地部署的Ollama大模型无缝结合那么teddylee777/langserve_ollama这个项目绝对值得你花时间深入研究。简单来说它提供了一个预配置的LangServe服务模板让你能一键式地将基于Ollama本地大模型构建的LangChain应用封装成标准的、可通过API调用的服务。这解决了我们在本地开发AI应用时的一个核心痛点如何将实验性的、脚本式的LangChain链Chain或智能体Agent快速转化为可供其他系统集成、具备生产级接口规范的微服务。回想一下我们平时的开发流程在Jupyter Notebook里调试好一个基于本地Llama 2或Mistral模型的问答链功能跑通了但接下来呢你想把它集成到你的Web应用、移动端或者给其他团队成员调用就需要手动处理HTTP服务器、API路由、请求/响应序列化、文档生成等一系列繁琐的工程化工作。langserve_ollama项目正是为此而生。它基于LangChain官方推出的LangServe框架并预先集成了对Ollama模型的支持相当于为你搭建好了从“本地实验脚本”到“可部署API服务”的桥梁。你只需要关注核心应用逻辑即你的Chain剩下的服务化部署工作这个模板已经帮你完成了八九成。这个项目非常适合以下几类开发者一是希望将本地大模型实验项目产品化的个人开发者或小团队二是需要在内部网络中部署私有AI能力且要求低延迟、数据不出域的企业开发者三是学习LangChain和Ollama并想了解如何将AI应用工程化的初学者。通过它你可以快速获得一个包含Swagger UI交互文档、支持批量预测和流式输出的标准化端点大大提升了开发效率。2. 核心架构与设计思路拆解要理解langserve_ollama的价值我们需要先拆解其依赖的核心技术栈和设计理念。整个项目的基石是三个关键组件LangChain、LangServe和Ollama。它们各自扮演着不可替代的角色而本项目则巧妙地将其串联成一个高效的工作流。2.1 技术栈深度解析LangChain、LangServe与Ollama的三角关系LangChain是灵魂它负责应用逻辑的编排。你可以把它想象成AI应用的“乐高积木”框架。它提供了链Chains、智能体Agents、记忆Memory、检索器Retrievers等高级抽象让我们能够通过组合的方式构建出复杂的、多步骤的AI应用例如一个先检索知识库、再总结、最后润色的问答系统。在langserve_ollama项目中我们最终要对外暴露的服务其核心就是一个或多个由LangChain定义的Chain。Ollama是大脑它提供了本地运行的大型语言模型。Ollama的伟大之处在于它极大简化了在本地包括个人电脑和服务器部署和运行各种开源大模型如Llama 2、Mistral、CodeLlama等的复杂度。它通过一个简单的命令行工具和统一的API接口管理模型的拉取、加载和推理。在本项目中Ollama是LangChain中LLM大语言模型组件的具体实现提供者。LangServe是桥梁它负责将LangChain应用“服务化”。这是LangChain官方推出的一个库专门用于将LangChain的Runnable对象包括Chain、Agent部署为REST API服务。它会自动处理请求路由、输入输出序列化、异步支持、生成OpenAPI文档等所有Web服务相关的脏活累活。langserve_ollama项目的本质就是一个预先配置好了LangServe并指定使用Ollama作为LLM后端的项目模板。项目的设计思路非常清晰以LangServe为骨架嵌入以Ollama为动力的LangChain应用最终输出一个即插即用的Docker容器或可执行服务。这种设计带来了几个显著优势解耦与专注开发者只需在app/chain.py中定义自己的LangChain逻辑无需关心Web服务的实现细节。标准化生成的API遵循RESTful规范并自带交互式文档降低了与其他系统集成的成本。可移植性Docker化部署确保了环境一致性无论是在开发机、测试服务器还是生产环境都能获得相同的运行表现。资源高效本地化部署的Ollama模型避免了公网API调用的延迟、费用和隐私风险特别适合处理敏感数据或需要快速响应的场景。2.2 项目结构导航从模板到服务的生成路径当我们克隆teddylee777/langserve_ollama仓库后会看到一个结构清晰、职责分明的目录。理解这个结构是进行二次开发的基础。langserve_ollama/ ├── Dockerfile # 定义构建Docker镜像的指令 ├── docker-compose.yml # 用于一键式启动服务包含Ollama ├── requirements.txt # Python项目依赖包列表 ├── app/ # 核心应用代码目录 │ ├── chain.py # 【关键】在这里定义你的LangChain应用逻辑 │ └── server.py # LangServe服务器的启动入口 ├── packages/ # 可能存在的自定义工具或工具包 └── ... (配置文件等)核心文件解读app/chain.py这是你的“主战场”。模板通常会提供一个简单的示例比如一个调用Ollama模型进行对话的链。你的主要工作就是修改或重写这个文件引入你需要的工具、记忆、检索器构建出功能更复杂的Chain。例如你可以创建一个支持联网搜索的智能体或者一个基于本地向量数据库的文档问答链。app/server.py这是LangServe服务器的启动脚本。它负责导入你在chain.py中定义的链并将其挂载到特定的API路径上。一般情况下你无需修改此文件除非你需要添加自定义的中间件、修改服务器配置或挂载多个链。Dockerfile与docker-compose.yml这是项目工程化、一体化的关键。Dockerfile定义了如何将你的Python应用打包成镜像。docker-compose.yml则更进一步通常会将langserve服务与ollama服务定义在同一个编排文件中。这意味着通过一条docker-compose up命令可以同时启动Ollama模型服务和你基于LangChain的API服务两者在容器网络内直接通信开箱即用。requirements.txt列出了运行所需的所有Python库核心包括langchain,langchain-community,langserve,fastapi等。根据你链的复杂程度可能需要在这里添加额外的依赖比如langchain-openai如果你要混用API、chromadb用于向量检索等。注意在实际使用中务必仔细检查requirements.txt中的版本号。LangChain生态更新较快版本不兼容是常见问题。建议在虚拟环境中进行开发并优先使用模板指定的版本如需升级请逐一测试。3. 从零开始环境搭建与快速启动理论清晰之后我们进入实战环节。假设你已经在开发机上安装了Docker和Docker Compose这是运行本项目最推荐的方式能最大程度避免环境冲突。3.1 基础环境准备与依赖安装首先将项目代码拉取到本地git clone https://github.com/teddylee777/langserve_ollama.git cd langserve_ollama接下来我们需要准备Ollama模型。虽然docker-compose会启动Ollama服务但模型文件需要提前或运行时拉取。建议先单独启动Ollama并拉取所需模型这样在启动完整服务时速度更快。打开一个终端运行Ollama如果你已安装ollama run llama2:7b这条命令会拉取如果本地没有并运行llama2:7b模型。你可以根据需求替换成其他模型如mistral、codellama等。运行成功后可以在另一个终端用curl测试一下curl http://localhost:11434/api/generate -d {model: llama2:7b, prompt:Hello}看到返回的JSON响应说明Ollama服务正常。保持这个终端运行或者记住模型名称后续在链中需要用到。3.2 自定义你的LangChain应用链现在打开app/chain.py文件。你会看到类似以下的内容具体可能随版本更新from langchain_community.llms import Ollama from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1. 初始化Ollama LLM # 注意这里的模型名必须与Ollama中拉取的模型一致 llm Ollama(modelllama2:7b, base_urlhttp://ollama:11434) # 2. 构建一个简单的提示词模板 prompt ChatPromptTemplate.from_messages([ (system, You are a helpful assistant. Respond in a concise manner.), (human, {input}) ]) # 3. 构建链提示词 - 模型 - 输出解析器 chain prompt | llm | StrOutputParser()这是一个最基础的对话链。我们来对它进行一个实用的改造比如创建一个能够进行文本摘要的链from langchain_community.llms import Ollama from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough # 初始化LLM可以尝试不同的模型 llm Ollama(modelmistral, base_urlhttp://ollama:11434, temperature0.1) # 降低temperature使输出更确定 # 构建一个专门用于摘要的提示词模板 summary_prompt ChatPromptTemplate.from_template( Please provide a concise summary of the following text. Focus on the key points and main ideas. Text: {document} Concise Summary: ) # 构建摘要链 summary_chain summary_prompt | llm | StrOutputParser() # 我们可以再构建一个链用于判断文本是否适合摘要比如是否过短 def length_checker(text: str) - dict: 检查文本长度如果过短则直接返回否则交给摘要链 if len(text.split()) 30: # 假设少于30词认为过短 return {output: Text is too short for a meaningful summary. Original text: text[:100] ...} else: # 这里返回一个字典后续链会处理 return {document: text} # 组合成一个更智能的链 smart_summary_chain RunnablePassthrough() | length_checker | summary_chain # 注意这里的组合需要根据length_checker的返回值调整这是一个简化的示例。实际中可能需要使用RunnableBranch进行条件判断。这个例子展示了如何超越简单的问答开始构建有业务逻辑的链。保存你的chain.py。3.3 一键部署与服务启动修改好链之后使用Docker Compose来启动整个服务栈是最方便的。确保你的docker-compose.yml文件中ollama服务的模型名称与chain.py中引用的模型一致。在项目根目录下运行docker-compose up --build--build参数会重新构建langserve应用的Docker镜像。这个过程会安装Python依赖打包你的应用。启动成功后你会在日志中看到两个服务ollama服务运行在11434端口提供模型推理。langserve服务运行在8080端口默认这是你的API服务。现在打开浏览器访问http://localhost:8080/docs。你应该能看到自动生成的Swagger UI界面里面列出了你的链所暴露的API端点通常是/invoke同步调用和/stream流式调用。你可以直接在这个界面上测试你的摘要链4. 核心API详解与高级调用技巧服务跑起来了我们来看看它具体提供了哪些接口以及如何高效地使用它们。4.1 标准端点解析/invoke, /stream, /batchLangServe会自动为你的链生成几个标准端点POST /invoke这是最常用的同步调用端点。你发送一个JSON请求获得一个完整的JSON响应。请求体{input: Your input string here}。这里的input键名取决于你的链所声明的输入键。在简单的链中它通常是input。如果你的链输入是一个更复杂的字典你需要对应地调整请求结构。响应{output: The models response here}。POST /stream用于流式输出。对于生成较长文本时流式传输可以提供更快的首字元时间提升用户体验。请求体与/invoke相同。响应是一个Server-Sent Events (SSE)流每个chunk是一个JSON对象例如{output: Hello}{output: world}。POST /batch用于批量处理输入提高吞吐量。请求体{inputs: [input1, input2, ...]}响应{outputs: [output1, output2, ...]}使用示例使用curl和Python requests# 同步调用 curl -X POST http://localhost:8080/my_chain/invoke \ -H Content-Type: application/json \ -d {input: A long article about climate change here...} # 流式调用 (使用SSE客户端或观察原始流) curl -N -X POST http://localhost:8080/my_chain/stream \ -H Content-Type: application/json \ -d {input: Write a short story about a robot.}# Python示例 import requests # 同步调用 response requests.post( http://localhost:8080/my_chain/invoke, json{input: 需要总结的文本} ) print(response.json()[output]) # 流式调用 import json response requests.post( http://localhost:8080/my_chain/stream, json{input: 写一首关于春天的诗}, streamTrue ) for line in response.iter_lines(): if line: decoded_line line.decode(utf-8) if decoded_line.startswith(data: ): data json.loads(decoded_line[6:]) # 去掉data: 前缀 print(data.get(output, ), end, flushTrue)4.2 输入输出架构与自定义适配默认情况下链的输入输出被简单视为单个字符串。但在复杂应用中输入可能是一个包含多个字段的对象例如{question: ..., context: ...}输出也可能是一个结构化的对象。LangServe通过input_schema和output_schema支持这一点。你可以在创建链时使用with_types或通过Pydantic模型来声明复杂的输入输出类型LangServe会自动据此生成更准确的API文档和进行请求验证。例如为摘要链定义输入模型from pydantic import BaseModel, Field from langchain_core.runnables import RunnableSequence class SummaryInput(BaseModel): document: str Field(descriptionThe full text document to be summarized) max_length: int Field(default100, descriptionMaximum word count for the summary) class SummaryOutput(BaseModel): summary: str Field(descriptionThe generated summary) length: int Field(descriptionActual word count of the summary) # 假设我们有一个函数式链 def custom_summary_chain(input_data: SummaryInput) - SummaryOutput: # ... 你的摘要逻辑调用llm等 ... result_summary llm.invoke(fSummarize in under {input_data.max_length} words: {input_data.document}) word_count len(result_summary.split()) return SummaryOutput(summaryresult_summary, lengthword_count) # 将函数转换为Runnable并绑定输入输出模式 from langchain_core.runnables import RunnableLambda chain_with_schema RunnableLambda(custom_summary_chain).with_types( input_typeSummaryInput, output_typeSummaryOutput )然后在server.py中挂载这个chain_with_schema。这样Swagger UI上就会显示清晰的输入输出字段说明并且请求时会进行自动验证。5. 生产环境部署考量与优化实践将服务运行在本地开发机只是第一步。要用于生产或团队内部共享还需要考虑更多因素。5.1 性能调优与资源配置Ollama模型选择与加载量化模型为平衡速度与质量优先使用Ollama支持的量化版本模型如llama2:7b-chat-q4_K_M。q4_K_M是比较常用的平衡选择。GPU加速如果服务器有NVIDIA GPU确保在运行Ollama容器时挂载了GPU驱动--gpus all并在Ollama中配置使用GPUOLLAMA_NUM_GPU1。这能带来数十倍的推理速度提升。模型预热对于关键服务可以在启动后先发送一个简单的预热请求触发模型完全加载到GPU显存中避免第一个真实请求的冷启动延迟。LangServe服务配置工作进程在server.py中可以通过Uvicorn的workers参数启动多个工作进程利用多核CPU处理并发请求。例如uvicorn.run(..., workers2)。超时与重试在客户端调用代码中必须设置合理的超时时间。因为大模型生成长文本耗时可能很长。同时考虑实现简单的重试机制应对偶发的服务波动。限流对于公开或共享的服务必须实施限流Rate Limiting防止被滥用。可以在LangServe前端加一个Nginx反向代理利用ngx_http_limit_req_module模块实现或者使用API网关。Docker资源限制 在docker-compose.yml中为ollama和langserve服务设置合理的资源限制防止单个服务耗尽主机资源。services: ollama: image: ollama/ollama deploy: resources: limits: memory: 16G # 根据模型大小和GPU情况调整 cpus: 4.0 # ... 其他配置 langserve: build: . deploy: resources: limits: memory: 2G cpus: 2.0 # ... 其他配置5.2 监控、日志与持久化日志收集确保Docker容器的日志被正确收集和集中管理如使用Fluentd、Loki或直接输出到宿主机文件。在chain.py的关键步骤中添加结构化日志记录请求ID、输入摘要、输出摘要、耗时和错误信息。健康检查为两个服务添加健康检查端点。Ollama有/api/tagsLangServe有/healthz。在docker-compose.yml中配置healthcheck以便编排工具能感知服务状态。数据持久化Ollama模型卷将Ollama的模型存储目录挂载到宿主机持久化卷避免容器重启后重新下载模型。services: ollama: volumes: - ollama_data:/root/.ollama volumes: ollama_data:应用数据如果你的链涉及向量数据库如Chroma、SQLite等同样需要挂载持久化卷。5.3 安全加固与网络隔离API认证生产环境的API绝不能裸奔。最简单的办法是在LangServe服务前加一层反向代理如Nginx并配置HTTP Basic认证或API Key验证。更复杂的可以使用OAuth2、JWT等。网络隔离使用Docker的自定义网络确保ollama服务只被langserve服务访问而不暴露在公网。docker-compose.yml默认就会创建一个独立的网络。输入净化与提示词注入防护对用户输入进行基本的清理和长度限制。警惕提示词注入攻击避免用户输入破坏你精心设计的系统提示词。一种策略是将用户输入放在提示词的明确位置如使用{input}变量并与系统指令清晰分隔。6. 常见问题排查与实战经验分享在实际部署和使用过程中你肯定会遇到各种问题。下面是我总结的一些典型问题及其解决方案。6.1 服务启动与连接故障问题1启动docker-compose up时LangServe服务不断重启日志显示无法连接到ollama:11434。排查思路检查网络确保docker-compose.yml中两个服务在同一个自定义网络下。LangServe容器中使用的base_urlhttp://ollama:11434里的ollama是服务名Docker会将其解析为对应容器的IP。检查Ollama启动状态查看Ollama容器的日志确认它是否成功启动并加载了模型。有时模型较大加载需要几分钟LangServe可能在Ollama就绪前就开始连接。添加依赖等待在docker-compose.yml的LangServe服务配置中使用depends_on结合健康检查或等待脚本来确保启动顺序。services: langserve: build: . depends_on: ollama: condition: service_healthy # 需要ollama配置healthcheck # ... ollama: image: ollama/ollama healthcheck: test: [CMD, curl, -f, http://localhost:11434/api/tags] interval: 10s timeout: 5s retries: 5 # ...问题2调用API时返回5xx错误或LangServe日志显示Python异常如ImportError或AttributeError。排查思路依赖版本冲突这是最常见的问题。确保requirements.txt中所有包的版本是兼容的。特别是langchain,langchain-community,langchain-core等最好锁定到已知兼容的版本组合。可以尝试使用模板原版的requirements.txt。链定义错误检查app/chain.py中的代码。一个常见的错误是使用了新版本LangChain已弃用的导入路径或方法。查阅对应版本的LangChain官方文档。模型名称错误确认chain.py中Ollama(model...)的模型名与Ollama容器内实际拉取和运行的模型名完全一致包括标签如:7b。可以在Ollama容器内执行ollama list查看。6.2 模型推理与性能问题问题3API响应速度极慢尤其是首次调用。解决方案确认GPU是否启用进入Ollama容器检查日志或使用ollama ps查看模型运行时是否显示使用了GPU。使用量化模型如前述换用q4_K_M或q8_0等量化版本。调整Ollama参数在初始化Ollama类时可以传递num_predict最大生成token数、temperature等参数来控制生成速度和确定性。预热在服务启动后发送一个简单的预热请求。问题4生成的内容不符合预期比如不遵循指令、胡言乱语。解决方案检查提示词Prompt这是问题的首要根源。确保你的系统提示词和用户提示词模板清晰、明确。可以先将你的提示词直接在Ollama的聊天界面ollama run中测试排除链中其他环节的影响。调整模型参数降低temperature如0.1可以减少随机性使输出更稳定。对于需要事实性回答的任务可以设置top_p0.9或top_k40。尝试不同模型不同模型在指令遵循、逻辑推理、创造性方面的能力差异很大。如果llama2不行可以试试mistral或mixtral。6.3 高级功能与扩展技巧技巧1在链中使用本地工具或自定义函数。LangChain的强大之处在于链可以调用工具。你可以在chain.py中定义自定义函数并使用tool装饰器或Tool.from_function将其包装成工具然后让智能体Agent来使用它。from langchain.tools import Tool from langchain.agents import create_react_agent, AgentExecutor from langchain_community.llms import Ollama def get_weather(city: str) - str: # 模拟一个查询天气的函数 return fThe weather in {city} is sunny, 25°C. weather_tool Tool.from_function( funcget_weather, nameGetWeather, descriptionUseful for getting the current weather in a city. ) llm Ollama(modelmistral, base_urlhttp://ollama:11434) # 创建一个使用工具的智能体这里简化了提示词和流程 # 注意实际创建智能体需要更完整的设置包括提示词模板、解析输出等。 # 此处仅为展示工具定义。技巧2集成向量数据库实现RAG检索增强生成。这是本地知识库问答的核心。你需要安装并运行一个本地向量数据库如ChromaDB可以将其加入docker-compose.yml。在chain.py中编写文档加载、切分、嵌入向量、存入数据库的脚本可以单独运行。构建一个RAG链用户提问 - 检索相关文档片段 - 将片段与问题组合成增强提示词 - 发送给LLM生成答案。# 伪代码示例 from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings # Ollama也提供嵌入模型 from langchain.chains import RetrievalQA # 初始化嵌入模型和向量库 embeddings OllamaEmbeddings(modelnomic-embed-text, base_urlhttp://ollama:11434) vectorstore Chroma(persist_directory./chroma_db, embedding_functionembeddings) # 创建检索器 retriever vectorstore.as_retriever(search_kwargs{k: 3}) # 创建RAG链 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrieverretriever, return_source_documentsTrue ) # 将这个qa_chain挂载到LangServe这个过程涉及更多细节如文档预处理、分块策略、检索评分等但框架是清晰的。langserve_ollama项目为这种复杂链的部署提供了完美的服务化基础。最后分享一个我个人的深刻体会langserve_ollama这类项目最大的价值在于它标准化了本地大模型应用的交付流程。它让开发者从繁琐的Web服务搭建中解放出来能更专注于AI应用逻辑本身。当你习惯了这种模式后你会发现迭代和部署一个AI功能的速度快得惊人。无论是做一个内部数据分析助手还是为一个老旧系统添加智能对话接口这个技术栈都能提供稳定、可控且高性能的解决方案。刚开始可能会在环境配置和依赖版本上踩些坑但一旦跑通它就是一条通往高效AI应用开发的快车道。