2024 年我做一个功能:让用户拍一张照片,模型帮他认出照片里是什么、读出照片上的信息。第一版我做得很省事:把用户的原图和一句话 prompt 一起塞进多模态 API,等模型回答。本地一测——真神:模型真的"看懂"了照片,描述得头头是道。我心里很踏实:"多模态嘛,不就是把图片和文字一起发给模型,它就什么都能看懂。"可等它真正上线、跑在真实用户的照片上,一串问题冒了出来。第一种最先吓到我:一张普通的手机高清照片,光是发给模型,就烧掉了惊人的 token,月底账单翻了好几倍。第二种:让它读一张单据上的金额,它读出来的数字,经常错一两位——把 1280 读成 1208。第三种:我一次发三张图让它对比,它把第二张图里的东西,安到了第一张头上,张冠李戴。第四种最让我后背发凉:照片里根本没有的东西,它也"看见"了,而且描述得有鼻子有眼。我盯着这一连串问题想了很久才彻底想明白,第一版错在一个根本的认知上:我以为"多模态就是把图片和文字一起发给模型,它就什么都能看懂"。这句话把图片当成了一种"模型能直接读懂的东西"。可它不是。图片不会被"直接看懂":它先被切成图块、编码成 token——token 有成本、分辨率有上限;视觉理解擅长"看懂场景",却不等于"精确认字";多图必须显式编号模型才不会串;图片该预处理;幻觉必须主动约束。真正用好多模态,核心不是"把图片塞进去",而是理解图片变成 token 的代价、视觉模型的能力边界,并把成本、精度、幻觉这几件事一项项管住。这篇文章就把多模态大模型应用梳理一遍:为什么"把图片发过去"不算多模态应用、图片是怎么变成 token 的、视觉理解为什么不等于精确 OCR、多图输入怎么编号、图片怎么预处理,以及幻觉约束、结构化输出、超时重试这些把多模态真正做对要避开的坑。
问题背景
先把那串问题的现象和我的误判讲清楚,后面所有的设计都是冲着纠正这个误判去的。
现象:把用户原图直接发给多模态模型后,上线冒出一串问题:一张高清照片烧掉惊人的 token、账单翻倍;让它读单据金额经常错一两位;一次发多张图它张冠李戴;照片里没有的东西它也"看见"了、还描述得活灵活现。
我当时的错误认知:"多模态就是把图片和文字一起发给模型,它就什么都能看懂,会拼 message 就够了。"
真相:图片不是被模型直接看懂的。它要先被缩放、切成图块、编码成视觉 token——这些 token 和文字 token 一样计费,分辨率越高、图块越多,成本成倍上涨。而且视觉模型擅长理解"画面里大致是什么",不擅长逐字精确识别,读金额、卡号这类不能错一位的信息要先过专门 OCR;多图不显式编号模型就会串;原图不预处理就是白烧钱;模型"看不清"时默认会编而不是说不知道。把图片塞进去只是开头,把成本、精度、幻觉这几件事管住才是关键。
要把多模态应用做对,需要几块认知:
- 为什么"把图片发过去"不等于多模态应用——图片是被编码成 token 的,有真实代价;
- 图片如何变成 token——分辨率、图块、detail 档位怎么决定成本;
- 能力边界——视觉理解擅长什么、不擅长什么,精确认字该交给谁;
- 多图输入——为什么必须给每张图显式编号;
- 图片预处理、幻觉约束、结构化输出、超时重试这些工程坑怎么处理。
一、为什么"把图片发过去"不等于多模态应用
先把这件最根本的事钉死:对模型来说,一张图片不是一个"它扫一眼就懂"的整体。它和文字一样,要先被拆成一个个"单元",再编码成 token,模型处理的自始至终都是 token。文字被拆成词、图片被拆成图块——一张高清照片可能被切成几十个图块,每个图块都要单独编码成一串 token。这意味着:你发的不是"一张图",而是一大把 token,它们要占用上下文、要被计费、还要受分辨率上限的约束。你只把原图往 message 里一塞,等于完全没意识到这把 token 有多大、有多贵。
下面这段代码,就是我那个"账单一爆才反应过来"的第一版:
import base64
from openai import OpenAI
client = OpenAI()
# 反面教材:以为把图片和文字一起塞进去,模型就什么都能看懂
def naive_vision(image_path, question):
with open(image_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": question},
{"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
],
}],
)
return resp.choices[0].message.content
# 破绽一:原图直接 base64 塞进去 —— 一张几 MB 的高清照片,
# 会被切成大量图块,光编码成 token 就烧掉一大笔钱。
# 破绽二:没做任何预处理 —— 尺寸、格式、体积全是用户给什么是什么。
# 破绽三:没约束输出 —— 模型"看不清"时不会说不知道,它会编。
这段代码能跑、模型也确实"看懂"了图,它的问题不在代码本身,而在一个被忽略的前提:它默认"把图片传进去,就是多模态应用的全部"。可它漏掉了图片背后那把 token 的代价,也漏掉了视觉模型的能力边界。于是那串问题就有了解释:账单爆炸,是因为它把高清原图整个编码成了海量视觉 token;读错金额,是因为它把"逐字精确识别"这件视觉模型并不擅长的事,硬交给了视觉模型;幻觉,是因为它没给模型"不知道就说不知道"的退路。问题的根子清楚了:多模态应用的工程量,全在"图片变成 token 之后"——成本、精度、幻觉,你不专门去管,它就出问题。先从"图片到底怎么变成 token"说起。
二、图片是怎么变成 token 的:分辨率与成本
要管住成本,得先知道成本是怎么来的。视觉模型处理图片,大致是这样一条路:先把过大的图片按边长上限缩放,再把缩放后的图切成固定大小(比如 512×512)的图块,每个图块单独编码成一串 token。所以图块数量,直接决定了 token 数量。下面这个函数,把这个估算逻辑写出来:
import math
def estimate_image_tokens(width, height, detail="high"):
"""估算一张图片在视觉模型里大致消耗多少 token。"""
# low 档:无论原图多大,都固定一个很小的 token 数
if detail == "low":
return 85
# high 档:先把图缩放到边长上限内,再按 512x512 切成图块,
# 每个图块单独编码,块数越多 token 越多
max_side = 2048
scale = min(1.0, max_side / max(width, height))
w, h = width * scale, height * scale
# 再保证最短边不超过 768
shrink = min(1.0, 768 / min(w, h))
w, h = w * shrink, h * shrink
tiles = math.ceil(w / 512) * math.ceil(h / 512)
return 85 + 170 * tiles
这个函数里最关键的旋钮,是那个 detail 参数。它有两档:low 和 high。low 档,模型只看一个很粗的缩略图,无论原图多大,token 数固定且极小;high 档,模型会按上面的逻辑把图切成图块、逐块细看,token 数随图块数成倍上涨。用一张真实尺寸的照片,把这个差距看清楚:
# 同一张 3000x2000 的手机照片,detail 不同,token 差出几十倍
photo = (3000, 2000)
low = estimate_image_tokens(*photo, detail="low")
high = estimate_image_tokens(*photo, detail="high")
print(f"detail=low : {low} tokens") # 85
print(f"detail=high: {high} tokens") # 上千
# 经验:只需要"大致看懂画面是什么"时,用 low 就够了;
# 只有需要看清细节(读小字、找画面里的小目标)时,才用 high。
看清楚这个差距,多模态应用的第一条成本纪律就立住了:detail 档位,要按任务需要来选,不能一律用 high。很多场景——判断"这是室内还是室外"、"图里有没有人"、"这是猫还是狗"——模型用 low 档的缩略图完全够看,你却默认用了 high,等于为根本不需要的细节,付了几十倍的钱。这里的认知要点是:图片的成本不是固定的,它是你"让模型看多细"的函数——你要主动为每个任务选择合适的清晰度。成本的来路清楚了,下一个要面对的,是视觉模型能力的边界。
三、视觉理解不等于精确 OCR
开头那个"读金额错一两位",根子是我用错了工具。这里要建立一个清醒的认知:视觉大模型真正擅长的,是"理解一个画面"——这里面大概是什么场景、有哪些物体、它们什么关系、整体氛围如何。但它并不擅长"逐字逐符地精确识别"——一串数字、一个卡号、一个序列号,它常常会读错一两位,而且错得很自然,你根本看不出来。对于金额、卡号、单据编号这类错一位就是事故的信息,正确的做法是分工:
# 视觉模型擅长"理解画面",不擅长"逐字精确认字"。
# 要读单据金额、卡号、序列号这类不能错一位的信息,
# 先用专门的 OCR 引擎把文字抠出来,再把文字交给大模型理解。
def read_invoice(image_path, ocr_engine):
# 第一步:用专门的 OCR 引擎识别文字
# (专用 OCR 在"认字准确率"上,远高于通用视觉模型)
ocr_text = ocr_engine.recognize(image_path)
# 第二步:把 OCR 出来的纯文字,当作文本交给大模型做结构化理解
prompt = f"下面是一张发票的 OCR 文字,请提取金额和开票日期:\n{ocr_text}"
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content
# 关键:让 OCR 干"认字"的活,让大模型干"理解"的活,各司其职。
这段代码的分工思路,值得专门讲一下:"认字"和"理解",是两件不同的事。专用 OCR 引擎,是专门为"把图里的字一个不差地抠出来"训练的,在这件事上它又快又准;而大模型的长处,是拿到一段文字后,理解它的结构和含义——哪个是金额、哪个是日期、哪个是抬头。把这两步串起来:OCR 负责把字认对,大模型负责把认对的字理解对。如果你跳过 OCR、让视觉模型直接读单据,就是逼一个"擅长理解"的模型,去干"精确认字"这件它不擅长的活。这里的认知要点是:用多模态模型前,先分清你的任务到底是"理解画面"还是"精确认字"——前者交给视觉模型,后者要先过专用 OCR。单张图的能力边界讲清了,下一个问题是:一次发多张图,会出什么岔子?
四、多图输入:必须给每张图显式编号
开头那个"三张图对比、它张冠李戴",是多图输入极容易踩的坑。原因在于:当你把多张图依次放进 message 时,模型收到的是一串图块 token 接着另一串图块 token,中间并没有天然的"分隔标记"。你心里清楚"第一张、第二张、第三张",可模型看到的只是一长串视觉 token,它很难自己分清"刚才那串是第几张图"。解决办法很朴素:在每张图前面,插一句文字,明确告诉模型"接下来这张是第几张":
def compare_images(image_paths, question):
"""多图输入:必须给每张图显式编号,模型才不会张冠李戴。"""
content = []
for i, path in enumerate(image_paths, start=1):
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
# 关键:每张图前面,都先加一句文字标签,
# 明确告诉模型"接下来这张是第几张图"
content.append({"type": "text", "text": f"第 {i} 张图:"})
content.append({
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{b64}"},
})
# 最后再放问题,问题里也用编号来指代,例如"对比第 1 张和第 2 张"
content.append({"type": "text", "text": question})
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": content}],
)
return resp.choices[0].message.content
这个做法的关键,在那一句 f"第 {i} 张图:"。它给模型立了一套坐标系:有了这套编号,你在问题里就能精确地指代——"第 1 张和第 2 张哪个更亮"、"第 3 张里的东西,第 1 张里有没有"。模型的回答也会顺着这套编号走,不再含糊。如果你不加这些标签,几张图糊在一起,模型分不清边界,就会出现"把第二张的内容说成第一张"这种错。这里还有一条连带的纪律:多图意味着多倍的 token——发三张 high 档的图,token 就是单张的三倍。所以多图场景,更要回头用上一节的 detail 纪律,把不需要细看的图降到 low 档。编号的事说清了,下一个问题是:图片在发出去之前,该不该先收拾一下?
五、图片预处理:缩放、转码、压体积
用户给你的原图,什么样的都有:几千万像素的超清大图、带透明通道的 PNG、甚至奇怪的色彩模式。这些原图直接发出去,既烧 token、又上传慢、还可能触发体积上限。所以发送前,该统一做一遍预处理:
from PIL import Image
import io
def preprocess_image(image_path, max_side=1536):
"""上传前预处理:缩放到合理尺寸、转 JPEG、压掉体积。"""
img = Image.open(image_path)
# 统一转成 RGB:去掉透明通道、CMYK 等模型并不需要的信息
if img.mode != "RGB":
img = img.convert("RGB")
# 按最长边等比缩放:过大的图,缩到 max_side 以内
# (再大的分辨率,对模型理解画面也没有额外帮助,只是徒增 token)
w, h = img.size
scale = min(1.0, max_side / max(w, h))
if scale < 1.0:
img = img.resize((int(w * scale), int(h * scale)))
# 重新编码成 JPEG:质量 85 对模型理解足够,体积却小很多
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=85)
return buf.getvalue()
这个 preprocess_image 做了三件事,每件都有它的道理:转 RGB,是把透明通道、CMYK 这些模型用不上的信息去掉;按最长边缩放,是因为超过某个分辨率后,再清晰的图对"理解画面"也没额外帮助,只是白白多切出图块、多烧 token;转 JPEG、质量 85,是用一个肉眼和模型都几乎无感的画质损失,换来体积的大幅缩小。处理完字节,还要转成模型能接收的格式,顺手卡一道体积上限:
def to_data_url(image_bytes, max_bytes=5 * 1024 * 1024):
"""把图片字节转成 data URL,顺手做体积上限检查。"""
# 超过体积上限的图,提前拦下来,别等 API 报错才发现
if len(image_bytes) > max_bytes:
raise ValueError(
f"图片 {len(image_bytes)} 字节,超过 {max_bytes} 上限,请先压缩"
)
b64 = base64.b64encode(image_bytes).decode()
return f"data:image/jpeg;base64,{b64}"
这两段合起来说明一件事:用户的原图,几乎从来不是"可以直接发出去"的状态。预处理这一步看着不起眼,却同时压住了三样东西:压住了成本(图小了、图块少了)、压住了延迟(体积小了、上传快了)、压住了失败率(体积上限提前拦截了)。下面这张图,把一张用户图片走完整条多模态链路的路径串起来:
六、工程坑:幻觉约束、结构化输出与重试
五块设计之外,还有几个工程坑,不处理就会让多模态应用用得别别扭扭、甚至出事故。坑 1:给模型"看不清就说不知道"的明确许可。开头那个最让我后背发凉的幻觉问题——照片里没有的东西它也"看见"了——根子在于:模型默认倾向于"给一个回答",哪怕图模糊、被遮挡、信息根本不全,它也会硬编一个看似合理的描述。要治它,得在 system 里明确给它"承认不确定"的退路:
# 用 system prompt 明确给模型"看不清就说不知道"的许可
ANTI_HALLUCINATION = (
"你是一个看图助手。只描述图片里你能确实看到的内容。"
"如果图片模糊、关键信息被遮挡、或你无法确定,"
"必须直接回答\"无法确定\","
"绝对不要猜测或编造图片里没有的细节。"
)
def describe_safely(data_url, question):
resp = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": ANTI_HALLUCINATION},
{"role": "user", "content": [
{"type": "text", "text": question},
{"type": "image_url", "image_url": {"url": data_url}},
]},
],
temperature=0, # 看图取信息的任务,温度调到 0,减少自由发挥
)
return resp.choices[0].message.content
这段 system 的分量在于:它把"无法确定"变成了一个合法的、被鼓励的答案。没有这句话,模型会觉得"答不上来是失职",于是编;有了这句话,它多了一条诚实的退路。配合 temperature=0,把模型的"自由发挥"压到最低——看图取信息这种任务,要的是老实,不是创意。坑 2:用结构化输出,逼模型对"没看到"的字段诚实。光靠 prompt 说"别编"还不够稳,更硬的办法,是让模型按固定的 JSON 结构输出,并为每个缺失字段留一个明确的 null:
import json
def extract_fields(data_url):
"""让模型按固定 JSON 结构输出:看不到的字段必须填 null,而不是编。"""
schema_hint = (
"请严格按这个 JSON 结构输出,看不到或不确定的字段一律填 null:"
'{"brand": null, "model": null, "color": null, "visible_text": null}'
)
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": [
{"type": "text", "text": schema_hint},
{"type": "image_url", "image_url": {"url": data_url}},
]}],
response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content)
# null 字段,代表"模型确实没看到",由调用方决定怎么兜底,
# 而不是拿一个编出来的值,去污染后面的业务逻辑。
return data
结构化输出的妙处,在于它给了"我没看到"一个明确的位置——null。一个自由文本的回答里,"编出来的内容"和"真看到的内容"混在一起,你分不出来;而一个 JSON 里,"color": null 和 "color": "红色" 泾渭分明,你的代码一眼能判断。这等于把"诚实"做成了数据结构的一部分。坑 3:视觉请求必须配超时和重试。图片体积大、上传慢,网络抖动也比纯文本请求更常见,一次失败不该让整个功能崩掉:
import time
def vision_with_retry(data_url, question, retries=3, timeout=30):
"""视觉请求加超时和重试:大图上传慢,网络抖动也比纯文本更常见。"""
for attempt in range(1, retries + 1):
try:
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": [
{"type": "text", "text": question},
{"type": "image_url", "image_url": {"url": data_url}},
]}],
timeout=timeout,
)
return resp.choices[0].message.content
except Exception as e:
if attempt == retries:
raise # 最后一次还失败,就把异常抛出去
# 指数退避:第 1 次等 2s,第 2 次等 4s,别一失败就猛冲
wait = 2 ** attempt
print(f"第 {attempt} 次失败({e}),{wait}s 后重试")
time.sleep(wait)
坑 4:同一张图反复要分析,就缓存结果。如果你的业务里,同一张图片会被多次分析(比如用户反复查看、或多个流程都要用它),那把图片内容的哈希当 key、把分析结果缓存起来,能省下大量重复的视觉 token 开销——视觉 token 比文字 token 贵得多,这笔账很值得算。坑 5:别把敏感图片随手发出去。用户上传的照片里,可能有身份证、人脸、家庭住址、车牌。把这些图原样发给第三方模型,是实打实的隐私风险。该做的是:发送前评估图片敏感度,对敏感信息做打码或裁剪,并在产品上明确告知用户图片会被用于 AI 分析。坑 6:分清"模型看不到"和"模型看错了"。当结果不对时,要先判断是哪一类:如果是图太糊、目标太小、被遮挡——这是"看不到",该去提高 detail 档位、或改善图片质量;如果是图很清楚、模型却理解错了——这是"看错了",该去改 prompt、补充上下文、或换更强的模型。这两类问题的药方完全不同,方向不能搞反。
关键概念速查
| 概念 / 手段 | 说明 |
|---|---|
| 视觉 token | 图片被切成图块后编码成的 token,和文字 token 一样计费 |
| 图块 tile | 图片被切成的固定大小方块,块数越多 token 越多 |
| detail 档位 | low 看缩略图省 token,high 切块细看,按任务选 |
| OCR 分工 | 精确认字交专用 OCR,理解含义交大模型,各司其职 |
| 多图编号 | 每张图前加文字标签,模型才不会张冠李戴 |
| 图片预处理 | 缩放转 RGB 转 JPEG,同时压成本延迟和失败率 |
| 幻觉约束 | system 明确许可"看不清就说不知道",temperature 调 0 |
| 结构化输出 | 固定 JSON 结构,缺失字段填 null 而非编造 |
| 超时重试 | 大图上传慢,需配超时与指数退避重试 |
| 看不到 vs 看错了 | 前者改图片质量与 detail,后者改 prompt 与模型 |
避坑清单
- 图片不是被直接看懂的,它被切块编码成视觉 token,有真实成本。
- detail 别一律用 high,只需看懂画面时用 low 能省几十倍 token。
- 读金额卡号这类不能错一位的信息,先过专用 OCR 再交给模型。
- 视觉模型擅长理解画面,不擅长逐字精确识别,别用错工具。
- 多图输入必须在每张图前加显式编号标签,否则模型会张冠李戴。
- 用户原图必须预处理:缩放、转 RGB、转 JPEG,压成本和体积。
- 发送前卡一道体积上限,别等 API 报错才发现图太大。
- 用 system 明确给模型"看不清就说不知道"的许可,治幻觉。
- 用结构化 JSON 输出,让缺失字段填 null,把诚实做进数据结构。
- 视觉请求配超时和重试,敏感图片发送前要打码并告知用户。
总结
回头看那串"账单爆炸、读错金额、多图串味、凭空看见不存在的东西"的问题,以及我后来在多模态上接连踩的坑,最该记住的不是某一个 API 参数,而是我动手前那个想当然的判断——"多模态就是把图片和文字一起发给模型,它就什么都能看懂"。这句话错在它把图片当成了一种"模型能像人一样扫一眼就懂"的东西。我以为我递过去的是"一张图",模型接过去"一看"就完事了。可模型根本没有"看"这个动作。我递过去的不是一张图,而是一大把被切碎、被编码的 token——它们要占上下文、要计费、要受分辨率上限约束;模型对这把 token 的处理,擅长归纳"画面大致是什么",却并不擅长"逐字认得分毫不差";它看不清的时候,默认会编一个,而不是承认。
所以做多模态应用,真正的工程量不在"拼一个带 image_url 的 message"那一步操作上。那一步,任何文档的第一页就教完了。真正的工程量,在于你要理解图片"变成 token"之后引出的全部代价和边界,并把它们一项项管住:它会烧 token,你就得用 detail 档位和预处理把成本压下来;它不擅长精确认字,你就得把"认字"的活分给专用 OCR;多图会串,你就得给每张图编号;它会幻觉,你就得用 system 约束和结构化输出给它一条诚实的退路。这篇文章的几节,其实就是顺着这条思路展开的:先想清楚"把图片发过去"为什么不算多模态应用,再讲透图片如何变成 token、视觉理解的能力边界在哪,用编号接住"多图串味",用预处理压住成本,最后是幻觉约束、结构化输出、超时重试这几个把多模态用扎实的工程细节。
你会发现,多模态模型这个角色,和现实里"请一位见多识广的朋友帮你看照片"完全相通。一个不会用这位朋友的人,会怎么做?他把一沓几十张的高清照片整个塞过去,问"你看看吧"(这就是不挑 detail、不预处理、不编号);他让这位朋友帮他抄一串很长的银行卡号,而朋友瞄一眼就报了个数,错了两位他还浑然不觉(这就是拿视觉模型干精确认字的活);照片糊得根本看不清,他还追着问细节,朋友不好意思说"看不清",只好顺着编(这就是不给模型承认不确定的退路)。而一个会用这位朋友的人怎么做?他一次只递一两张、还会说清"这是第几张";大致看看场景的,递个缩略图就行,要抠细节的才递清晰大图;需要一字不错地抄号码,他会自己拿放大镜逐位核对,而不是让朋友凭印象报;他还会明明白白告诉朋友:"看不清你就直说,别勉强。"多模态用得好不好,从来不在于你能不能"把图片传进去",而在于你清不清楚这位"朋友"的本事边界在哪,并据此把任务一件件交对。
最后想说,多模态应用配没配对,差距永远不会在"本地拿一张图测通"时暴露——本地只有一张你精心挑的、清晰的图,你也不在乎那一次调用花了多少 token,你会觉得"把图传进去模型就懂"这几个字已经是全部。它只在真实的、有海量用户照片、有真金白银账单、有错一位就是事故的场景里才显形。那时候它会用最直接的方式给你结账:做不好,你会像我一样,被一串看似无关的麻烦同时缠上——账单莫名其妙地翻倍,你查不出钱花哪了;用户投诉金额识别错了;模型信誓旦旦地描述着一个根本不存在的东西;而做对了,你的多模态功能会又快又省又靠得住:成本稳稳压在预算内,该精确的地方分毫不差,模型看不清时会老老实实说"无法确定"。所以别等"翻倍的账单和离谱的错字"一起找上门,在你写下那个 image_url 的那一刻就该想清楚:我发出去的不是一张图,而是一把有成本的 token;接住它的不是一双能精确认字的眼睛,而是一个擅长理解、却也会编的模型——这把 token 的代价、这个模型的边界,我是不是每一项都管上了?这些问题有了答案,你的多模态应用才不只是一个"看起来能看图"的 demo,而是一套真正省钱、准确、扛得住真实用户照片的可靠工程。
—— 别看了 · 2026