Mage 输出和图片对不上 / 是提示词顺序还是模板没配对?

文章导读
图片里的内容明明在,Mage 的回答却像没看过图——这种时候先不要反复改问法。顺序、模板、占位符是三条各自独立的链路:提示词里图片和文本的先后顺序,决定图像标记落在序列的哪个位置;对话模板决定角色标记和图像标记有没有被正确套用;占位符替换决定图片到底有没有被真的塞进输入串。三条里错任何一条,模型看到的输入都和你想的不一样,回答自然会“答非所问”。建议用同一张图、同一句问题做最小复现,每次只改一个变
📋 目录
  1. 壹 同一张图同一句问题,换两种提示词顺序各跑一次
  2. 贰 打印送进模型的原始输入,数清图片占位符个数
  3. 叁 换一套对话模板重跑同一组输入
  4. 肆 用单张只含一行文字的图做最小验证
A A

图片里的内容明明在,Mage 的回答却像没看过图——这种时候先不要反复改问法。顺序、模板、占位符是三条各自独立的链路:提示词里图片和文本的先后顺序,决定图像标记落在序列的哪个位置;对话模板决定角色标记和图像标记有没有被正确套用;占位符替换决定图片到底有没有被真的塞进输入串。三条里错任何一条,模型看到的输入都和你想的不一样,回答自然会“答非所问”。建议用同一张图、同一句问题做最小复现,每次只改一个变量,靠打印出来的原始输入和回答原文判断,而不是凭感觉猜。

判断方向:先固定同一张图、同一句问题,把「图片在前/文本在前」「换对话模板」「打印原始输入数占位符」三条线各跑一次,只改一个变量。适用场景是多模态模型回答看不出用图的情况。验证方式是比对两次的输入串和回答原文,并检查图像占位符个数是否等于图片张数。风险边界:如果连单张只含一行文字的图都读不对,问题多半不在图片复杂度或提示词顺序,需要回到模板和占位符两条线上查;具体结论必须结合你本地的库版本与模板文件确认。

同一张图同一句问题,换两种提示词顺序各跑一次

先隔离顺序这一个变量。准备一张图,问题固定不变,只调换 content 里 image 与 text 两个块的先后。下面的 generate 是占位函数,替换成你自己的调用方式(本地推理、HTTP 请求或 SDK 均可),关键是两次调用除顺序外完全一致。

from PIL import Image

img = Image.open('one_line.png')
q = '图里的那行字是什么?'

# A 组:图片在前
msgs_a = [{'role': 'user', 'content': [
    {'type': 'image'},
    {'type': 'text', 'text': q},
]}]

# B 组:文本在前
msgs_b = [{'role': 'user', 'content': [
    {'type': 'text', 'text': q},
    {'type': 'image'},
]}]

print('A 组回答原文:')
print(generate(msgs_a, images=[img]))
print('B 组回答原文:')
print(generate(msgs_b, images=[img]))

两次输出请原样贴回记录,不要凭印象写“差不多”。差异点按这三项标注:

  • 是否读出了图里的具体字符(不是“我看到一张图”这类泛泛描述);
  • 回答里有没有出现与图片内容无关但与前文文本高度相关的句子,这通常说明图像部分被忽略;
  • 回答长度和结构是否明显不同——若只有顺序变了而回答几乎一致,说明该模型对顺序不敏感,可以把顺序从怀疑名单里划掉。
结论只用一句话记:顺序是否影响本次回答。

打印送进模型的原始输入,数清图片占位符个数

顺序排掉之后,看真正拼出来的那串文本。多数实现都会在做完整理后给出一个字符串,先用 repr 打印,避免换行和特殊字符被终端吃掉。图像标记的名称因模型而异,先用 <image> 举例,实际请替换成你模板里真正使用的标记(可能是 <|image|>、<img> 等)。

text = processor.apply_chat_template(
    msgs_a, tokenize=False, add_generation_prompt=True)

print(repr(text))
print('占位符个数:', text.count('<image>'))
print('实际图片张数:', len(images) if isinstance(images, list) else 1)

检查要点是一一对应:占位符个数应当等于本次传入的图片张数。少一个,说明图片没被替换进去或者替换被截断;多一个,说明模板里自带了一个固定图像标记,和外面传进来的图叠加了。这两种情况都会让模型拿到的图像特征与图像标记数量不匹配。异常时常见的报错形态是提示 image tokens 与 image features 数量不一致,措辞随实现不同,请以你日志里的原文为准,把那一行完整抄进排查记录,比转述可靠。另外注意:如果拼接发生在服务端,本地打印不到,就去看服务端日志中打印输入串的位置,或临时打开调试输出。

换一套对话模板重跑同一组输入

模板决定了角色标记和图像标记怎么套到消息上。常见位置有三处:tokenizer 配置里的 chat_template 字段、独立的 jinja 模板文件(例如 chat_template.jinja)、以及服务端启动参数(例如某些推理服务用 `--chat-template` 指定)。先确认当前生效的是哪一份,再换成另一份重跑完全相同的输入。

Mage 输出和图片对不上 / 是提示词顺序还是模板没配对?
# 方式一:运行时替换模板
with open('templates/mage_chat.jinja', 'r', encoding='utf-8') as f:
    processor.chat_template = f.read()

text_alt = processor.apply_chat_template(
    msgs_a, tokenize=False, add_generation_prompt=True)
print(repr(text_alt))
print(generate(msgs_a, images=[img]))

两次回答并列对比时,重点看两件事:一是替换模板后打印出的输入串里,角色标记与图像标记的位置是否发生了变化;二是回答是否从“看不到图”变成能引用图中内容。如果只有换模板才恢复正常,问题就落在模板上——多半是原模板缺少图像分支,或者图像分支写在了错误的角色块里。如果换模板后依旧如此,模板可以排除。切换前建议保留原模板文本,排查结束后还原,避免把临时改动留在配置里。

用单张只含一行文字的图做最小验证

前面三条都跑完还不确定,就换一张复杂度最低的图,排除“图太复杂所以读不出来”这种误判。生成一张白底、只写一行字符的图即可:

from PIL import Image, ImageDraw

img = Image.new('RGB', (640, 160), 'white')
d = ImageDraw.Draw(img)
d.text((20, 60), 'MAGE-7391', fill='black')
img.save('one_line.png')

期望回答应当包含 MAGE-7391 这串字符。实际回答请粘贴原文。结论边界这样划:

  • 单行字图能读对,说明链路基本通了,原先的“对不上”更可能来自复杂图的细节阅读,而不是提示词顺序或模板没配;
  • 单行字图也读不出,说明问题在输入构造层,回到占位符与模板两条线上继续查,而不是继续调提示词;
  • 字体渲染、图片分辨率、字符集都可能影响识别,这一步只用来判断“链路是否通”,不用于比较模型强弱。

四条线跑完后的落点判断可以这样归类:打印出的占位符个数与图片张数不等,属于占位符问题;占位符正常但换模板后输入串里的角色与图像标记位置变了、回答也随之变化,属于模板问题;两者都正常、只是调整图片与文本先后顺序就改变了回答,属于顺序问题;三者都正常而单行字图仍读不对,则需要结合你的库版本、图像预处理方式和部署配置继续确认,不宜直接归到提示词写法上。