编程 Hermes Agent 深度拆解:当 AI 学会「自我进化」——从动态技能生成、三层记忆架构到强化学习闭环的工程全貌(2026)

2026-07-18 07:15:45 +0800 CST views 6

Hermes Agent 深度拆解:当 AI 学会「自我进化」——从动态技能生成、三层记忆架构到强化学习闭环的工程全貌(2026)

引言:为什么 Hermes Agent 值得深读

2026年上半年,一个名为 Hermes Agent 的开源项目在 GitHub 上以惊人的速度攀升至 21.6 万 Stars。它由 Nous Research 开发,口号是 "self-improving AI agent"——自进化

这不是营销词汇。当你翻阅其 3 万行 Python 代码(截至 v0.14.0),你会发现一个真正实现了 "从经验中学习" 的工程方案:

  • 每次对话结束后自动评估自己的表现
  • 决定哪些经验值得写入技能库
  • 哪些事情应该记录进长期记忆
  • 如何在使用过程中持续优化自身能力

传统 Agent(包括早期的 OpenClaw、LangChain 等)的上下文管理只服务于"当前会话"的稳定性——任务执行完毕,试错经验随之丢失,智能上限被死死锚定在基座模型与静态 Skill 上。

Hermes 的架构突破在于:它构建了一条从"任务执行"到"经验沉淀"再到"权重内化"的完整数据闭环,实现了真正的 Self-Evolving。

本文将深度拆解 Hermes Agent 的核心架构,涵盖动态技能生成、三层记忆系统、ACP 跨 Agent 通信协议、强化学习闭环、以及生产级部署实践。


一、核心架构概览:从静态执行到动态进化

1.1 传统 Agent 的根本局限

传统 AI Agent 架构通常是这样的:

用户请求 → 意图识别 → 工具调用 → 生成响应 → 结束
                ↓
            静态 Skill 库

问题在于:

  1. Skill 是静态的:预定义好,不更新,遇到新问题只能硬凑
  2. 经验不沉淀:每次对话从零开始,不会"记住"上次怎么解决的
  3. 能力上限固定:智能水平 = 基座模型 + 静态工具,不会随使用增长

Hermes 的架构设计正是为了解决这三个痛点。

1.2 Hermes 的双轮驱动架构

Hermes 采用 "外驱 + 内驱" 双轮驱动的自进化架构:

┌─────────────────────────────────────────────────────────────┐
│                     用户交互层                                │
│   Telegram / Discord / Slack / WhatsApp / CLI / Email       │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   Gateway 消息网关                           │
│         多平台消息统一接入、协议转换、会话管理                  │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   Agent 核心引擎                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ 对话主循环   │  │ 上下文引擎   │  │ 工具调度器   │      │
│  │conversation_ │  │context_engine│  │tool_router   │      │
│  │loop.py       │  │              │  │              │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────┬───────────────────────────────────────┘
                      │
          ┌───────────┴───────────┐
          │                       │
┌─────────▼─────────┐   ┌────────▼────────┐
│   外驱演进引擎     │   │   内驱演进引擎   │
│ Skill Curator     │   │  GRPO RL Loop   │
│ 记忆管理器        │   │  权重优化器      │
│ 经验审查 Agent    │   │                 │
└───────────────────┘   └─────────────────┘
          │                       │
          └───────────┬───────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                    数据持久层                                │
│  SQLite(FTS5) / Redis / PostgreSQL / 8种外部记忆后端         │
└─────────────────────────────────────────────────────────────┘

外驱演进:通过动态 Skill 创建、记忆沉淀、经验审查,让 Agent 在使用过程中积累能力。

内驱演进:基于 GRPO(Group Relative Policy Optimization)的强化学习闭环,通过权重微调让模型本身变得更适合特定任务场景。

这两条线并行推进,构成了 Hermes 的"自进化"内核。


二、外驱演进:动态技能生成与经验沉淀

2.1 从"静态调用"到"动态资产"

传统框架中,Skill 是预定义的静态模块:

# 传统 Skill 定义(静态)
class WeatherSkill(BaseSkill):
    name = "get_weather"
    description = "获取天气信息"
    
    def execute(self, location: str) -> dict:
        # 固定逻辑
        return weather_api.get(location)

Hermes 将 Skill 升级为 "动态进化资产"

# Hermes 的 Skill 动态生成流程(简化示意)
class SkillCurator:
    """后台审查 Agent,负责从交互轨迹中提取 Skill"""
    
    async def review_trajectory(self, trajectory: Trajectory):
        """
        trajectory = [
            {"role": "user", "content": "帮我分析这个 PDF 的关键条款"},
            {"role": "assistant", "tool_calls": [{"name": "read_pdf", ...}]},
            {"role": "tool", "content": "...PDF 内容..."},
            {"role": "assistant", "content": "基于分析,我提取了三个关键条款..."},
            ...
        ]
        """
        
        # Step 1: 记忆审查 — 提取关键事实
        facts = await self.extract_facts(trajectory)
        # facts = ["用户偏好 PDF 先摘要再详细", "用户关注合同条款风险点"]
        
        # Step 2: 技能审查 — 评估解决路径的通用性
        skill_candidate = await self.extract_skill_pattern(trajectory)
        # skill_candidate = {
        #     "name": "contract_clause_analysis",
        #     "pattern": "read_pdf → extract_clauses → risk_assessment",
        #     "trigger": "用户请求分析合同/协议/条款",
        #     "success_rate": 0.92
        # }
        
        # Step 3: 综合审查 — 生成优化策略
        if skill_candidate["success_rate"] > 0.85:
            await self.create_skill(skill_candidate)
            
        return facts, skill_candidate

