news 2026/10/26 3:53:31

Ollama本地部署与多模态模型实战:显存、参数与接口避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ollama本地部署与多模态模型实战:显存、参数与接口避坑

上周有个朋友丢过来一句话:想在自己机器上跑个模型,不联网、不按次计费,最好还能看懂图片。我第一反应是让他先把 ollama 装上,结果第二天他回我三个问题——模型拉下来放哪了、为什么回答慢得像在思考人生、传了张图模型却说没看到图。这三个问题基本覆盖了 ollama 从装完到真正能用的全部坑位。

这篇东西就是围绕 ollama 的大语言模型与多模态模型使用展开的。它适合三类人:手上有一台显卡还算过得去的机器、想把它变成自己的模型服务的人;做过接口调用但没碰过本地推理、想搞清楚显存和上下文到底怎么算的人;以及已经跑通了纯文本对话、准备往图片理解方向走一步的人。我不打算复述官方文档里已经写清楚的那部分,重点放在选型判断、参数含义、接口细节,以及我自己反复踩过的那些坑上。文中涉及的模型规格、显存估算、参数取值,都会给出计算依据,能直接拿去对着自己的机器套。

1. 把模型搬回自己机器:ollama 到底省掉了哪些活

1.1 从"按次调接口"到"本地养一个"的真实动机

很多人第一次接触本地推理,动机都很朴素:不想为了一次性任务付费,或者数据不方便往外送。但真正用起来之后,促使你留下来的往往不是省钱,而是可控。本地跑模型,你能决定版本什么时候升级,能决定系统提示词长什么样,能在断网环境里跑批处理,也能把同一个模型复制到十台内网机器上而不必关心额度。

代价也很明确。第一是硬件门槛,第二是运维成本,第三是你得自己承担"模型选错了"的后果。云端接口的好处是随时换更强的模型,本地部署换模型意味着重新下载几十 GB 的权重,还得重新验证效果。

ollama 在这个权衡里扮演的角色,本质上是把 llama.cpp 那一套底层推理引擎包了一层好用的壳。它替你做掉了三件事:模型权重的下载与版本管理、量化格式的统一与加载、以及一个足够简单的 HTTP 接口。如果你直接用底层推理框架,光是编译参数、量化格式选择、GPU 层数分配这几项就够折腾半天;而 ollama 里这些都被收进了一个叫 Modelfile 的东西。

所以判断要不要用它,标准很简单:你需要的是"跑起来能用",而不是"把每一层推理都调到极致"。前者选它,后者还是自己去啃底层框架更合适。

1.2 Modelfile 是整个工具的核心抽象

大部分人用 ollama 就停在ollama run 模型名这一步,把它当成一个命令行聊天工具。这就浪费了它最有价值的部分。Modelfile 才是它真正的设计核心,理解了这个文件,你才能把"临时对话"变成"可复用的产品能力"。

一个 Modelfile 大致包含这几类指令:

指令作用典型用法
FROM指定基础模型或本地权重文件FROM qwen2.5:7b
SYSTEM固化系统提示词SYSTEM "你是一名严谨的技术文档校对员"
PARAMETER设置采样与运行参数PARAMETER temperature 0.3
TEMPLATE覆盖对话模板一般不需要动,除非模型自带模板有问题
ADAPTER挂载微调适配器有 LoRA 权重时使用

关键在于,FROM后面既可以跟一个已经存在的模型名,也可以指向一个本地的 GGUF 文件路径。后者意味着你可以自己从别处拿到量化好的权重文件,丢进 Modelfile 里直接构建,而不必依赖官方仓库里现成的版本。这个能力在需要跑特定微调模型时特别有用。

SYSTEM和PARAMETER固化下来之后,你调用接口时就不用每次都把一大段系统提示词塞进请求体里,省 token 也省心。我自己维护的几个自定义模型,就是把角色设定、输出格式约束、温度参数全部写死在 Modelfile 里,接口那边只传用户消息,代码干净很多。

构建命令很直接:

ollama create my-proofreader -f ./Modelfile

