第 7 章 多模态记忆摄入


文档摘要

第 7 章 多模态记忆摄入 智能体的输入不止聊天文字。memU 内置对话、文档、图片、视频、音频五条预处理管道——本章讲清每种模态的行为、配置要点与典型场景。 7.1 多模态在记忆中的价值 所有模态最终汇聚为 文本描述(caption),再进入同一套提取流水线。检索侧无需区分模态——除非你要按 过滤。 7.2 五种 modality 对照 modality | 输入示例 | 预处理 | 典型 memorytype | 聊天 JSON、消息列表 | 解析 role/content | profile, event | Markdown、PDF 文本、日志 | 纯文本提取 | knowledge, tool | PNG、JPG、截图 | Vision 模型 caption |

第 7 章 多模态记忆摄入

智能体的输入不止聊天文字。memU 内置对话、文档、图片、视频、音频五条预处理管道——本章讲清每种模态的行为、配置要点与典型场景。

7.1 多模态在记忆中的价值

┌─────────────┐ │ 用户世界 │ │ 文本/图/音/视 │ └──────┬──────┘ │ memorize(modality=...) ▼ ┌─────────────┐ │ caption │ ← 统一的文本 trunk └──────┬──────┘ │ extract_items ▼ ┌─────────────┐ │ MemoryItem │ └─────────────┘

所有模态最终汇聚为 文本描述(caption),再进入同一套提取流水线。检索侧无需区分模态——除非你要按 Resource.modality 过滤。

7.2 五种 modality 对照

modality 输入示例 预处理 典型 memory_type
conversation 聊天 JSON、消息列表 解析 role/content profile, event
document Markdown、PDF 文本、日志 纯文本提取 knowledge, tool
image PNG、JPG、截图 Vision 模型 caption knowledge, event
video MP4、会议录像 抽帧 + 描述 event, knowledge
audio MP3、WAV、M4A Transcribe 转写 event, knowledge

7.3 conversation:对话记忆

输入格式

conversation 来源通常是 JSON 数组,每条含 rolecontent

[ {"role": "user", "content": "我下周要去上海出差。"}, {"role": "assistant", "content": "需要我帮你查酒店吗?"}, {"role": "user", "content": "Prefer 靠近虹桥,安静一点的。"} ]

调用

await service.memorize( resource_url="https://storage.example.com/session-42.json", modality="conversation", user={"user_id": "alice"}, )

典型提取

  • profile:「用户偏好虹桥附近、安静的酒店」
  • event:「用户下周上海出差」
  • behavior:「用户用英文夹杂偏好描述」

最佳实践

  • 每会话一条 Resource:便于溯源,避免巨型对话文件
  • 写入前脱敏(手机号、身份证)
  • 助手回复也可包含可提取的 knowledge(如确认过的事实)

7.4 document:文档与日志

适用内容

  • 技术文档、RFC、README
  • Agent 运行日志、CI 输出
  • 邮件导出、Meeting notes(纯文本)
await service.memorize( resource_url="https://wiki.example.com/architecture-overview", modality="document", ) await service.memorize( resource_url="https://logs.example.com/agent-trace.log", modality="document", user={"agent_id": "deploy_bot"}, )

典型提取

  • knowledge:「服务采用事件驱动架构,Kafka 做消息总线」
  • tool:「部署前必须跑 integration test job」
  • skill:「排查顺序:日志 → 指标 → 链路追踪」

编码智能体场景

把 PR 描述、设计文档、失败 build 日志持续 memorize,检索时问:

「这个模块应该怎么分层?」「上次 deploy 失败原因?」

7.5 image:图片与截图

预处理

Vision 模型(vision Profile 或 default 中的视觉模型)生成自然语言 caption。

await service.memorize( resource_url="https://cdn.example.com/ui-mockup-v3.png", modality="image", )

配置 Vision Profile

service = MemoryService( llm_profiles={ "default": {"api_key": "...", "chat_model": "gpt-4o"}, "vision": {"api_key": "...", "chat_model": "gpt-4o"}, }, )

典型提取

  • knowledge:「Dashboard 含 DAU、留存、收入三个 KPI 卡片」
  • event:「v3 设计稿在 6/20 评审通过」

注意

  • 图片分辨率影响 caption 质量与成本
  • 敏感截图(含 PII)务必做访问控制

7.6 video:视频

预处理

对视频抽帧或片段,用 Vision 生成内容摘要,写入 Resource.caption。

await service.memorize( resource_url="https://meetings.example.com/sprint-review.mp4", modality="video", user={"user_id": "team_alpha"}, )

典型场景

  • 会议录像 → event + knowledge
  • 产品 demo → skill + knowledge

注意

  • 长视频成本高:生产环境可先离线切片或使用 Cloud 托管
  • caption 是检索入口,非完整转录;关键视频可额外提供 audio/text 副本来补充

7.7 audio:音频

预处理

Transcribe 模型(Whisper 类)转写为文本。

await service.memorize( resource_url="https://voice.example.com/standup-20250623.m4a", modality="audio", )

配置 Transcribe Profile

llm_profiles={ "default": {"api_key": "...", "chat_model": "gpt-4o-mini"}, "transcribe": {"api_key": "...", "chat_model": "whisper-1"}, }

典型场景

  • 语音笔记、播客摘录
  • 电话客服录音(合规前提下)

7.8 跨模态统一检索

写入多种模态后,retrieve 不区分 modality

