AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

模态路由、时间对齐与压缩

我的学习 →

正在读取学习记录…

多模态 Agent 接收文字、图片和声音。模态是信息的表现形式:订单截图提供表格位置,声音提供“查这张订单”的请求。模态路由负责选择哪些输入交给哪个处理器;时间对齐负责把同一时刻的信息配对;压缩负责留下足以完成任务的材料。

观看录屏时,人在 1080 毫秒说“查这张订单”,画面在 1000 毫秒切到订单表。两者相差 80 毫秒,允许误差 120 毫秒时可配成一组。像会议纪要需要把“这一页”对应到当时展示的幻灯片:若把话配到一分钟前的封面,句子虽然没有错,指代却错了。

从输入到可核对结果

输入是人工给定的三条画面描述与一条语句时间戳。它们代表采集器与识别器已经产出的记录,程序实际执行最近时间匹配与记录压缩。

先用绝对时间差找到最近的一帧,再用 tolerance 检查是否接近。通过后保留原时间戳、画面描述、文字和gap_ms,这些字段帮助后续模型知道图文如何关联。真实视频的采样间隔、语音识别返回延迟和设备时钟差都会影响对齐:录制时刻与识别完成时刻要分开。OpenAI 图像输入文档说明模型图像输入的组织方式;处理器接收哪个模态是应用侧路由决策。

route 把任务名称映射到处理器和所需载荷,缺少任一必要模态就拒绝。音频、图像用人工材料标签表示,函数实际构造路由载荷;ASR与视觉模型在应用接入后消费这些载荷。

任务处理器需要的载荷缺材料时
转写录音ASRaudio音频片段停止提交
读图金额视觉image订单截图等待图片
解释这张图图文image与text指代语句补齐两种材料

压缩动作随任务选择。采样减少帧数,可能漏掉两帧之间的事件;裁剪保留目标区域,可能丢失位置与周边关系;摘要保留关键句,可能损失原词、语调或精确数字。需要逐字转写时保留音频,需要核对金额时保留原图文字区域,需要判断事件先后时保留时间戳。本例采用最近帧采样,并留下帧时间与语句作为对应依据。

画面时间画面内容到1080ms的距离选择结果
0ms封面1080ms舍弃
1000ms订单表80ms保留
2000ms物流图920ms舍弃
转写录音到ASR,读图到视觉,图文任务组合时间对齐载荷
上半部是任务到处理器与载荷的路由;图文分支再按时间对齐并采样保留最近帧。

保存并运行这两个文件

在 PowerShell 创建练习目录:

PowerShell
New-Item -ItemType Directory -Force l9-multimodal
cd l9-multimodal

macOS 终端:

Bash
mkdir -p l9-multimodal
cd l9-multimodal

把下面第一段完整代码保存为 align_lab.py,第二段保存为 test_align_lab.py,两份文件放在这个目录。

Python
TOLERANCE_MS = 120
FRAMES = [(0, "封面"), (1000, "订单表"), (2000, "物流图")]
UTTERANCE = (1080, "查这张订单")

def align(frames, utterance, tolerance):
    if tolerance < 0:
        raise ValueError("时间容差不能为负数")
    if not frames:
        return None
    # 时间戳单位统一为毫秒;用绝对距离选择最近画面。
    stamp, text = utterance
    nearest = min(frames, key=lambda frame: abs(frame[0] - stamp))
    distance = abs(nearest[0] - stamp)
    if distance > tolerance:
        return None
    # 压缩保留任务相关的一帧、原时间戳和语句,便于追溯。
    return {"route": "图文", "frame_ms": nearest[0],
            "frame": nearest[1], "text": text, "gap_ms": distance}


def route(task, *, image=None, audio=None, text=None):
    # 路由是应用规则:材料送往哪个处理器,并把缺少的模态及时返回。
    choices = {
        "转写录音": ("ASR", {"audio": audio}),
        "读图金额": ("视觉", {"image": image}),
        "解释这张图": ("图文", {"image": image, "text": text}),
    }
    if task not in choices:
        raise ValueError("未知任务")
    processor, payload = choices[task]
    if any(value is None or value == "" for value in payload.values()):
        raise ValueError("任务所需模态缺失")
    return {"processor": processor, "payload": payload}