创建完成后它就是个普通的本地模型,ollama run能跑,接口也能调,跟官方拉下来的没区别。这个思路在后面第 6 节还会展开。

1.3 先算一笔硬件账,再决定拉多大的模型

新手最容易犯的错是凭感觉下载。看到 70B 的模型觉得厉害就拉,拉完发现跑一个 token 要好几秒,风扇狂转,体验极差。其实这笔账很好算。

模型权重的体积约等于参数量 × 每参数字节数。量化等级决定了每参数字节数:

量化等级每参数字节(约)7B 模型权重体积质量损失感受
Q8_01.0约 7.5 GB几乎无损
Q5_K_M0.7约 5.2 GB很小
Q4_K_M0.6约 4.4 GB可接受,社区默认
Q3_K_M0.45约 3.4 GB明显下降
Q2_K0.3约 2.4 GB不建议日常使用

但权重只是基础,还要加两块钱:上下文缓存(KV cache)和运行时开销。KV cache 随上下文长度线性增长,8B 级别模型在 8K 上下文下大约占 1 GB 左右,开到 32K 就接近 4 GB。运行时开销一般留 1 到 2 GB 余量。

给你一个可以直接用的估算式:

所需显存 ≈ 权重体积 + (上下文长度 / 8192) × 每 8K 缓存开销 + 1.5 GB

按这个式子,一张 8 GB 显存的卡跑 7B 的 Q4_K_M、上下文开 8K,大概是 4.4 + 1 + 1.5 = 6.9 GB,勉强够用但没余量;如果上下文开到 32K,就变成 4.4 + 4 + 1.5 ≈ 10 GB,直接爆。这就是很多人"模型能加载但一长对话就变慢"的根本原因——显存不够,后面的层被分流到 CPU 上跑。

验证方法很简单,跑起来之后执行:

ollama ps

输出里有一列会显示 GPU 占用比例。如果是 100% GPU,说明全在显存里;如果显示类似 70%/30% CPU/GPU,那说明有一部分层跑到内存去了,速度会断崖式下跌。这个命令我基本每次调参都会看一遍,比猜靠谱得多。

2. 装完之后的两小时:环境变量与模型拉取的实操

2.1 安装路径与那几个值得改的环境变量

安装本身没什么好说的,各平台的安装包或脚本跑一遍就行。真正影响后续体验的是环境变量,默认值往往不适合长期使用。

第一个必须改的是模型存储目录。默认会放在用户主目录下,而模型动辄几十 GB,很容易把系统盘撑爆。改法是在服务配置里指定:

# Linux 下用 systemd 覆盖配置 systemctl edit ollama # 在打开的编辑器里填入 [Service] Environment="OLLAMA_MODELS=/data/models"

改完systemctl daemon-reload && systemctl restart ollama生效。Windows 下则是通过系统环境变量面板设置,然后重启 ollama 托盘程序。注意这个变量改了之后,之前拉下来的模型不会自动搬过去,需要手动剪切整个目录。

第二个值得关注的是服务监听地址。默认只监听本机回环地址,局域网里的其他设备访问不到。要让同一网络下的手机、平板、另一台电脑都能用,需要设置:

Environment="OLLAMA_HOST=0.0.0.0:11434"

注意:开放监听等于把模型服务暴露给同网段所有设备,家里或实验室内网问题不大,公共网络环境下不要这么干。

第三个是常驻时长OLLAMA_KEEP_ALIVE。默认模型在闲置 5 分钟后会从显存卸载,下次请求要重新加载。对于交互式使用这没问题,但如果你在做批处理或者需要低延迟响应,就把它调长,比如30m。这个参数后面第 5 节还会专门讲。另外OLLAMA_NUM_PARALLEL控制同时处理几个请求,OLLAMA_MAX_LOADED_MODELS控制同时驻留几个模型,这两个值调大都会成倍吃显存,机器不大的话保持默认 1 就行。

2.2 模型名加标签的读法:参数量、量化、体积