2.2 触发机制:自动化的学习循环

Hermes 内置 _iters_since_skill 计数器,当 Agent 连续执行多轮对话(默认 10 轮)且未更新技能时,会自动触发经验整理机制:

# agent/conversation_loop.py(核心对话主循环,约 3900 行)

class ConversationLoop:
    def __init__(self, agent: "AIAgent"):
        self.agent = agent
        self._iters_since_skill = 0  # 技能更新计数器
        
    async def run_turn(self, user_input: str) -> str:
        # ... 执行对话逻辑 ...
        
        self._iters_since_skill += 1
        
        # 每 10 轮对话检查一次是否需要经验整理
        if self._iters_since_skill >= 10:
            await self._maybe_curate_experience()
            self._iters_since_skill = 0
            
        return response
        
    async def _maybe_curate_experience(self):
        """
        后台异步触发经验审查
        主 Agent 立即响应用户,审查 Agent 在后台运行
        """
        trajectory = self.get_recent_trajectory()
        
        # Fork 一个轻量级审查 Agent
        curator = SkillCurator(self.agent.llm_client)
        
        # 异步执行,不阻塞主对话
        asyncio.create_task(curator.review_trajectory(trajectory))

这种 "前台即时响应、后台异步进化" 的架构设计,既保证了用户体验,又实现了持续学习。

2.3 技能的结构化表示

Hermes 生成的 Skill 不是简单的"记录",而是结构化的可执行模块:

# ~/.hermes/skills/contract_clause_analysis.yaml
name: contract_clause_analysis
version: 1.2
created_at: 2026-06-15T10:30:00Z
updated_at: 2026-07-02T14:22:00Z

trigger:
  patterns:
    - "分析.*合同"
    - "审核.*条款"
    - "这份协议.*风险"
  intent_keywords: ["合同", "条款", "协议", "法律", "风险"]

workflow:
  steps:
    - name: read_document
      tool: read_pdf
      params:
        extract_mode: structured
        focus_areas: ["权利义务", "违约责任", "争议解决"]
        
    - name: extract_clauses
      tool: clause_extractor
      params:
        clause_types: ["termination", "liability", "payment", "confidentiality"]
        
    - name: risk_assessment
      tool: legal_risk_analyzer
      params:
        jurisdiction: "CN"  # 可从用户配置读取
        risk_levels: ["high", "medium", "low"]

output_format:
  summary: "{{extract_clauses.summary}}"
  risks: "{{risk_assessment.risks}}"
  recommendations: "{{risk_assessment.recommendations}}"

performance:
  success_rate: 0.92
  avg_duration_ms: 3400
  user_satisfaction: 4.7  # 1-5 分

这种结构化表示使得 Skill 可以被:

  • 版本管理:跟踪 Skill 的演进历史
  • 性能监控:实时统计成功率、耗时、满意度
  • 持续优化:基于性能数据自动调整参数

三、三层记忆架构:让 AI 真正"记住"

3.1 为什么需要三层?

传统 Agent 的记忆通常只有一种:对话历史。问题显而易见:

  • 短对话还行,长对话上下文爆炸
  • 无法区分"当前任务上下文"和"长期知识"
  • 用户偏好、习惯等信息散落在海量对话中,无法提取

Hermes 的记忆架构是三层堆叠:

┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Built-in Memory (始终激活)                         │
│   MEMORY.md (Agent 个人笔记, 2200 字符上限)                  │
│   USER.md (用户画像, 1375 字符上限)                          │
│   作用: 高频关键事实的零成本访问                               │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: External Memory Providers (可插拔)                 │
│   8 种后端: Honcho / Holographic / Mem0 / Hindsight /       │
│            OpenViking / RetainDB / ByteRover / Supermemory  │
│   作用: 语义化深度记忆, 向量检索                              │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: Session Search (无限容量)                          │
│   SQLite + FTS5 全文索引                                    │
│   作用: 历史回溯, 精确查找                                   │
└─────────────────────────────────────────────────────────────┘

3.2 Layer 1:Built-in Memory

这是最轻量的一层,两个 Markdown 文件:

MEMORY.md(Agent 个人笔记):

# Agent Memory

## 核心原则
- 用户偏好简洁回答,避免冗长解释
- 涉及金融数据时,始终先校验再输出
- 用户主要使用 Go 和 Python

## 学到的模式
- 用户习惯在早上 9-10 点提交代码审查任务
- 用户对性能优化特别关注
- 用户喜欢带代码示例的技术方案

## 避免事项
- 不要用 Markdown 表格,用户觉得在 Telegram 上阅读体验差
- 不要在对话中突然切换语言

USER.md(用户画像):

# User Profile

## 基本信息
- 角色: 后端架构师
- 技术栈: Go, PostgreSQL, Kubernetes
- 时区: Asia/Shanghai

