☰
大模型空间智能评测:鹈鹕骑自行车测试与SVG自动化实践
2026/10/6 6:14:09 网站建设 项目流程

1. 这道题到底在考什么

第一次看到“让 AI 画一只鹈鹕骑自行车”这个需求,我差点笑出声。这不明摆着整活吗?但真把这句话丢给几个主流大模型跑了一圈之后,我笑不出来了——能画对的没几个,画得好的更是凤毛麟角。后来我把它当成一个固定的测试用例,反复跑了上百次,才慢慢琢磨明白:这根本不是在测画图能力,而是在给大模型做一场空间智商考试。

为什么这么说?你想想,鹈鹕这种鸟,嘴长、脖子长、身子大、腿短,自行车呢,两个轮子、一个车架、一个车把、一副脚踏。要让鹈鹕“骑”上去,模型得同时处理好几层空间关系:鹈鹕的屁股得落在车座上,翅膀得够到车把,脚蹼得踩在脚踏上,长嘴还不能戳到前轮。这里面涉及部件识别、姿态推理、接触点判断、遮挡关系,任何一环掉链子,出来的图就是“鹈鹕飘在自行车旁边”或者“自行车长了个鸟头”。

我拿这个题目测过不少模型,包括一些号称多模态能力很强的。结果很有意思:有的模型画出来的鹈鹕确实像鹈鹕,自行车也确实像自行车,但两者就是各玩各的,完全没有“骑”这个动作。还有的模型把鹈鹕的嘴画成了自行车横梁,或者把翅膀画成了车把。这些失败案例恰恰暴露了大模型在空间推理上的短板——它能识别物体,但搞不定物体之间的相对位置和物理约束。

所以这个测试用例的价值,不在于它有多搞笑,而在于它用极低的成本,把大模型的空间智能问题给具象化了。你不需要搭复杂的评测环境,不需要标注数据,只要一句话,就能看出一个模型在空间理解上到底有几斤几两。对于做AI测试开发、大模型评测、甚至AI Agent方向的人来说,这是一个非常实用的“土办法”。

2. 为什么偏偏是鹈鹕和自行车

2.1 这个组合的刁钻之处

你可能会问,为什么不是“猫骑自行车”或者“狗骑自行车”?猫和狗太常见了,训练数据里估计有一堆,模型可能靠记忆就能拼出来。鹈鹕就不一样了,它的身体结构太特殊了:巨大的喉囊、细长的脖子、短小的腿、宽大的翅膀。这些特征在训练数据里出现的频率远低于猫狗,模型没法靠“背答案”蒙混过关,必须真正理解空间关系才能画对。

自行车也是个妙选。它的结构足够复杂,有车架、车轮、车把、车座、脚踏、链条,但又足够常见,模型肯定认识。难点在于,自行车是一个功能性物体,它的各个部件有明确的交互逻辑:手要握车把,脚要踩脚踏,屁股要坐车座。模型如果只是把鹈鹕和自行车两个图像“贴”在一起,就会露馅。

我试过把题目改成“鹈鹕骑滑板车”,难度立刻下降——滑板车结构简单,接触点少,模型更容易蒙对。改成“鹈鹕骑独轮车”,难度又上升了——独轮车对平衡要求更高,模型更难推理出合理的姿态。所以“鹈鹕+自行车”这个组合,刚好卡在一个难度适中但区分度极高的位置上。

2.2 从SVG到像素图的测试差异

这里要区分一下测试场景。如果你让模型生成SVG代码,那考的是它的结构化空间描述能力。SVG是矢量图,每个元素的位置、大小、旋转角度都得用数值精确表达。模型得知道鹈鹕的翅膀应该放在哪个坐标,自行车的车轮半径是多少,两者怎么对齐。这种测试更硬核,因为数值错了就是错了,没有“大概像”的模糊空间。

如果你让模型生成像素图(比如通过扩散模型),那考的是它的视觉空间推理能力。模型得在潜空间里把鹈鹕和自行车的特征融合起来,生成一个合理的构图。这种测试更接近人类直觉,但评估起来更主观,需要人眼判断。

我两种都试过。SVG测试更适合做自动化评测,你可以写脚本解析生成的SVG,检查关键元素的位置关系。像素图测试更适合做定性分析,看看模型到底“理解”了多少。对于做AI测试开发的人来说,SVG路线更值得投入,因为它的可复现性和可量化性更好。

2.3 为什么这个测试能火

这个测试之所以在圈子里传开,我觉得有几个原因。第一,它门槛极低,谁都能试,不需要任何专业工具。第二,它结果直观,画得好不好一眼就能看出来,不需要解释。第三,它戳中了痛点,大模型的空间智能确实是个短板,但平时不容易暴露,这个测试把它给揪出来了。