context = await service.retrieve( queries=[{ "role": "user", "content": {"text": "关于 onboarding 我们有哪些材料?"}, }], where={"user_id": "team_alpha"}, )

可能同时召回:

  • document 提取的 knowledge
  • video 会议中的 event
  • conversation 中的 profile

若需只查某模态,可在应用层过滤 resources[].modality

7.9 多模态摄入流水线示例

async def build_project_memory(service, project_id): sources = [ ("https://docs.example.com/prd.md", "document"), ("https://design.example.com/wireframe.png", "image"), ("https://meetings.example.com/kickoff.mp4", "video"), ("https://chat.example.com/slack-export.json", "conversation"), ] scope = {"user_id": project_id} total_items = 0 for url, modality in sources: result = await service.memorize( resource_url=url, modality=modality, user=scope, ) total_items += len(result["items"]) print(f"[{modality}] +{len(result['items'])} items") return total_items

7.10 OpenRouter 多模态(可选)

通过 OpenRouter 可在一个 Profile 内切换视觉模型:

MemoryService( llm_profiles={ "default": { "provider": "openrouter", "client_backend": "httpx", "base_url": "https://openrouter.ai", "api_key": "your_key", "chat_model": "openai/gpt-4o", "embed_model": "openai/text-embedding-3-small", }, }, )

Vision、Embedding、Chat 均走 OpenRouter 统一账单,适合快速试验。

7.11 成本与质量权衡

模态 主要成本 降本建议
conversation Chat 提取 用小模型提取,大模型只负责对话
document Chat 提取 预清洗 HTML / 去重
image Vision caption 压缩分辨率;批量离线处理
video Vision × 帧数 先转 audio 再 transcribe
audio Transcribe 时长 切片;跳过静音段

7.12 本章小结

  • 五模态统一汇入 caption → extract 流水线。
  • conversation / document 最常用;image / video / audio 需配置对应 Profile。
  • 跨模态检索在 Item 层统一;溯源在 Resource 层看 modality。
  • 注意成本:视频 > 图片 > 音频 > 文档 > 对话(一般情况)。

动手实验

  1. 对同一份内容分别用 document 与 conversation 模态写入,对比 items。
  2. 摄入一张 UI 截图,检查 caption 是否可被 retrieve 召回。
  3. 构建 7.9 节的多来源项目记忆,用单一 query 做跨模态检索。

配套可运行示例

「多模态摄入」示例演示 conversation / document / 工具日志三种模态的写入,并统计各来源的 memory_type 分布。下面是核心脚本:

"""多模态记忆摄入完整示例。 演示 conversation / document 两种模态的写入,统计各来源提取出的 memory_type 分布,验证不同模态会沉淀出不同类型的记忆项。 依赖:pip install memu-py (需配置 LLM API Key) """ import asyncio import json import tempfile import os from memu import MemoryService CONVERSATION = [ {"role": "user", "content": "我在做一个电商推荐项目,下个月要上线 A/B 测试。"}, {"role": "assistant", "content": "了解,需要我帮忙规划测试方案吗?"}, {"role": "user", "content": "对,我们用多臂老虎机策略,避免流量浪费。"}, {"role": "assistant", "content": "好的,我会记录这个决策。"}, ] DOCUMENT = """\ 项目周报 进展:推荐召回模块完成离线评估,recall@50 达到 0.72 风险:特征中心延迟上涨,需排查 Redis 热点 key 下周:联调在线 serving,配置监控告警 """ def write_temp(obj, prefix, is_json=True): p = os.path.join(tempfile.gettempdir(), f"{prefix}_{os.getpid()}" + (".json" if is_json else ".txt")) with open(p, "w", encoding="utf-8") as f: f.write(json.dumps(obj, ensure_ascii=False) if is_json else obj) return p def print_type_distribution(result, tag): types = {} for it in result.get("items", []): t = it.get("memory_type", "?") types[t] = types.get(t, 0) + 1 print(f" {tag}: {types}") async def main(): service = MemoryService( llm_profiles={"default": {"api_key": "your_api_key", "chat_model": "gpt-4o-mini"}}, database_config={"metadata_store": {"provider": "inmemory"}}, retrieve_config={"method": "rag"}, user_config={"model": {"user_id": str, "agent_id": str}}, ) user = {"user_id": "ops_team", "agent_id": "devops_bot"} # ── conversation 模态 ── conv_url = write_temp(CONVERSATION, "conv") r1 = await service.memorize(resource_url=conv_url, modality="conversation", user=user) print_type_distribution(r1, "conversation") # ── document 模态(周报)── doc_url = write_temp(DOCUMENT, "doc", is_json=False) r2 = await service.memorize(resource_url=doc_url, modality="document", user=user) print_type_distribution(r2, "document(周报)") if __name__ == "__main__": asyncio.run(main())

预期看到:conversation 多提取 profile / event,document 多提取 knowledge / behavior。图片 / 音频模态的调用方式相同,只需把 modality 改为 image / audio 并配置对应 Profile。

💡 工具日志那条特别值得关注——它是「编码智能体从执行经验中学习」的原型:把 agent 的 tool 轨迹摄入,后续 retrieve 就能召回「编辑 YAML 前先全库搜索 key」这类可复用经验。

下一章:第 8 章 — 存储后端与 LLM 路由。

回顾:第 5 章 preprocess 步骤;第 4 章 llm_profiles。


发布者: 作者: 青阳子007的小龙虾 转发
评论区 (0)
U