乐于分享
好东西不私藏

Hermes Agent 源码-多模态处理

Hermes Agent 源码-多模态处理

Hermes Agent 源码解析

第 10 讲:多模态处理——视觉分析、TTS 语音合成、语音模式、图像/视频生成与 STT 转写

基于 Hermes Agent v0.16.0 源码 · 2026-06-23

一、多模态:从"纯文本 Agent"到"全感官 AI"

前九讲我们覆盖了 Hermes 从核心架构到 MCP 集成的全部关键路径。这一讲我们进入 Hermes 的多模态能力矩阵——一个让 Agent 不仅能"读"和"写",还能"看"、"说"、"听"、"画"、"拍"的完整系统。

Hermes 的多模态架构遵循同一设计哲学:统一工具入口 + 插件化 Provider + 路由自动决策。用户无需关心底层用的是 Edge TTS 还是 ElevenLabs、FAL 还是 OpenAI 图像生成——配置一次,Agent 自动路由。

📦 本讲核心文件

tools/vision_tools.py(1591 行)— 视觉分析主模块

tools/tts_tool.py(2731 行)— TTS 语音合成引擎

tools/voice_mode.py(1218 行)— 语音模式(Push-to-talk)

tools/transcription_tools.py(1798 行)— STT 语音转文字

tools/image_generation_tool.py(1180 行)— 图像生成

tools/video_generation_tool.py(562 行)— 视频生成

agent/image_routing.py(540 行)— 图像输入路由决策

agent/image_gen_provider.py(324 行)— 图像生成 Provider ABC

agent/video_gen_provider.py(299 行)— 视频生成 Provider ABC

agent/tool_dispatch_helpers.py(417 行)— 多模态信封处理

plugins/image_gen/ — FAL/OpenAI/xAI/Krea 图像后端

plugins/video_gen/ — FAL/xAI 视频后端

二、多模态能力全景图

Hermes 的多模态系统可以抽象为五大能力维度,每个维度都有完整的 Provider 抽象层:

能力 输入 输出 工具名 Provider
视觉分析 图片 URL 或本地文件 文本描述 vision_analyze 自动路由 (aux LLM)
语音合成 (TTS) 文本 音频文件 (mp3/ogg) text_to_speech Edge/ElevenLabs/OpenAI/MiniMax/xAI/Mistral/Gemini/NeuTTS/KittenTTS/Piper
语音转写 (STT) 音频文件 文本 (自动) 网关层触发 local/Groq/OpenAI/Mistral/xAI
图像生成 文本提示 + 可选参数 图片 URL 或文件路径 image_generate FAL/OpenAI/xAI/Krea
视频生成 文本提示 + 可选图片 视频 URL 或文件 video_generate FAL/xAI
语音模式 (VUI) 麦克风输入 对话循环 /voice 命令 音频 Recorder

这五大能力并非孤立——它们通过网关层自动触发(STT 转写语音消息)、工具链串联(vision_analyze + image_generate 实现图片风格迁移)、多模态信封(native vision fast path)深度整合。

三、视觉分析系统:Native Vision Fast Path

1. 双路径架构:辅助 LLM vs Native 直连

vision_tools.py 实现了两条并行的视觉分析路径,由 _should_use_native_vision_fast_path() 自动决策:

视觉分析双路径:

用户发送图片
     |
     v
+-------------------+
| _should_use_      |  决策条件:
| native_vision_    |  1. image_input_mode == "native"
| fast_path()?      |  2. provider 支持 tool_result 中的 image
+--------+----------+  3. 或 model.supports_vision 显式开启
         |
    +----+----+
    |           |
   YES         NO
    |           |
    v           v
+------------+  +------------------+
| Native     |  | Auxiliary LLM    |
| Fast Path  |  | (Gemini 3 Flash  |
|            |  |  Preview via     |
| 下载图片   |  |  OpenRouter)     |
| base64编码 |  |                  |
| 尺寸压缩   |  | 下载图片 -> 分析 |
| 构建信封   |  | -> 返回文本描述  |
| 返回给主   |  |                  |
| 模型直看   |  +--------+---------+
+------------+             |
         |                 v
         +--------+ 文本描述注入对话
                  v
         主模型看到图片像素

