Ai大模型接入day 6

本地搭建大语言模型:从"手动三步"到 Ollama 接入

模型不是双击就能运行的软件。本文先拆解在不依赖第三方工具的前提下,本地跑通一个大模型的完整链路;再介绍用 Ollama 将这套流程"一键化"的本地接入方案,并与远程接入方式做对比。

一、不用第三方工具:本地跑通大模型要过三关

第 1 关:下载模型

去哪里下? 主流渠道如下:

下载渠道 地址
Hugging Face Hub https://huggingface.co/
ModelScope(魔搭) ModelScope - 模型列表页
模型官方仓库 以模型官方发布页为准

怎么选型? 结合自己的任务需求硬件环境(内存 / 显存)挑选合适的模型与规格。

一个关键认知: 模型下载到本地后并不能直接运行 。模型不是可执行程序,下载模型实际上是下载了一堆文件------权重分片、配置与校验信息等,如图 1。

📌 插图 1:模型目录下的文件列表(sha256 命名的权重分片等)|此处替换为你的截图

第 2 关:准备推理引擎

推理引擎本质是一个专门的模型执行器,负责:

  • 加载模型权重
  • 处理输入文本
  • 执行数学计算
  • 生成输出文本

常见的开源推理引擎:llama.cppTransformer

第 3 关:编写加载 / 推理代码

最后还需要自己实现一个简单的 Python / C++ 程序,完成模型的加载与交互调用。

小结: 下载模型 → 推理引擎 → 交互代码,三环相扣、缺一不可。流程不复杂但足够琐碎------于是有了更省心的方案:Ollama

二、Ollama:把三步封装成一条命令

  1. 下载并安装 Ollama 工具;
  2. 拉取对应模型,例如:ollama pull deepseek-r1:1.5b
  3. 在代码中实现接入程序 LLMManager,由它负责与本地模型交互。

三、两种接入方式:远程 vs 本地

方式 A:远程接入 DeepSeek

本机只负责"发请求",模型运行在官方服务器上:

  1. 本机 LLMManager(发送请求的代码)通过网络向 DeepSeek 官方服务器发起请求;
  2. 服务器上的 deepseekServer 运行对应模型,并将结果返回本机。

📌 插图 2:远程接入 DeepSeek 架构示意图|此处替换为你的截图

方式 B:Ollama 本地接入

接入程序、OllamaServer、模型文件全部跑在自己的机器上:

  1. LLMManager 调用 sendMessage() 向本机 OllamaServer 发起请求;
  2. OllamaServer 解析出模型名称(如 deepseek-r1:1.5b);若模型尚未运行,则从 Ollama 官方模型仓库 pull 模型文件并加载进内存运行;
  3. OllamaServer 将用户传入的模型参数转发给模型,模型返回生成结果。

📌 插图 3:Ollama 本地接入架构示意图|此处替换为你的截图

Ollama 的请求处理机制:

请求阶段 OllamaServer 的行为
第一次请求 解析模型名 → 发现模型未运行 → 将模型加载进内存运行 → 把用户的模型参数转给模型
第二次及以后 模型已在内存中,直接将请求转发给大模型

一张表看懂两种接入

对比项 远程接入 DeepSeek Ollama 本地接入
模型运行位置 DeepSeek 官方服务器 自己的机器
模型获取方式 无需下载,直接调用服务 从 Ollama 官方仓库 pull 到本地
接入程序 LLMManager(发送请求的代码) LLMManager → 本机 OllamaServer
网络依赖 每次请求都走网络 仅首次拉取模型时需要
本地硬件要求 需足以承载模型运行

四、写在最后

手动搭建的三步,帮你真正理解"模型是如何被跑起来的";Ollama 则把这三步工程化、命令化。先懂原理,再用工具,本地大模型并没有想象中遥远。

Ollama /api/chat 接口接入实战:参数详解 · 代理排坑 · 流式响应解析

本文整理 Ollama 对话接口的核心参数、请求失败时的代理排查思路,以及非流式/流式两种响应的解析要点。

一、接口概览

复制代码
POST /api/chat

该接口基于给定模型生成下一条对话消息。它是一个流式端点(streaming endpoint) ,默认会返回一系列增量响应;若想一次性拿到完整结果,可在请求中设置 "stream": false,此时最终响应对象会附带统计信息和附加数据。

二、请求参数详解

① 核心参数

参数 必填 说明
model 模型名称
messages 对话消息列表,可用于维持聊天记忆
tools 模型可用的工具列表(JSON 格式,需模型支持)

② message 对象字段