模型名的格式是名称:标签,不写标签时默认是latest。标签里通常编码了参数量和量化等级,比如qwen2.5:7b表示 7B 参数、默认量化,qwen2.5:7b-instruct-q5_K_M则把量化和版本都写清楚了。

看标签时要抓两个信息:

  • 参数量(7b、14b、32b、70b):决定能力上限,也决定硬件门槛。
  • 量化后缀(q4_K_M、q5_K_M、q8_0):决定体积和质量折中。

社区默认推荐 Q4_K_M,它在体积和质量之间平衡得最好。Q5_K_M 稍微大一点但质量提升能感知到,尤其是代码和数学任务。如果你的显存刚好卡在临界点,宁可降量化也别降参数量——同样 8 GB 显存,跑 7B 的 Q5 通常比跑 14B 的 Q3 效果好,因为小模型被过度量化之后逻辑能力掉得很厉害。

拉模型之前先看体积:

ollama pull qwen2.5:7b ollama list # 列出本地已有模型和体积 ollama show qwen2.5:7b # 查看参数、模板、量化信息

ollama show特别有用,它会告诉你这个模型的上下文窗口上限、默认参数、以及模板长什么样。排查"为什么回答格式不对"的时候,先看模板往往能省很多时间。

2.3 拉取卡住或失败时的排查顺序

模型拉取是新手第一个卡点。表现通常是进度条卡在某个百分比不动,或者直接报错退出。按这个顺序排查,基本能覆盖九成情况。

第一步,看磁盘空间。这听起来很傻,但确实是最常见的原因。一个 30B 的模型加上解压临时文件,峰值可能要 40 GB 以上。用df -h确认目标分区够不够。

第二步,看是不是网络抖动导致的分片下载失败。ollama 的拉取是分层下载的,中断后重新执行同一条命令会续传已经完成的部分,不会从头再来。所以遇到卡住,最有效的动作是 Ctrl+C 然后重新pull一次,通常能接着往下走。

第三步,确认模型名拼写。标签写错时,报错信息往往只说"找不到",不会提示你正确写法。这时候去官方模型库页面搜一下名称,复制准确的那个。

第四步,如果是企业内网环境,需要给服务进程配置网络访问方式,这属于环境配置问题,跟 ollama 本身无关。这类情况建议让负责网络的同学协助处理,不要自己去猜。

拉取完成后,用ollama list确认体积是否和预期一致。如果体积明显偏小,可能是拉到了一个精简版或量化更激进的变体,这时候ollama show看一下量化参数就能确认。

3. 文本模型跑通只是起点:接口调用与上下文管理

3.1 generate 与 chat 两个接口的分工

ollama 默认在 11434 端口提供 HTTP 服务,核心是两个接口:/api/generate和/api/chat。

generate是单轮补全模式,你给一段 prompt,它还一段 completion。它内部会套用模型的对话模板把 prompt 包装成对话格式,但接口层面只有一问一答。适合的场景是:文本改写、摘要、分类、信息抽取这类无状态任务。

chat是真正的多轮模式,请求体里传的是 messages 数组,每条消息有 role 和 content。角色分 system、user、assistant 三种。适合的场景是:客服对话、多轮追问、需要引用前文的复杂任务。

选哪个的判断标准很清晰:你的任务需不需要记住上一轮说了什么。不需要就用 generate,代码更简单,也不会有上下文膨胀的问题;需要就用 chat,但要自己管理 messages 数组的长度。

一个最小可用的 chat 调用长这样:

import requests resp = requests.post( "http://localhost:11434/api/chat", json={ "model": "qwen2.5:7b", "messages": [ {"role": "system", "content": "你是一名严谨的技术文档校对员,只指出错误,不做润色。"}, {"role": "user", "content": "检查这段话里的术语使用是否一致:……"} ], "stream": False, "options": {"temperature": 0.2, "num_ctx": 8192} }, timeout=120 ) print(resp.json()["message"]["content"])

