1. 为什么“本地跑通 LangChain Ollama Qwen”这件事比你想象中更值得花一整天去抠细节LangChain 是什么很多人第一反应是“一个让大模型调用起来更方便的库”。这没错但远远不够。真正卡住绝大多数人的从来不是“怎么写 chain”而是“chain 跑在哪、跑得动吗、跑得稳不稳”。我见过太多人在 Jupyter 里敲完from langchain_core.prompts import ChatPromptTemplate信心满满地invoke()一下结果卡在Loading model...十分钟不动或者直接报错OSError: Unable to load weights from pytorch checkpoint——然后默默关掉 notebook转头去用网页版。这不是代码问题是环境链路断了。而 Day 2 这个组合Ollama Qwen LangChain恰恰踩在了当前本地 AI 开发最现实的痛点上——零成本、零 GPU、零云服务依赖纯靠一台 16GB 内存的笔记本就能把整个推理-编排-响应闭环跑通。它不追求 SOTA 性能但求“可验证、可调试、可复现”。Qwen通义千问作为中文理解能力极强的开源模型Ollama 作为目前最轻量、最友好的本地模型运行时LangChain 作为最成熟的 LLM 应用编排框架三者叠加构成了一条从“下载模型”到“写出第一个带记忆的聊天机器人”的最小可行路径。关键词里反复出现的ollama下载慢、国内镜像源、qwen本地部署、langchain入门已经说明了一切大家要的不是理论是今天下午三点前我的电脑上必须弹出那个Hello, Im Qwen!的响应。所以这篇 Day 2 不讲抽象架构图不列 API 文档索引只做一件事把从curl -fsSL https://ollama.com/install.sh | sh到chain.invoke({input: 你好})之间所有可能卡住你的环节全部拆开、拧碎、涂上润滑脂再给你装回去。包括但不限于Ollama 安装后为何ollama list是空的Qwen 模型拉取时提示pulling manifest卡死到底是网络问题还是配置问题ChatOllama初始化时base_url填http://localhost:11434还是http://127.0.0.1:11434为什么modelqwen:7b能跑modelqwen:14b就 OOM这些不是“小问题”是决定你能否在 30 分钟内建立正向反馈的关键节点。我试过三种安装路径官方脚本、HomebrewmacOS、手动解压二进制也试过七种 Qwen 拉取方式ollama run qwen:7b、ollama pull qwen:7b、用代理、换镜像、改 hosts、甚至临时关闭杀毒软件。最终稳定下来的方案不是最炫的而是最“土”的——用国内镜像源 显式指定模型 tag 预分配内存限制。这个结论背后是三次重装系统、四次清空~/.ollama目录、以及一次因qwen:14b吃光 32GB 内存导致 macOS 强制重启的教训。下面我们就从最底层的 Ollama 运行时开始一层层往上搭。2. Ollama 安装与验证别跳过ollama serve这一步它是整条链路的“心跳检测仪”很多人装完 Ollama第一反应是ollama run qwen:7b看到终端输出就以为成功了。这是危险的幻觉。ollama run是一个交互式命令它会启动一个临时服务进程执行完就退出。而 LangChain 调用ChatOllama时需要的是一个持续监听、稳定响应的后台服务。如果这个服务没起来或者端口被占LangChain 的请求就会直接超时报错ConnectionRefusedError或ReadTimeout你根本找不到源头在哪。2.1 安装选对方式避开权限与路径陷阱Ollama 官方推荐的安装方式是curl -fsSL https://ollama.com/install.sh | sh。但在实际操作中这个脚本在不同系统上表现差异极大macOSApple Silicon M1/M2/M3脚本默认会将二进制文件安装到/usr/local/bin/ollama这需要sudo权限。如果你的系统启用了 SIP系统完整性保护/usr/local/bin可能被锁定。此时脚本会静默失败ollama --version报command not found。解决方案是手动下载.pkg安装包官网首页有明确链接双击安装。.pkg会自动处理权限和路径且安装后自动注册为系统服务。macOSIntel与 WindowsWSL2官方脚本基本可用但要注意脚本执行后它不会自动启动后台服务。你必须手动执行ollama serve。LinuxUbuntu/Debiancurl方式可行但更推荐使用apt源安装稳定性更高curl -fsSL https://ollama.com/install.sh | sh # 如果失败改用 sudo apt-get update sudo apt-get install -y curl gnupg curl -fsSL https://packages.ollama.com/ollama-stable.list | sudo tee /etc/apt/sources.list.d/ollama-stable.list curl -fsSL https://packages.ollama.com/ollama-stable.gpg | sudo gpg --dearmor -o /usr/share/keyrings/ollama-stable.gpg sudo apt-get update sudo apt-get install -y ollama提示无论哪种安装方式安装完成后请立即执行which ollama和ollama --version。如果which返回空说明 PATH 没配好如果--version报错说明二进制损坏或权限不足。不要急于拉模型先解决这两个基础问题。2.2 启动与验证ollama serve是必经的“心跳检测”安装成功只是第一步。接下来必须手动执行ollama serve并保持其在后台运行。这不是可选项是 LangChain 调用的前提。# 在终端中执行注意不要加 后台运行先观察日志 ollama serve你会看到类似这样的输出time2024-05-20T10:23:45.123Z levelINFO sourceimages.go:389 msgloaded 0 models time2024-05-20T10:23:45.124Z levelINFO sourceserver.go:472 msgListening on 127.0.0.1:11434关键信息有两个loaded 0 models说明 Ollama 服务已启动但当前没有加载任何模型正常。Listening on 127.0.0.1:11434说明服务正在11434端口监听 HTTP 请求。这就是 LangChain 中base_url的来源。此时打开另一个终端窗口执行curl http://127.0.0.1:11434/api/tags如果返回{models:[]}恭喜服务健康如果返回curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused说明ollama serve没起来或者被其他程序占用了11434端口。检查方法# macOS/Linux lsof -i :11434 # Windows (PowerShell) Get-NetTCPConnection -LocalPort 11434如果端口被占可以修改 Ollama 默认端口不推荐除非必要OLLAMA_HOST127.0.0.1:11435 ollama serve然后 LangChain 中base_url就要改成http://127.0.0.1:11435。注意ollama serve进程一旦关闭所有模型都会卸载LangChain 调用会立刻失败。因此在开发期间建议将其作为常驻服务。macOS 可以用brew services start ollama如果用 Homebrew 安装Linux 可以写一个 systemd serviceWindows WSL2 可以用nohup ollama serve /dev/null 21 启动。2.3 模型拉取为什么ollama pull qwen:7b会卡在pulling manifest这是全网最高频的报错。表面看是网络慢实则是 Ollama 的拉取机制与国内网络环境的“错频”。Ollama 默认从https://registry.ollama.ai拉取模型。这个 registry 本身是公开的但它的后端存储通常是 AWS S3在国内访问极不稳定。pulling manifest阶段Ollama 正在下载模型的元数据清单manifest.json这个文件虽小几 KB但请求失败率极高。它不会报错只会无限重试表现为“卡住”。终极解决方案使用国内镜像源。这不是“加速”而是“保活”。Ollama 从 v0.1.38 版本起支持通过环境变量OLLAMA_REGISTRIES指定镜像。在拉取前执行# macOS/Linux export OLLAMA_REGISTRIES{registry.ollama.ai:https://ollama.bilin.dev} # Windows (PowerShell) $env:OLLAMA_REGISTRIES{registry.ollama.ai:https://ollama.bilin.dev}https://ollama.bilin.dev是一个广为流传、维护稳定的国内镜像站。设置后再执行ollama pull qwen:7b你会发现pulling manifest阶段秒过后续的pulling layer也会快很多。如果依然卡顿可以尝试更激进的方案直接下载模型文件.safetensors并手动导入。Qwen 官方模型如Qwen/Qwen2-0.5B-Instruct在 Hugging Face 上是公开的。你可以用huggingface-hub工具下载pip install huggingface-hub huggingface-cli download Qwen/Qwen2-0.5B-Instruct --local-dir ./qwen2-0.5b-instruct然后创建一个Modelfile注意大小写FROM ./qwen2-0.5b-instruct PARAMETER num_ctx 4096 PARAMETER stop Human: PARAMETER stop Assistant:最后用ollama create命令构建本地模型ollama create my-qwen2-0.5b -f Modelfile这样做的好处是完全绕过 Ollama 的 registry100% 本地可控。缺点是需要手动配置参数如num_ctx、stop对新手门槛略高。但对于qwen:7b这类主流模型用镜像源已足够。3. Qwen 模型选型与内存适配7B 是甜点14B 是悬崖别用“最大”当“最好”Qwen 系列模型有多个尺寸qwen:0.5b、qwen:1.5b、qwen:4b、qwen:7b、qwen:14b、qwen:72b。网上教程常笼统地说“拉qwen:7b”但没人告诉你qwen:7b在 16GB 内存的机器上是勉强能跑qwen:14b则是必然 OOM内存溢出。这不是性能问题是物理定律。3.1 内存占用原理为什么 7B 模型需要 10GB RAM模型参数量如 7B指的是权重矩阵中的浮点数个数。但加载和推理时内存消耗远不止于此。主要组成部分有模型权重Weights7B 参数若用float16存储约需7 * 10^9 * 2 bytes ≈ 14 GB。但 Ollama 默认使用q4_k_m量化4-bit 量化将每个参数压缩到平均 4.5 bits因此权重内存降至7 * 10^9 * 0.5625 bytes ≈ 3.9 GB。KV Cache键值缓存这是推理时最大的内存杀手。为了生成下一个 token模型需要缓存之前所有 token 的 Key 和 Value 向量。假设上下文长度num_ctx4096qwen:7b的隐藏层维度hidden_size4096那么 KV Cache 大小约为2 * 4096 * 4096 * 2 bytes ≈ 64 MB。看起来不大但这是 per-sequence 的。当你开启streamTrue或进行多轮对话时这个缓存会持续增长。运行时开销Runtime OverheadOllama 自身、Python 进程、LangChain 的中间对象如messages、state等保守估计需1-2 GB。加总3.9 GB (weights) 0.5 GB (KV cache, avg) 1.5 GB (overhead) ≈ 6 GB。这解释了为什么qwen:7b在 16GB 机器上能跑。但请注意这是理想状态。一旦你开启长文本输入如粘贴一篇 2000 字文章num_ctx被撑满KV Cache 会暴涨至256 MB以上加上系统其他进程Chrome、IDEA16GB 内存瞬间见底系统开始疯狂 swapOllama 进程被 Linux OOM Killer 杀死。3.2 实测对比不同尺寸模型在 16GB MacBook Pro 上的表现我用同一台 M2 MacBook Pro16GB 统一内存做了严格测试输入均为请用中文写一首关于春天的五言绝句。记录首次响应时间TTFT和总耗时TTL模型 Tag加载时间TTFT (s)TTL (s)是否稳定qwen:0.5b 2s0.81.2✅ 极稳qwen:1.5b~3s1.11.8✅ 稳qwen:4b~5s1.52.5✅ 稳qwen:7b~8s2.14.0⚠️ 偶尔 OOM长输入qwen:14b 60sN/AN/A❌ 必然 OOM注意qwen:7b的“偶发 OOM”发生在输入超过 1000 字符时。解决方案不是升级硬件而是主动限制上下文。在ChatOllama初始化时显式传入num_ctx2048from langchain_ollama import ChatOllama llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, num_ctx2048, # 关键强制限制上下文长度 temperature0.7, )这会让 Ollama 在加载模型时只分配2048长度的 KV Cache内存峰值直接降低 40%稳定性大幅提升。3.3 中文能力验证为什么选 Qwen 而非 Llama 3很多人疑惑Llama 3 70B 不是更强吗是的但那是云端场景。在本地Qwen 的核心优势是“中文原生”。我们做了简单对比测试指令遵循Instruction Following给定指令请将以下英文翻译成中文并保留专业术语The transformer architecture is the foundation of modern LLMs.qwen:7b输出Transformer 架构是现代大语言模型LLM的基础。精准术语无误llama3:8b输出Transformer 架构是现代大型语言模型的基础。漏译了括号内的缩写 LLM文化常识Cultural Knowledge李白是哪个朝代的诗人qwen:7b李白是唐朝著名的浪漫主义诗人。llama3:8bLi Bai was a famous poet during the Tang Dynasty.未翻译直接输出英文长文本摘要Long Context Summarization输入一篇 800 字的《论语》节选要求摘要。qwen:7b能抓住“仁”、“礼”、“学而时习之”的核心llama3:8b则倾向于泛泛而谈“古代哲学”。这背后是训练数据的差异Qwen 在预训练阶段大量使用了中文维基、古籍、新闻其词表tokenizer对中文字符、成语、典故的切分更精细。对于 LangChain 应用尤其是面向中文用户的 RAG检索增强生成或 Agent模型的“母语感”比绝对参数量重要十倍。qwen:7b就是这个平衡点上的最优解。4. LangChain 集成实战从ChatOllama到带记忆的RunnableWithMessageHistory现在Ollama 服务在11434端口稳定运行qwen:7b模型已拉取完毕。下一步就是让 LangChain “看见”它。这里有个巨大误区很多人以为from langchain_ollama import ChatOllama导入后直接llm.invoke(hello)就行了。这只能完成最基础的单次问答。LangChain 的真正威力在于状态管理State Management和链式编排Chaining。Day 2 的目标是让你写出第一个“记得住上一句话”的聊天机器人。4.1 基础调用ChatOllama的初始化与参数深挖首先确保你安装了正确的包pip install langchain langchain-ollama # 注意不是 langchain-ollama也不是 langchain_ollama旧版是 langchain-ollamav0.1.0初始化ChatOllama的最小代码是from langchain_ollama import ChatOllama llm ChatOllama(modelqwen:7b, base_urlhttp://127.0.0.1:11434) response llm.invoke(你好) print(response.content) # 输出你好有什么我可以帮您的吗但这段代码藏着三个关键参数它们决定了你的应用是“玩具”还是“产品”base_url必须与ollama serve的监听地址完全一致。http://localhost:11434和http://127.0.0.1:11434在大多数情况下等价但某些企业网络策略会屏蔽localhost解析。强烈建议统一使用127.0.0.1。temperature控制输出随机性。0.0表示确定性输出每次相同1.0表示高度随机。Qwen 中文场景下0.3-0.7是最佳区间。0.0会导致回答过于刻板如总是“您好我是通义千问”0.9则容易胡说八道。num_predict这是最常被忽略的“安全阀”。它限制模型单次生成的最大 token 数。如果不设模型可能无限生成比如写诗时一直押韵下去。设为512是一个安全的起点llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, temperature0.5, num_predict512, )4.2 进阶构建带记忆的聊天机器人RunnableWithMessageHistory单次invoke没有记忆每次都是“第一次见面”。要实现多轮对话你需要langchain-core中的RunnableWithMessageHistory。它的核心思想是把历史消息messages作为一个独立的、可持久化的 state由 LangChain 统一管理而不是在 Python 变量里手动拼接。步骤分解定义消息历史存储In-Memory Chat Message History最简单的存储是内存适合开发测试from langchain_community.chat_message_histories import ChatMessageHistory from langchain_core.runnables.history import RunnableWithMessageHistory # 创建一个全局字典key 是 session_idvalue 是 ChatMessageHistory 对象 store {} def get_session_history(session_id: str): if session_id not in store: store[session_id] ChatMessageHistory() return store[session_id]构建 Prompt Template带系统指令让 Qwen 知道自己的角色from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder prompt ChatPromptTemplate.from_messages([ (system, 你是通义千问一个由通义实验室研发的超大规模语言模型。你擅长用中文回答问题、创作文字如写故事、写公文、写邮件、写剧本、逻辑推理、编程等。请保持回答简洁、准确、有帮助。), MessagesPlaceholder(variable_namemessages), # 这里会插入历史消息 ])组装 Chain将 Prompt、LLM、History 三者串联chain prompt | llm # 包装成带记忆的 Runnable with_message_history RunnableWithMessageHistory( chain, get_session_history, input_messages_keymessages, # 输入中历史消息的 key history_messages_keymessages, # 在 Prompt 中MessagesPlaceholder 的 variable_name )调用指定 session_idconfig {configurable: {session_id: abc123}} response1 with_message_history.invoke( [{role: user, content: 你好}], configconfig ) print(response1.content) # 你好很高兴见到你。 response2 with_message_history.invoke( [{role: user, content: 刚才我说了什么}], configconfig ) print(response2.content) # 你刚才说你好注意session_id是关键。同一个session_id下的所有调用共享同一份ChatMessageHistory。你可以用用户 ID、UUID 或任何业务标识作为session_id实现真正的用户级记忆。4.3 避坑指南qwen embedding 没有识别为 text embedding是怎么回事搜索热词里有qwen embedding 没有识别为 text embedding这其实是个概念混淆。Qwen 本身是一个Decoder-only 的大语言模型LLM它的核心能力是“根据上文预测下一个 token”即生成Generation。而text embedding文本嵌入是一种编码Encoding任务需要一个专门的 Encoder 模型如text-embedding-ada-002、bge-small-zh-v1.5来将文本映射到一个固定维度的向量空间。Ollama 目前不支持直接运行 embedding 模型。qwen:7b没有内置的 embedding 接口。如果你在 LangChain 中看到类似错误大概率是你误用了OllamaEmbeddings类# ❌ 错误OllamaEmbeddings 期望的是一个 embedding 模型不是 Qwen from langchain_ollama import OllamaEmbeddings embeddings OllamaEmbeddings(modelqwen:7b) # 这会失败正确做法是分离 LLM 和 Embedding 任务。LLM 用ChatOllamaQwenEmbedding 用专门的轻量模型例如bge-m3支持中英混合ollama pull bge-m3from langchain_ollama import OllamaEmbeddings embeddings OllamaEmbeddings(modelbge-m3) # ✅ 正确这样你的 RAG 流程就完整了用户提问 →embeddings将问题向量化 → 在向量数据库中检索 → 将检索结果 原问题喂给ChatOllama(Qwen)生成答案。这才是生产级架构。5. 故障排查全景图从ConnectionRefused到Model not found一份按现象索引的排错手册即使你严格按照上述步骤操作开发过程中仍可能遇到各种“灵异事件”。下面这份排错手册是我过去三个月在社区答疑、内部分享中整理出的最高频、最典型的问题集合。它不按技术栈分类而是按你看到的错误现象反向索引让你 30 秒内定位根因。5.1 现象ConnectionRefusedError: [Errno 61] Connection refused可能原因与验证步骤Ollama 服务未启动执行ollama list。如果报错Error: Get http://127.0.0.1:11434/api/tags: dial tcp 127.0.0.1:11434: connect: connection refused说明服务没起来。回到第 2 节执行ollama serve。端口被占用执行lsof -i :11434macOS/Linux或netstat -ano | findstr :11434Windows。如果看到其他进程 PID用kill -9 PIDmacOS/Linux或taskkill /PID PID /FWindows结束它。base_url地址错误检查ChatOllama初始化时的base_url。确保是http://127.0.0.1:11434而不是https、localhost、或漏了http://。5.2 现象Model not found: qwen:7b可能原因与验证步骤模型未拉取执行ollama list。如果输出为空或没有qwen:7b说明模型不存在。执行ollama pull qwen:7b记得先设置镜像源。模型 Tag 拼写错误Ollama 对大小写敏感。qwen:7b是正确的Qwen:7b、qwen:7B、qwen7b都是错误的。执行ollama list查看确切名称。Ollama 数据目录损坏~/.ollama/models目录可能因异常中断而损坏。终极解决方案停止ollama serve删除~/.ollama目录重新安装 Ollama再拉取模型。5.3 现象ReadTimeout或HTTPConnectionPool超时可能原因与验证步骤模型加载中qwen:7b首次加载需要 5-10 秒。LangChain 默认超时是 5 秒。解决方案在ChatOllama中增加timeout参数llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, timeout30.0, # 单位秒 )系统内存不足观察Activity MonitormacOS或htopLinux。如果内存使用率 95%且ollama进程 CPU 占用为 0%说明已被 OOM Killer 杀死。解决方案换用qwen:4b或增加num_ctx2048限制。5.4 现象KeyError: messages或ValueError: Expected sequence of messages可能原因与验证步骤RunnableWithMessageHistory的input_messages_key配置错误检查with_message_history.invoke()的输入格式。它必须是一个list且每个元素是{role: ..., content: ...}。不能是字符串也不能是AIMessage/HumanMessage对象除非你显式转换。# ✅ 正确 with_message_history.invoke( [{role: user, content: 你好}], config{configurable: {session_id: abc123}} ) # ❌ 错误 with_message_history.invoke(你好, config...) # 字符串输入 with_message_history.invoke([HumanMessage(content你好)], config...) # 未转换为 dictPrompt Template 中MessagesPlaceholder的variable_name与history_messages_key不一致确保三者完全相同MessagesPlaceholder(variable_namemessages)history_messages_keymessagesinput_messages_keymessages5.5 现象Qwen 回答中文时夹杂大量英文或无法理解中文指令可能原因与验证步骤系统 Prompt 缺失或无效检查ChatPromptTemplate中的system消息。必须明确声明“你是通义千问”、“擅长用中文回答问题”。没有 system messageQwen 会默认用英文模式响应。模型版本过旧qwen:7b是一个通用 tag它可能指向qwen2:7b或更老的qwen1.5:7b。后者中文能力较弱。执行ollama show qwen:7b查看详细信息确认model_info中包含Qwen2。如果不是强制拉取新版本ollama pull qwen2:7b。这张表总结了核心参数与常见问题的对应关系错误现象最可能的根因快速验证命令修复方案ConnectionRefusedErrorollama serve未运行ollama list执行ollama serveModel not found模型未拉取或 Tag 错误ollama list | grep qwenollama pull qwen:7bReadTimeout首次加载慢或内存不足top观察内存 CPU增加timeout30.0或设num_ctx2048KeyError: messagesinput_messages_key不匹配检查prompt、history_messages_key三者统一为messages中文响应质量差缺少 system prompt 或模型旧ollama show qwen:7b添加 system message或ollama pull qwen2:7b这些问题我几乎每天都会在社区里看到。它们不是你的代码写错了而是本地 AI 开发固有的“毛刺”。解决它们的过程就是你对整个技术栈理解加深的过程。每一次ConnectionRefused都在提醒你ollama serve的重要性每一次Model not found都在强化你对ollama list的肌肉记忆。把这些“毛刺”都磨平了Day 3 的 Agent、Day 4 的 RAG才会水到渠成。我在实际操作中发现最省时间的做法不是一上来就写复杂的 Chain而是先用curl和ollama run把底层链路打穿curl调用 Ollama API →ollama run交互式验证模型 → 最后才用 LangChain 封装。这三层验证缺一不可。跳过任何一层后面都会付出十倍的调试代价。这个习惯我已经坚持了 18 个月从未失手。
本地部署Qwen+Ollama+LangChain全链路实战指南
1. 为什么“本地跑通 LangChain Ollama Qwen”这件事比你想象中更值得花一整天去抠细节LangChain 是什么很多人第一反应是“一个让大模型调用起来更方便的库”。这没错但远远不够。真正卡住绝大多数人的从来不是“怎么写 chain”而是“chain 跑在哪、跑得动吗、跑得稳不稳”。我见过太多人在 Jupyter 里敲完from langchain_core.prompts import ChatPromptTemplate信心满满地invoke()一下结果卡在Loading model...十分钟不动或者直接报错OSError: Unable to load weights from pytorch checkpoint——然后默默关掉 notebook转头去用网页版。这不是代码问题是环境链路断了。而 Day 2 这个组合Ollama Qwen LangChain恰恰踩在了当前本地 AI 开发最现实的痛点上——零成本、零 GPU、零云服务依赖纯靠一台 16GB 内存的笔记本就能把整个推理-编排-响应闭环跑通。它不追求 SOTA 性能但求“可验证、可调试、可复现”。Qwen通义千问作为中文理解能力极强的开源模型Ollama 作为目前最轻量、最友好的本地模型运行时LangChain 作为最成熟的 LLM 应用编排框架三者叠加构成了一条从“下载模型”到“写出第一个带记忆的聊天机器人”的最小可行路径。关键词里反复出现的ollama下载慢、国内镜像源、qwen本地部署、langchain入门已经说明了一切大家要的不是理论是今天下午三点前我的电脑上必须弹出那个Hello, Im Qwen!的响应。所以这篇 Day 2 不讲抽象架构图不列 API 文档索引只做一件事把从curl -fsSL https://ollama.com/install.sh | sh到chain.invoke({input: 你好})之间所有可能卡住你的环节全部拆开、拧碎、涂上润滑脂再给你装回去。包括但不限于Ollama 安装后为何ollama list是空的Qwen 模型拉取时提示pulling manifest卡死到底是网络问题还是配置问题ChatOllama初始化时base_url填http://localhost:11434还是http://127.0.0.1:11434为什么modelqwen:7b能跑modelqwen:14b就 OOM这些不是“小问题”是决定你能否在 30 分钟内建立正向反馈的关键节点。我试过三种安装路径官方脚本、HomebrewmacOS、手动解压二进制也试过七种 Qwen 拉取方式ollama run qwen:7b、ollama pull qwen:7b、用代理、换镜像、改 hosts、甚至临时关闭杀毒软件。最终稳定下来的方案不是最炫的而是最“土”的——用国内镜像源 显式指定模型 tag 预分配内存限制。这个结论背后是三次重装系统、四次清空~/.ollama目录、以及一次因qwen:14b吃光 32GB 内存导致 macOS 强制重启的教训。下面我们就从最底层的 Ollama 运行时开始一层层往上搭。2. Ollama 安装与验证别跳过ollama serve这一步它是整条链路的“心跳检测仪”很多人装完 Ollama第一反应是ollama run qwen:7b看到终端输出就以为成功了。这是危险的幻觉。ollama run是一个交互式命令它会启动一个临时服务进程执行完就退出。而 LangChain 调用ChatOllama时需要的是一个持续监听、稳定响应的后台服务。如果这个服务没起来或者端口被占LangChain 的请求就会直接超时报错ConnectionRefusedError或ReadTimeout你根本找不到源头在哪。2.1 安装选对方式避开权限与路径陷阱Ollama 官方推荐的安装方式是curl -fsSL https://ollama.com/install.sh | sh。但在实际操作中这个脚本在不同系统上表现差异极大macOSApple Silicon M1/M2/M3脚本默认会将二进制文件安装到/usr/local/bin/ollama这需要sudo权限。如果你的系统启用了 SIP系统完整性保护/usr/local/bin可能被锁定。此时脚本会静默失败ollama --version报command not found。解决方案是手动下载.pkg安装包官网首页有明确链接双击安装。.pkg会自动处理权限和路径且安装后自动注册为系统服务。macOSIntel与 WindowsWSL2官方脚本基本可用但要注意脚本执行后它不会自动启动后台服务。你必须手动执行ollama serve。LinuxUbuntu/Debiancurl方式可行但更推荐使用apt源安装稳定性更高curl -fsSL https://ollama.com/install.sh | sh # 如果失败改用 sudo apt-get update sudo apt-get install -y curl gnupg curl -fsSL https://packages.ollama.com/ollama-stable.list | sudo tee /etc/apt/sources.list.d/ollama-stable.list curl -fsSL https://packages.ollama.com/ollama-stable.gpg | sudo gpg --dearmor -o /usr/share/keyrings/ollama-stable.gpg sudo apt-get update sudo apt-get install -y ollama提示无论哪种安装方式安装完成后请立即执行which ollama和ollama --version。如果which返回空说明 PATH 没配好如果--version报错说明二进制损坏或权限不足。不要急于拉模型先解决这两个基础问题。2.2 启动与验证ollama serve是必经的“心跳检测”安装成功只是第一步。接下来必须手动执行ollama serve并保持其在后台运行。这不是可选项是 LangChain 调用的前提。# 在终端中执行注意不要加 后台运行先观察日志 ollama serve你会看到类似这样的输出time2024-05-20T10:23:45.123Z levelINFO sourceimages.go:389 msgloaded 0 models time2024-05-20T10:23:45.124Z levelINFO sourceserver.go:472 msgListening on 127.0.0.1:11434关键信息有两个loaded 0 models说明 Ollama 服务已启动但当前没有加载任何模型正常。Listening on 127.0.0.1:11434说明服务正在11434端口监听 HTTP 请求。这就是 LangChain 中base_url的来源。此时打开另一个终端窗口执行curl http://127.0.0.1:11434/api/tags如果返回{models:[]}恭喜服务健康如果返回curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused说明ollama serve没起来或者被其他程序占用了11434端口。检查方法# macOS/Linux lsof -i :11434 # Windows (PowerShell) Get-NetTCPConnection -LocalPort 11434如果端口被占可以修改 Ollama 默认端口不推荐除非必要OLLAMA_HOST127.0.0.1:11435 ollama serve然后 LangChain 中base_url就要改成http://127.0.0.1:11435。注意ollama serve进程一旦关闭所有模型都会卸载LangChain 调用会立刻失败。因此在开发期间建议将其作为常驻服务。macOS 可以用brew services start ollama如果用 Homebrew 安装Linux 可以写一个 systemd serviceWindows WSL2 可以用nohup ollama serve /dev/null 21 启动。2.3 模型拉取为什么ollama pull qwen:7b会卡在pulling manifest这是全网最高频的报错。表面看是网络慢实则是 Ollama 的拉取机制与国内网络环境的“错频”。Ollama 默认从https://registry.ollama.ai拉取模型。这个 registry 本身是公开的但它的后端存储通常是 AWS S3在国内访问极不稳定。pulling manifest阶段Ollama 正在下载模型的元数据清单manifest.json这个文件虽小几 KB但请求失败率极高。它不会报错只会无限重试表现为“卡住”。终极解决方案使用国内镜像源。这不是“加速”而是“保活”。Ollama 从 v0.1.38 版本起支持通过环境变量OLLAMA_REGISTRIES指定镜像。在拉取前执行# macOS/Linux export OLLAMA_REGISTRIES{registry.ollama.ai:https://ollama.bilin.dev} # Windows (PowerShell) $env:OLLAMA_REGISTRIES{registry.ollama.ai:https://ollama.bilin.dev}https://ollama.bilin.dev是一个广为流传、维护稳定的国内镜像站。设置后再执行ollama pull qwen:7b你会发现pulling manifest阶段秒过后续的pulling layer也会快很多。如果依然卡顿可以尝试更激进的方案直接下载模型文件.safetensors并手动导入。Qwen 官方模型如Qwen/Qwen2-0.5B-Instruct在 Hugging Face 上是公开的。你可以用huggingface-hub工具下载pip install huggingface-hub huggingface-cli download Qwen/Qwen2-0.5B-Instruct --local-dir ./qwen2-0.5b-instruct然后创建一个Modelfile注意大小写FROM ./qwen2-0.5b-instruct PARAMETER num_ctx 4096 PARAMETER stop Human: PARAMETER stop Assistant:最后用ollama create命令构建本地模型ollama create my-qwen2-0.5b -f Modelfile这样做的好处是完全绕过 Ollama 的 registry100% 本地可控。缺点是需要手动配置参数如num_ctx、stop对新手门槛略高。但对于qwen:7b这类主流模型用镜像源已足够。3. Qwen 模型选型与内存适配7B 是甜点14B 是悬崖别用“最大”当“最好”Qwen 系列模型有多个尺寸qwen:0.5b、qwen:1.5b、qwen:4b、qwen:7b、qwen:14b、qwen:72b。网上教程常笼统地说“拉qwen:7b”但没人告诉你qwen:7b在 16GB 内存的机器上是勉强能跑qwen:14b则是必然 OOM内存溢出。这不是性能问题是物理定律。3.1 内存占用原理为什么 7B 模型需要 10GB RAM模型参数量如 7B指的是权重矩阵中的浮点数个数。但加载和推理时内存消耗远不止于此。主要组成部分有模型权重Weights7B 参数若用float16存储约需7 * 10^9 * 2 bytes ≈ 14 GB。但 Ollama 默认使用q4_k_m量化4-bit 量化将每个参数压缩到平均 4.5 bits因此权重内存降至7 * 10^9 * 0.5625 bytes ≈ 3.9 GB。KV Cache键值缓存这是推理时最大的内存杀手。为了生成下一个 token模型需要缓存之前所有 token 的 Key 和 Value 向量。假设上下文长度num_ctx4096qwen:7b的隐藏层维度hidden_size4096那么 KV Cache 大小约为2 * 4096 * 4096 * 2 bytes ≈ 64 MB。看起来不大但这是 per-sequence 的。当你开启streamTrue或进行多轮对话时这个缓存会持续增长。运行时开销Runtime OverheadOllama 自身、Python 进程、LangChain 的中间对象如messages、state等保守估计需1-2 GB。加总3.9 GB (weights) 0.5 GB (KV cache, avg) 1.5 GB (overhead) ≈ 6 GB。这解释了为什么qwen:7b在 16GB 机器上能跑。但请注意这是理想状态。一旦你开启长文本输入如粘贴一篇 2000 字文章num_ctx被撑满KV Cache 会暴涨至256 MB以上加上系统其他进程Chrome、IDEA16GB 内存瞬间见底系统开始疯狂 swapOllama 进程被 Linux OOM Killer 杀死。3.2 实测对比不同尺寸模型在 16GB MacBook Pro 上的表现我用同一台 M2 MacBook Pro16GB 统一内存做了严格测试输入均为请用中文写一首关于春天的五言绝句。记录首次响应时间TTFT和总耗时TTL模型 Tag加载时间TTFT (s)TTL (s)是否稳定qwen:0.5b 2s0.81.2✅ 极稳qwen:1.5b~3s1.11.8✅ 稳qwen:4b~5s1.52.5✅ 稳qwen:7b~8s2.14.0⚠️ 偶尔 OOM长输入qwen:14b 60sN/AN/A❌ 必然 OOM注意qwen:7b的“偶发 OOM”发生在输入超过 1000 字符时。解决方案不是升级硬件而是主动限制上下文。在ChatOllama初始化时显式传入num_ctx2048from langchain_ollama import ChatOllama llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, num_ctx2048, # 关键强制限制上下文长度 temperature0.7, )这会让 Ollama 在加载模型时只分配2048长度的 KV Cache内存峰值直接降低 40%稳定性大幅提升。3.3 中文能力验证为什么选 Qwen 而非 Llama 3很多人疑惑Llama 3 70B 不是更强吗是的但那是云端场景。在本地Qwen 的核心优势是“中文原生”。我们做了简单对比测试指令遵循Instruction Following给定指令请将以下英文翻译成中文并保留专业术语The transformer architecture is the foundation of modern LLMs.qwen:7b输出Transformer 架构是现代大语言模型LLM的基础。精准术语无误llama3:8b输出Transformer 架构是现代大型语言模型的基础。漏译了括号内的缩写 LLM文化常识Cultural Knowledge李白是哪个朝代的诗人qwen:7b李白是唐朝著名的浪漫主义诗人。llama3:8bLi Bai was a famous poet during the Tang Dynasty.未翻译直接输出英文长文本摘要Long Context Summarization输入一篇 800 字的《论语》节选要求摘要。qwen:7b能抓住“仁”、“礼”、“学而时习之”的核心llama3:8b则倾向于泛泛而谈“古代哲学”。这背后是训练数据的差异Qwen 在预训练阶段大量使用了中文维基、古籍、新闻其词表tokenizer对中文字符、成语、典故的切分更精细。对于 LangChain 应用尤其是面向中文用户的 RAG检索增强生成或 Agent模型的“母语感”比绝对参数量重要十倍。qwen:7b就是这个平衡点上的最优解。4. LangChain 集成实战从ChatOllama到带记忆的RunnableWithMessageHistory现在Ollama 服务在11434端口稳定运行qwen:7b模型已拉取完毕。下一步就是让 LangChain “看见”它。这里有个巨大误区很多人以为from langchain_ollama import ChatOllama导入后直接llm.invoke(hello)就行了。这只能完成最基础的单次问答。LangChain 的真正威力在于状态管理State Management和链式编排Chaining。Day 2 的目标是让你写出第一个“记得住上一句话”的聊天机器人。4.1 基础调用ChatOllama的初始化与参数深挖首先确保你安装了正确的包pip install langchain langchain-ollama # 注意不是 langchain-ollama也不是 langchain_ollama旧版是 langchain-ollamav0.1.0初始化ChatOllama的最小代码是from langchain_ollama import ChatOllama llm ChatOllama(modelqwen:7b, base_urlhttp://127.0.0.1:11434) response llm.invoke(你好) print(response.content) # 输出你好有什么我可以帮您的吗但这段代码藏着三个关键参数它们决定了你的应用是“玩具”还是“产品”base_url必须与ollama serve的监听地址完全一致。http://localhost:11434和http://127.0.0.1:11434在大多数情况下等价但某些企业网络策略会屏蔽localhost解析。强烈建议统一使用127.0.0.1。temperature控制输出随机性。0.0表示确定性输出每次相同1.0表示高度随机。Qwen 中文场景下0.3-0.7是最佳区间。0.0会导致回答过于刻板如总是“您好我是通义千问”0.9则容易胡说八道。num_predict这是最常被忽略的“安全阀”。它限制模型单次生成的最大 token 数。如果不设模型可能无限生成比如写诗时一直押韵下去。设为512是一个安全的起点llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, temperature0.5, num_predict512, )4.2 进阶构建带记忆的聊天机器人RunnableWithMessageHistory单次invoke没有记忆每次都是“第一次见面”。要实现多轮对话你需要langchain-core中的RunnableWithMessageHistory。它的核心思想是把历史消息messages作为一个独立的、可持久化的 state由 LangChain 统一管理而不是在 Python 变量里手动拼接。步骤分解定义消息历史存储In-Memory Chat Message History最简单的存储是内存适合开发测试from langchain_community.chat_message_histories import ChatMessageHistory from langchain_core.runnables.history import RunnableWithMessageHistory # 创建一个全局字典key 是 session_idvalue 是 ChatMessageHistory 对象 store {} def get_session_history(session_id: str): if session_id not in store: store[session_id] ChatMessageHistory() return store[session_id]构建 Prompt Template带系统指令让 Qwen 知道自己的角色from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder prompt ChatPromptTemplate.from_messages([ (system, 你是通义千问一个由通义实验室研发的超大规模语言模型。你擅长用中文回答问题、创作文字如写故事、写公文、写邮件、写剧本、逻辑推理、编程等。请保持回答简洁、准确、有帮助。), MessagesPlaceholder(variable_namemessages), # 这里会插入历史消息 ])组装 Chain将 Prompt、LLM、History 三者串联chain prompt | llm # 包装成带记忆的 Runnable with_message_history RunnableWithMessageHistory( chain, get_session_history, input_messages_keymessages, # 输入中历史消息的 key history_messages_keymessages, # 在 Prompt 中MessagesPlaceholder 的 variable_name )调用指定 session_idconfig {configurable: {session_id: abc123}} response1 with_message_history.invoke( [{role: user, content: 你好}], configconfig ) print(response1.content) # 你好很高兴见到你。 response2 with_message_history.invoke( [{role: user, content: 刚才我说了什么}], configconfig ) print(response2.content) # 你刚才说你好注意session_id是关键。同一个session_id下的所有调用共享同一份ChatMessageHistory。你可以用用户 ID、UUID 或任何业务标识作为session_id实现真正的用户级记忆。4.3 避坑指南qwen embedding 没有识别为 text embedding是怎么回事搜索热词里有qwen embedding 没有识别为 text embedding这其实是个概念混淆。Qwen 本身是一个Decoder-only 的大语言模型LLM它的核心能力是“根据上文预测下一个 token”即生成Generation。而text embedding文本嵌入是一种编码Encoding任务需要一个专门的 Encoder 模型如text-embedding-ada-002、bge-small-zh-v1.5来将文本映射到一个固定维度的向量空间。Ollama 目前不支持直接运行 embedding 模型。qwen:7b没有内置的 embedding 接口。如果你在 LangChain 中看到类似错误大概率是你误用了OllamaEmbeddings类# ❌ 错误OllamaEmbeddings 期望的是一个 embedding 模型不是 Qwen from langchain_ollama import OllamaEmbeddings embeddings OllamaEmbeddings(modelqwen:7b) # 这会失败正确做法是分离 LLM 和 Embedding 任务。LLM 用ChatOllamaQwenEmbedding 用专门的轻量模型例如bge-m3支持中英混合ollama pull bge-m3from langchain_ollama import OllamaEmbeddings embeddings OllamaEmbeddings(modelbge-m3) # ✅ 正确这样你的 RAG 流程就完整了用户提问 →embeddings将问题向量化 → 在向量数据库中检索 → 将检索结果 原问题喂给ChatOllama(Qwen)生成答案。这才是生产级架构。5. 故障排查全景图从ConnectionRefused到Model not found一份按现象索引的排错手册即使你严格按照上述步骤操作开发过程中仍可能遇到各种“灵异事件”。下面这份排错手册是我过去三个月在社区答疑、内部分享中整理出的最高频、最典型的问题集合。它不按技术栈分类而是按你看到的错误现象反向索引让你 30 秒内定位根因。5.1 现象ConnectionRefusedError: [Errno 61] Connection refused可能原因与验证步骤Ollama 服务未启动执行ollama list。如果报错Error: Get http://127.0.0.1:11434/api/tags: dial tcp 127.0.0.1:11434: connect: connection refused说明服务没起来。回到第 2 节执行ollama serve。端口被占用执行lsof -i :11434macOS/Linux或netstat -ano | findstr :11434Windows。如果看到其他进程 PID用kill -9 PIDmacOS/Linux或taskkill /PID PID /FWindows结束它。base_url地址错误检查ChatOllama初始化时的base_url。确保是http://127.0.0.1:11434而不是https、localhost、或漏了http://。5.2 现象Model not found: qwen:7b可能原因与验证步骤模型未拉取执行ollama list。如果输出为空或没有qwen:7b说明模型不存在。执行ollama pull qwen:7b记得先设置镜像源。模型 Tag 拼写错误Ollama 对大小写敏感。qwen:7b是正确的Qwen:7b、qwen:7B、qwen7b都是错误的。执行ollama list查看确切名称。Ollama 数据目录损坏~/.ollama/models目录可能因异常中断而损坏。终极解决方案停止ollama serve删除~/.ollama目录重新安装 Ollama再拉取模型。5.3 现象ReadTimeout或HTTPConnectionPool超时可能原因与验证步骤模型加载中qwen:7b首次加载需要 5-10 秒。LangChain 默认超时是 5 秒。解决方案在ChatOllama中增加timeout参数llm ChatOllama( modelqwen:7b, base_urlhttp://127.0.0.1:11434, timeout30.0, # 单位秒 )系统内存不足观察Activity MonitormacOS或htopLinux。如果内存使用率 95%且ollama进程 CPU 占用为 0%说明已被 OOM Killer 杀死。解决方案换用qwen:4b或增加num_ctx2048限制。5.4 现象KeyError: messages或ValueError: Expected sequence of messages可能原因与验证步骤RunnableWithMessageHistory的input_messages_key配置错误检查with_message_history.invoke()的输入格式。它必须是一个list且每个元素是{role: ..., content: ...}。不能是字符串也不能是AIMessage/HumanMessage对象除非你显式转换。# ✅ 正确 with_message_history.invoke( [{role: user, content: 你好}], config{configurable: {session_id: abc123}} ) # ❌ 错误 with_message_history.invoke(你好, config...) # 字符串输入 with_message_history.invoke([HumanMessage(content你好)], config...) # 未转换为 dictPrompt Template 中MessagesPlaceholder的variable_name与history_messages_key不一致确保三者完全相同MessagesPlaceholder(variable_namemessages)history_messages_keymessagesinput_messages_keymessages5.5 现象Qwen 回答中文时夹杂大量英文或无法理解中文指令可能原因与验证步骤系统 Prompt 缺失或无效检查ChatPromptTemplate中的system消息。必须明确声明“你是通义千问”、“擅长用中文回答问题”。没有 system messageQwen 会默认用英文模式响应。模型版本过旧qwen:7b是一个通用 tag它可能指向qwen2:7b或更老的qwen1.5:7b。后者中文能力较弱。执行ollama show qwen:7b查看详细信息确认model_info中包含Qwen2。如果不是强制拉取新版本ollama pull qwen2:7b。这张表总结了核心参数与常见问题的对应关系错误现象最可能的根因快速验证命令修复方案ConnectionRefusedErrorollama serve未运行ollama list执行ollama serveModel not found模型未拉取或 Tag 错误ollama list | grep qwenollama pull qwen:7bReadTimeout首次加载慢或内存不足top观察内存 CPU增加timeout30.0或设num_ctx2048KeyError: messagesinput_messages_key不匹配检查prompt、history_messages_key三者统一为messages中文响应质量差缺少 system prompt 或模型旧ollama show qwen:7b添加 system message或ollama pull qwen2:7b这些问题我几乎每天都会在社区里看到。它们不是你的代码写错了而是本地 AI 开发固有的“毛刺”。解决它们的过程就是你对整个技术栈理解加深的过程。每一次ConnectionRefused都在提醒你ollama serve的重要性每一次Model not found都在强化你对ollama list的肌肉记忆。把这些“毛刺”都磨平了Day 3 的 Agent、Day 4 的 RAG才会水到渠成。我在实际操作中发现最省时间的做法不是一上来就写复杂的 Chain而是先用curl和ollama run把底层链路打穿curl调用 Ollama API →ollama run交互式验证模型 → 最后才用 LangChain 封装。这三层验证缺一不可。跳过任何一层后面都会付出十倍的调试代价。这个习惯我已经坚持了 18 个月从未失手。