字段 说明
role 消息角色:system / user / assistant / tool
content 消息内容
images(可选) 附带的图片列表,用于多模态模型(如 llava
tool_calls(可选) 模型想调用的工具列表(JSON 格式)

③ 高级参数(可选)

参数 说明
format 返回格式,可为 json 或 JSON Schema
options 额外模型参数,见 Modelfile 文档,如 temperature
stream 设为 false 时返回单个完整响应对象,而非流式对象序列
keep_alive 控制模型加载后驻留内存的时长(默认 5m

三、请求失败的排查:代理问题

给 Ollama 服务发请求失败时,优先排查本机代理,按以下顺序处理:

① 关闭系统代理

关闭 Clash 等代理工具,并屏蔽 ~/.bashrc 中代理相关的环境变量。

⚠️ 注意:bashrc 修改后必须重新加载才能生效:

复制代码
source ~/.bashrc

② 检查 curl 是否仍在走代理

即使环境变量已清理,~/.curlrc 中可能仍残留代理配置,注释掉即可:

复制代码
$ cat .curlrc
#proxy = http://127.0.0.1:7890   # 行首加 # 屏蔽代理

(📷 此处可插入 .curlrc 内容截图)

四、非流式响应(stream: false)与解析要点

设置 "stream": false 后,一次请求即可拿到完整响应:

复制代码
{
  "model": "deepseek-r1:1.5b",
  "created_at": "2025-10-10T07:20:42.745656618Z",
  "message": {
    "role": "assistant",
    "content": "<think>\n\n</think>\n\n您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1,如您有任何问题,我会尽我所能为您提供帮助。"
  },
  "done_reason": "stop",
  "done": true,
  "total_duration": 6983002409,
  "load_duration": 1525778783,
  "prompt_eval_count": 6,
  "prompt_eval_duration": 523945627,
  "eval_count": 40,
  "eval_duration": 4932264848
}

💡 原始 JSON 中的 \u003c / \u003e< / > 的 Unicode 转义,解析后自动还原。

解析要点(层层校验,防止空指针):

反序列化之后,按如下顺序逐层校验:

  1. JSON 中是否包含 message 字段;
  2. message 字段是否为 JSON 对象;
  3. 该对象中是否包含 content 字段。

任一环节缺失都可能引发解析异常,取值前逐层判断更稳妥。

五、流式响应:done 字段是关键

默认情况下接口为流式返回,响应是一连串增量 JSON 对象。边收边拼接 content,盯住 done 判断是否结束即可还原完整回答:

复制代码
{'model': 'deepseek-r1:1.5b', 'created_at': '2025-10-10T08:35:15.223353663Z', 'message': {'role': 'assistant', 'content': '<think>'},  'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '\n\n'},       'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '</think>'},   'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '您好'},        'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '!'},          'done': False}
...  # 我是 → 由 → 中国的 → 深度 → 求 → 索 → Deep → Seek → 公司 ...
{'model': 'deepseek-r1:1.5b', ..., 'content': '为您提供'},     'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '帮助'},        'done': False}
{'model': 'deepseek-r1:1.5b', ..., 'content': '。'},          'done': False}

# 最后一个数据包:content 为空,携带统计信息,done = true
{'model': 'deepseek-r1:1.5b', 'created_at': '2025-10-10T08:35:20.331607895Z',
 'message': {'role': 'assistant', 'content': ''},
 'done_reason': 'stop', 'done': True,
 'total_duration': 5327945887, 'load_duration': 91426389,
 'prompt_eval_count': 6, 'prompt_eval_duration': 124785703,
 'eval_count': 40, 'eval_duration': 5110917433}

规律总结:

done 取值 含义 客户端动作
false 增量数据尚未结束 message.content 追加到结果缓冲区
true 增量数据已结束 停止接收,读取统计信息(token 数、耗时等)

*(📷 此处可插入完整流式响应日志截图)*

六、小结

  • /api/chat 默认流式返回,"stream": false 可切换为一次性完整响应;
  • 请求失败先排查代理:系统代理 → ~/.bashrc~/.curlrc 三处逐一确认;
  • 响应解析按 messagecontent 的层级逐层校验
  • 流式场景抓住 done 字段:false 拼接、true 收尾

*标签建议:Ollama | DeepSeek-R1 | API 调用 | 流式响应*

以上内容直接全选复制即可粘贴进 CSDN 编辑器(表格、代码块格式都会保留)。文中我标注了两处「📷 配图建议」,方便你把原截图插到对应位置,不需要的话删掉即可。

智能聊天助手会话管理设计:从问题到解决方案

在开发基于 LLMManager 封装的聊天助手时,我们遇到了几个典型痛点。这些痛点看似独立,实则都指向同一个核心缺失------会话(Session)机制。本文将从实际问题出发,推导出合理的数据结构与接口设计,并解释为何返回会话 ID 比返回对象更优雅。


一、四个"连环"问题

