乐于分享
好东西不私藏

Hermes Agent 源码-Gateway 与平台适配器

Hermes Agent 源码-Gateway 与平台适配器

Hermes Agent 源码解析

第 7 讲:Gateway 与平台适配器——20+ 平台如何共享同一个 Agent 内核

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

一、Gateway:Agent 的"通信中枢"

前六讲我们完成了全局架构地图、对话循环、工具系统、插件系统、Provider 抽象层、记忆与技能系统的深度解析。这一讲聚焦 Hermes 最庞大的子系统——Gateway,它让同一个 Agent 内核同时服务 Telegram、Discord、Slack、微信、飞书、钉钉、WhatsApp、Signal、Email、SMS 等 20+ 平台。

Gateway 不是一个简单的"消息转发器"——它是一个完整的通信运行时:管理多平台连接生命周期、会话隔离、流式响应分发、媒体处理、安全沙箱、跨平台镜像投递……每一个环节都有精密的设计。

📦 源码仓库

https://github.com/NousResearch/hermes-agent

本地源码:~/.hermes/hermes-agent/

本讲核心文件:

gateway/run.py(16292 行)— GatewayRunner 主循环

gateway/platforms/base.py(4884 行)— BasePlatformAdapter ABC

gateway/platform_registry.py(260 行)— 平台注册中心

gateway/config.py(2139 行)— 配置管理 + Platform 枚举

gateway/session.py(1444 行)— 会话管理

gateway/stream_events.py(171 行)— 结构化流事件

gateway/stream_dispatch.py(132 行)— 事件分发器

gateway/delivery.py(433 行)— 投递路由

gateway/mirror.py(168 行)— 跨平台镜像

gateway/platforms/telegram.py, discord.py, ...(20+ 平台适配器)

二、Platform 枚举:动态扩展的平台注册表

Platform 枚举是 Gateway 的"平台字典"。它有一个精妙的设计:内置平台硬编码 + 插件平台动态注册,通过 Python 枚举的 _missing_() 钩子实现无缝扩展。

📄 gateway/config.py (第 136-231 行)

class Platform(Enum):
    """Supported messaging platforms."""
    LOCAL = "local"
    TELEGRAM = "telegram"
    DISCORD = "discord"
    WHATSAPP = "whatsapp"
    SLACK = "slack"
    SIGNAL = "signal"
    MATRIX = "matrix"
    FEISHU = "feishu"
    WECOM = "wecom"
    WEIXIN = "weixin"
    QQBOT = "qqbot"
    DINGTALK = "dingtalk"
    # ... 共 20+ 内置平台

    @classmethod
    def _missing_(cls, value):
        """Accept unknown platform names only for known plugin adapters.

        Creates a pseudo-member cached in _value2member_map_ so that
        Platform("irc") is Platform("irc") holds True (identity-stable).
        """
        # 1. 检查缓存 (已创建的伪成员)
        if value in cls._value2member_map_:
            return cls._value2member_map_[value]

        # 2. 扫描内置插件目录 plugins/platforms/
        if value in _Platform__bundled_plugin_names:
            pseudo = object.__new__(cls)
            pseudo._value_ = value
            cls._value2member_map_[value] = pseudo
            return pseudo

        # 3. 运行时插件注册 (platform_registry)
        from gateway.platform_registry import platform_registry
        if platform_registry.is_registered(value):
            # 同上, 创建伪成员
            ...

        return None  # 拒绝任意字符串, 防止枚举污染

设计亮点:通过 _missing_() 钩子,Platform("irc") 可以正常工作——只要 IRC 适配器已注册。这避免了每次添加新平台都要修改核心枚举。同时 _scan_bundled_plugin_platforms() 扫描 plugins/platforms/ 目录,自动发现插件平台。

三、BasePlatformAdapter ABC:平台适配器的契约

所有平台适配器继承 BasePlatformAdapter。这个 ABC 定义了最小必要接口,同时提供了大量可选的默认实现——适配器只需覆盖它需要定制的部分。

1. 核心抽象方法(必须实现)

📄 gateway/platforms/base.py (第 1793-2277 行)

