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.py 的 decide_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.py 的 detect_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 实现代理间的协作与任务委派。
夜雨聆风