## 偏好
- 代码风格: 显式优于隐式
- 文档偏好: 先给结论再展开
- 沟通风格: 直接,不喜欢客套话

## 项目上下文
- 当前项目: 支付网关重构
- 痛点: 高并发下的分布式事务

这两个文件在每次会话启动时注入系统提示,零延迟访问,确保 Agent 始终"记得"关键信息。

3.3 Layer 2:External Memory Providers

当记忆超过几百字符,就需要外部存储。Hermes 支持 8 种可插拔后端:

# agent/memory_provider.py

class MemoryProvider(ABC):
    """记忆提供者抽象基类"""
    
    @abstractmethod
    async def store(self, key: str, value: str, metadata: dict = None):
        """存储记忆"""
        pass
        
    @abstractmethod
    async def retrieve(self, query: str, top_k: int = 5) -> List[MemoryItem]:
        """语义检索"""
        pass
        
    @abstractmethod
    async def forget(self, key: str):
        """删除记忆"""
        pass


class Mem0Provider(MemoryProvider):
    """Mem0 云端记忆服务"""
    
    def __init__(self, api_key: str, user_id: str):
        self.client = mem0.Client(api_key)
        self.user_id = user_id
        
    async def store(self, key: str, value: str, metadata: dict = None):
        self.client.add({
            "content": value,
            "user_id": self.user_id,
            "metadata": metadata or {}
        })
        
    async def retrieve(self, query: str, top_k: int = 5) -> List[MemoryItem]:
        results = self.client.search(query, user_id=self.user_id, limit=top_k)
        return [MemoryItem(
            content=r["memory"],
            score=r["score"],
            metadata=r.get("metadata", {})
        ) for r in results]


class HonchoProvider(MemoryProvider):
    """
    Honcho: 对话式用户建模
    特点: 通过对话理解用户意图,构建动态用户模型
    """
    
    async def build_user_model(self) -> UserModel:
        """
        基于历史对话构建用户模型
        不是简单的记忆检索,而是"理解用户"
        """
        # Honcho 的独特之处:
        # 1. 分析用户的提问模式
        # 2. 识别用户的思维偏好
        # 3. 预测用户可能的需求
        pass

选择建议

后端特点适用场景
Mem0云端托管,开箱即用个人用户,快速上手
Honcho对话式建模,深度理解需要精细用户画像
RetainDB本地向量库,隐私优先企业内网,数据不出域
Supermemory超大规模,分布式大量用户,高并发

所有历史会话都进 SQLite,带 FTS5 全文索引:

# agent/session_search.py

import sqlite3
import json

class SessionSearch:
    def __init__(self, db_path: str = "~/.hermes/sessions.db"):
        self.conn = sqlite3.connect(db_path)
        self._init_tables()
        
    def _init_tables(self):
        self.conn.executescript("""
            CREATE TABLE IF NOT EXISTS sessions (
                id INTEGER PRIMARY KEY,
                user_id TEXT,
                started_at TIMESTAMP,
                ended_at TIMESTAMP,
                summary TEXT
            );
            
            -- FTS5 全文索引
            CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
                session_id,
                role,
                content,
                tokenize = 'porter unicode61'
            );
        """)
        
    async def search(self, query: str, limit: int = 10) -> List[SearchResult]:
        """
        全文搜索历史对话
        支持中文(unicode61 tokenizer)
        """
        cursor = self.conn.execute("""
            SELECT 
                m.session_id,
                m.role,
                m.content,
                s.summary,
                bm25(messages_fts) as score
            FROM messages_fts m
            JOIN sessions s ON m.session_id = s.id
            WHERE messages_fts MATCH ?
            ORDER BY score
            LIMIT ?
        """, (query, limit))
        
        return [SearchResult(
            session_id=row[0],
            role=row[1],
            content=row[2],
            summary=row[3],
            score=row[4]
        ) for row in cursor.fetchall()]

FTS5 的优势:

  • 毫秒级检索:百万级消息也能快速响应
  • BM25 排序:结果相关性有保障
  • Unicode 支持:中文搜索无障碍

3.5 记忆的上下文围栏

防止记忆污染是关键问题。Hermes 引入 "上下文围栏"(Context Fence) 机制:

class ContextFence:
    """
    隔离不同来源的记忆,防止相互干扰
    """
    
    def __init__(self):
        self.fences = {
            "user_profile": Fence(
                max_tokens=500,
                priority=10,  # 最高优先级
                allowed_tools=["user_lookup"]
            ),
            "learned_skills": Fence(
                max_tokens=1500,
                priority=7,
                allowed_tools=["skill_execute"]
            ),
            "recent_sessions": Fence(
                max_tokens=2000,
                priority=5,
                allowed_tools=["session_search"]
            )
        }
        
    def build_context(self, query: str) -> str:
        """
        根据当前查询,从各围栏中提取相关上下文
        拼接时遵循优先级和 token 预算
        """
        context_parts = []
        total_tokens = 0
        max_context_tokens = 4000  # 上限
        
        for name, fence in sorted(
            self.fences.items(), 
            key=lambda x: x[1].priority, 
            reverse=True
        ):
            if total_tokens >= max_context_tokens:
                break
                
            relevant = fence.retrieve(query)
            tokens = count_tokens(relevant)
            
            if total_tokens + tokens <= fence.max_tokens:
                context_parts.append(f"[{name}]\n{relevant}")
                total_tokens += tokens
                
        return "\n\n".join(context_parts)