if __name__ == "__main__":
    result = align(FRAMES, UTTERANCE, TOLERANCE_MS)
    if result is None:
        print("路由:等待更接近的画面")
    else:
        bundle = route("解释这张图", image=result["frame"], text=result["text"])
        print("路由:" + bundle["processor"])
        print("保留画面:" + result["frame"] + ",时间=" + str(result["frame_ms"]) + "ms")
        print("对齐误差:" + str(result["gap_ms"]) + "ms")
        print("压缩:3帧 → 1帧")
    audio_route = route("转写录音", audio="录音片段A")
    image_route = route("读图金额", image="订单截图A")
    print("录音任务 → " + audio_route["processor"] + ",载荷键=audio")
    print("读图任务 → " + image_route["processor"] + ",载荷键=image")
Python
import unittest
import align_lab as lab


class MechanismTests(unittest.TestCase):
    # 正常、边界和失败输入都由这里提供,直接运行即可。

    def test_normal(self):
        self.assertEqual(lab.align(lab.FRAMES, lab.UTTERANCE, 120)["frame"], "订单表")
        self.assertEqual(lab.route("转写录音", audio="片段")["processor"], "ASR")
        self.assertEqual(lab.route("读图金额", image="图")["payload"], {"image": "图"})
        self.assertEqual(lab.route("解释这张图", image="图", text="解释")["processor"], "图文")

    def test_boundary_tolerance(self):
        self.assertEqual(lab.align(lab.FRAMES, lab.UTTERANCE, 80)["gap_ms"], 80)
        self.assertIsNone(lab.align([], lab.UTTERANCE, 80))

    def test_failure(self):
        self.assertIsNone(lab.align(lab.FRAMES, lab.UTTERANCE, 79))
        with self.assertRaises(ValueError):
            lab.align(lab.FRAMES, lab.UTTERANCE, -1)
        with self.assertRaises(ValueError):
            lab.route("解释这张图", image="图")
        with self.assertRaises(ValueError):
            lab.route("未知任务", text="请求")


if __name__ == "__main__":
    unittest.main()

打开 PowerShell,逐行输入下列命令。cd 是进入文件夹,dir 是列出文件;确认列表里有刚保存的两个 .py 文件,再运行 Python。

Bash
# 在刚才创建的练习目录检查文件,再运行程序。
ls
python -X utf8 align_lab.py
python -X utf8 -m unittest -v test_align_lab.py

程序输出:

TXT
路由:图文
保留画面:订单表,时间=1000ms
对齐误差:80ms
压缩:3帧 → 1帧
录音任务 → ASR,载荷键=audio
读图任务 → 视觉,载荷键=image

测试末尾应显示 Ran 3 tests 与 OK。若找不到文件,检查当前目录与扩展名;若导入失败,确认测试和主程序在同一文件夹。测试描述的三组材料就在上方代码,不需要另外准备输入。

改一处,再看结果

输出显示路由为“图文”,保留订单表而不是封面或物流图。压缩从三帧降到一帧,缩减的是本次请求材料,原始采集资料仍应按保存策略处理。压缩不能只看字节数:一句“这张图”如果失去对应画面,材料就失去了任务意义。

把 TOLERANCE_MS = 120 改成 TOLERANCE_MS = 79,保存并重跑。80 毫秒超出门限,图文任务输出“等待更接近的画面”;不需要这次画面的转写与读图任务仍各自显示处理器。恢复为 120 后重新运行。测试还给出正好等于80的边界,此时应通过。

模态选择要跟着任务:抄图片里的金额应保留文字分辨率,理解表格关系应保留行列结构,分析声调则需要音频而非仅转写文字。接入真实图像或语音服务时,检查个人资料是否需要上传,再按服务实际计费方式限制采样与请求数量。

改动后仍在刚才的 PowerShell 目录运行:

Bash
python -X utf8 align_lab.py

确认上述变化后,把参数恢复成原值,保存并再次执行这条命令。

把机制接回 Agent

采集层应为视频帧和音频段标注同一时钟下的时间戳,语音转写再带回对应音频段时间。路由层根据任务选择图像、音频或图文组合,对齐层输出有出处的材料包。下游模型回答时保留引用帧与时间,用户才能回到原记录核对指代。

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v1.0.1

选择题 · 先判断边界

关于“模态路由、时间对齐与压缩”,哪种做法更稳妥?

对应知识点:L9-multimodal

选择一个答案
练习 2 / 4 · v1.0.0

简答题 · 解释失败

写出一个失败输入并说明记录字段。

对应知识点:L9-multimodal

练习 3 / 4 · v1.0.0

代码题 · 边界测试

补充正常、边界、失败三组输入和预期结果。

对应知识点:L9-multimodal

练习 4 / 4 · v1.0.0

场景题 · 结果未知

证据不足时如何设计状态和人工接管?

对应知识点:L9-multimodal

图片查看器
100%

文章配图 · 按 Esc 关闭