注意options里的参数是每次请求单独生效的,跟 Modelfile 里写死的参数会合并,请求里的优先。这一点很实用——你可以在 Modelfile 里定一个保守的默认值,然后在特定请求里临时调高温度做创意任务。

3.2 流式返回的数据格式与前端消费方式

默认情况下stream是 true,服务端会以 NDJSON 格式一行一行往回推。每行是一个完整的 JSON 对象,最后一行的done字段为 true,并附带统计信息,包括总耗时、生成 token 数、首 token 延迟等。

这个格式对命令行很友好,对前端就不太方便了。如果你的场景是把结果展示在网页上,有两个选择:一是把stream设成 false 一次性拿完整结果,实现最简单但用户要等;二是自己写解析逻辑,把 NDJSON 拆开逐条追加到页面上。

解析逻辑的关键是按行切分,不能按 JSON 对象整体解析,因为一个网络分片里可能只有半行。稳妥的写法是维护一个缓冲区,每次收到数据就拼接,然后按换行符切分,切出来的最后一段留着等下次数据。

流式模式下还有一个统计字段值得关注:eval_count和eval_duration可以算出生成速度。我一般会在调试阶段把它打出来,作为判断"是模型慢还是我的代码慢"的依据。7B 模型在纯 GPU 上,Q4 量化,生成速度通常在每秒 30 到 60 token 之间;如果只有个位数,那基本可以确定有层跑到 CPU 上去了,回头去看ollama ps的 GPU 占用比例。

3.3 num_ctx、keep_alive 与显存之间的连锁反应

num_ctx是上下文窗口大小,默认值往往比较小(不同版本默认不同,常见是 2048 或 4096)。很多人不知道这一点,做了长文档处理,结果发现模型"记不住"前面的内容——不是模型不行,是上下文被截断了。

调大它的代价直接体现在显存上。前面算过,8B 模型每 8K 上下文大约占 1 GB 缓存,翻倍就是翻倍吃。所以正确的做法是按任务需要设定,而不是无脑拉满。做单段文本改写,4K 够用;做长文档问答,按文档长度算,比如 3 万字的中文文档大约 4 到 5 万 token,就得开到 64K,这时候显存需求会非常夸张,通常得配合更激进的量化或者换更小的模型。

keep_alive控制模型在内存里驻留多久。默认 5 分钟,闲置超时就卸载。这个默认值在交互式场景没问题,但如果你的程序是"每隔几分钟来一次请求"的节奏,就会反复经历加载卸载,每次冷启动几秒到十几秒,体验很糟。

这两个参数配合起来有个隐含的坑:加载时的 num_ctx 决定了缓存分配的大小,而缓存一旦分配就按这个规模常驻。如果你先用 64K 上下文跑了一次长文档,然后keep_alive让模型一直驻留,那这 64K 的缓存会一直占着显存,后续跑短任务时显存就不够了。这种情况下的现象是"明明只是问个短问题也报显存不足"。

解决方法是在请求里显式指定一个小一点的num_ctx,或者在长任务结束后手动卸载:

# 立即卸载模型,释放显存 curl http://localhost:11434/api/generate -d '{"model":"qwen2.5:7b","keep_alive":0}'

keep_alive设成 0 表示请求处理完立刻卸载。这个技巧在多模型轮换的场景里很有用,我自己的脚本里就有一个封装函数,任务开始前加载、结束后卸载,避免几个模型互相挤占显存。

4. 多模态接入:图片是怎么被"看"进去的

4.1 视觉模型能做和不能做的事

多模态模型在 ollama 里的接入方式是"文本模型 + 视觉编码器"。图片先经过一个视觉编码器变成一组向量,然后当成特殊的 token 拼进文本序列里,交给语言模型一起处理。理解这个结构,就能理解它的能力边界。

它能做的:描述图片内容、读出图片里的文字(OCR)、识别图表的大致趋势、判断物体类别和位置关系、根据图片回答具体问题。这些任务在文档理解、票据信息抽取、界面元素识别、简单质检场景里都够用。

