news 2026/9/8 22:13:06

LocalAI GPT Vision 视觉理解实战:OpenAI 兼容 API、视觉模型安装与图片标记注入原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LocalAI GPT Vision 视觉理解实战:OpenAI 兼容 API、视觉模型安装与图片标记注入原理

LocalAI GPT Vision 视觉理解实战:OpenAI 兼容 API、视觉模型安装与图片标记注入原理

【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI

LocalAI 通过集成 LLaVA 系列视觉模型(llama-cpp 后端)实现了 OpenAI GPT Vision API 的本地兼容版本,让你无需 GPU、无需外部服务即可让大模型"看懂"图片。本文基于仓库中的功能文档 gpt-vision.md 展开,完整覆盖从安装视觉模型、调用/v1/chat/completions传图提问,到结合 Grammar 约束输出的全部用法,并深入到源码层面解释图片是如何被转换为模型可理解的标记的。

功能概述:LocalAI 的 GPT Vision 实现

LocalAI 使用 LLaVA 一类视觉模型来理解图片,并在接口层面实现了 OpenAI 的 GPT Vision API。这意味着:

  • 请求走标准的/v1/chat/completions端点,消息体结构与 OpenAI 完全一致(image_url内容块 +text内容块混排);
  • 视觉能力由 llama-cpp 后端承载,模型为 GGUF 格式的文本模型 + 视觉投影器(mmproj)组合;
  • 视觉请求与文本生成共享同一套能力:约束语法(grammar)、function tools、流式输出等均可叠加使用。

安装视觉模型

方式一:CLI 一键安装运行

从模型画廊(gallery)安装一个具备视觉能力的模型,功能文档中的示例使用较小的视觉模型moondream2-20250414

local-ai run moondream2-20250414

方式二:Web UI 的 Models 页面

也可以直接打开 Web UI 的Models页面浏览并安装视觉模型。画廊中可用的其他视觉模型包括smolvlm-instructsmolvlm2-2.2b-instruct等,详见 模型画廊文档。

视觉模型在画廊中的实际构成

从 gallery/index.yaml 中可以看到moondream2-20250414的注册信息:它由两个模型文件组成——

  • moondream2-text-model-f16_ct-vicuna.gguf:文本主干模型(F16 精度);
  • moondream2-mmproj-f16-20250414.gguf:视觉投影器(multimodal projector),负责把图片编码结果映射到语言模型空间。

对应的行为配置文件为 gallery/moondream.yaml,其关键配置如下:

config_file: | backend: llama-cpp context_size: 2046 f16: true known_usecases: - chat roles: assistant: "\nAnswer: " system: "\nSystem: " user: "\nQuestion: " stopwords: - 'Question:' - template: chat: | {{.Input}} Answer: completion: | Complete the following sentence: {{.Input}}

几个要点:context_size: 2046限制了该小模型的上下文规模;roles/stopwords/template定义了 Vicuna 风格的对话模板;f16: true与 gguf 文件的 F16 量化保持一致。

smolvlm系列(gallery/smolvlm.yaml)的配置则额外声明了known_usecases: [chat, vision],并通过一组 stopword(<dummy32000></s><end_of_utterance>等)防止模型输出模板符号,聊天模板形如:

{{ .Input -}} Assistant:

而经典的 LLaVA 模型配置见 gallery/llava.yaml,采用USER:/ASSISTANT:角色前缀与context_size: 4096。这三份配置展示了 LocalAI 视觉模型接入的通用模式:llama-cpp 后端 + GGUF 文本模型 + mmproj 投影器 + 对话模板

用 /v1/chat/completions 让模型理解图片

安装模型后,向/v1/chat/completions发起请求即可让 LocalAI 理解并回复图片内容。messages数组中的content是一个数组,混排textimage_url两种内容块:

curl http://localhost:8080/v1/chat/completions -H "Content-Type: application/json" -d '{ "model": "moondream2-20250414", "messages": [{"role": "user", "content": [{"type":"text", "text": "What is in the image?"}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" }}], "temperature": 0.9}]}'