关键设计:Native fast path 不消耗额外的 LLM 调用——图片直接作为 tool result 的多模态信封返回,主模型在下一轮直接"看到"像素。辅助 LLM 路径则多一次 API 调用(用 Gemini 3 Flash Preview 做描述),适合非视觉模型。

2. 多模态信封(Multimodal Envelope)

Native fast path 的核心数据结构是 _multimodal 信封——一个特殊的 dict,被 tool_dispatch_helpers.py 中的 _is_multimodal_tool_result() 识别:

📄 多模态信封结构

{
    "_multimodal": True,
    "content": [
        {"type": "text", "text": "Image loaded into your context..."},
        {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
    ],
    "text_summary": "Image attached natively for the main model (128.5 KB)...",
    "meta": {
        "image_url": "https://example.com/image.jpg",
        "size_bytes": 131584,
        "native_vision": True
    }
}

Agent 循环在收到信封后,将其解包为 OpenAI 风格的 content list,再由 Provider 适配器(Anthropic/Codex/OpenAI/Gemini)翻译为各自的多模态格式。这意味着同一个信封可以适配所有主流 Provider

3. 图像安全与尺寸控制

视觉系统有三层安全保护

1. SSRF 防护:下载前校验 URL 是否为内网地址,下载中每个 redirect 重新校验(_ssrf_redirect_guard

2. 下载大小限制:硬上限 50 MB(_VISION_MAX_DOWNLOAD_BYTES),Content-Length 预检 + 实际 body 二次校验

3. 嵌入大小限制:主动压缩至 4 MB(_EMBED_TARGET_BYTES),像素维度限制 7900px(_EMBED_MAX_DIMENSION),防止 Anthropic 5 MB / 8000px 限制导致会话永久卡死

自动缩放策略:Pillow 渐进式缩小(最多 5 轮),JPEG 质量阶梯(85→70→50),PNG 仅缩放。硬上限 20 MB(_MAX_BASE64_BYTES)——超过此值直接拒绝,因为没有任何 Provider 能接受。

4. 图像输入路由决策器

agent/image_routing.pydecide_image_input_mode() 在每轮对话开始时决定用户附加图片的处理方式:

📄 路由决策逻辑(auto 模式)

decide_image_input_mode(provider, model, cfg):
    # 1. 用户显式配置 auxiliary.vision.provider?
    if cfg.auxiliary.vision.provider != "auto" and != "":
        return "text"   # 用户选了特定 vision 后端,走文本路径

    # 2. 主模型 supports_vision=True?
    if model.metadata.supports_vision:
        return "native"  # 直接嵌入图片

    # 3. 兜底
    return "text"        # 非视觉模型,走辅助 LLM 描述

extract_image_refs() 函数从用户消息中自动提取图片引用:本地路径(~// 开头)和 HTTP(S) URL。智能跳过代码块内的引用,避免误触发。

四、TTS 语音合成:10+ Provider 的统一接口

1. Provider 矩阵与分级策略

tts_tool.py 是 Hermes 最大的单文件之一(2731 行),支撑了 10 个内置 Provider + 任意自定义 Command Provider

TTS Provider 分级:

┌─────────────────────────────────────────────────────┐
│  免费/本地(无需 API Key)                           │
│  Edge TTS  ── Microsoft Edge 神经语音(默认)        │
│  NeuTTS    ── 本地设备 TTS                           │
│  KittenTTS ── 25MB 本地模型                          │
│  Piper     ── OHF-Voice 神经 VITS, 44 语言           │
├─────────────────────────────────────────────────────┤
│  云端付费(需 API Key)                              │
│  ElevenLabs ── 高质量, 流式, 声音克隆                │
│  OpenAI    ── gpt-4o-mini-tts                       │
│  MiniMax   ── speech-02-hd, 声音克隆                 │
│  xAI       ── Grok 语音                             │
│  Mistral   ── Voxtral TTS, 多语言, 原生 Opus         │
│  Gemini    ── gemini-2.5-flash-preview-tts           │
├─────────────────────────────────────────────────────┤
│  自定义 Command Provider(type: command)            │
│  任意 shell 命令: Piper CLI, Kokoro, 自定义脚本等    │
└─────────────────────────────────────────────────────┘

2. 分发链与优先级

text_to_speech_tool() 的分发链严格遵循优先级顺序:

📄 TTS 分发链

text_to_speech_tool(text, output_path):
    1. 加载 config.yaml 的 tts: section
    2. 解析 provider 名称
    3. 优先级分发:
       a) Command Provider (tts.providers.<name>.type == command)
       b) Plugin Provider (注册的 TTSProvider 插件)
       c) 内置 Provider elif 链:
          elevenlabs -> openai -> minimax -> xai -> mistral
          -> gemini -> neutts -> kittentts -> piper
       d) 兜底: Edge TTS (免费, 无需 Key)
    4. 输出格式:
       Telegram -> .ogg (Opus, 原生语音气泡)
       其他平台 -> .mp3
    5. 路径安全: 拒绝 '..' 遍历组件

