AI学习 - 大模型基础入门
AI学习 - 大模型基础入门从零开始Ollama 安装 → 本地模型运行 → Python 代码接入 → 理解核心概念摘要本文记录了在 Windows 上使用 Ollama 部署本地大模型、并通过 Python 代码接入调用的完整过程。内容涵盖Ollama 安装与模型拉取、大模型基础概念科普Token / Context / Context Window / Temperature / Role、三种调用方式对比以及多轮对话的完整实现代码。跟着做完你能在自己电脑上跑起一个本地大模型并用代码与它对话。一、环境准备1.1 硬件建议配置能跑的模型体验8GB 显存独显 16GB 内存7b-9b 模型流畅无独显 / 核显3b 以下模型慢但能跑16GB 显存14b-30b 模型良好模型文件大小 ≈ 显存需求。qwen3.5:9b 文件 6.6GB需要约 7GB 显存。1.2 安装 Ollama推荐安装官方版本# 方式一winget 安装推荐winget install Ollama.Ollama# 方式二官网下载安装包# https://ollama.com/download 选 Windows安装完成后验证ollama--version# 应输出类似ollama version 0.9.x二、拉取并运行本地模型2.1 推荐的入门模型模型大小显存需求特点qwen3.5:9b6.6GB~7GB中文友好速度快推荐入门deepseek-r1:7b4.7GB~5GB有思考链适合观察推理过程qwen3-coder:30b18GB~20GB代码能力强需要大显存2.2 拉取模型# 拉取第一次会下载之后直接用缓存ollama pull qwen3.5:9b# 查看已安装的模型ollama list# 查看当前运行中的模型ollamaps2.3 命令行直接对话验证是否正常ollama run qwen3.5:9b# 进入交互模式直接输入问题# 输入 /bye 退出2.4 常见问题问题Error: 500 Internal Server Error: memory layout cannot be allocated排查顺序# 1. 查看显存占用nvidia-smi# 2. 查看内存占用Get-CimInstanceWin32_OperatingSystem|Select-Object {N总内存(GB);E{[math]::Round($_.TotalVisibleMemorySize/1MB,1)}}, {N可用内存(GB);E{[math]::Round($_.FreePhysicalMemory/1MB,1)}}# 3. 停止已加载的模型释放显存ollama stop deepseek-r1:7b# 4. 版本太旧导致不兼容尝试升级winget upgrade Ollama.Ollama# 5. 模型文件损坏重新下载ollamarmqwen3.5:9b ollama pull qwen3.5:9b三、大模型基础概念在写代码之前先把几个核心概念搞清楚后面看代码会顺很多。3.1 Token模型处理文字的最小单位模型不认识字或词只认识 token。可以理解为把文字切成碎片你好北京 → [你, 好, 北, 京] → 4 个 token hello → [hello] → 1 个 token unhappy → [un, happy] → 2 个 token为什么要了解 tokenAPI 按 token 计费调云端模型时模型有最大处理量限制context windowmax_tokens参数控制的就是最多生成多少 token经验换算1000 个中文字符 ≈ 500-700 token。3.2 Context模型这次推理能看到的全部内容模型没有记忆每次调用都是全新的。它能记住上下文是因为你把历史对话都传进去了。一次 API 调用时context 包含 ┌─────────────────────────────┐ │ system prompt │ → 角色设定 │ 第1轮用户消息 │ → 历史 │ 第1轮模型回复 │ → 历史 │ 第2轮用户消息 │ → 历史 │ 第2轮模型回复 │ → 历史 │ 当前用户消息最新 │ → 当前问题 └─────────────────────────────┘ 模型基于以上全部内容生成回复思考题如果对话进行了 100 轮每次都要把 100 轮历史传进去token 消耗会越来越大。这就是为什么长对话会越来越慢、越来越贵。3.3 Context Window模型一次能处理的 token 上限Context Window 128,000 token以 Claude 为例 意思是system prompt 所有历史对话 当前问题 加起来不能超过 128,000 token 超出的部分模型直接看不到就像被裁掉了这也是 RAG检索增强生成存在的核心原因当你有一本 500 页的书塞不进 context window就只能先检索最相关的几段塞进去而不是全文。3.4 Temperature控制输出的随机程度第一步模型输出的是 logits不是概率模型最后一层输出的是原始分数叫logits数值没有限制可以是任意实数不能直接当概率用# 模型对下一个词是什么给出的原始分数logits[2.1,1.8,1.2,0.9]# 9 5 3 7第二步softmax 把 logits 转成概率importmathdefsoftmax(logits):exp_values[math.exp(x)forxinlogits]# 对每个值取 e^xtotalsum(exp_values)return[x/totalforxinexp_values]# 归一化加起来 1softmax([2.1,1.8,1.2,0.9])# → [0.38, 0.28, 0.16, 0.12] 加起来 1.0第三步temperature 在 softmax 之前介入操作很简单就是把 logits除以 T再做 softmaxdefsoftmax_with_temperature(logits,T):scaled[x/Tforxinlogits]# 先除以 temperaturereturnsoftmax(scaled)# 再转概率为什么除法能控制尖锐程度关键在e^x这个函数对数值差距极其敏感# 原始 logits9 和 5 差距是 0.3logits[2.1,1.8]# 直接做 e^x比值 1.35math.exp(2.1)8.17math.exp(1.8)6.05# ── temperature 0.1差距放大 10 倍 ──scaled[21.0,18.0]math.exp(21.0)1,318,815,734math.exp(18.0)65,659,969# 比值 20.1差距被指数级拉开 → 分布尖锐# ── temperature 2.0差距压缩到一半 ──scaled[1.05,0.90]math.exp(1.05)2.86math.exp(0.90)2.46# 比值 1.16差距被压缩 → 分布平坦完整数字对比原始 logits[2.1, 1.8, 1.2, 0.9] token 9 5 3 7 T 1.0不变 scaled: [2.1, 1.8, 1.2, 0.9] 概率: [38%, 28%, 16%, 12%] 有差距其他词仍有机会 T 0.1极低差距放大 10 倍 scaled: [21.0, 18.0, 12.0, 9.0] 概率: [95%, 4%, ~0%, ~0%] 9 几乎必然被选输出稳定 T 0.5适中差距放大 2 倍 scaled: [4.2, 3.6, 2.4, 1.8] 概率: [63%, 27%, 7%, 3%] 9 概率提升5 还有机会 T 2.0极高差距压缩一半 scaled: [1.05, 0.90, 0.60, 0.45] 概率: [30%, 27%, 23%, 20%] 接近均匀什么都可能选 T → 0趋近于零 → 等价于直接取最大值greedy decoding → [100%, 0%, 0%, 0%]必然选 9用图形理解概率分布的山峰形状 temperature 低0.1 temperature 高2.0 █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ 9 5 3 7 9 5 3 7 尖锐9 一枝独秀 平坦每个词机会差不多 → 输出稳定 → 输出多样一句话总结temperature 通过缩放 logits 的数值差距 利用 e^x 对差距极其敏感的特性 在 softmax 之后产生尖锐或平坦的概率分布 T 小 → 差距放大 → e^x 把差距指数级拉开 → 分布尖锐 → 输出稳定 T 大 → 差距压缩 → e^x 的放大效果减弱 → 分布平坦 → 输出多样 T→0 → 差距无限大 → 最高分词概率→100% → greedy decoding实际使用建议场景temperature 推荐值分类、判断、提取需要稳定0 - 0.2问答、总结平衡0.3 - 0.7写作、头脑风暴需要创意0.7 - 1.0deepseek-r1 推理模型0.6官方推荐注意deepseek-r1 这类推理模型即使 temperature0输出也可能不固定。原因是它先生成think思考链几百次采样思考路径的微小差异会影响最终答案的措辞。3.5 Role对话里的角色标识发给模型的消息必须标明是谁说的三个固定角色{role:system,content:...}# 系统指令定角色和规则{role:user,content:...}# 人类说的话{role:assistant,content:...}# 模型自己说过的话这三个值是训练时就固化的不能随意更改。模型内部会把消息拼接成|system|你是一个助手 |user|我叫张三 |assistant|你好张三 |user|我叫什么名字 |assistant| ← 模型从这里续写assistant 历史为什么不能省没有 assistant 的历史对话上下文就断了模型不知道自己之前说过什么。3.6 API Key身份验证凭证不是模型的一部分云端服务Claude/GPT 请求 → [业务层验证 Key → 计费 → 限速] → 模型推理 → 返回 本地 Ollama 请求 → 模型推理 → 返回 没有业务层不需要 Key用 openai 库调本地 ollama 时api_key填任意非空字符串都行因为 openai 库在代码层面要求这个字段不能为空但 ollama 收到请求后直接忽略它。# 这三种写法效果完全相同OpenAI(api_keyollama,base_urlhttp://localhost:11500/v1)OpenAI(api_key任意字符串,base_urlhttp://localhost:11500/v1)OpenAI(api_keyhelloworld,base_urlhttp://localhost:11500/v1)如果要对外提供服务并做鉴权需要在 ollama 前面加反向代理如 FastAPI 或 one-api自己实现 Key 的生成和验证逻辑。四、三种调用方式方式对比anthropic 库openai 库调GPTopenai 库调ollama目标模型ClaudeGPT 系列本地模型需要 Key是付费是付费不需要认证 HeaderX-Api-KeyAuthorization: Bearer忽略额外 Headeranthropic-version无无费用按 token 计费按 token 计费免费数据隐私发到云端发到云端本地不出网学习阶段推荐方式三openai 库 本地 ollama不花钱、数据不出网、openai 的接口格式是行业标准学会后换 GPT/Claude 只改两行代码。五、完整项目代码 本地大模型接入示例 依赖pip install openai 运行前确保ollama 已启动且已拉取对应模型 importrefromopenaiimportOpenAI# 初始化客户端 client_ollamaOpenAI(api_keyollama,# 本地不验证随便填但不能为空base_urlhttp://localhost:11500/v1# 改成你的 ollama 端口)DEFAULT_MODELqwen3.5:9b# 默认使用的模型# 工具函数 defparse_think(content:str,show_think:boolTrue)-str: 解析 deepseek-r1 的思考链输出 deepseek-r1 会在回答前输出 think.../think 思考过程 这个函数把思考过程和最终答案分离 ifthinknotincontent:returncontent think_matchre.search(rthink(.*?)/think,content,re.DOTALL)answerre.sub(rthink.*?/think,,content,flagsre.DOTALL).strip()ifshow_thinkandthink_match:print(f 思考过程\n{-*40})print(think_match.group(1).strip())print(f{-*40}\n)returnanswer# 单轮对话 defask(question:str,system:str你是一个助手请用中文回答,temperature:float0.7,model:strDEFAULT_MODEL,show_think:boolTrue)-str: 单轮问答 每次调用都是独立的不保留上下文 Args: question: 用户问题 system: 系统提示词定义模型角色和行为规则 temperature: 随机程度0稳定1多样推理模型建议0.6 model: 使用的模型名称 show_think: 是否打印思考过程deepseek-r1 专用 respclient_ollama.chat.completions.create(modelmodel,temperaturetemperature,messages[{role:system,content:system},{role:user,content:question},])contentresp.choices[0].message.contentreturnparse_think(content,show_think)# 多轮对话 classChatSession: 多轮对话会话 维护对话历史实现上下文记忆 模型本身没有记忆记住上下文的原因是 每次调用都把完整的历史对话传给模型history 列表 随着对话轮数增加传入的 token 也越来越多 def__init__(self,system:str你是一个助手请用中文回答,model:strDEFAULT_MODEL,temperature:float0.7,show_think:boolFalse):self.modelmodel self.temperaturetemperature self.show_thinkshow_think self.history[{role:system,content:system}]defchat(self,user_input:str)-str: 发送一条消息返回模型回复 自动维护 history实现多轮对话 # 把用户消息加入历史self.history.append({role:user,content:user_input})# 把完整历史传给模型respclient_ollama.chat.completions.create(modelself.model,temperatureself.temperature,messagesself.history# 关键传完整历史)replyresp.choices[0].message.content replyparse_think(reply,self.show_think)# 把模型回复也加入历史下一轮需要用到self.history.append({role:assistant,content:reply})returnreplydefclear(self):清空对话历史保留 system promptsystem_msgself.history[0]self.history[system_msg]defshow_history(self):打印当前对话历史print(\n 当前对话历史)fori,msginenumerate(self.history):role_icon{system:⚙️,user:,assistant:}.get(msg[role],?)print(f{role_icon}[{msg[role]}]:{msg[content][:80]}...)print(f共{len(self.history)}条消息\n)# 演示代码 if__name____main__:# ---------- 演示1temperature 对比 ----------print(*60)print(演示1temperature 对比使用 qwen3.5:9b)print(*60)print(\n--- temperature0稳定每次结果应该相同---)print(ask(随机给我一个1-10之间的数字只回答数字,temperature0,modelqwen3.5:9b,show_thinkFalse))print(ask(随机给我一个1-10之间的数字只回答数字,temperature0,modelqwen3.5:9b,show_thinkFalse))print(\n--- temperature1多样每次结果可能不同---)print(ask(随机给我一个1-10之间的数字只回答数字,temperature1,modelqwen3.5:9b,show_thinkFalse))print(ask(随机给我一个1-10之间的数字只回答数字,temperature1,modelqwen3.5:9b,show_thinkFalse))# ---------- 演示2system prompt 的效果 ----------print(\n*60)print(演示2system prompt 角色限定)print(*60)print(\n--- 没有角色限定 ---)print(ask(帮我写首诗,modelqwen3.5:9b,show_thinkFalse))print(\n--- 限定为客服角色 ---)print(ask(帮我写首诗,system你是一家叫「星河科技」的公司的客服只回答产品相关问题其他问题礼貌拒绝,modelqwen3.5:9b,show_thinkFalse))# ---------- 演示3deepseek-r1 思考链 ----------print(\n*60)print(演示3deepseek-r1 思考链观察推理过程)print(*60)print(ask(9.9 和 9.11 哪个数字更大,modeldeepseek-r1:7b,temperature0.6,show_thinkTrue))# ---------- 演示4多轮对话记忆 ----------print(\n*60)print(演示4多轮对话验证上下文记忆)print(*60)sessionChatSession(system你是一个友好的助手,modelqwen3.5:9b,show_thinkFalse)print( 我叫张三)print(f{session.chat(我叫张三)}\n)print( 我在学习 Python 和大模型开发)print(f{session.chat(我在学习 Python 和大模型开发)}\n)print( 我叫什么名字在学什么)print(f{session.chat(我叫什么名字在学什么)}\n)# 查看传给模型的完整历史session.show_history()# ---------- 演示5交互式对话 ----------print(\n*60)print(演示5交互式对话输入 quit 退出)print(*60)interactive_sessionChatSession(system你是一个耐心的 AI 学习助手用简单易懂的方式解释概念,modelqwen3.5:9b,show_thinkFalse)whileTrue:user_inputinput(\n 你).strip()ifuser_input.lower()in[quit,exit,退出,q]:print(对话结束)breakifnotuser_input:continuereplyinteractive_session.chat(user_input)print(f 助手{reply})六、思考与延伸Q模型越大越好吗不一定。9b 的模型在日常问答、写作、代码辅助上已经够用而且速度更快。30b 的模型在复杂推理、长文档理解上有明显优势但需要更强的硬件。实际项目里先用小模型跑通再按需升级。Q本地模型和云端模型的选择本地模型数据不出网适合隐私数据、免费、受硬件限制 云端模型能力更强、随用随取、按量计费、数据上传云端学习阶段用本地生产环境根据数据敏感程度和能力需求决定。Qhistory 一直增长怎么办对话很长时history 会越来越大超出 context window 后模型就会失忆。实际项目里的处理方式只保留最近 N 轮对话把早期对话摘要压缩后放入 system prompt用向量数据库存历史按需检索RAG 思路Q为什么 deepseek-r1 的 temperature0 仍然不稳定因为推理模型先生成think思考链几百次采样GPU 浮点运算的微小误差会在思考链中累积导致最终答案措辞不同。对于需要稳定输出的场景推理模型反而不如普通模型合适。七、参考资料Ollama 官网Ollama 模型库OpenAI Python 库文档deepseek-r1 官方说明Qwen3 模型介绍