这确保了:

  • 用户画像始终可见
  • 学到的技能按需加载
  • 历史对话不淹没关键信息

四、ACP 协议:跨 Agent 通信的标准

4.1 为什么需要 Agent 间通信?

单 Agent 有能力上限。复杂场景需要多 Agent 协作:

  • 专业分工:一个 Agent 擅长代码,另一个擅长数据分析
  • 负载均衡:高峰期分流请求
  • 权限隔离:不同 Agent 不同权限级别

但问题来了:不同 Agent 框架如何通信?

4.2 ACP:Agent Communication Protocol

Hermes 定义了 ACP(Agent Communication Protocol),一种轻量级的 Agent 间通信协议:

# ACP 消息格式
version: "1.0"
message_id: "uuid-xxx"
timestamp: "2026-07-18T10:30:00Z"

from:
  agent_id: "hermes-primary"
  capabilities: ["code_execution", "web_search", "file_ops"]
  
to:
  agent_id: "hermes-analyst"
  capabilities: ["data_analysis", "visualization"]
  
task:
  id: "task-123"
  type: "delegation"  # delegation | query | notification
  action: "analyze_dataset"
  params:
    dataset_path: "/data/sales_2026.csv"
    analysis_type: "trend"
    
context:
  session_id: "sess-456"
  user_id: "user-789"
  parent_task_id: "task-100"
  
priority: 7  # 1-10
timeout_ms: 30000

4.3 ACP 适配器实现

# agent/acp_adapter.py

class ACPAdapter:
    """ACP 协议适配器"""
    
    def __init__(self, agent_id: str, registry_url: str):
        self.agent_id = agent_id
        self.registry = ACPRegistry(registry_url)
        
    async def delegate(
        self, 
        target_capability: str,
        task: dict,
        timeout_ms: int = 30000
    ) -> ACPResponse:
        """
        委托任务给具有特定能力的 Agent
        """
        # 1. 从注册中心查找合适的 Agent
        candidates = await self.registry.find_by_capability(target_capability)
        
        if not candidates:
            raise NoAgentFoundError(f"No agent with capability: {target_capability}")
            
        # 2. 选择最优候选(基于负载、延迟等)
        target = self._select_best_candidate(candidates)
        
        # 3. 构造 ACP 消息
        message = ACPMessage(
            version="1.0",
            message_id=str(uuid.uuid4()),
            timestamp=datetime.utcnow().isoformat(),
            from_agent=AgentRef(
                agent_id=self.agent_id,
                capabilities=self.get_own_capabilities()
            ),
            to_agent=AgentRef(
                agent_id=target.agent_id,
                capabilities=target.capabilities
            ),
            task=Task(
                id=str(uuid.uuid4()),
                type="delegation",
                action=task["action"],
                params=task.get("params", {})
            ),
            context=Context(
                session_id=self.current_session_id,
                user_id=self.current_user_id
            ),
            priority=task.get("priority", 5),
            timeout_ms=timeout_ms
        )
        
        # 4. 发送并等待响应
        response = await self._send_and_wait(message, timeout_ms)
        
        return ACPResponse(
            success=response.success,
            result=response.result,
            error=response.error
        )
        
    async def handle_incoming(self, message: ACPMessage) -> ACPResponse:
        """
        处理收到的 ACP 消息
        """
        # 验证消息
        if not self._validate_message(message):
            return ACPResponse(success=False, error="Invalid message")
            
        # 检查是否有对应能力
        action = message.task.action
        if action not in self._action_handlers:
            return ACPResponse(success=False, error=f"Action not supported: {action}")
            
        # 执行任务
        handler = self._action_handlers[action]
        result = await handler(message.task.params)
        
        return ACPResponse(success=True, result=result)

4.4 ACP 注册中心

# agent/acp_registry.py

class ACPRegistry:
    """
    Agent 注册中心
    支持 etcd / Consul / 内存模式
    """
    
    async def register(self, agent: AgentInfo):
        """
        注册 Agent 及其能力
        """
        key = f"/agents/{agent.agent_id}"
        value = {
            "agent_id": agent.agent_id,
            "capabilities": agent.capabilities,
            "endpoint": agent.endpoint,
            "status": "online",
            "load": 0,  # 当前负载
            "last_heartbeat": time.time()
        }
        await self.store.set(key, json.dumps(value))
        
    async def find_by_capability(self, capability: str) -> List[AgentInfo]:
        """
        按能力查找 Agent
        """
        agents = []
        for key, value in await self.store.list("/agents/"):
            agent_data = json.loads(value)
            if capability in agent_data["capabilities"]:
                if agent_data["status"] == "online":
                    agents.append(AgentInfo(**agent_data))
        return agents
        
    async def heartbeat(self, agent_id: str):
        """
        心跳更新
        """
        key = f"/agents/{agent_id}"
        value = await self.store.get(key)
        if value:
            agent_data = json.loads(value)
            agent_data["last_heartbeat"] = time.time()
            await self.store.set(key, json.dumps(agent_data))

五、内驱演进:强化学习闭环