请求结构说明:

字段说明
model已安装的视觉模型名,如moondream2-20250414
messages[].role消息角色,视觉内容一般放在user消息中
messages[].content内容块数组,支持textimage_url两种type混排
image_url.url图片的 URL 或 base64 data URI(见下文源码解析)
temperature采样温度,视觉问答场景常用 0.9 左右

除 OpenAI 风格的image_url外,Realtime / Responses 接口还定义了input_image内容块,其中image_url字段接受 base64 编码的 data URI(如data:image/png;base64,...),支持 PNG 和 JPEG 格式,并可附带detail精度字段——类型定义见 message_item.go。

结合 Grammar 约束视觉输出

视觉 API 可以像纯文本 API 一样叠加约束语法(grammar)与 function tools。例如用root ::= ("yes" | "no")强制模型只回答 yes 或 no,从而把视觉理解变成一个二分类判断:

curl http://localhost:8080/v1/chat/completions -H "Content-Type: application/json" -d '{ "model": "moondream2-20250414", "grammar": "root ::= (\"yes\" | \"no\")", "messages": [{"role": "user", "content": [{"type":"text", "text": "Is there some grass in the image?"}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" }}], "temperature": 0.9}]}'

这一组合非常适合把视觉理解接入自动化流程:用 grammar 锁定输出词表后,响应可以直接作为程序判断条件使用,无需再解析自由文本。

源码解析:图片如何变成模型能看懂的标记

从源码结构看,一张图片进入模型要走三段处理:内容块解析、模板渲染标记、后端标记对齐。

1. 中间件解析内容块(middleware)

所有请求先经过 request.go 中的内容解析循环。对于image_url(及别名image)类型的内容块:

case "image_url", "image": // Decode content as base64 either if it's a URL or base64 text base64, err := utils.GetContentURIAsBase64(pp.ImageURL.URL) if err != nil { xlog.Error("Failed encoding image", "error", err) continue CONTENT } input.Messages[i].StringImages = append(input.Messages[i].StringImages, base64) imgIndex++ nrOfImgsInMessage++

这里GetContentURIAsBase64会同时处理两种输入:远端 URL(下载后编码)与已经是 base64 的内容,统一存入StringImages并累计本条消息的图片数量。视频(video_url)、音频(audio_url/input_audio)在同一个循环中按相同模式处理,因此该管道天然支持多模态混排。

2. 多模态模板渲染(templates)

解析得到的纯文本部分,随后交由 multimodal.go 的TemplateMultiModal渲染为带占位标记的提示词。LocalAI 使用一个哨兵标记:

// DefaultMultiMediaMarker is the sentinel marker LocalAI emits in the rendered // prompt for each image/audio item. It matches llama.cpp's historical // mtmd_default_marker() ("<__media__>"). ... const DefaultMultiMediaMarker = "<__media__>" const DefaultMultiModalTemplate = "{{ range .Audio }}<__media__>{{end}}{{ range .Images }}<__media__>{{end}}{{ range .Video }}[vid-{{.ID}}]{{end}}{{.Text}}"

也就是说:一条消息里有 2 张图,渲染结果就是"<__media__><__media__>What is in the image?"。模板本身是可通过配置覆盖的(模型配置的multimodal模板字段),默认模板会为每张图片/音频输出一个标记、为每个视频输出[vid-N]。单元测试 multimodal_test.go 覆盖了 0 张图、多图以及自定义模板的各种渲染场景。

中间件处的调用见 request.go:TemplateMultiModal收到TotalImages(全局图片序号)与ImagesInMessage(本消息内图片数),据此生成正确数量的标记。

3. 后端标记对齐与图片传递(backend)

哨兵标记是"先渲染、后对齐"的:中间件执行时后端尚未加载,不知道具体后端期望的真实标记。待模型加载完成后,llm.go 完成两件事:

// The prompt was rendered with the sentinel "<__media__>" marker because // middleware templating runs before the backend is loaded and probed. // Once we know the backend's actual media marker, substitute so marker // count matches the bitmap count passed through opts.Images/Videos/Audios. prompt := s if c.MediaMarker != "" && c.MediaMarker != templates.DefaultMultiMediaMarker { prompt = strings.ReplaceAll(prompt, templates.DefaultMultiMediaMarker, c.MediaMarker) } opts.Prompt = prompt opts.Images = images
  • MediaMarker来自对后端元数据的探测:llama.cpp 服务端会为每个服务进程随机生成一个多模态标记并通过ModelMetadataResponse.media_marker上报(见 gguf.go 的探测逻辑,以及 llm.go 中"仅当MediaMarker为空才探测"的按需探测条件);探测结果会持久化回模型配置(persistProbedReasoning),后续请求无需重复探测。MediaMarker字段定义在 model_config.go。
  • 替换确保提示词中"标记的个数"与通过opts.Images传递的图片张数严格一致——这是多模态模型正确对齐图文位置的关键。

另外有一个重要分支:若模型配置了UseTokenizerTemplate(即由后端自己完成 tokenize 与模板渲染,llama.cpp 服务端会自行注入媒体标记),中间件就只传递纯文本、不再重复输出标记,避免双重标记——源码注释见 request.go。

适用前提与限制

  • 视觉模型必须是画廊中的视觉模型(llama-cpp 后端 + mmproj 投影器组合),纯文本模型无法处理image_url内容块;
  • 文档示例所用moondream2-20250414是小型视觉模型,context_size仅 2046,适合快速验证与低资源部署;更高质量的视觉理解可选smolvlm-instructsmolvlm2-2.2b-instruct等更大规格;
  • image_url支持远端 URL 与 base64 两种形式,远端 URL 会在服务端下载并编码,需保证网络可达;
  • 视觉请求与 grammar、function tools 的组合受所用模型能力限制,约束效果以模型实际表现为准。

小结

LocalAI 的 GPT Vision 能力把"传图给模型"标准化为三件事:从画廊安装视觉模型(local-ai run <model>)、用 OpenAI 兼容的image_url内容块发起/v1/chat/completions请求、按需叠加 grammar 约束输出。底层由中间件内容解析、<__media__>哨兵标记模板渲染与后端标记探测对齐三个环节协作完成,整条链路对调用方透明——这也是现有 OpenAI 客户端可以几乎不改代码地切换到 LocalAI 本地视觉服务的原因。

【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

Agent Skills实战:从设计到落地,构建可复用的AI能力包

说真的&#xff0c;最近一年我几乎天天在跟Agent打交道。框架从LangChain换到CrewAI再换到官方SDK&#xff0c;折腾一圈之后才弄明白一件事&#xff1a;真正决定一个Agent好用不好用的&#xff0c;往往不是模型选得多大、框架铺得多全&#xff0c;而是你到底给它配了什么样的sk…

作者头像 李华
网站建设 2026/9/8 22:10:41

AI编程助手实战:用Claude Code提速开发全流程

1. 快速原型&#xff1a;从零到可运行看板只花了一个午休做开发这几年&#xff0c;我见过太多好想法死在“写代码太慢”这一步。需求评审时说得头头是道&#xff0c;一落到代码上&#xff0c;光搭项目骨架、配路由、连数据库就能磨掉一整天。直到我把 Claude Code 正式用在日常…

作者头像 李华
网站建设 2026/9/8 22:09:02

基于ResNet的水果图像分类系统实战:从数据准备到部署

简介&#xff1a;基于深度残差网络&#xff08;ResNet&#xff09;的水果分类识别系统完整代码包&#xff0c;面向具备一定Python基础、希望快速落地图像分类项目的开发者与学生&#xff0c;尤其适合需要完成课程设计、毕业设计或工程演示的入门者。项目以水果分类为例&#xf…

作者头像 李华