它不能做或者做不好的:精确定位像素坐标、数清楚密集小物体(比如数一堆硬币)、识别极小字体、理解需要专业知识的医学或工程图像、处理多图之间的复杂逻辑关系。还有一个容易被忽略的限制——视觉编码器的输入分辨率是固定的,图片传进去会被缩放到固定尺寸,所以细节会丢。这就是为什么小字经常识别不出来。

选模型时按任务难度排:轻量任务用 2B 到 4B 级别的视觉模型就够,速度快显存占用小;需要读复杂文档、理解表格结构,得上 7B 以上的视觉模型。实测下来,同样是识别一张发票,小模型经常漏字段,大模型能完整抽出来,这个差距在 OCR 类任务上非常明显。

4.2 两种传图方式的代码与常见失败点

ollama 的图片传递不走独立的文件上传接口,而是把图片转成 base64 字符串,放进请求体的images数组里。这是最容易出问题的地方。

用 generate 接口时,结构是这样的:

import base64, requests with open("invoice.jpg", "rb") as f: img_b64 = base64.b64encode(f.read()).decode("utf-8") resp = requests.post("http://localhost:11434/api/generate", json={ "model": "qwen2.5vl:7b", "prompt": "提取这张票据上的金额、日期和开票方名称,用 JSON 输出。", "images": [img_b64], "stream": False, "options": {"temperature": 0} })

用 chat 接口时,图片挂在对应的那条 user 消息上:

"messages": [{ "role": "user", "content": "这张图里有什么问题?", "images": [img_b64] }]

第一个高频失败点是base64 字符串里混进了前缀。有些工具在做编码时会自动加上data:image/jpeg;base64,这样的头部,如果你直接把这个完整字符串塞进 images 数组,模型收到的就是一串无效数据,表现出来就是"模型说没看到图"或者胡乱编造内容。正确的做法是只取逗号后面的纯 base64 部分。

第二个失败点是用错了模型。纯文本模型收到 images 字段会直接忽略,不报错,然后基于 prompt 里的文字硬答。这个坑特别隐蔽,因为接口返回 200,格式也正常,只有内容不对。判断方法很简单:ollama show 模型名看它的架构信息里有没有视觉相关字段,或者先问一句"你能看到图片吗",纯文本模型会给出明显空洞的回答。

第三个失败点是文件读取模式。用open(path, "rb")是对的,如果误用文本模式读二进制文件会直接抛异常或者得到乱码。还有一种情况是图片格式不被支持,比如某些模型的视觉编码器只认 JPEG 和 PNG,传 WebP 或 HEIC 会静默失败。稳妥起见,统一转成 JPEG 再传。

4.3 图片预处理:分辨率、格式与张数的取舍

这一节是我踩坑最多的地方,也是收益最大的地方。同一张图,预处理方式不同,识别准确率能差出三成。

分辨率:视觉编码器的输入尺寸是固定的,比如 336×336 或者 448×448 的网格。如果你传一张 4000×3000 的照片,系统会把它缩到目标尺寸,细节全丢;如果你传一张 200×150 的小图,会被放大,同样是糊的。最佳区间通常是长边 1024 到 1568 之间。对于文档类图片,宁可裁成几块分别识别,也不要整张塞进去。

格式与压缩:JPEG 质量设到 85 左右是个好平衡点,再高对识别没帮助但体积翻倍。PNG 适合有大量文字和线条的截图,不会因为压缩产生振铃效应。我处理票据类图片的固定流程是:先转灰度、再适度提高对比度、最后存成质量 85 的 JPEG。灰度化对表格线识别帮助很大,因为颜色信息在这个任务里基本没用,反而增加编码器负担。

张数:ollama 支持一次传多张图,但上下文里每张图都会占用相当数量的 token(视模型而定,一张图往往顶几百到上千个文本 token)。传五张图基本就把 8K 上下文吃掉大半了。所以多图场景要么减张数,要么明确提高num_ctx。

一个实际的处理策略表格:

场景建议分辨率建议格式张数
单据字段抽取长边 1280,裁切成单栏JPEG q85 灰度1
网页截图理解原始分辨率,不缩放PNG1-2
商品图描述长边 1024JPEG q851
多页文档问答每页单独处理再汇总JPEG q85 灰度1 页 / 次

多页文档这块,我的做法是逐页调用做信息抽取,把每页结果存成结构化文本,最后再让文本模型基于汇总结果回答跨页问题。这比一次性把十页塞进上下文要可靠得多,也不容易撞上上下文上限。

5. 实测踩坑记录:那些文档里不会写的问题

5.1 冷启动慢与 keep_alive 的误用

第一次请求某个模型时,会有一个明显的等待,短则两三秒,长则十几秒。这不是模型在思考,是在把权重从磁盘读进显存。模型越大、磁盘越慢,这个延迟越明显。30B 级别的模型在机械硬盘上冷启动能到半分钟以上。

理解了这一点,很多"性能问题"就解释得通了。有人写了个脚本,每分钟调一次接口,发现响应时间忽快忽慢,快的 1 秒慢的 15 秒。原因就是默认 5 分钟的 keep_alive,脚本的调用间隔超过了它,每次都在冷启动。解决办法要么把keep_alive设成远大于调用间隔的值,要么在脚本启动时先发一个预热请求把模型加载进来。

反过来也有坑。做批量任务时,一口气跑了十个不同的模型,每个都设了很长的 keep_alive,结果显存被塞满,后面的模型加载失败。前面提过,OLLAMA_MAX_LOADED_MODELS默认是 1(部分版本是更小的值),超出时会卸载最久未使用的那个,卸载和重新加载的代价很高。

我现在的做法是:交互式场景keep_alive设 30 分钟,提升响应体验;批处理场景设 0,每次用完就释放;混合场景按任务类型分组,同一组的任务连着跑完再切换模型。

5.2 模型"看不见图"的三类原因与定位方法

这个问题的排查我有一套固定流程,从外到内三层。

第一层,确认模型确实支持视觉。ollama show 模型名输出里如果看不到视觉编码器相关信息,那就到此为止,换模型。也可以做个对照实验:同一张纯色图配文字"这张图是什么颜色",纯文本模型会编一个答案,视觉模型能准确说出来。

第二层,确认 base64 数据是干净的。把传出去的字符串取前 50 个字符打印出来,如果开头是data:或者/9j/以外的奇怪字符,就有问题。正常的 JPEG 编码后开头一般是/9j/,PNG 开头一般是iVBOR。

第三层,确认图片本身有效。有些从网页上下载的图片扩展名是 jpg 但实际是 WebP,有些截图工具输出的 PNG 带有额外的元数据。用图像处理库重新读一遍再存一次,通常能解决。

这三层排查下来还不行,就去看服务端日志。ollama 的日志会打出请求处理的细节,图片解析失败时会有相应记录。很多人不看日志就在代码里瞎改,浪费大量时间。

5.3 中文场景下的输出质量波动

中文任务的输出质量在两个地方容易出问题。一是结构化输出。要求模型输出 JSON 时,中文模型有时会在 JSON 前后加上"好的,以下是提取结果:"这类话术,导致解析失败。解决办法是在提示词里明确"只输出 JSON,不要任何解释和代码块标记",同时在代码里做容错——用正则提取第一个{到最后一个}之间的内容再解析。

二是长文本摘要。中文的 token 效率比英文低,同样的字数占用的 token 更多,这意味着中文任务更容易撞上上下文上限。做长文档处理时,中文的实际可用长度要比你按英文估算的打个折。

还有一个体感上的差异:不同中文模型对提示词里"不要做什么"的遵守程度差别很大。有些模型你写三条禁令它能遵守两条,换一个模型可能三条全破。这种差异没法从参数上判断,只能实测。我的做法是维护一个小型的评估集——二十条左右的真实任务输入配上期望输出,每次换模型先跑一遍,看通过率。这比看任何榜单都管用,因为榜单测的是通用能力,你的任务是具体的。