5.1 为什么需要内驱演进?

外驱演进(Skill + 记忆)解决的是 "知识积累" 问题,但有一个上限:基座模型的能力。

如果模型本身不擅长某个任务,积累再多 Skill 也是在"硬凑"。

内驱演进的目标:通过强化学习微调模型权重,让它本身变得更适合特定任务场景

5.2 GRPO:Group Relative Policy Optimization

Hermes 采用 GRPO(Group Relative Policy Optimization) 算法,这是 PPO 的高效变体:

# rl/grpo_trainer.py

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

class GRPOTrainer:
    """
    Group Relative Policy Optimization
    相比 PPO 的改进:
    1. 不需要单独的 value model
    2. 使用组内相对优势,减少方差
    3. 更稳定,更适合 LLM
    """
    
    def __init__(
        self,
        model: AutoModelForCausalLM,
        ref_model: AutoModelForCausalLM,  # 参考模型,冻结
        tokenizer: AutoTokenizer,
        beta: float = 0.04,  # KL 散度系数
        learning_rate: float = 1e-6
    ):
        self.model = model
        self.ref_model = ref_model
        self.tokenizer = tokenizer
        self.beta = beta
        self.optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
        
    def compute_advantages(self, scores: List[float]) -> torch.Tensor:
        """
        GRPO 的核心: 组内相对优势
        对一组样本的得分进行标准化
        """
        scores_tensor = torch.tensor(scores)
        mean = scores_tensor.mean()
        std = scores_tensor.std() + 1e-8
        advantages = (scores_tensor - mean) / std
        return advantages
        
    async def train_step(
        self,
        prompts: List[str],
        responses: List[str],
        scores: List[float]
    ):
        """
        训练步骤
        scores 由 reward model 给出(或人工标注)
        """
        # 编码
        prompt_ids = self.tokenizer(prompts, return_tensors="pt", padding=True)
        response_ids = self.tokenizer(responses, return_tensors="pt", padding=True)
        
        # 计算当前模型的 log probability
        with torch.no_grad():
            # 参考模型的 log prob(用于 KL 惩罚)
            ref_log_probs = self._compute_log_probs(self.ref_model, prompt_ids, response_ids)
            
        # 当前模型的 log prob
        log_probs = self._compute_log_probs(self.model, prompt_ids, response_ids)
        
        # 计算优势
        advantages = self.compute_advantages(scores)
        
        # GRPO loss
        # L = -E[A * log(pi) - beta * KL(pi || pi_ref)]
        ratio = torch.exp(log_probs - ref_log_probs)
        kl_penalty = self.beta * (log_probs - ref_log_probs)
        
        loss = -(advantages * log_probs - kl_penalty).mean()
        
        # 反向传播
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        
        return {"loss": loss.item(), "mean_score": sum(scores) / len(scores)}
        
    def _compute_log_probs(self, model, prompt_ids, response_ids):
        """计算序列的 log probability"""
        input_ids = torch.cat([prompt_ids.input_ids, response_ids.input_ids], dim=1)
        attention_mask = torch.cat([prompt_ids.attention_mask, response_ids.attention_mask], dim=1)
        
        outputs = model(input_ids=input_ids, attention_mask=attention_mask)
        logits = outputs.logits
        
        # 只取 response 部分的 logits
        prompt_len = prompt_ids.input_ids.shape[1]
        response_logits = logits[:, prompt_len-1:-1, :]
        
        # 计算 log prob
        log_probs = torch.log_softmax(response_logits, dim=-1)
        
        # 收集实际 token 的 log prob
        response_token_ids = response_ids.input_ids
        token_log_probs = log_probs.gather(
            2, 
            response_token_ids.unsqueeze(-1)
        ).squeeze(-1)
        
        # 序列 log prob = sum of token log probs
        sequence_log_probs = token_log_probs.sum(dim=1)
        
        return sequence_log_probs

5.3 Reward Model:从用户反馈到训练信号

GRPO 需要 scores,这些分数从哪来?

Hermes 设计了多层 reward 来源:

# rl/reward_model.py

class RewardModel:
    """
    多源奖励聚合
    """
    
    def __init__(self):
        self.sources = {
            "explicit_feedback": ExplicitFeedbackSource(),  # 用户直接评分
            "implicit_signal": ImplicitSignalSource(),      # 隐式信号(是否采纳、是否追问)
            "task_success": TaskSuccessSource(),            # 任务是否完成
            "efficiency": EfficiencySource()                # 效率(耗时、token 数)
        }
        
    async def compute_score(
        self, 
        session: Session,
        response: str
    ) -> float:
        """
        综合计算奖励分数
        """
        scores = {}
        weights = {
            "explicit_feedback": 0.4,
            "implicit_signal": 0.3,
            "task_success": 0.2,
            "efficiency": 0.1
        }
        
        for name, source in self.sources.items():
            scores[name] = await source.evaluate(session, response)
            
        # 加权平均
        total = sum(scores[name] * weights[name] for name in scores)
        
        return total
        