还有一个原因,就是它自带传播属性。“鹈鹕骑自行车”这个画面本身就够荒诞,生成出来的失败案例更是笑料百出。大家在转发和讨论的过程中,其实是在集体探索大模型的能力边界。这种自下而上的评测方式,比官方跑分有意思多了。

3. 拆解空间智商的几个维度

3.1 部件识别与语义理解

第一层考的是部件识别。模型得知道鹈鹕有哪些部件(嘴、脖子、身子、翅膀、腿、脚蹼),自行车有哪些部件(车轮、车架、车把、车座、脚踏)。这层看起来简单,但实际测试中,有些模型会把鹈鹕的嘴和自行车的车把搞混,或者把翅膀和车架混在一起。这说明模型在细粒度语义区分上还有问题。

我试过给模型一些提示,比如“鹈鹕的嘴是长在头上的,自行车的车把是金属的”,帮助它区分。结果发现,加了提示之后,部件识别确实改善了,但空间关系还是错。这说明部件识别和空间推理是两个相对独立的能力,得分开训练和评测。

3.2 姿态推理与关节约束

第二层考的是姿态推理。鹈鹕要骑自行车,它的身体得摆出一个合理的姿势:脖子得往前伸,翅膀得往下够,腿得弯曲踩脚踏。这涉及到关节约束——鹈鹕的翅膀能转多少度,腿能弯多少,脖子能伸多长。模型如果不懂这些约束,画出来的鹈鹕就是“橡皮鸟”,翅膀能360度旋转,腿能拉成面条。

我观察到一个有趣的现象:有些模型会把鹈鹕画成“站立”姿势,只是把自行车放在它脚下。这说明模型没有理解“骑”这个动作的姿态要求,只是把两个物体简单叠加。要解决这个问题,模型需要学习动作语义,知道“骑”意味着什么。

3.3 接触点与物理合理性

第三层考的是接触点判断。鹈鹕的屁股得接触车座,翅膀得接触车把,脚蹼得接触脚踏。这些接触点必须同时满足,而且不能有穿模。我见过一些生成结果,鹈鹕的翅膀穿过了车把,或者脚蹼悬空在脚踏上方。这些都是接触点判断失败的表现。

物理合理性还包括重心和平衡。鹈鹕骑自行车,重心得落在两个轮子之间,否则就会倒。有些模型画出来的鹈鹕,身子完全在前轮上方,一看就要翻车。这说明模型没有物理直觉,不知道物体需要保持平衡。

3.4 遮挡关系与深度排序

第四层考的是遮挡关系。鹈鹕的腿应该在车架后面,翅膀应该在车把前面,身子应该挡住车座的一部分。这些遮挡关系决定了画面的深度感。如果遮挡错了,画面就会显得扁平、混乱。

我试过让模型生成SVG,然后检查元素的z-order。结果发现,很多模型生成的SVG里,元素的顺序是随机的,没有考虑遮挡。这说明模型在深度排序上还有很大的提升空间。

4. 实测:用Python和PowerShell跑一轮评测

4.1 环境准备与工具选型

要系统性地跑这个测试,得先搭个环境。我用的方案是Python + PowerShell,Python负责调用模型API和处理结果,PowerShell负责批量调度和日志记录。为什么选这个组合?因为Python的生态最丰富,处理SVG和图像的库很多;PowerShell在Windows上原生支持,调度脚本写起来方便,而且可以轻松调用系统命令。

Python环境建议用3.10以上版本,安装几个关键库:

pip install requests pillow cairosvg lxml numpy
  • requests:调用模型API
  • pillow:处理像素图
  • cairosvg:把SVG转成PNG,方便肉眼检查
  • lxml:解析SVG的XML结构
  • numpy:做数值计算,比如检查元素位置

PowerShell这边,主要是写一个批量调度脚本,循环调用Python脚本,把每次生成的结果保存下来。如果你在Windows上跑,记得先更新PowerShell到7.x版本,老版本的兼容性有问题。

注意:如果你在PowerShell里遇到乱码,大概率是编码问题。在脚本开头加上$OutputEncoding = [System.Text.Encoding]::UTF8和[Console]::OutputEncoding = [System.Text.Encoding]::UTF8就能解决。

4.2 调用模型生成SVG的完整脚本

下面是我实际用的Python脚本,功能是调用模型生成SVG,然后保存到本地:

import requests import json import os from datetime import datetime API_URL = "你的模型API地址" API_KEY = "你的API密钥" def generate_svg(prompt, model="your-model"): headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": model, "messages": [ {"role": "system", "content": "你是一个SVG生成助手,请根据用户描述生成完整的SVG代码。"}, {"role": "user", "content": prompt} ], "temperature": 0.7 } response = requests.post(API_URL, headers=headers, json=payload, timeout=60) if response.status_code == 200: return response.json()["choices"][0]["message"]["content"] else: return None def save_svg(svg_content, filename): os.makedirs("output", exist_ok=True) filepath = os.path.join("output", filename) with open(filepath, "w", encoding="utf-8") as f: f.write(svg_content) return filepath if __name__ == "__main__": prompt = "Generate an SVG of a pelican riding a bicycle. The pelican should be sitting on the seat, with its wings on the handlebars and its feet on the pedals." svg = generate_svg(prompt) if svg: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filepath = save_svg(svg, f"pelican_bike_{timestamp}.svg") print(f"SVG saved to {filepath}") else: print("Generation failed")

这个脚本的关键点在于prompt的设计。我特意把“sitting on the seat”、“wings on the handlebars”、“feet on the pedals”这些空间关系写进去,就是为了看模型能不能理解并执行。如果模型忽略了这些约束,生成的SVG就会有问题。

4.3 PowerShell批量调度与日志记录

单次生成不够,得批量跑才能看出规律。下面这个PowerShell脚本可以循环调用Python脚本,并把每次的结果记录到日志里:

$OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $logFile = "eval_log_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt" $iterations = 20 for ($i = 1; $i -le $iterations; $i++) { Write-Host "Running iteration $i..." $result = python generate_svg.py 2>&1 $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" "$timestamp | Iteration $i | $result" | Out-File -FilePath $logFile -Append Start-Sleep -Seconds 2 } Write-Host "Evaluation complete. Log saved to $logFile"

这个脚本会跑20次,每次间隔2秒,避免触发API限流。日志文件里记录了每次的运行结果,方便后续分析。如果你要跑更多次,可以把$iterations调大,但记得加长间隔时间。

实操心得:批量跑的时候,建议把temperature设成0.7左右。太低的话,每次生成的结果都差不多,看不出模型的多样性;太高的话,生成结果太随机,难以分析规律。

4.4 结果解析与评分脚本

生成了一堆SVG之后,得有个自动化的方式来评分。我写了一个简单的解析脚本,检查几个关键的空间关系:

from lxml import etree import os def parse_svg(filepath): tree = etree.parse(filepath) root = tree.getroot() ns = {'svg': 'http://www.w3.org/2000/svg'} elements = {} for elem in root.iter(): tag = etree.QName(elem).localname if tag in ['circle', 'ellipse', 'rect', 'path', 'polygon']: elem_id = elem.get('id', '') if elem_id: elements[elem_id] = { 'tag': tag, 'x': elem.get('cx') or elem.get('x'), 'y': elem.get('cy') or elem.get('y'), 'r': elem.get('r'), 'd': elem.get('d') } return elements def score_svg(elements): score = 0 checks = [] # 检查是否有车轮 wheels = [k for k in elements if 'wheel' in k.lower() or 'circle' in k.lower()] if len(wheels) >= 2: score += 20 checks.append("车轮数量正确") else: checks.append("车轮数量不足") # 检查是否有鹈鹕的身体 body = [k for k in elements if 'body' in k.lower() or 'pelican' in k.lower()] if body: score += 20 checks.append("鹈鹕身体存在") else: checks.append("缺少鹈鹕身体") # 检查是否有车把 handlebar = [k for k in elements if 'handle' in k.lower() or 'bar' in k.lower()] if handlebar: score += 20 checks.append("车把存在") else: checks.append("缺少车把") # 检查是否有车座 seat = [k for k in elements if 'seat' in k.lower() or 'saddle' in k.lower()] if seat: score += 20 checks.append("车座存在") else: checks.append("缺少车座") # 检查是否有脚踏 pedal = [k for k in elements if 'pedal' in k.lower()] if pedal: score += 20 checks.append("脚踏存在") else: checks.append("缺少脚踏") return score, checks if __name__ == "__main__": output_dir = "output" results = [] for filename in os.listdir(output_dir): if filename.endswith(".svg"): filepath = os.path.join(output_dir, filename) try: elements = parse_svg(filepath) score, checks = score_svg(elements) results.append((filename, score, checks)) except Exception as e: results.append((filename, 0, [f"解析失败: {str(e)}"])) results.sort(key=lambda x: x[1], reverse=True) for filename, score, checks in results: print(f"{filename}: {score}分") for check in checks: print(f" - {check}")

这个评分脚本比较粗糙,但能快速筛出明显有问题的生成结果。比如,如果连车轮都没有,那肯定是不合格的。如果车轮、身体、车把、车座、脚踏都齐了,至少说明模型在部件识别上过关了。

5. 常见翻车现场与排查思路

5.1 鹈鹕和自行车各画各的

这是最常见的翻车方式。模型生成的SVG里,鹈鹕是一个独立的group,自行车是另一个独立的group,两者没有任何空间上的交互。你打开图一看,鹈鹕飘在自行车旁边,或者自行车悬在鹈鹕头顶。

排查思路:检查SVG里有没有对鹈鹕和自行车元素做坐标对齐。如果两者的坐标系是独立的,那大概率就是各画各的。解决办法是在prompt里明确要求“鹈鹕的屁股坐标应该和车座坐标一致”,或者让模型先生成自行车的坐标,再基于自行车坐标生成鹈鹕。

5.2 部件张冠李戴

有的模型会把鹈鹕的嘴画成车把,或者把翅膀画成车架。这种错误说明模型在语义绑定上出了问题,它知道有这些部件,但不知道哪个部件属于哪个物体。

排查思路:检查SVG里元素的命名和分组。如果鹈鹕的嘴被放在了自行车的group里,那就是分组错了。解决办法是在prompt里强调“鹈鹕的嘴是鹈鹕的一部分,不要和自行车的部件混淆”。

5.3 接触点悬空或穿模

鹈鹕的脚蹼悬在脚踏上方几厘米,或者翅膀穿过了车把。这种错误说明模型没有接触点约束的概念。

排查思路:检查关键接触点的坐标是否一致。比如,脚蹼的y坐标应该和脚踏的y坐标相同。如果差太多,就是悬空;如果脚蹼的x坐标在脚踏的x坐标范围内,但y坐标重叠,就是穿模。解决办法是在prompt里给出具体的坐标约束,比如“脚蹼的底部y坐标等于脚踏的顶部y坐标”。

5.4 姿态扭曲

鹈鹕的脖子扭了180度,或者翅膀反向折叠。这种错误说明模型没有关节约束的概念。

排查思路:检查鹈鹕各部件之间的相对角度。如果脖子和身体的夹角超过合理范围,就是姿态扭曲。解决办法是在prompt里描述合理的姿态,比如“鹈鹕的脖子向前伸展,与身体呈45度角”。

5.5 常见问题速查表

问题现象可能原因排查方法解决思路
鹈鹕和自行车分离坐标系独立检查两者坐标是否对齐prompt中明确坐标约束
部件张冠李戴语义绑定错误检查元素分组强调部件归属
接触点悬空缺少接触约束检查关键点坐标给出具体坐标等式
姿态扭曲缺少关节约束检查部件夹角描述合理姿态角度
遮挡错误z-order随机检查元素顺序指定遮挡关系
比例失调缺少尺寸约束检查元素大小给出相对尺寸比例

实操心得:排查的时候,建议先把SVG转成PNG,肉眼看一下。有些问题光看代码看不出来,但一看图就明白了。cairosvg这个库很好用,一行命令就能转:cairosvg input.svg -o output.png。

6. 从测试到应用:这个思路还能怎么用

6.1 扩展到其他空间推理任务

“鹈鹕骑自行车”只是一个切入点,同样的思路可以扩展到很多空间推理任务。比如:

  • 动物使用工具:猴子用石头砸坚果,大象用鼻子卷树枝
  • 人物交互:两个人握手,一个人递给另一个人东西
  • 复杂场景:桌子上放着杯子,杯子旁边有本书,书下面压着纸

这些任务的共同点是,都需要模型理解物体之间的空间关系和交互逻辑。你可以把它们做成一个测试集,系统性地评估模型的空间智能。

6.2 用于AI Agent的规划能力测试

如果你在做AI Agent,这个测试还能用来评估规划能力。比如,让Agent规划一个“画鹈鹕骑自行车”的任务,看它能不能拆解成合理的步骤:先画自行车,再画鹈鹕,然后调整鹈鹕的姿态,最后检查接触点。如果Agent的规划步骤混乱,那它的空间推理能力大概率也不行。

我试过让一个Agent做这个任务,它的规划是“先画鹈鹕,再画自行车,然后把自行车放在鹈鹕下面”。这个规划明显有问题,因为“放在下面”没有考虑接触点。后来我调整了prompt,要求它“先确定车座位置,再确定鹈鹕屁股位置”,它的规划就合理多了。

6.3 结合SVG做可视化调试

SVG的好处是可编辑、可调试。你可以把模型生成的SVG导入到矢量编辑工具里,手动调整元素位置,看看怎么改才能让画面合理。这个过程本身就是在逆向理解模型的空间推理逻辑。

我经常这么做:把生成的SVG打开,把鹈鹕和自行车分开,然后手动对齐接触点,看看需要移动多少距离。这个距离往往就是模型空间推理的误差量。如果误差很大,说明模型的空间感知很粗糙;如果误差很小,说明模型只是差一点点微调。

6.4 作为大模型微调的评测指标

如果你在做大模型微调,这个测试可以作为一个轻量级的评测指标。你不需要标注大量数据,只需要准备几十个类似“鹈鹕骑自行车”的prompt,然后写个脚本自动评分。微调前后各跑一遍,就能看出空间推理能力有没有提升。

我试过用这个指标评估一个微调后的模型,发现它在“部件识别”上提升了,但在“接触点判断”上反而下降了。这说明微调数据里可能缺少接触点相关的样本。后来我补充了一些接触点标注数据,再微调,两个指标都上去了。

7. 一些实操中的体会

这个测试我断断续续跑了小半年,积累了一些零散的经验,这里一并分享一下。

第一,prompt的写法很关键。如果你只写“画一只鹈鹕骑自行车”,模型大概率会自由发挥,结果不可控。如果你把空间关系写清楚,比如“鹈鹕的屁股坐在车座上,翅膀握住车把,脚蹼踩在脚踏上”,模型的生成质量会明显提升。这说明模型不是不能理解空间关系,而是需要明确的指令。

第二,不同模型的失败模式不一样。有的模型擅长画鹈鹕,但自行车画得稀烂;有的模型自行车画得很标准,但鹈鹕像个气球。了解每个模型的强弱项,有助于你针对性地设计prompt。比如,对于鹈鹕画得好的模型,你可以多给一些自行车的结构描述;对于自行车画得好的模型,你可以多给一些鹈鹕的姿态描述。

第三,SVG比像素图更适合做自动化评测。像素图的评估太主观,而且需要大量人工。SVG是结构化的,你可以写脚本自动检查元素的位置、大小、顺序。虽然SVG的生成难度更高,但一旦跑通,评测效率会高很多。

第四,不要指望一次就能画对。我跑了上百次,真正完全正确的不到10%。大部分结果都是“差不多但有点问题”。这说明空间推理对大模型来说仍然是个难题。但换个角度想,这也意味着这个领域还有很大的提升空间,值得投入精力去研究。

第五,这个测试可以做得更细。比如,你可以把“鹈鹕骑自行车”拆解成多个子任务:先画自行车,再画鹈鹕,再调整姿态,再检查接触点。每个子任务单独评测,就能更精确地定位模型的短板。我试过这种拆解方式,发现模型在“画自行车”这个子任务上表现最好,在“调整姿态”上表现最差。

第六,别忘了保存每次的生成结果。我一开始没注意,跑完就删了,后来想对比不同版本的表现,发现没数据了。现在我会把每次的SVG、PNG、日志都保存下来,按时间戳命名。这样过一段时间回头看,能清楚地看到模型的进步轨迹。

第七,可以结合其他评测维度。空间推理只是大模型能力的一个方面,你还可以结合语义理解、指令遵循、创造力等维度一起评测。比如,让模型画“鹈鹕骑自行车,背景是夕阳下的海滩”,就能同时测试空间推理和场景构建能力。

第八,这个测试很适合做教学案例。我带过几个新人,让他们用这个题目练手,效果很好。因为它足够简单,谁都能上手;又足够深刻,能引出很多空间推理的问题。新人通过这个案例,能快速理解大模型的能力边界和评测方法。

第九,不要被失败案例打击到。看到模型画出一堆乱七八糟的东西,确实容易让人沮丧。但换个角度想,这些失败案例恰恰是最有价值的数据。它们告诉你模型在哪里不行,为什么不行,怎么才能让它行。做AI测试开发,最重要的能力就是从失败中找规律。

第十,保持好奇心。这个测试最开始只是我随手一试,没想到后来成了我的一个固定评测项目。很多时候,最有价值的发现就藏在那些看似“整活”的想法里。你永远不知道哪个小测试会变成大发现,所以多试、多玩、多折腾,总没错。

最后再分享一个小技巧:如果你想让模型生成更合理的SVG,可以在prompt里加一句“请确保所有元素都在viewBox范围内,且接触点坐标一致”。这句话能显著减少元素溢出和接触点悬空的问题。我试过,加了这句话之后,合格率大概能提升20%左右。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询