class BasePlatformAdapter(ABC):
    """Base class for platform adapters."""

    # ── 必须实现的抽象方法 ──
    @abstractmethod
    async def connect(self) -> bool:
        """连接到平台并开始接收消息。"""

    @abstractmethod
    async def disconnect(self) -> None:
        """断开连接。"""

    @abstractmethod
    async def send(
        self,
        chat_id: str,
        content: str,
        reply_to: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> SendResult:
        """发送消息到聊天。返回 SendResult(success, message_id, error)。"""

    # ── 可选覆盖的方法(有默认实现) ──
    async def edit_message(self, ...) -> SendResult:
        """编辑已发送消息。默认返回 success=False。"""
        return SendResult(success=False, error="Not supported")

    async def delete_message(self, ...) -> bool:
        """删除消息。默认返回 False。"""
        return False

    async def send_typing(self, chat_id, metadata=None) -> None:
        """发送输入指示器。默认 no-op。"""
        pass

    async def send_draft(self, ...) -> SendResult:
        """流式草稿预览 (Telegram Bot API 9.5+)。默认 raise。"""
        raise NotImplementedError(...)

    # ── 能力标志 ──
    supports_code_blocks: bool = False
        # 平台是否渲染 Markdown 代码块
    typed_command_prefix: str = "/"
        # 用户可输入的命令前缀 (Slack/Matrix 用 "!")
    REQUIRES_EDIT_FINALIZE: bool = False
        # 是否需要显式 finalize (钉钉 AI Cards 等)

2. 会话隔离与中断控制

BasePlatformAdapter 内置了会话级并发控制——每个会话(session)在同一时间只能处理一个消息,防止并发冲突:

BasePlatformAdapter 会话控制结构:

  _active_sessions: Dict[str, asyncio.Event]
    └─ 每个 session_key → 一个 Event 对象
    └─ 消息到达时: 如果 Event.is_set() → 会话忙
    └─ 处理完成后: Event.clear() → 会话空闲

  _session_tasks: Dict[str, asyncio.Task]
    └─ session → 正在处理该会话的 Task
    └─ /stop, /new, /reset 可取消正确的 Task
    └─ 避免旧 Task 的 finally 块删除新 Task 的锁

  _pending_messages: Dict[str, MessageEvent]
    └─ 照片连拍/相册合并: 多条 PHOTO 事件
       合并为一条, 避免截断用户意图

  _busy_text_mode: str  ("interrupt" / "queue" / "ignore")
    └─ 会话忙时的策略:
       interrupt → 中断当前, 处理新的
       queue    → 排队等待
       ignore   → 丢弃并回复 "busy"

  输入指示器 (_typing_paused):
    └─ 审批等待等场景暂停 "正在输入..."
    └─ 避免审批弹窗和输入指示器同时出现

3. 流式渲染钩子:适配器决定呈现方式

这是 Gateway 架构中最精妙的设计之一——Agent 发出结构化事件,适配器决定如何呈现

📄 gateway/platforms/base.py (第 1990-2051 行)

class BasePlatformAdapter(ABC):
    # ── 结构化流事件渲染 ──
    # 适配器决定 *如何* 呈现每个流事件
    # 默认实现 1:1 复现历史行为, 适配器覆盖以实现原生渲染

    def render_message_event(self, event, sink) -> None:
        """渲染 MessageChunk / MessageStop / Commentary。

        默认: 映射到 stream consumer 的已有原语。
        Telegram 覆盖: 用 MarkdownV2 渲染 ```bash``` 代码块。
        iMessage 覆盖: 丢弃工具 chrome (纯文本平台无法格式化)。
        """
        if isinstance(event, MessageChunk):
            sink.on_delta(event.text)
        elif isinstance(event, MessageStop):
            if not event.final:
                sink.on_segment_break()
        elif isinstance(event, Commentary):
            sink.on_commentary(event.text)

    def format_tool_event(self, event, *, mode="all",
                          preview_max_len=40) -> Optional[str]:
        """返回工具调用的渲染 chrome, 或 None 表示"吃掉"该事件。

        默认: emoji + tool_name + 短预览 (40 字符限制)。
        纯文本平台: 返回 None (不显示工具进度)。
        verbose 模式: 显示完整 args dict。
        """
        emoji = get_tool_emoji(event.tool_name, default="⚙️")
        if mode == "verbose":
            return f"{emoji} {event.tool_name}({keys})\n{args}"
        preview = event.preview or ""
        if len(preview) > 40:
            preview = preview[:37] + "..."
        return f"{emoji} {event.tool_name}: \"{preview}\""

设计哲学:Agent 不关心 Telegram 用什么 markdown、Discord 用什么 embed、钉钉用什么 AI Card——Agent 只发出结构化事件,Gateway 负责翻译。这实现了真正的关注点分离

四、PlatformRegistry:插件化平台发现

PlatformRegistry 是平台适配器的注册中心。插件可以通过它注册新平台,无需修改核心代码:

📄 gateway/platform_registry.py (第 38-159 行)

@dataclass
class PlatformEntry:
    """单个平台适配器的元数据和工厂。"""
    name: str                          # 配置标识: "irc", "viber"
    label: str                         # 人类可读: "IRC", "Viber"
    adapter_factory: Callable          # 工厂函数, 接收 PlatformConfig
    check_fn: Callable                 # 依赖是否可用?
    validate_config: Callable          # 配置是否正确?
    is_connected: Callable             # 是否已连接?
    setup_fn: Callable                 # 交互式配置向导
    required_env: list                 # hermes setup 显示的变量
    install_hint: str                  # 缺失时的安装提示

    # ── 高级特性 ──
    platform_hint: str                 # 注入系统提示词的平台指引
    emoji: str                         # CLI/Gateway 显示的 emoji
    pii_safe: bool                     # 是否脱敏 PII
    max_message_length: int            # 智能分块的消息上限
    cron_deliver_env_var: str          # Cron 投递目标
    standalone_sender_fn: Callable     # 独立进程发送 (Cron 专用)
    env_enablement_fn: Callable        # 环境变量自动配置
    apply_yaml_config_fn: Callable     # YAML→环境变量桥接

class PlatformRegistry:
    def register(self, entry: PlatformEntry) -> None:
        """注册平台适配器。同名覆盖 (last-writer wins)。"""

    def create_adapter(self, name: str, config: PlatformConfig):
        """查找并实例化适配器。"""
        entry = self.get(name)
        if entry:
            return entry.adapter_factory(config)
        # 回退到内置 if/elif 链 (向后兼容)

插件使用示例:

插件侧注册:

  from gateway.platform_registry import platform_registry, PlatformEntry

  platform_registry.register(PlatformEntry(
      name="irc",
      label="IRC",
      adapter_factory=lambda cfg: IRCAdapter(cfg),
      check_fn=lambda: importlib.util.find_spec("irc"),
      validate_config=lambda cfg: bool(cfg.extra.get("server")),
      required_env=["IRC_SERVER"],
      install_hint="pip install irc",
      platform_hint="You are on IRC. Do not use markdown.",
      emoji="💬",
  ))

Gateway 侧使用:

  adapter = platform_registry.create_adapter("irc", platform_config)
  # 插件优先, 未找到则回退到内置适配器

五、GatewayRunner:16K 行的通信运行时

gateway/run.py 是 Hermes 最大的单一文件(16292 行)。它包含 GatewayRunner 类——Gateway 的大脑,管理所有平台的连接生命周期、消息路由、流式响应、安全策略。

1. 启动管线:从配置到在线

GatewayRunner 启动管线:

  start_gateway()
    │
    ├─ 1. Windows 特殊处理 (_ensure_windows_gateway_venv_imports)
    │    └─ 修复 venv 包可见性 (MCP SDK 等)
    │
    ├─ 2. 安装事件循环异常处理器
    │    └─ _gateway_loop_exception_handler
    │       └─ 捕获瞬态网络错误, 防止网关崩溃
    │       └─ 白名单: TimedOut, NetworkError, ConnectError...
    │
    ├─ 3. 加载配置 (GatewayConfig)
    │    └─ config.yaml + 环境变量桥接
    │    └─ 平台发现: 内置 + 插件
    │
    ├─ 4. 创建 GatewayRunner 实例
    │    └─ Agent 缓存: LRU + TTL 驱逐
    │       _AGENT_CACHE_MAX_SIZE = 128
    │       _AGENT_CACHE_IDLE_TTL_SECS = 3600
    │
    ├─ 5. 为每个平台创建适配器
    │    └─ platform_registry.create_adapter()
    │    └─ 设置消息处理器: adapter.set_message_handler()
    │    └─ 设置会话存储: adapter.set_session_store()
    │
    ├─ 6. 并行连接所有平台
    │    └─ asyncio.gather(*[adapter.connect() for ...])
    │    └─ 连接超时: _PLATFORM_CONNECT_TIMEOUT_SECS_DEFAULT = 30s
    │
    └─ 7. 启动后台任务
         └─ Agent 缓存驱逐 (_session_expiry_watcher)
         └─ 图片缓存清理 (cleanup_image_cache)
         └─ 音频缓存清理 (cleanup_audio_cache)

2. 消息处理管线:从用户输入到 Agent 响应

消息处理管线 (GatewayRunner._process_message_background):

  用户消息到达
    │
    ├─ Phase 0: 预处理
    │    ├─ coerce_plaintext_gateway_command()
    │    │   └─ DM 中 "restart gateway" → "/restart"
    │    │   └─ 防止自然语言触发自我重启 (#16007)
    │    │
    │    ├─ 主题恢复 (_apply_topic_recovery)
    │    │   └─ Telegram DM Topic 模式: 修正 thread_id
    │    │
    │    ├─ 会话忙检查 (_active_sessions)
    │    │   └─ interrupt / queue / ignore 策略
    │    │
    │    └─ 用户授权检查 (_is_user_authorized)
    │        └─ 环境白名单: TELEGRAM_ALLOWED_USERS
    │        └─ 特殊: 适配器自有策略 (WeCom/Weixin) 跳过
    │
    ├─ Phase 1: 会话恢复/创建
    │    ├─ 查找或创建 SessionContext
    │    ├─ 加载历史 (_build_gateway_agent_history)
    │    │   └─ 保留 assistant 推理字段 (reasoning, reasoning_content,
    │    │       codex_reasoning_items, codex_message_items, finish_reason)
    │    │   └─ Telegram 观察上下文分离 (observed group chatter)
    │    │
    │    ├─ 自动继续检查 (_is_fresh_gateway_interruption)
    │    │   └─ 新鲜窗口: 1 小时 (可配置)
    │    │   └─ 旧中断标记不自动恢复 (#31066)
    │    │
    │    └─ 创建/复用 AIAgent 实例
    │        └─ LRU 缓存, 128 上限, 1h TTL
    │
    ├─ Phase 2: Agent 执行
    │    ├─ 流式回调 → GatewayEventDispatcher
    │    │   └─ 结构化事件: MessageChunk, ToolCallChunk, Commentary...
    │    │   └─ 适配器渲染: Telegram draft / Discord edit / 钉钉 AI Card
    │    │
    │    ├─ 工具进度队列 (send_progress_messages)
    │    │   └─ 独立 drain 循环, 不与流式响应竞争
    │    │
    │    └─ 响应截断与分块
    │        └─ UTF-16 长度限制 (Telegram: 4096 code units)
    │        └─ 二进制搜索安全截断点 (不劈半 surrogate pair)
    │
    ├─ Phase 3: 后处理
    │    ├─ 持久化到会话存储 (SQLite)
    │    ├─ 记忆同步 (sync_all → 后台线程)
    │    ├─ 媒体提取 (extract_media / extract_local_files)
    │    │   └─ MEDIA:/path/to/file.png → 原生附件投递
    │    │
    │    └─ 清理: 释放会话锁, 清除回调
    │
    └─ 错误处理
         ├─ Provider 错误 → 用户友好分类 (认证/限流/策略)
         ├─ 密钥脱敏 (_redact_gateway_user_facing_secrets)
         └─ Telegram 特殊: 噪声状态过滤

3. 安全防线:多层防护

Gateway 是面向网络的,安全设计极其重要。源码中有五层安全防线

📄 gateway/run.py (第 71-136 行)

# 第一层: 瞬态网络错误白名单
# 防止 TimedOut/NetworkError 杀死整个网关进程
_transient_class_names = {
    "TimedOut", "NetworkError", "ReadError", "WriteError",
    "ConnectError", "ConnectTimeout", "ReadTimeout", ...
}

# 第二层: Provider 错误分类与脱敏
# 用户看到分类摘要, 不看到原始 HTTP body
_GATEWAY_PROVIDER_ERROR_RE = re.compile(
    r"api\s+(?:call\s+)?failed|provider\s+authentication\s+failed|...")

# 第三层: 密钥脱敏
_GATEWAY_SECRET_PATTERNS = (
    re.compile(r"\bsk-[A-Za-z0-9][A-Za-z0-9_\-]{12,}\b"),     # OpenAI
    re.compile(r"\bgh[pousr]_[A-Za-z0-9_]{20,}\b"),           # GitHub
    re.compile(r"\bxox[baprs]-[A-Za-z0-9\-]{20,}\b"),         # Slack
    re.compile(r"\bhf_[A-Za-z0-9]{20,}\b"),                   # HuggingFace
    re.compile(r"\bglpat-[A-Za-z0-9_\-]{20,}\b"),             # GitLab
    re.compile(r"(?i)\b(Bearer\s+)[A-Za-z0-9._\-]{20,}\b"),   # Generic
)

# 第四层: Telegram 噪声过滤
# 压缩摘要/重试/降级等内部状态不推送到聊天
_TELEGRAM_NOISY_STATUS_RE = re.compile(
    r"auxiliary\s+.+?\s+failed|compression\s+summary\s+failed|...")

# 第五层: 媒体投递路径验证
# validate_media_delivery_path() 在 base.py 中
# 白名单 (缓存目录) + 黑名单 (凭证目录) + 时间窗口

六、流式事件系统:结构化交付契约

历史上海报通过一堆松散类型的回调函数驱动 Gateway 投递(stream_delta_callback(text), tool_progress_callback(event_type, tool_name, ...)……),导致 Telegram 上工具进度气泡和流式草稿互相竞争。新架构用结构化事件彻底解耦:

1. 事件词汇表

📄 gateway/stream_events.py (第 43-145 行)

# ── 消息事件 ──
@dataclass(frozen=True)
class MessageChunk:
    """流式助手文本增量。"""
    text: str

@dataclass(frozen=True)
class MessageStop:
    """当前助手消息段完成。"""
    final: bool = False  # True = 整个 turn 结束

@dataclass(frozen=True)
class Commentary:
    """工具迭代间的完整中间消息。"""
    text: str

# ── 工具调用事件 ──
@dataclass(frozen=True)
class ToolCallChunk:
    """工具调用开始。"""
    tool_name: str
    preview: Optional[str] = None
    args: Optional[Dict[str, Any]] = None
    index: int = 0  # 单调索引, 关联 start/finish

@dataclass(frozen=True)
class ToolCallFinished:
    """工具调用完成。"""
    tool_name: str
    duration: float = 0.0
    ok: bool = True
    index: int = 0

# ── 网关控制事件 ──
@dataclass(frozen=True)
class LongToolHint:
    """工具运行超时的新手引导。"""
    tool_name: str = ""
    duration: float = 0.0

@dataclass(frozen=True)
class GatewayNotice:
    """网关级控制消息 (restart, online, long_run)。"""
    kind: str
    text: str = ""
    extra: Dict[str, Any] = field(default_factory=dict)

StreamEvent = Union[
    MessageChunk, MessageStop, Commentary,
    ToolCallChunk, ToolCallFinished,
    LongToolHint, GatewayNotice,
]  # 显式联合, 穷尽 match 时缺失 case = 类型错误

关键约束:事件描述传输,不描述上下文。它们不持久化到对话历史——历史由 Agent 拥有。Gateway 选择"吃掉"某些事件(如纯文本平台丢弃工具 chrome)不会改变 Agent 存储的字节。

2. GatewayEventDispatcher:同步路由器

Dispatcher 是 Agent 和 Gateway 之间的同步路由器——无 asyncio、无平台知识,可以从 Agent 的工作线程安全调用:

📄 gateway/stream_dispatch.py (第 40-132 行)

class GatewayEventDispatcher:
    """将类型化流事件通过适配器路由到投递 sink。"""

    def __init__(self, adapter, sink=None, *,
                 enqueue_tool_line=None,
                 tool_mode="all",
                 preview_max_len=40,
                 on_long_tool=None,
                 on_notice=None):
        ...

    def dispatch(self, event: StreamEvent) -> None:
        """路由单个事件。绝不向 Agent 工作线程抛出异常。"""
        try:
            self._dispatch(event)
        except Exception:  # 呈现层绝不能破坏 Agent 循环
            logger.debug("stream-event dispatch error", exc_info=True)

    def _dispatch(self, event: StreamEvent) -> None:
        if isinstance(event, (MessageChunk, MessageStop, Commentary)):
            # 消息事件 → 适配器.render_message_event → sink
            self.adapter.render_message_event(event, self.sink)

        elif isinstance(event, ToolCallChunk):
            # 工具事件 → 适配器.format_tool_event → 进度队列
            line = self.adapter.format_tool_event(
                event, mode=self.tool_mode, ...)
            if line:  # None = 适配器选择"吃掉"
                self._enqueue_tool_line(line)

设计要点:Dispatcher 是纯同步的,因为它在 Agent 的工作线程上运行。任何阻塞或异常都会被捕获——呈现层的问题绝不能中断 Agent 的思考。

七、会话管理与跨平台投递

1. SessionSource 与 SessionContext

📄 gateway/session.py (第 70-193 行)

@dataclass
class SessionSource:
    """描述消息来源。"""
    platform: Platform
    chat_id: str
    chat_name: Optional[str] = None
    chat_type: str = "dm"  # "dm", "group", "channel", "thread"
    user_id: Optional[str] = None
    user_name: Optional[str] = None
    thread_id: Optional[str] = None  # Forum topics, Discord threads
    guild_id: Optional[str] = None   # Discord guild / Slack workspace
    parent_chat_id: Optional[str] = None
    message_id: Optional[str] = None
    role_authorized: bool = False

    @property
    def description(self) -> str:
        """人类可读描述, 注入系统提示词。"""
        # "DM with Alice" / "group: #dev-channel" / "channel: general"

@dataclass
class SessionContext:
    """完整会话上下文, 用于动态系统提示词注入。"""
    source: SessionSource
    connected_platforms: List[Platform]
    home_channels: Dict[Platform, HomeChannel]
    shared_multi_user_session: bool = False

2. 会话重置策略

📄 gateway/config.py (第 274-299 行)

@dataclass
class SessionResetPolicy:
    """控制会话何时重置 (丢失上下文)。"""
    mode: str = "both"  # "daily", "idle", "both", "none"
    at_hour: int = 4     # 每日重置时间 (0-23, 本地时区)
    idle_minutes: int = 1440  # 空闲超时 (24 小时)
    notify: bool = True    # 自动重置时通知用户
    notify_exclude_platforms: tuple = ("api_server", "webhook")

3. 跨平台镜像投递

当 Agent 通过 send_message 工具向某个平台发送消息时,mirror.py 会将该消息写入目标会话的对话记录——这样目标平台的 Agent 也能看到这条消息:

📄 gateway/mirror.py (第 25-81 行)

def mirror_to_session(
    platform: str, chat_id: str, message_text: str,
    source_label: str = "cli", thread_id: Optional[str] = None,
    user_id: Optional[str] = None,
) -> bool:
    """将投递镜像写入目标会话的对话记录。"""
    session_id = _find_session_id(platform, chat_id, ...)
    if not session_id:
        return False

    mirror_msg = {
        "role": "assistant",
        "content": message_text,
        "timestamp": datetime.now().isoformat(),
        "mirror": True,
        "mirror_source": source_label,
    }
    _append_to_sqlite(session_id, mirror_msg)
    return True

# 回放时, mirror=True 的消息被标记:
# "[Delivered from cli] <content>"
# 目标 Agent 知道这是从另一个会话投递的

4. DeliveryRouter:Cron 投递路由

DeliveryRouter 处理定时任务的输出投递。支持多种目标格式:

DeliveryTarget 解析:

  "origin"      → 回到任务创建源
  "local"       → 保存到本地文件
  "telegram"    → Telegram home channel
  "telegram:123" → 特定 Telegram 聊天
  "telegram:123:thread" → 特定线程

  投递管线:
    DeliveryRouter.deliver()
      ├─ 解析 DeliveryTarget
      ├─ 查找适配器 (self.adapters[platform])
      ├─ 发送消息 (adapter.send / adapter.send_image ...)
      ├─ 镜像到目标会话 (mirror_to_session)
      └─ 失败回退: 保存到 cron/output/

  静默检测:
    _SILENCE_NARRATION 正则匹配:
      "silent", "silence", "no response", 🔇, "."
      → 不投递, 避免空消息轰炸用户

八、媒体处理管线

Gateway 处理来自平台的图片、音频、视频、文档——全部经过统一的缓存管线:

媒体处理管线 (base.py):

  平台收到附件
    │
    ├─ 1. 下载 (cache_image_from_url / cache_audio_from_url)
    │    ├─ SSRF 防护: is_safe_url() 检查
    │    ├─ 重定向守卫: _ssrf_redirect_guard()
    │    │   每次重定向重新验证目标地址
    │    └─ 指数退避重试 (默认 2 次)
    │
    ├─ 2. 分类 (cache_media_bytes)
    │    ├─ 按扩展名 + MIME 类型分类
    │    ├─ 图片 → image cache (~/.hermes/cache/images/)
    │    ├─ 音频 → audio cache (~/.hermes/cache/audio/)
    │    ├─ 视频 → video cache (~/.hermes/cache/videos/)
    │    └─ 文档 → document cache (~/.hermes/cache/documents/)
    │
    ├─ 3. 安全验证
    │    ├─ 图片: magic bytes 检查 (PNG/JPEG/GIF/BMP/WEBP)
    │    ├─ 音频: 扩展名白名单
    │    └─ 文档: 扩展名白名单 (40+ 类型)
    │
    ├─ 4. 沙箱路径转换 (to_agent_visible_cache_path)
    │    └─ Agent 看到的安全路径, 非真实文件系统路径
    │
    └─ 5. 返回 CachedMedia 对象
         └─ path, media_type, kind, display_name
         └─ context_note(): "[image 'photo.jpg' saved at: ...]"

  媒体投递 (Agent → 平台):
    validate_media_delivery_path(path)
      ├─ 白名单: 缓存目录无条件信任
      ├─ 黑名单: /etc, /proc, ~/.ssh, ~/.aws, ~/.hermes/.env
      ├─ 严格模式: 还需在时间窗口内 (默认 1s)
      └─ 非严格模式 (默认): 不在黑名单即可

九、设计总结

Gateway 核心设计

  • 动态平台枚举:内置 + 插件平台通过 _missing_() 无缝扩展
  • BasePlatformAdapter ABC:最小接口 + 大量默认实现 + 能力标志
  • PlatformRegistry:插件化发现,last-writer wins 覆盖策略
  • 会话隔离:per-session Event + Task 映射,精确中断控制
  • Agent 缓存:LRU + TTL 驱逐,128 上限防内存泄漏

流式架构关键设计

  • 结构化事件:8 种冻结 dataclass,Agent 发出,Gateway 渲染
  • 关注点分离:Agent 不关心 Telegram markdown vs Discord embed
  • 同步路由器:GatewayEventDispatcher 无 asyncio,Agent 线程安全
  • 适配器渲染钩子:render_message_event + format_tool_event
  • 历史隔离:事件不持久化,历史由 Agent 拥有

安全设计

  • 瞬态错误白名单:网络错误不杀进程,事件循环级捕获
  • Provider 错误分类:用户看到摘要,不看到原始 HTTP body
  • 密钥脱敏:6 种 API 密钥模式 + Bearer token
  • Telegram 噪声过滤:内部状态不推送到聊天
  • SSRF 防护:URL 安全检查 + 重定向守卫
  • 媒体投递沙箱:白名单 + 黑名单 + 时间窗口

跨平台协同

  • 镜像投递:send_message 写入目标会话记录
  • DeliveryRouter:Cron 输出路由,支持 origin/local/platform:chat_id
  • 静默检测:避免空消息轰炸用户
  • 会话重置策略:daily/idle/both/none 四种模式
  • Home Channel:每平台默认目的地,/sethome 配置

下一讲预告:Cron 调度器与定时任务——Hermes 如何管理后台任务、会话恢复、以及跨会话的长期工作流

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-06-20 17:53:15 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/774051.html
  2. 运行时间 : 0.090650s [ 吞吐率:11.03req/s ] 内存消耗:4,683.84kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=7badceb8a848b29a81ae734f8725eed1
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000413s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000740s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000544s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000274s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000475s ]
  6. SELECT * FROM `set` [ RunTime:0.000203s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000577s ]
  8. SELECT * FROM `article` WHERE `id` = 774051 LIMIT 1 [ RunTime:0.001494s ]
  9. UPDATE `article` SET `lasttime` = 1781949195 WHERE `id` = 774051 [ RunTime:0.003763s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000317s ]
  11. SELECT * FROM `article` WHERE `id` < 774051 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000481s ]
  12. SELECT * FROM `article` WHERE `id` > 774051 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000355s ]
  13. SELECT * FROM `article` WHERE `id` < 774051 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000625s ]
  14. SELECT * FROM `article` WHERE `id` < 774051 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001300s ]
  15. SELECT * FROM `article` WHERE `id` < 774051 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.006113s ]
0.092463s