class ImplicitSignalSource:
    """
    隐式信号评估
    用户不一定给显式反馈,但行为会透露偏好
    """
    
    async def evaluate(self, session: Session, response: str) -> float:
        score = 0.5  # 基准分
        
        # 正向信号
        if session.user_edited_response:
            # 用户编辑了响应 → 原响应不够好
            score -= 0.2
        if session.user_ended_conversation:
            # 用户结束对话 → 任务可能完成
            score += 0.2
        if session.user_said_thanks:
            # 用户说谢谢 → 满意
            score += 0.3
        if session.user_reasked_same:
            # 用户重复问同一问题 → 理解有误
            score -= 0.3
            
        return max(0, min(1, score))

5.4 在线学习流程

# rl/online_learner.py

class OnlineLearner:
    """
    在线学习:边用边学
    """
    
    def __init__(
        self,
        trainer: GRPOTrainer,
        reward_model: RewardModel,
        buffer_size: int = 1000,
        min_samples: int = 50
    ):
        self.trainer = trainer
        self.reward_model = reward_model
        self.buffer = ReplayBuffer(buffer_size)
        self.min_samples = min_samples
        
    async def add_experience(
        self,
        prompt: str,
        response: str,
        session: Session
    ):
        """
        添加新经验到 buffer
        """
        score = await self.reward_model.compute_score(session, response)
        
        self.buffer.add(Experience(
            prompt=prompt,
            response=response,
            score=score,
            timestamp=time.time()
        ))
        
        # 达到阈值时触发训练
        if len(self.buffer) >= self.min_samples:
            await self._train_batch()
            
    async def _train_batch(self):
        """
        批量训练
        """
        batch = self.buffer.sample(self.min_samples)
        
        prompts = [e.prompt for e in batch]
        responses = [e.response for e in batch]
        scores = [e.score for e in batch]
        
        metrics = await self.trainer.train_step(prompts, responses, scores)
        
        print(f"[GRPO] loss={metrics['loss']:.4f}, mean_score={metrics['mean_score']:.2f}")

六、工具链:40+ 内置工具与自定义扩展

6.1 内置工具分类

Hermes 内置 40+ 工具,覆盖 8 大类别:

类别工具示例用途
终端执行execute_command, run_script本地命令执行
文件操作read_file, write_file, search_files文件系统访问
网页搜索web_search, fetch_url信息检索
浏览器自动化browser_navigate, browser_clickWeb 交互
图像生成generate_image, edit_imageAI 绘图
语音处理tts, stt语音合成/识别
数据分析analyze_csv, plot_data数据处理
系统信息get_weather, get_time环境查询

6.2 工具定义规范

# tools/base.py

from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from abc import ABC, abstractmethod

class ToolParameter(BaseModel):
    """工具参数定义"""
    name: str
    type: str  # "string" | "number" | "boolean" | "array" | "object"
    description: str
    required: bool = True
    default: Optional[Any] = None
    enum: Optional[List[str]] = None

class ToolResult(BaseModel):
    """工具执行结果"""
    success: bool
    data: Optional[Any] = None
    error: Optional[str] = None
    metadata: Dict[str, Any] = {}

class BaseTool(ABC):
    """工具基类"""
    
    name: str
    description: str
    parameters: List[ToolParameter]
    
    @abstractmethod
    async def execute(self, **kwargs) -> ToolResult:
        """执行工具"""
        pass
        
    def to_openai_tool(self) -> dict:
        """转换为 OpenAI tool 格式"""
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        p.name: {
                            "type": p.type,
                            "description": p.description,
                            **({"enum": p.enum} if p.enum else {}),
                            **({"default": p.default} if p.default is not None else {})
                        }
                        for p in self.parameters
                    },
                    "required": [p.name for p in self.parameters if p.required]
                }
            }
        }


# tools/web_search.py

class WebSearchTool(BaseTool):
    """网页搜索工具"""
    
    name = "web_search"
    description = "搜索网络信息,返回相关结果"
    parameters = [
        ToolParameter(
            name="query",
            type="string",
            description="搜索关键词",
            required=True
        ),
        ToolParameter(
            name="count",
            type="number",
            description="返回结果数量,默认 10",
            required=False,
            default=10
        ),
        ToolParameter(
            name="freshness",
            type="string",
            description="时间范围: day | week | month | year",
            required=False,
            default="week",
            enum=["day", "week", "month", "year"]
        )
    ]
    
    async def execute(self, query: str, count: int = 10, freshness: str = "week") -> ToolResult:
        # 实际搜索逻辑
        results = await self._search_provider.search(query, count, freshness)
        
        return ToolResult(
            success=True,
            data=results,
            metadata={
                "query": query,
                "count": count,
                "provider": self._search_provider.name
            }
        )

6.3 自定义工具开发

# my_tools/company_api.py

from hermes.tools import BaseTool, ToolParameter, ToolResult

class CompanyAPITool(BaseTool):
    """企业内部 API 工具"""
    
    name = "company_api"
    description = "查询企业内部数据:员工信息、项目状态、代码仓库等"
    parameters = [
        ToolParameter(
            name="resource",
            type="string",
            description="资源类型: employee | project | repo",
            required=True,
            enum=["employee", "project", "repo"]
        ),
        ToolParameter(
            name="query",
            type="string",
            description="查询条件",
            required=True
        )
    ]
    
    def __init__(self, api_base: str, api_key: str):
        self.api_base = api_base
        self.api_key = api_key
        
    async def execute(self, resource: str, query: str) -> ToolResult:
        import aiohttp
        
        async with aiohttp.ClientSession() as session:
            url = f"{self.api_base}/{resource}"
            headers = {"Authorization": f"Bearer {self.api_key}"}
            
            async with session.get(url, params={"q": query}, headers=headers) as resp:
                if resp.status == 200:
                    data = await resp.json()
                    return ToolResult(success=True, data=data)
                else:
                    return ToolResult(success=False, error=f"API error: {resp.status}")

