【深度解析】Hermes Agent 0.1.3 Tenacity:面向长运行 AI Agent 的可靠性工程实践

摘要

Hermes Agent 0.1.3 的核心不是"多几个功能",而是围绕长运行智能体的可靠性重构:持久化任务队列、心跳检测、状态恢复、目标保持、安全默认值、插件化模型提供商与工具链增强,为真实生产级 Agent 工作流提供了更稳的工程底座。


背景介绍:AI Agent 正从 Demo 走向工程系统

很多 AI Agent 在单轮任务中表现不错,例如生成代码、总结文档、调用工具。但一旦进入真实场景,问题会迅速暴露:

  • 多步骤任务容易偏离最初目标;
  • 工作者进程崩溃后任务状态不一致;
  • 长时间运行时会话中断、上下文丢失;
  • Agent 声称"已完成",但真实文件或看板状态并未变化;
  • 多平台接入、模型切换、安全边界越来越复杂。

Hermes Agent 0.1.3 被命名为 Tenacity Release,重点正是解决这些问题。它更像一次"可靠性版本",而不是单纯的功能版本。


核心原理:Hermes 0.1.3 的可靠性设计

1. 持久化多智能体 Kanban:从看板到工作队列

过去的 Kanban 更偏向可视化任务管理,而 Hermes 0.1.3 将其进一步演进为 durable work queue

关键机制包括:

  • Heartbeat:Worker 定期上报存活状态;
  • Zombie Detection:检测长时间无心跳的异常任务;
  • Reclaim Logic:自动回收卡死任务;
  • Retry Budget:限制单任务最大重试次数;
  • Automatic Blocking:超过重试上限后自动阻塞,避免无限循环;
  • Hallucination Gate:校验 Agent 声称完成的结果是否与真实状态一致。

这类机制本质上借鉴了分布式任务队列的设计思想,例如 Celery、Temporal、Sidekiq 中的租约、重试、死信队列等能力。

2. Slash Goal:跨回合目标保持

长任务中,Agent 最常见的问题是 goal drift,即随着上下文变长逐渐偏离原始目标。

/goal 的价值在于为会话设置一个持久目标,使后续推理、工具调用、任务拆解都围绕该目标优化。对于代码重构、数据分析、自动化运维等长链路任务,这比单次 prompt 更可靠。

3. Checkpoints v2:状态恢复与会话续跑

Hermes 0.1.3 重写了状态持久化层,引入:

  • checkpoint pruning;
  • gateway restart auto-resume;
  • interrupted session recovery;
  • guard rails。

这意味着 Agent 不再依赖"进程一直活着"。即使网关重启、会话中断,也可以尽量从最近检查点恢复,而不是从头开始。

4. 安全默认值升级

本次版本修复了多个 P0 安全问题,并强化了:

  • Secret redaction;
  • Discord guild scoped allowlist;
  • Webhook stranger rejection;
  • MCP OAuth handling;
  • SSRF 防护;
  • 云元数据访问保护;
  • Prompt injection scanning;
  • Debug log redaction。

对于接入浏览器、文件系统、消息平台和第三方 API 的 Agent 来说,安全默认值比功能数量更重要。


工具选型:统一模型接入与多模型实验

在 Agent 系统中,模型提供商插件化非常关键。Hermes 0.1.3 引入 provider profile abstraction 和 model provider plugin directory,方便在不同模型、不同路由之间切换。

