最近不少开发者开始关注 DeepSeek 在多模态方向上的进展。刚好我手头接了一批图片理解、OCR 抽取、图表分析类的需求,趁这个机会把 DeepSeek 最新多模态模型的测评方法和结果整理成了一份完整笔记。这篇文章不打算只罗列结论,而是把测评思路、 Prompt 设计、脚本代码、结果解读和常见坑位都拆开讲清楚。如果你想快速验证一个多模态模型能不能用、适合哪些业务场景,可以直接拿这篇文章当作操作手册。
先说明一点:本文会避免依赖不确定的版本信息,重点讲测评方法和落地经验。模型标识符、API 地址、参数名请以 DeepSeek 官方开放平台的最新文档为准。我会在涉及具体配置时给出通用写法,并标注哪些地方需要你按实际环境修改。
1. 多模态模型测评到底在测什么
多模态模型这个词现在很常见,但很多人对它的理解比较模糊。简单来说,多模态模型指的是模型可以同时处理文本、图片、音频、视频等多种输入信息,并基于这些信息生成文本、代码或其他内容。相比纯文本模型,多模态模型最典型的价值是“能看图”和“能看图说话”。
在 DeepSeek 的多模态模型语境下,我们通常关注几个核心方向:
- 图像理解:给定一张图片,模型能否准确描述图片内容、识别物体、理解场景关系。
- 视觉问答(VQA):针对图片提出具体问题,例如“这张图表里哪个月份销量最高”,模型能否给出准确答案。
- OCR 文字识别与信息抽取:识别图片中的印刷体、手写体文字,并抽取结构化字段,例如发票号码、身份证信息、表格数据。
- 推理与图表分析:不只识别文字,还能结合图像中的坐标轴、数值、趋势来回答问题。
- 多轮对话记忆:在多轮图文对话中,模型能否记住之前的图片内容并回答后续问题。
实际测评不能只看单一指标,因为不同业务对能力权重的要求不一样。比如你做票据识别,OCR 准确率就是第一优先级;你做智能客服中的图片咨询,场景理解能力更关键。
这里要强调一个容易混淆的概念:多模态模型测评和纯文本模型测评不是一回事。纯文本模型可以靠标准问答数据集、代码生成正确率、语义相似度来评价。多模态模型需要同时考虑图片输入质量、Prompt 对视觉信息的利用方式、模型输出格式稳定性、结果可复现性等维度。被测模型不确定时,盲目用一套通用 Prompt 去测不同厂商的模型,结论往往不可靠。
因此,本文测评方案的设计思路是:先确定测评维度,再准备统一规格的测试图片和测试问题,通过脚本批量调用 API,最后把返回结果落盘并进行人工复核。这套思路同样适用于其他多模态模型。
2. 测评环境准备与版本说明
开始写代码之前,先准备一个干净可复现的测评环境。不同读者的网络环境、注册渠道、API 版本可能不同,所以下面给出的是通用配置思路,你需要按实际情况调整。
2.1 基础环境
推荐使用 Python 3.10 以上版本,因为新版 OpenAI SDK 对 Python 3.10 的支持比较顺畅。操作系统方面,Windows 10/11、macOS、主流 Linux 发行版都可以,本文示例在 Windows 11 和 Ubuntu 22.04 下都验证过。
安装依赖时,核心是 OpenAI SDK,因为 DeepSeek 官方开放平台提供了兼容 OpenAI 格式的接口。也就是说,你可以通过配置base_url和api_key的方式,用 OpenAI SDK 调用 DeepSeek 的多模态模型接口。
安装命令如下:
pip install openai==1.40.0 pip install python-dotenv pip install pillow说明一下。
openai库用来调用兼容接口。python-dotenv用来读取.env配置文件,避免把 API Key 写死在代码里。pillow用来做图片的基础校验,例如打开图片、缩放、转格式。
如果你用的是旧版openai库,例如 0.x 版本,调用方式和新的 1.x 版本差异很大,建议统一升级到 1.x。版本不兼容是最常见的启动报错原因。
2.2 获取 API Key
登录 DeepSeek 开放平台,创建 API Key,然后把 Key 写入项目根目录的.env文件。格式如下:
DEEPSEEK_API_KEY=sk-你的密钥 DEEPSEEK_BASE_URL=https://api.deepseek.com注意,不要把这个文件提交到 Git 仓库,建议在.gitignore中加上.env。密钥泄露不仅可能导致账号被盗用,还可能产生额外费用。
2.3 测试图片准备
测评不能只用一张图。建议准备 4 到 8 张不同难度的测试图,覆盖至少 4 种类型:
- 自然场景照片,例如街景、厨房、人物合影。
- 文档扫描件,例如发票、合同、身份证照片。
- 数据图表,例如柱状图、折线图、饼图截图。
- 手工绘制草图或复杂示意图。
图片格式优先用 JPG 或 PNG,单张大小控制在 5MB 以内。如果图片太大,接口可能裁剪或者压缩,影响识别准确率。建议统一处理成长边不超过 2048 像素的图片,这样既减少传输耗时,也能避免超限问题。
实际测评时,建议把图片路径和对应的标准答案整理成一个 JSON 或 CSV 文件。这样脚本运行时可以直接读取,方便后续统计准确率。
下面是一个测试用例文件示例:
[ { "id": "case_01", "image_path": "./images/natural_01.jpg", "question": "描述这张图中的主要场景,并指出三个人物的动作。", "expected": "图中是公园野餐场景,一个人在铺餐布,一个人在摆放食物,另一个人在拍照。" }, { "id": "case_02", "image_path": "./images/invoice_01.jpg", "question": "请提取发票号码、开票日期、价税合计金额。", "expected": "发票号码 12345678,开票日期 2025年1月15日,价税合计 2300.00元。" }, { "id": "case_03", "image_path": "./images/chart_01.png", "question": "2024年哪个季度销售额最高?具体数值是多少?", "expected": "第三季度最高,销售额为 85 万元。" } ]这一步看起来简单,但非常关键。没有标准答案,后续评估就只能靠人工逐条看,效率低且容易遗漏。
3. DeepSeek API 调用核心写法
很多新手在调用多模态 API 时最常犯的错误,是把图片直接塞进content字符串里,结果模型根本看不到图片。DeepSeek 的接口遵循 OpenAI 的多模态消息格式,图片需要通过image_url结构传递。
下面这段代码演示了最基础的图片理解调用方式。
# 文件路径:src/basic_call.py import os import base64 from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL") ) def encode_image(image_path): with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") image_path = "./images/natural_01.jpg" response = client.chat.completions.create( model="deepseek-chat", # 这里以实际可用模型为准,例如 deepseek-vl 或官方最新多模态模型 messages=[ { "role": "user", "content": [ { "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{encode_image(image_path)}" } }, { "type": "text", "text": "请详细描述这张图片中的内容。" } ] } ], max_tokens=1024, temperature=0.2 ) print(response.choices[0].message.content)这段代码的核心点有几个。
第一,base64编码图片。网络上很多教程为了方便直接用 URL 方式传图,但你本地测试时没有公网 URL,所以 Base64 是最稳妥的方式。图片转 Base64 会增加约 33% 的体积,所以前面才强调图片大小要控制。
第二,content是列表结构。列表里的每个元素代表一种内容块,image_url块放图片,text块放问题。这个结构与纯文本调用的差异很大,需要重点注意。
第三,temperature要调低。多模态场景下,凡是涉及事实提取、OCR、数值读取的任务,都建议把temperature设为 0.2 或更低,这样输出更稳定。如果做创意描述类任务,可以适当调高到 0.7 左右。
第四,model参数不要写死。DeepSeek 的模型列表可能会调整,建议先通过接口拉取模型列表,或者查阅官方文档确认当前可用的多模态模型标识符。
如果接口返回 404 或者模型不存在,多数情况下是模型名称写错了。此时可以打印一下client.models.list()来查看可用模型列表。
下面补充一个查看模型列表的小脚本:
# 文件路径:src/list_models.py import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL") ) models = client.models.list() for model in models.data: print(model.id)运行后,你会看到当前账号可调用的模型列表。如果这里没有出现任何多模态相关模型,说明你的账号权限或接入点还需要调整。
4. 完整测评脚本设计
把基础调用跑通之后,就可以进入批量测评阶段了。我设计的脚本分四个模块:读取用例、循环调用、保存结果、统计得分。下面逐个说明。
4.1 读取测试用例
这里读取第 2 节里生成的test_cases.json文件。要注意编码问题,JSON 文件统一用 UTF-8,否则中文容易乱码。
# 文件路径:src/load_cases.py import json def load_test_cases(file_path="./data/test_cases.json"): with open(file_path, "r", encoding="utf-8") as f: cases = json.load(f) return cases4.2 批量调用与结果保存
这是整个脚本的核心部分。每张测试图都会经历“编码图片 -> 构造消息 -> 调用 API -> 提取文本 -> 保存结果”这个流程。
# 文件路径:src/run_evaluation.py import os import time import json import base64 from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL") ) def encode_image(image_path): with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") def build_messages(image_path, question): ext = os.path.splitext(image_path)[1].lstrip(".").lower() mime_type = "image/png" if ext == "png" else "image/jpeg" return [ { "role": "user", "content": [ { "type": "image_url", "image_url": { "url": f"data:{mime_type};base64,{encode_image(image_path)}" } }, { "type": "text", "text": question } ] } ] def run_case(case): response = client.chat.completions.create( model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"), messages=build_messages(case["image_path"], case["question"]), max_tokens=int(os.getenv("DEEPSEEK_MAX_TOKENS", "1024")), temperature=0.2 ) return response.choices[0].message.content def main(): with open("./data/test_cases.json", "r", encoding="utf-8") as f: cases = json.load(f) results = [] for idx, case in enumerate(cases): print(f"正在处理第 {idx + 1} 个用例:{case['id']}") try: output = run_case(case) except Exception as e: output = f"[ERROR] {str(e)}" results.append({ "id": case["id"], "image_path": case["image_path"], "question": case["question"], "expected": case["expected"], "model_output": output }) time.sleep(1) # 避免请求过于频繁 os.makedirs("./output", exist_ok=True) with open("./output/results.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print("测评完成,结果已保存到 ./output/results.json") if __name__ == "__main__": main()这个脚本有几个值得注意的设计。
第一个是time.sleep(1)。批量调用 API 时,如果不加控制,很容易触发频率限制,导致部分请求失败。适当加延时不会影响整体结果,但能显著提升成功率。
第二个是异常捕获。多模态调用涉及的网络状态、图片格式、API 限制因素很多,单条失败不应该中断整个测评流程。遇到异常时先把错误信息记录到结果文件,后续再统一排查。
第三个是结果保存格式。每个用例的原始输出、标准答案、模型输出都存在同一个对象里,方便后面做人工核对和统计分析。
4.3 结果统计脚本
拿到results.json之后,可以写一个简单的评分脚本。评分方式不搞复杂,采用人工复核加分和规则匹配两种方式结合。
对于答案明确的任务,例如“发票号码是多少”,可以直接做关键词匹配或数值匹配。对于开放式问答,则需要人工查看。这里给出一个最简单的关键词匹配版本,适合首次过滤。
# 文件路径:src/evaluate_scores.py import json def normalize_text(text): return "".join(text.split()) with open("./output/results.json", "r", encoding="utf-8") as f: results = json.load(f) score = 0 details = [] for item in results: expected = normalize_text(item["expected"]) output = normalize_text(item["model_output"]) hit = expected in output or output in expected if hit: score += 1 details.append({ "id": item["id"], "hit": hit, "model_output": item["model_output"] }) print(f"简单匹配得分:{score}/{len(results)}") for d in details: print(d["id"], "正确" if d["hit"] else "待人工复核")这个脚本只适合做第一轮筛选,不能完全代替人工判断。比如模型输出包含更多冗余信息、或者用不同表达给出了正确答案,关键词匹配就会漏判。更稳妥的方案是把results.json导出成表格,由业务人员逐条标注“正确 / 部分正确 / 错误”。
5. 测评维度与 Prompt 设计细节
很多人在测评多模态模型时,习惯直接问“这是什么”,然后根据模型描述来打分。但这种方式太粗糙,无法区分不同模型的真实能力差距。下面按业务场景拆开讲 Prompt 设计。
5.1 图像理解类
图像理解类任务适合用开放式描述来考察,但问题不能太泛。比如“描述这张图”这样的 Prompt 虽然简单,但不同模型描述详略差异很大,难以定量比较。
推荐采用结构化指令:
请从以下维度描述这张图片: 1. 主要场景和背景环境 2. 图中出现的核心物体或人物 3. 人物动作或物体状态 4. 图片中的文字内容(如有) 5. 图片可能拍摄的场景或用途结构化 Prompt 的作用是约束模型输出的格式,让结果可对比。如果不加约束,模型可能只输出一句话,也可能输出长篇小说,评分时非常痛苦。
5.2 OCR 与信息抽取类
OCR 类任务必须给模型明确的抽取目标和输出格式。例如发票信息抽取:
请从图片中提取以下字段,并以 JSON 格式返回: - 发票号码 - 开票日期 - 购买方名称 - 销售方名称 - 项目名称 - 金额 - 税率 - 税额 如果某个字段无法识别,请返回 null,不要猜测。JSON 输出格式有两个好处:一是方便程序解析结果,二是可以避免模型自由发挥导致的多余内容。很多模型的 OCR 能力并不差,差的是把文字放到正确字段上的能力。明确的输出格式能在一定程度上缓解字段错位问题。
这里需要提醒:OCR 任务非常依赖图片质量。模糊、倾斜、遮挡严重的图片,即使人类也很难辨认。测评时要区分“模型识别错误”和“图片本身无法辨认”。建议在测试用例中标记图片难度,例如“清晰 / 一般 / 模糊”,方便在分析结果时分类处理。
5.3 图表分析类
图表分析是多模态模型的高价值场景之一。图表中包含文字、数字、颜色、坐标轴信息,模型要同时处理多类信息才能给出正确答案。
对于柱状图或者折线图,推荐这样设计 Prompt:
请根据这张图表回答以下问题: 1. 图表的标题和坐标轴含义是什么? 2. 数据变化趋势是怎样的? 3. 哪个数据点最突出?请给出具体类别和数值。 4. 如果用一句话总结这张图表,你会怎么说?在实测中,模型能否准确读取柱状图上的数值,通常取决于图片像素清晰度以及图表中是否有明确的数据标签。如果柱状图上没有数字标签,模型往往只能给出趋势判断,数值会估算,误差可能很大。这是模型能力边界,不是简单的 Prompt 问题。
5.4 开放场景描述类
如果你的业务是内容审核、客服对话、辅助创作,那么模型对图片的开放式理解能力更重要。这类任务不适合用固定格式约束,建议考察模型能否抓住关键信息、逻辑是否清晰、描述是否自然。
示例 Prompt:
假设你是客服助手,用户发送了这张图片,请用友好的语气描述用户可能遇到的问题,并给出处理建议。这种 Prompt 更接近业务真实场景。测评时,可以从相关性、完整性、可操作性三个角度人工打分。
6. 常见问题与排查思路
实测过程中,我遇到了不少问题,这里整理成表格,方便大家直接排查。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 接口返回 404 或 model not found | 模型名称写错或账号无权限 | 调用client.models.list()查看可用模型,对照官方文档修改模型名 |
| 接口返回 400 或请求格式错误 | content结构拼错,缺少type字段 | 检查消息格式,确保图片块和文本块结构完整 |
| 图片无法识别,模型说“看不到图片” | Base64 前缀错误或 MIME 类型不匹配 | 根据图片扩展名设置data:image/jpeg或data:image/png |
| 请求超时或频繁报错 | 图片过大、并发过高 | 压缩图片,增加time.sleep或使用限流策略 |
| 输出结果不稳定,前后两次回答不同 | temperature过高 | 将temperature调低至 0.2 以下,或固定seed参数 |
| 中文输出乱码或字段错位 | 输出格式不明确,或图片文字模糊 | 在 Prompt 中强制指定 JSON 结构,并提高图片分辨率 |
| 长文本图片无法完整处理 | 超出上下文长度限制或图片被压缩 | 裁剪图片为多个区域,分批调用后再合并结果 |
这里挑两个高频问题展开说明。
第一个是 Request timed out。这个问题在图片较大时尤其明显。Base64 编码后传输数据量变大,网络延迟高时很容易超时。解决方法是把图片压缩到长边 1024 或 1280 像素,质量损失对大多数 OCR 和图表识别任务影响不大。另外可以在客户端设置超时时间,例如OpenAI(timeout=60)。
第二个是模型输出格式不稳定。即使你在 Prompt 里说了“返回 JSON”,模型偶尔还是会输出多余的解释文字。加强约束的方法有两种。一种是在 Prompt 末尾追加一句“只输出 JSON,不要任何解释”;另一种是在后处理时尝试截取 JSON 片段并解析。对于测评脚本,我建议后处理时用正则或字符串查找拿到第一个{到最后一个}之间的内容,再用json.loads解析。
import json import re def extract_json(text): match = re.search(r"\{.*\}", text, re.S) if match: try: return json.loads(match.group()) except json.JSONDecodeError: return None return None这个函数虽然简单,但在处理模型返回结果时很实用。注意它不适合所有场景,如果 JSON 嵌套复杂或内部有字符串括号,正则可能截取错误。测试阶段可以先验证输出样例。
7. 最佳实践与工程建议
测评只是开始,真正把模型用到业务里才是重点。这里分享一些工程层面的建议。
7.1 图片预处理是准确率的第一杠杆
不要想着模型足够强大,就可以无脑上传原图。图片预处理直接影响 OCR 和信息抽取效果。建议统一做以下处理:
- 转正方向:对于手机拍摄的文档,优先进行透视校正或旋转校正。
- 提高对比度:发票、证件类图片,适当增加对比度能让文字更清晰。
- 裁剪留白:去掉多余背景,仅保留有效区域,减少模型注意力分散。
- 统一尺寸:长边限制在 1024 到 2048 像素之间。
这些操作可以用PIL库实现。示例如下:
# 文件路径:src/preprocess_image.py from PIL import Image def preprocess(image_path, target_long=1280): img = Image.open(image_path) img = img.convert("RGB") w, h = img.size if max(w, h) > target_long: ratio = target_long / max(w, h) new_w = int(w * ratio) new_h = int(h * ratio) img = img.resize((new_w, new_h), Image.LANCZOS) return img7.2 Prompt 模板要沉淀成配置文件
写死 Prompt 在代码里,不利于后续迭代。建议把不同场景的 Prompt 模板放在独立的配置目录,例如prompts/文件夹下,用文本文件或 JSON 管理。
{ "invoice_extract": "请从图片中提取以下字段:发票号码、开票日期、价税合计金额,并以 JSON 格式输出。", "chart_summary": "请根据这张图表回答趋势、最高点、最低点、总结四个问题。", "scene_description": "请描述图片主要场景、核心物体、可能事件。" }这样做的好处是,当你需要调 Prompt 时,不需要改代码重新发布,只需要更新配置并重新跑测评即可。工程上也能把不同 Prompt 版本的测评结果做对比,找出最优模板。
7.3 注意数据安全与合规
业务图片中往往包含敏感信息,例如身份证号、发票号码、人脸。接入 API 前务必确认:
- 数据是否允许发送到第三方平台。
- 是否需要对图片进行脱敏处理,例如遮盖关键字段。
- 是否需要在本地私有化部署模型,如果对数据出境有要求,建议考虑本地部署方案。
同时,不要把敏感测试图片提交到公开仓库或公开分享。测评用图最好是自己生成的模拟数据,不要用真实客户数据。
7.4 建立回归测试机制
模型版本升级后,能力可能提升也可能回退。只靠一次测评不够。建议把标准测试集长期维护下来,每次模型升级或 Prompt 更新后都跑一遍,并记录分数变化。这样可以尽早发现能力回退。
下面是一个简单的成绩记录表格:
| 测评日期 | 模型版本 | 图片理解得分 | OCR 得分 | 图表分析得分 | 备注 |
|---|---|---|---|---|---|
| 2025-01-10 | v1 | 90% | 85% | 80% | 首次全量测评 |
| 2025-01-20 | v2 | 92% | 88% | 84% | 提升明显 |
7.5 成本控制
多模态请求的 token 消耗通常比纯文本高,因为图片信息会被转换成较长的视觉 token。批量测评时建议先估算成本,避免一次性跑完太多用例后账单超标。
控制成本的方法:
- 使用小图或压缩图,减少视觉 token 数量。
- 先跑少量样例验证 Prompt 效果,再扩大测试集。
- 同一张图多个问题可以尽量合并成一个请求,减少重复上传图片的 token 开销。
当然,具体的 token 计费规则要以官方文档为准,这里不展开估算公式。
8. 总结与后续建议
这篇测评笔记从多模态模型的概念出发,整理了环境准备、API 调用写法、批量测评脚本、Prompt 设计、常见问题和工程落地经验。核心收获有三点:多模态测评需要先设计维度再选指标;API 调用必须理解图片消息的构造格式;任何自动评分都需要人工复核兜底。
如果你只是入门,下一步建议先从 10 个测试用例的小批量开始,跑通一次完整流程,再逐步扩充到上百个用例。这样可以快速建立自己的测评数据集,后续换模型、调 Prompt 时都能复用。
对于准备在生产环境中使用 DeepSeek 多模态能力的团队,建议优先关注三个风险点:数据合规边界、模型版本升级带来的行为变化、图片输入质量对业务准确率的影响。这三个点无论模型能力多强,都会在实际业务中反复遇到。
如果这篇文章对你有帮助,可以收藏备用,后续有新的测评结论我会继续更新。也欢迎在评论区聊聊你在多模态模型落地时踩过的坑。