问题编号 现象描述 本质原因
1 多轮消息在程序中如何存储? 缺乏统一的消息容器
2 模型无记忆,每次请求需携带历史消息 历史消息未被持久化或组织
3 多轮聊天中的消息归属混乱 缺少"轮次"标识
4 如何一次性获取某次聊天的全部历史? 消息与聊天未绑定

推导结论:需要引入会话概念,一次会话即一次完整的多轮对话过程。


二、会话数据结构设计

一个会话需要承载的信息远不止消息列表,还应包括模型名称、时间戳等元数据。我们设计的 Session 结构如下:

cpp

复制代码
struct Session {
    std::string _sessionId;          // 唯一标识
    std::string _modelName;          // 本次会话使用的模型
    std::vector<Message> _messages;  // 消息列表(按时间顺序)
    std::time_t _createdAt;          // 创建时间
    std::time_t _updatedAt;          // 最后更新时间

    Session(const std::string& modelName = "") : _modelName(modelName) {}
};

设计要点

  • _messages 存储该会话下所有消息,解决归属问题(问题3)。

  • 携带 _modelName 便于切换模型时追溯。

  • 双时间戳支持排序与清理策略。


三、会话管理器的接口设计

3.1 获取所有会话列表

我们提供 getSessionLists() 方法,返回 会话 ID 的集合 ,而不是直接返回 Session 对象。

cpp

复制代码
std::vector<std::string> SessionManager::getSessionLists() const {
    std::lock_guard<std::mutex> lock(_mutex);
    std::vector<std::string> sessionIds;
    for (const auto& pair : _sessions) {
        sessionIds.push_back(pair.first);
    }
    return sessionIds;
}

3.2 为什么只返回 ID,而不是对象?

考虑维度 返回对象 返回 ID
内存开销 拷贝整个对象,包含大量消息,影响性能 仅拷贝轻量级字符串
模块耦合 外部模块直接操作 Session 内部字段,管理逻辑分散 所有操作通过 ID 委托给 Manager,单一职责
数据源适配 若后续改用 SQLite,对象反序列化成本高 ID 是持久化主键,天然支持 CURD
维护性 修改 Session 字段需同步所有调用方 只需修改 Manager 内部实现

结论:返回 ID 更轻量、解耦,且更贴合持久化存储(如 SQLite)的操作方式。


四、额外优化:按更新时间降序排列

在返回会话列表时,我们强制按 _updatedAt 降序排列(最近更新的在前)。这符合用户习惯,也方便前端展示"最新会话"列表。

实现伪代码:

cpp

复制代码
std::sort(sessionIds.begin(), sessionIds.end(),
    [&](const std::string& a, const std::string& b) {
        return _sessions[a]._updatedAt > _sessions[b]._updatedAt;
    });

五、总结

核心设计 解决的问题
Session 结构包含会话ID、模型名、消息列表、时间戳 消息存储、归属、历史获取
管理器返回会话 ID 而非对象 降低内存开销、解耦、适配持久化
按更新时间降序排列 提升用户体验,快速定位活跃会话

这一套会话机制不仅让代码更清晰,也为后续扩展(如会话重命名、导出、删除)提供了统一的操作入口。实现时建议结合 SQLite 存储,让 _sessionId 作为主键,真正实现数据与逻辑分离。

延伸思考:若会话消息量极大,可考虑分页加载,但核心结构不变。会话机制是智能聊天应用的基础设施,值得从一开始就设计周全。

相关推荐
沧沧凉凉1 小时前
让我退掉 Claude 的不是代码能力,是一张图
人工智能·游戏·ai编程
新知图书1 小时前
第 6 章DeepSeek的Function Calling与MCP应用实战
人工智能·智能体
xn71332 小时前
Funes Agent Memory 实测:Codex 长期记忆召回、旧记忆污染与 no-answer 边界
人工智能·llm·ai编程
天空属于哈夫克32 小时前
企业微信AI开发:如何让AI根据用户消息自动生成回复?
人工智能·microsoft
Raas1002 小时前
AI网关和LiteLLM区别在哪?MAI Gateway(魔芋企业级AI网关)统一治理方案深度解析
大数据·人工智能·gateway·mai gateway·企业级产品
GlobalInfo2 小时前
2026新能源汽车线束研究报告:市场规模与十五五风向
大数据·人工智能·新能源
William一直在路上2 小时前
GPT-6 Astra 研究报告——模型能力、API 演进与 Agent/MCP 生态影响分析
人工智能·gpt·llm·openai·astra
tntxia2 小时前
AI 应用开发生产落地实践指南
人工智能
林墨聊AIGC2 小时前
AI视频怎么做跳舞的动作效果:从入门到精通的舞蹈动画制作指南
大数据·人工智能·自动化·aigc·音视频