3. 每 Provider 输入长度限制

不同 Provider 有各自的输入字符上限,PROVIDER_MAX_TEXT_LENGTH 表精确映射:

Provider  最大字符数    说明
─────────────────────────────────────────────
edge      5,000       实际同步限制
openai    4,096       OpenAI 官方限制
xai       15,000      xAI Grok 限制
minimax   10,000      MiniMax 同步 API
mistral   4,000       保守估计
gemini    32,000      32k token 上下文窗口
elevenlabs 10,000     默认 (模型感知: 5k~40k)
neutts    2,000       本地模型, 长文本质量下降
kittentts 2,000       25MB 本地模型
piper     5,000       本地 VITS, 音素级上限

ElevenLabs 额外有模型感知上限eleven_flash_v2_5 支持 40k 字符,而 eleven_v3 仅 5k——_resolve_max_text_length() 自动根据配置的 model_id 选择正确上限。

五、语音模式(Voice Mode):Push-to-talk 全双工对话

1. 音频环境自动检测

voice_mode.pydetect_audio_environment() 函数在启动前执行全面的环境探测,覆盖 5 种部署场景:

1. SSH 远程:检测 SSH_CLIENT/SSH_TTY 环境变量,检查 PulseAudio/PipeWire socket 是否可达

2. Docker/Podman 容器:检测容器环境,检查 host audio forwarding 配置

3. WSL:读取 /proc/version 检测 Microsoft 内核,检查 PULSE_SERVER 桥接

4. Termux(Android):检测 termux-microphone-record 命令和 Termux:API 应用

5. 本地桌面:直接检测 PortAudio 设备

返回结构:{"available": bool, "warnings": [...], "notices": [...]}——warnings 是硬失败(阻断语音模式),notices 是信息提示(不阻断)。

2. 录音引擎与静音检测

AudioRecorder 类实现了线程安全的录音引擎,核心参数:

📄 录音参数

SAMPLE_RATE            = 16000     # Whisper 原生采样率
CHANNELS               = 1         # 单声道
DTYPE                  = "int16"   # 16-bit PCM
SILENCE_RMS_THRESHOLD  = 200       # RMS < 200 = 静音
SILENCE_DURATION       = 3.0s      # 连续静音 3 秒自动停止
MAX_WAIT               = 15.0s     # 等待说话最长 15 秒
MIN_SPEECH_DURATION    = 0.3s      # 最短有效语音 0.3 秒
MAX_DIP_TOLERANCE      = 0.3s      # 说话间隙容忍度

静音检测算法支持说话恢复:用户在静音期间重新开始说话(RMS 回升超过阈值),录音不会停止——这避免了因短暂停顿导致的截断。Termux 后端(TermuxAudioRecorder)不支持静音自动停止,因为 Termux:API 不暴露实时音频回调。

3. 音频提示音

play_beep() 函数生成正弦波提示音(默认 880 Hz = A5),带淡入淡出防点击伪影。播放使用轮询代替 sd.wait()(2 秒超时上限),防止音频设备挂起时永久阻塞。

六、STT 语音转写:网关层的透明转写

1. Provider 架构

transcription_tools.py 的 STT 系统与 TTS 对称——6 个内置 Provider + Command Provider 扩展:

STT Provider 矩阵:

+-----------+-----------+---------------------------+
| Provider  | 费用      | 说明                      |
+-----------+-----------+---------------------------+
| local     | 免费      | faster-whisper, ~150MB    |
|           |           | 首次使用自动下载模型       |
+-----------+-----------+---------------------------+
| local_cmd | 免费      | 自定义 whisper CLI 命令    |
+-----------+-----------+---------------------------+
| groq      | 免费层级  | Groq Whisper API          |
+-----------+-----------+---------------------------+
| openai    | 付费      | OpenAI Whisper API        |
+-----------+-----------+---------------------------+
| mistral   | 付费      | Voxtral Transcribe API    |
+-----------+-----------+---------------------------+
| xai       | 付费      | Grok STT, 21 语言,        |
|           |           | ITN, 说话人分离            |
+-----------+-----------+---------------------------+
| elevenlabs| 付费      | ElevenLabs Scribe API     |
+-----------+-----------+---------------------------+

2. 网关层透明触发

STT 的最大特点是用户不可见——当用户在 Telegram/Discord/WhatsApp/Slack/Signal 发送语音消息时,网关层自动调用 transcribe_audio(),将转写文本注入对话。支持的输入格式:

.mp3 .mp4 .mpeg .mpga .m4a .wav .webm .ogg .aac .flac

文件大小上限 25 MB。本地 Provider 原生支持 .wav/.aiff/.aif,其他格式需要 ffmpeg 转码。

3. 模型名称自动校正

_normalize_local_model() 函数处理一个常见陷阱:用户可能将 OpenAI 模型名(如 whisper-1)误用于本地 Provider。函数自动检测并映射到有效的 faster-whisper 模型名(tiny/base/small/medium/large-v3),同时发出警告日志。

七、图像与视频生成:插件化 Provider 架构

1. 图像生成 Provider ABC

agent/image_gen_provider.py 定义了 ImageGenProvider 抽象基类,与视频生成共享同一设计模式:

📄 ImageGenProvider ABC 接口

class ImageGenProvider(abc.ABC):
    @property
    @abstractmethod
    def name(self) -> str:            # 唯一标识符 (fal/openai/xai)

    @property
    def display_name(self) -> str:    # 显示名称 (默认 name.title())

    def is_available(self) -> bool:   # API Key 是否配置

    def list_models(self) -> List[Dict]:  # 模型目录

    def get_setup_schema(self) -> Dict:   # hermes tools 配置界面

    def default_model(self) -> str:       # 默认模型

    @abstractmethod
    def generate(self, prompt, aspect_ratio, **kwargs) -> Dict:
        # 返回 success_response() 或 error_response()
        # 响应键: success, image, model, prompt, aspect_ratio, provider

注册通过 PluginContext.register_image_gen_provider() 完成,活跃 Provider 由 image_gen.provider 配置选择。内置插件:FAL(5 个模型)、OpenAI、OpenAI Codex、xAI、Krea。

2. FAL 模型目录与参数映射

image_generation_tool.py 维护了一个 FAL_MODELS 目录,每个模型声明:

size_style:尺寸规范家族(image_size_preset/aspect_ratio/gpt_literal

supports:参数白名单——不在白名单的键在提交前被剥离,防止模型拒绝未知参数

upscale:是否链式调用 Clarity Upscaler(仅 FLUX 2 Pro 开启,其他模型关闭以避免延迟)

_build_fal_payload() 函数将统一输入(prompt + aspect_ratio)翻译为模型原生 payload,确保每个模型只收到它支持的参数。

3. 视频生成统一表面

video_generation_tool.py 设计更简洁——单一 video_generate 工具覆盖 text-to-video 和 image-to-video:

📄 video_generate 工具 Schema

video_generate(
    prompt: str,                    # 必需: 文本指令
    operation: str,                # "generate" | "edit" | "extend"
    image_url: str,                # 提供则走 image-to-video
    video_url: str,                # edit/extend 的源视频
    reference_image_urls: List[str], # 风格/角色参考图
    duration: int,                 # 秒数 (Provider 钳制)
    aspect_ratio: str,             # "16:9" | "9:16" | "1:1" | ...
    resolution: str,              # "480p" | "540p" | "720p" | "1080p"
    negative_prompt: str,          # 可选 (Pixverse/Kling)
    audio: bool,                   # 可选 (Veo3/Pixverse)
    seed: int,                     # 可选
    model: str,                    # 可选, 覆盖默认模型
)

工具层只做轻量验证(类型/必需参数),每个 Provider 在 generate() 内部做自己的钳制——这保持了工具表面的稳定性,新 Provider 可以有不同的能力集。

八、多模态系统的工程亮点

1. 懒加载与零冷启动惩罚

所有多模态模块使用延迟导入策略:

音频库sounddevice/numpy_import_audio() 中懒加载,避免无声卡环境(SSH/Docker/WSL)崩溃

FAL 客户端fal_client_load_fal_client() 中懒加载,节省 ~64ms 冷启动时间

Provider SDK:ElevenLabs、Mistral 等通过 tools.lazy_deps.ensure() 按需安装

2. 会话安全:图片不卡死对话

这是 Hermes 最精妙的设计之一。当一张大图被嵌入对话历史后,它在每轮后续请求中都会被重新发送——如果这张图超过了 Provider 的限制(Anthropic 5 MB / 8000px),会话就永久卡死,因为历史不可变,重试也无法清除已嵌入的字节。

解决方案:主动嵌入上限(4 MB / 7900px)在图片首次嵌入时就执行压缩,而非等到 API 报错。这与 conversation_compression.py 中的失败后缩小目标一致,确保主动和被动路径行为一致。

3. 并行安全

tool_dispatch_helpers.py 的并行规则引擎将 vision_analyze 列入 _PARALLEL_SAFE_TOOLS——多个视觉分析可以并发执行,因为它们无共享可变状态。TTS/图像/视频生成不在白名单中(有副作用:写文件),所以串行执行。

4. 错误分类与自动恢复

agent/error_classifier.py 中的多模态错误分类:

image_too_large:Provider 返回 400 含 "image exceeds" / "image too large" → 缩小图片重试

multimodal_tool_content_unsupported:Provider 拒绝 tool message 中的 list 类型内容(如 Xiaomi MiMo)→ 降级为纯文本

错误恢复策略针对每种多模态错误类型定制,而非通用重试。

九、配置指南

📄 config.yaml 多模态配置示例

# 视觉分析
agent:
  image_input_mode: auto           # auto | native | text

auxiliary:
  vision:
    provider: auto                 # 辅助视觉 LLM
    timeout: 60
    download_timeout: 30           # 图片下载超时

# TTS 语音合成
tts:
  provider: elevenlabs
  elevenlabs:
    model_id: eleven_flash_v2_5
    voice_id: pNInz6obpgDQGcFmaJgB

# STT 语音转写
stt:
  enabled: true
  provider: local
  local:
    model: base
    language: en

# 图像生成
image_gen:
  provider: fal
  model: fal-ai/flux-2/klein/9b

# 视频生成
video_gen:
  provider: xai
  model: grok-2-video

# 自定义 TTS Command Provider
tts:
  providers:
    my-piper:
      type: command
      command: "piper -m ~/models/en-us.onnx -f {output_path} < {input_path}"
      output_format: wav

所有多模态配置均可通过 hermes tools 交互式设置——不需要手动编辑 config.yaml。

十、总结与预告

本讲完成了 Hermes 多模态系统的全景分析。关键设计模式:

1. 统一工具入口——用户只调用 vision_analyze/text_to_speech/image_generate/video_generate,不关心后端

2. Provider 抽象层——ImageGenProvider/VideoGenProvider/TTSProvider/TranscriptionProvider ABC + 注册表

3. 自动路由——image_input_mode 自动决策 native vs text,STT 网关层透明触发

4. 安全优先——SSRF 防护、尺寸限制、路径遍历检查、会话不卡死保证

5. 懒加载——零冷启动惩罚,按需安装依赖

下一讲(第 11 讲)我们将深入 ACP 协议与子代理委托——Hermes 如何通过 Agent Communication Protocol 实现代理间的协作与任务委派。