另外提一句温度参数。中文创意类任务温度设 0.7 到 0.9 比较合适,再高会开始胡言乱语;信息抽取类任务必须设 0 到 0.2,否则同一个输入每次结果都不一样,做自动化会疯掉。这个参数在批量处理时特别重要,我见过有人用默认温度跑分类任务,同一批数据跑两遍结果不一致,排查了半天以为是代码 bug。

6. 从能跑到好用:用 Modelfile 固化配置与本地服务化

6.1 把系统提示词和采样参数写进自定义模型

前面提过 Modelfile 的价值,这里给一个完整可用的例子。假设你要做一个票据信息抽取的专用模型,需求是:固定的输出格式、极低的温度、以及一个针对性的系统提示词。

FROM qwen2.5vl:7b PARAMETER temperature 0 PARAMETER top_p 0.1 PARAMETER num_ctx 8192 PARAMETER repeat_penalty 1.05 SYSTEM """ 你是一个票据信息抽取引擎。 收到图片后,只输出一个 JSON 对象,不要输出任何解释、前后缀或代码块标记。 JSON 必须包含且仅包含以下字段: {"amount": 数字或null, "date": "YYYY-MM-DD"或null, "vendor": 字符串或null} 字段无法确定时填 null,禁止猜测。 """

构建并运行:

ollama create invoice-extractor -f ./Modelfile.invoice ollama run invoice-extractor

这样做的好处是接口调用方完全不需要知道提示词长什么样,也不会因为某个开发者手滑改了温度导致输出格式崩掉。整个服务的"行为契约"被固化在模型定义里,可以跟着代码一起做版本管理。改提示词就是改 Modelfile,重新 create 一次,旧版本还能通过不同名字共存,方便做 A/B 对比。

有一个细节要注意:PARAMETER num_ctx写进 Modelfile 之后,这个模型每次加载都会按这个值分配缓存。所以不同用途的模型最好分开建,别在一个模型里塞太多互相冲突的配置。

6.2 让服务常驻并开放给局域网内的其他设备

服务默认会随系统启动,但如果你想让它跑在指定用户下、或者在容器里跑,就需要自己管。容器方式的好处是环境隔离、迁移方便,需要注意两点:一是把模型目录挂载成数据卷,否则容器重建模型全丢;二是把监听地址设成0.0.0.0,否则容器外部访问不到。

容器里的环境变量配置大致是这样:

docker run -d --gpus all \ -v /data/models:/root/.ollama \ -p 11434:11434 \ -e OLLAMA_HOST=0.0.0.0:11434 \ -e OLLAMA_KEEP_ALIVE=30m \ --name ollama \ ollama/ollama

开放给局域网之后,第二台设备怎么连?直接把代码里的地址从 localhost 改成那台机器的内网 IP 就行,接口完全一样。手机和平板上有一些客户端应用,填同样的地址也能用。

这里有一个实际的经验:内网固定 IP 比主机名靠谱。有些环境里主机名解析不稳定,客户端偶尔连不上,排查半天发现是名字解析的问题。给跑模型的机器配个固定内网地址,所有客户端写死这个地址,能省掉很多莫名其妙的连接问题。

还有一点,如果同网段有多个设备同时使用,注意OLLAMA_NUM_PARALLEL的含义——它不是"并发处理请求数",而是"同时处理的请求槽位数",每个槽位都要独立分配一份上下文缓存。设成 4 就意味着显存要按 4 倍上下文来算,很容易爆。个人使用的场景保持 1 就够了,请求会排队处理,虽然不并发但不会 OOM。

6.3 用脚本把文本与图片任务串成一条流水线

最后给一个实际场景的完整串法:一批票据图片,先做 OCR 抽取结构化字段,再用文本模型做校验和异常标注。这是文本模型与多模态模型配合的典型用法——视觉模型负责"看",文本模型负责"判断"。

处理流程分三步。第一步,遍历图片目录,逐张调用视觉模型抽取字段。第二步,把抽取结果和原始图片路径一起存成 JSON Lines 文件,方便追溯。第三步,把每条记录喂给文本模型,让它判断字段之间是否自洽,比如日期是否在未来、金额是否为负、开票方名称是否包含明显的乱码。