# 注册工具
# config.yaml
tools:
  custom:
    - type: my_tools.company_api.CompanyAPITool
      config:
        api_base: "https://internal.company.com/api"
        api_key: "${COMPANY_API_KEY}"  # 从环境变量读取

七、多平台接入:Gateway 消息网关

7.1 统一接入架构

Hermes 支持同时接入多个消息平台,通过 Gateway 进程统一管理:

┌─────────────────────────────────────────────────────────────┐
│                      Gateway 进程                            │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐          │
│  │Telegram │ │Discord  │ │ Slack   │ │WhatsApp │  ...     │
│  │Handler  │ │Handler  │ │Handler  │ │Handler  │          │
│  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘          │
│       │           │           │           │                │
│       └───────────┴─────┬─────┴───────────┘                │
│                         │                                   │
│                  ┌──────▼──────┐                           │
│                  │ Message Bus │                           │
│                  │ (Redis Pub/Sub)                        │
│                  └──────┬──────┘                           │
└─────────────────────────┼───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                      Agent 进程                              │
│                  消费消息,生成响应                           │
└─────────────────────────────────────────────────────────────┘

7.2 消息处理器实现

# gateway/handlers/telegram.py

from telegram import Update
from telegram.ext import Application, MessageHandler, filters

class TelegramHandler:
    """Telegram 消息处理器"""
    
    def __init__(self, token: str, message_bus: MessageBus):
        self.app = Application.builder().token(token).build()
        self.message_bus = message_bus
        
        # 注册消息处理器
        self.app.add_handler(MessageHandler(filters.TEXT, self._handle_message))
        
    async def _handle_message(self, update: Update, context):
        """收到消息时触发"""
        message = update.message
        
        # 构造统一消息格式
        unified_msg = UnifiedMessage(
            platform="telegram",
            user_id=str(message.from_user.id),
            chat_id=str(message.chat_id),
            content=message.text,
            metadata={
                "username": message.from_user.username,
                "message_id": message.message_id,
                "timestamp": message.date.isoformat()
            }
        )
        
        # 发布到消息总线
        await self.message_bus.publish("incoming", unified_msg)
        
    async def send_response(self, chat_id: str, text: str, **kwargs):
        """发送响应"""
        await self.app.bot.send_message(
            chat_id=int(chat_id),
            text=text,
            parse_mode=kwargs.get("parse_mode", "Markdown")
        )

7.3 消息总线

# gateway/message_bus.py

import asyncio
import json
from typing import Callable
import redis.asyncio as redis

class MessageBus:
    """
    基于 Redis Pub/Sub 的消息总线
    解耦 Gateway 和 Agent
    """
    
    def __init__(self, redis_url: str = "redis://localhost"):
        self.redis = redis.from_url(redis_url)
        self.handlers = {}
        
    async def publish(self, channel: str, message: UnifiedMessage):
        """发布消息"""
        await self.redis.publish(
            channel,
            json.dumps(message.to_dict())
        )
        
    async def subscribe(self, channel: str, handler: Callable):
        """订阅消息"""
        pubsub = self.redis.pubsub()
        await pubsub.subscribe(channel)
        
        async for message in pubsub.listen():
            if message["type"] == "message":
                data = json.loads(message["data"])
                unified_msg = UnifiedMessage.from_dict(data)
                await handler(unified_msg)

八、部署实践:从本地到生产

8.1 本地快速启动

# 一键安装(Linux / macOS / WSL2)
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
source ~/.bashrc

# 配置
hermes config set model openrouter  # 使用 OpenRouter 接入 300+ 模型
hermes config set default_model anthropic/claude-sonnet-4

# 配置 API Key
export OPENROUTER_API_KEY="sk-or-xxx"

# 启动 CLI 模式
hermes chat

# 启动 Telegram Bot
hermes start --platform telegram

8.2 Docker 部署

# docker-compose.yml
version: "3.9"

services:
  hermes-agent:
    image: nousresearch/hermes-agent:latest
    restart: unless-stopped
    environment:
      - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
      - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
    volumes:
      - ./config:/root/.hermes
      - ./skills:/root/.hermes/skills
    ports:
      - "8080:8080"  # 管理 API
    depends_on:
      - redis
      
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis-data:/data
      
volumes:
  redis-data:

8.3 生产级配置

# config.yaml(完整生产配置)

core:
  name: "企业助手"
  description: "智能化办公助手"
  timezone: "Asia/Shanghai"

model:
  provider: openrouter
  default: anthropic/claude-sonnet-4
  fallbacks:
    - openai/gpt-4.1
    - google/gemini-2.5-flash
  temperature: 0.7
  max_tokens: 4096