我在日常 AI 开发中会使用 薛定猫AI(xuedingmao.com 作为统一模型接入层。它采用 OpenAI 兼容接口,适合快速接入多模型 Agent 工作流:

  • 聚合 500+ 主流大模型,例如 GPT-5.4、Claude 4.6、Gemini 3.1 Pro 等;
  • 新模型更新速度快,便于第一时间验证前沿 API;
  • 统一 base_url + api_key + model 调用方式,减少多供应商 SDK 适配成本;
  • 适合做模型路由、A/B 测试、Agent provider plugin 抽象。

下面实战示例中使用 claude-opus-4-6。该模型在复杂推理、长上下文规划、代码生成和 Agent 协调任务中表现很强,适合作为多步骤任务的规划器或执行器。


实战演示:用 Python 实现一个简化版可靠 Agent 工作队列

下面代码模拟 Hermes Tenacity 的核心思想:持久化任务、心跳、僵尸任务回收、重试预算、目标保持、模型调用与结果校验。

环境准备

bash 复制代码
pip install openai
export XUEDINGMAO_API_KEY="你的API_KEY"

完整代码示例

python 复制代码
import os
import json
import time
import sqlite3
import threading
from contextlib import contextmanager
from datetime import datetime, timedelta, timezone
from openai import OpenAI

DB_PATH = "agent_queue.db"
WORKER_ID = f"worker-{os.getpid()}"
HEARTBEAT_INTERVAL = 5
ZOMBIE_TIMEOUT_SECONDS = 20

client = OpenAI(
    api_key=os.getenv("XUEDINGMAO_API_KEY"),
    base_url="https://xuedingmao.com/v1"
)

MODEL = "claude-opus-4-6"


def now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


@contextmanager
def db():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    try:
        yield conn
        conn.commit()
    finally:
        conn.close()


def init_db():
    with db() as conn:
        conn.execute("""
        CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            status TEXT NOT NULL,
            goal TEXT NOT NULL,
            payload TEXT NOT NULL,
            result TEXT,
            error TEXT,
            worker_id TEXT,
            heartbeat_at TEXT,
            retries INTEGER NOT NULL DEFAULT 0,
            max_retries INTEGER NOT NULL DEFAULT 3,
            checkpoint TEXT,
            created_at TEXT NOT NULL,
            updated_at TEXT NOT NULL
        )
        """)


def create_task(goal: str, payload: dict, max_retries: int = 3):
    with db() as conn:
        conn.execute("""
        INSERT INTO tasks(status, goal, payload, max_retries, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?)
        """, (
            "queued",
            goal,
            json.dumps(payload, ensure_ascii=False),
            max_retries,
            now_iso(),
            now_iso()
        ))


def reclaim_zombies():
    deadline = datetime.now(timezone.utc) - timedelta(seconds=ZOMBIE_TIMEOUT_SECONDS)
    with db() as conn:
        rows = conn.execute("""
        SELECT * FROM tasks
        WHERE status = 'running' AND heartbeat_at < ?
        """, (deadline.isoformat(),)).fetchall()

        for row in rows:
            next_retries = row["retries"] + 1
            if next_retries >= row["max_retries"]:
                conn.execute("""
                UPDATE tasks
                SET status = 'blocked',
                    retries = ?,
                    error = ?,
                    updated_at = ?
                WHERE id = ?
                """, (
                    next_retries,
                    "Task blocked: retry budget exhausted after zombie detection.",
                    now_iso(),
                    row["id"]
                ))
            else:
                conn.execute("""
                UPDATE tasks
                SET status = 'queued',
                    retries = ?,
                    worker_id = NULL,
                    heartbeat_at = NULL,
                    updated_at = ?
                WHERE id = ?
                """, (next_retries, now_iso(), row["id"]))


def claim_task():
    with db() as conn:
        row = conn.execute("""
        SELECT * FROM tasks
        WHERE status = 'queued'
        ORDER BY id ASC
        LIMIT 1
        """).fetchone()

        if not row:
            return None

        conn.execute("""
        UPDATE tasks
        SET status = 'running',
            worker_id = ?,
            heartbeat_at = ?,
            checkpoint = ?,
            updated_at = ?
        WHERE id = ?
        """, (
            WORKER_ID,
            now_iso(),
            json.dumps({"phase": "claimed"}, ensure_ascii=False),
            now_iso(),
            row["id"]
        ))

        return dict(row)


def heartbeat(task_id: int, stop_event: threading.Event):
    while not stop_event.is_set():
        with db() as conn:
            conn.execute("""
            UPDATE tasks
            SET heartbeat_at = ?, updated_at = ?
            WHERE id = ? AND worker_id = ? AND status = 'running'
            """, (now_iso(), now_iso(), task_id, WORKER_ID))
        time.sleep(HEARTBEAT_INTERVAL)


def update_checkpoint(task_id: int, checkpoint: dict):
    with db() as conn:
        conn.execute("""
        UPDATE tasks
        SET checkpoint = ?, updated_at = ?
        WHERE id = ?
        """, (json.dumps(checkpoint, ensure_ascii=False), now_iso(), task_id))


def call_agent_model(goal: str, payload: dict) -> dict:
    """
    使用 OpenAI 兼容 API 调用薛定猫AI上的 claude-opus-4-6。
    要求模型输出严格 JSON,方便后续做 hallucination gate。
    """
    response = client.chat.completions.create(
        model=MODEL,
        temperature=0.2,
        messages=[
            {
                "role": "system",
                "content": (
                    "你是一个可靠的工程型AI Agent。"
                    "必须围绕用户目标执行任务,并输出严格JSON。"
                    "JSON字段包括: completed, summary, actions, risks。"
                )
            },
            {
                "role": "user",
                "content": json.dumps({
                    "persistent_goal": goal,
                    "task_payload": payload
                }, ensure_ascii=False)
            }
        ]
    )

    content = response.choices[0].message.content
    return json.loads(content)


def hallucination_gate(output: dict) -> None:
    """
    简化版幻觉门控:
    如果模型声称 completed=true,则必须提供 summary 和 actions。
    真实工程中可进一步校验文件、数据库、看板状态是否一致。
    """
    if not isinstance(output, dict):
        raise ValueError("Model output is not a JSON object.")

    required = {"completed", "summary", "actions", "risks"}
    missing = required - set(output.keys())
    if missing:
        raise ValueError(f"Missing required fields: {missing}")

    if output["completed"] is True and not output["actions"]:
        raise ValueError("Agent claims completion but no actions are recorded.")


def finish_task(task_id: int, result: dict):
    with db() as conn:
        conn.execute("""
        UPDATE tasks
        SET status = 'done',
            result = ?,
            checkpoint = ?,
            updated_at = ?
        WHERE id = ?
        """, (
            json.dumps(result, ensure_ascii=False),
            json.dumps({"phase": "finished"}, ensure_ascii=False),
            now_iso(),
            task_id
        ))


def fail_task(task_id: int, error: str):
    with db() as conn:
        conn.execute("""
        UPDATE tasks
        SET status = 'queued',
            error = ?,
            worker_id = NULL,
            heartbeat_at = NULL,
            updated_at = ?
        WHERE id = ?
        """, (error, now_iso(), task_id))


def run_once():
    reclaim_zombies()

    task = claim_task()
    if not task:
        print("No queued task.")
        return

    task_id = task["id"]
    stop_event = threading.Event()
    hb_thread = threading.Thread(
        target=heartbeat,
        args=(task_id, stop_event),
        daemon=True
    )
    hb_thread.start()

    try:
        goal = task["goal"]
        payload = json.loads(task["payload"])

        update_checkpoint(task_id, {"phase": "model_call_started"})
        output = call_agent_model(goal, payload)

        update_checkpoint(task_id, {"phase": "hallucination_gate_started"})
        hallucination_gate(output)

        finish_task(task_id, output)
        print(f"Task {task_id} done:", json.dumps(output, ensure_ascii=False, indent=2))

    except Exception as exc:
        fail_task(task_id, str(exc))
        print(f"Task {task_id} failed:", exc)

    finally:
        stop_event.set()
        hb_thread.join(timeout=2)


if __name__ == "__main__":
    init_db()

    # 首次运行时创建一个任务;后续可注释掉,观察任务恢复与重试行为
    create_task(
        goal="生成一份面向后端团队的AI Agent可靠性改造方案",
        payload={
            "system": "现有Agent支持代码生成和消息平台接入,但缺少任务恢复、心跳和安全门控",
            "expected_output": "给出架构建议、风险点和落地步骤"
        },
        max_retries=3
    )

    run_once()

注意事项:从 Demo 到生产还差哪些能力

1. Heartbeat 需要和任务租约结合

仅有心跳还不够,生产系统中建议增加 lease timeout,避免多个 Worker 同时处理同一任务。

2. Hallucination Gate 应校验真实状态

示例只校验 JSON 字段。真实 Agent 应检查:

  • 文件是否真实写入;
  • patch 是否成功应用;
  • 测试是否通过;
  • Kanban 状态是否与声明一致;
  • 外部 API 调用是否返回成功。

3. Retry Budget 需要区分错误类型

网络超时可以重试,权限错误、Prompt Injection、Schema 错误不一定适合重试。建议引入错误分类与死信队列。

4. 安全边界必须默认收紧

对于具备浏览器、Shell、文件系统、MCP 工具能力的 Agent,应默认启用:

  • Secret 脱敏;
  • SSRF 防护;
  • 云元数据 IP 阻断;
  • 工具 allowlist;
  • Prompt injection 扫描;
  • 审计日志。

总结

Hermes Agent 0.1.3 的重点是让 Agent 在复杂工作流中"持续运行":任务可恢复、目标可保持、状态可持久化、安全默认值更严格、模型和平台更加插件化。对于只需要基础 AI 编程助手的用户,这些能力可能偏重;但对于构建本地 Agent 系统、消息平台机器人、计划任务、长期自动化工作流的开发者来说,这类可靠性工程正是 Agent 走向生产环境的关键。

#AI #大模型 #Python #机器学习 #技术实战

相关推荐
Hotchip_MEMS1 小时前
高电压≠高风险:一颗ASIC芯片如何重构雾化器的安全边界?
人工智能·物联网
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【52】Interrupts 中断机制:案例演示
java·人工智能·spring
qq_411262421 小时前
基于 ESP32-S3 的四博 AI 双目智能音箱工程方案:四路触摸、IMU 姿态识别、震动反馈、双目屏状态机与语音克隆知识库接入
人工智能·智能音箱
老鱼说AI1 小时前
现代 LangChain 开发指南:从 LCEL 原理到企业级 RAG 与 Agent 实战
java·开发语言·人工智能·深度学习·神经网络·算法·机器学习
百度Geek说1 小时前
Browser Use:为 Agent 构建 Runtime Harness
人工智能
用户4330514143811 小时前
流程控制与并行工作
人工智能
云天AI实战派1 小时前
ChatGPT/API 调用故障排查指南:Realtime 音频、智能体浏览器操作与 AI 编码代理全流程修复手册
人工智能·chatgpt·音视频
水上冰石1 小时前
怎么查看olama是否用到了显卡加速
人工智能·显卡