第三步的判断逻辑用文本模型比用规则好,因为乱码和格式异常五花八门,写规则写不完。给文本模型的提示词大致是"以下是一条从票据中抽取的记录,判断其字段是否合理,只输出合理或不合理,以及一句话原因"。

关键实现细节有几个:

  • 图片处理阶段串行执行,因为视觉模型占用显存大,并发会 OOM。
  • 每张图处理完显式卸载模型(keep_alive: 0),再切换文本模型时不用等太久。
  • 失败重试最多两次,第二次把图片质量调高、分辨率调大,很多识别失败是图片质量导致的。
  • 每条记录带上耗时统计,跑完一批之后看分布,能快速定位是哪些图片拖慢了整体。

这套流程跑下来,一百张单据在消费级显卡上大概十几分钟,准确率取决于图片质量,清晰的扫描件字段抽取准确率能到九成以上,手机拍的歪斜照片就明显下降。所以如果条件允许,在拍照环节就把角度和光照控制好,比后期用什么模型都管用。

我自己在这套东西上折腾了大半年,最大的体会是:模型能力只是上限,实际效果的天花板往往被输入质量卡住。同一批任务,把图片预处理做好,比换一个更大的模型带来的提升还明显。另外就是参数一定要固化,别指望每次都手动调对——人会忘,脚本不会。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 3:52:37

从早报拆解AI工具更新:Codex迭代、Tibo额度重置与开源模型选型

1. 从一条早报里拆出四条独立情报线早上刷到这条更新汇总的时候,我第一反应不是"哦,又更新了",而是习惯性地把它拆成了四条互不干扰的情报线:一条是开发工具侧的版本迭代与额度机制调整,一条是多模态生成工具…

作者头像 李华
网站建设 2026/10/26 3:51:56

Java SE图书馆系统:Swing扫码借还+JDBC事务+权限分级实战

简介:本资源是一份面向Java初学者与课程设计实践者的图书馆书库管理系统完整开发资料,聚焦Web应用开发全流程,解决图书信息管理、用户借阅流程及后台数据维护等典型业务问题。压缩包共80个文件,含16个核心Java源码文件&#xff08…

作者头像 李华
网站建设 2026/10/26 3:50:04

警惕虚构AI技术事件:如何识别与规避伪发布会信息

我无法生成关于“OpenAI Day 2 更新汇总”的博文。原因如下:输入内容中项目正文为空(项目正文: [通常比较零散、不完整的原始描述,可是任意领域内容]未提供任何实际信息);关键词列表为空(关键词: [关键词1,…

作者头像 李华
网站建设 2026/10/26 3:49:57

Confluence到Wiki.js内网迁移实战与优化

1. 项目背景与挑战去年接手了一个棘手的任务:将公司内网环境中Confluence 9上积累的20GB核心知识库完整迁移到Wiki.js平台。这个看似简单的数据搬运工作,在实际操作中遇到了诸多技术挑战。不同于公有云环境,纯内网部署的限制让每个环节都需要…

作者头像 李华
网站建设 2026/10/26 3:49:55

STM32F103裸机工程骨架:Modbus从机+ADC多通道DMA+环形缓冲实战

简介:本资源是一套面向嵌入式初学者与STM32开发者的完整STM32F103RCT6实战例程合集,覆盖从外设驱动到系统级应用的全链路学习路径。资源包含3444个文件,主体为824个C源码与866个头文件(.c/.h),辅以大量编译…

作者头像 李华
网站建设 2026/10/26 3:49:09

架构设计实战:从单体到微服务的演进策略

1. 架构设计的本质:在约束中寻找平衡点十年前的某个深夜,我正对着满屏报错的监控系统发呆。那是我主导设计的第一个分布式架构,当时自信满满地采用了最前沿的微服务模式,却在流量高峰时被连环雪崩效应教做人。这个惨痛教训让我明白…

作者头像 李华