memory:
  layer1:
    enabled: true
    memory_md_max_chars: 2200
    user_md_max_chars: 1375
  layer2:
    provider: mem0
    api_key: "${MEM0_API_KEY}"
  layer3:
    type: postgres
    connection: "postgresql://user:pass@localhost/hermes"

skills:
  auto_create: true
  min_success_rate: 0.85
  storage_path: "./skills"

rl:
  enabled: true
  algorithm: grpo
  buffer_size: 1000
  min_samples_for_training: 50
  learning_rate: 1e-6
  beta: 0.04

tools:
  built_in:
    - web_search
    - file_ops
    - execute_command
    - browser_automation
  custom:
    - type: my_tools.company_api.CompanyAPITool
      config:
        api_base: "https://internal.company.com/api"

gateway:
  platforms:
    - type: telegram
      token: "${TELEGRAM_BOT_TOKEN}"
    - type: discord
      token: "${DISCORD_BOT_TOKEN}"
    - type: slack
      app_token: "${SLACK_APP_TOKEN}"
      bot_token: "${SLACK_BOT_TOKEN}"
      
monitoring:
  metrics:
    enabled: true
    port: 9090
  logging:
    level: INFO
    format: json

8.4 性能优化建议

# 性能调优清单

# 1. 上下文压缩
# 启用 LLM 摘要,避免 context 爆炸
context_compressor:
  enabled: true
  max_tokens: 3000
  strategy: "summary"  # summary | sliding_window | hybrid

# 2. 并行工具调用
# 支持一次响应中调用多个独立工具
tool_execution:
  parallel: true
  max_concurrent: 5

# 3. 响应缓存
# 相同问题直接返回缓存答案
cache:
  enabled: true
  ttl_seconds: 3600
  semantic_match: true  # 语义相似也命中

# 4. 流式输出
# 边生成边返回,提升用户体验
streaming: true

# 5. 负载均衡
# 多 Agent 实例分担请求
gateway:
  load_balancing:
    strategy: "least_connections"
    health_check_interval: 30

九、与其他框架对比

9.1 能力对比

特性Hermes AgentOpenClawLangChainAutoGPT
自进化学习✅ Skill + RL✅ Skills
记忆系统✅ 三层架构✅ 两层⚠️ 需扩展⚠️ 简单
跨 Agent 通信✅ ACP 协议✅ 会话管理
多平台接入✅ 8+ 平台✅ 10+ 平台⚠️ 需自建
工具生态✅ 40+ 内置✅ 丰富✅ 丰富⚠️ 有限
RL 微调✅ 内置 GRPO
开源协议MITMITMITMIT

9.2 选择建议

场景推荐框架
需要持续学习、越用越聪明Hermes Agent
需要多 Agent 编排、复杂工作流OpenClaw
需要快速原型、生态丰富LangChain
需要简单自动化任务AutoGPT

十、总结与展望

10.1 Hermes Agent 的核心价值

Hermes Agent 的突破不在于某个单点技术,而在于 "自进化闭环" 的完整工程实现:

  1. 外驱演进:从经验中创建 Skill,从交互中沉淀记忆
  2. 内驱演进:通过 RL 微调模型,让基座能力适配场景
  3. 三层记忆:解决"记住什么"和"如何检索"的问题
  4. ACP 协议:让 Agent 间协作有标准可循

这些模块协同工作,让 Agent 真正实现了 "越用越聪明"

10.2 未来发展方向

从代码和设计看,Hermes Agent 未来可能演进的方向:

  1. 多模态学习:目前主要是文本,未来会扩展到图像、语音、视频
  2. 联邦学习:多个 Agent 共享学习成果,但数据不出域
  3. 主动探索:不只是等用户提问,主动发现需要学习的领域
  4. 人格定制:不只是能力进化,还能进化"性格"和"风格"

10.3 对开发者的启示

Hermes Agent 的代码给我们几个重要启示:

  1. 架构分层要清晰:Agent / Gateway / Memory 各司其职
  2. 性能与体验要平衡:后台学习不阻塞前台响应
  3. 扩展性要留足:插件化的工具、可插拔的记忆后端
  4. 可观测性要内置:日志、指标、追踪从一开始就设计好

附录:核心代码模块索引

模块文件行数核心功能
对话主循环agent/conversation_loop.py~3900消息处理、工具调用、技能触发
Agent 初始化agent/agent_init.py~1200配置加载、模块初始化
上下文引擎agent/context_engine.py~800上下文构建、压缩、围栏
记忆管理agent/memory_manager.py~600三层记忆协调
技能审查agent/skill_curator.py~1500技能提取、验证、存储
ACP 适配器agent/acp_adapter.py~900Agent 间通信
GRPO 训练器rl/grpo_trainer.py~700强化学习训练

参考资源

推荐文章

Mysql允许外网访问详细流程
2024-11-17 05:03:26 +0800 CST
快速提升Vue3开发者的效率和界面
2025-05-11 23:37:03 +0800 CST
Vue3中如何处理SEO优化?
2024-11-17 08:01:47 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
总结出30个代码前端代码规范
2024-11-19 07:59:43 +0800 CST
如何在 Vue 3 中使用 TypeScript?
2024-11-18 22:30:18 +0800 CST
如何在 Linux 系统上安装字体
2025-02-27 09:23:03 +0800 CST
程序员茄子在线接单