编程 Hermes Agent 深度实战:自进化 AI Agent 的三层记忆架构与 Skill 自动生成完全指南(上篇)

2026-06-04 04:45:08 +0800 CST views 9

Hermes Agent 深度实战:自进化 AI Agent 的三层记忆架构与 Skill 自动生成完全指南(上篇)

摘要:Hermes Agent 作为 2026 年 GitHub 增速最快的 AI Agent 框架之一(Star 突破 6 万),其核心创新在于自进化闭环系统——通过三层记忆架构、动态 Skill 生成、Nudge Engine 定时复盘三大子系统,实现了 AI Agent 从"被动执行"到"主动进化"的范式跃迁。本文将从底层原理、架构设计、源码解析、代码实战四个维度,深度拆解 Hermes Agent 的自进化机制,并给出生产级最佳实践。


目录

  1. 背景与痛点:为什么传统 AI Agent 无法"成长"?
  2. Hermes Agent 核心理念:自进化闭环系统
  3. 三层记忆架构深度解析
    • 3.1 会话记忆(Session Memory):工作记忆的工程化
    • 3.2 持久记忆(Persistent Memory):跨会话的事实库
    • 3.3 技能记忆(Skill Memory):从经验到可复用代码
  4. 自进化子系统之一:Dynamic Skill Generation
    • 4.1 Skill 是什么?为什么比 Prompt 更强?
    • 4.2 Skill 自动生成的完整流程
    • 4.3 Skill 的渐进式加载与 Tool Search
  5. 自进化子系统之二:Nudge Engine 定时复盘
    • 5.1 Nudge Engine 的设计哲学
    • 5.2 复盘内容的生成与注入
    • 5.3 如何避免"过度 nudging"?
  6. 自进化子系统之三:Reinforcement Learning from Experience
    • 6.1 经验库的构建
    • 6.2 奖励信号的设计
    • 6.3 与 Model Fine-tuning 的协同
  7. 代码实战:从零搭建自进化 Hermes Agent
    • 7.1 环境准备与安装
    • 7.2 配置三层记忆系统
    • 7.3 编写一个自进化 Skill
    • 7.4 接入 Nudge Engine
    • 7.5 完整实战案例:自动化的代码审查 Agent
  8. 与其他 Agent 框架的深度对比
    • 8.1 Hermes vs OpenClaw
    • 8.2 Hermes vs LangGraph
    • 8.3 Hermes vs AutoGPT
  9. 生产级最佳实践
    • 9.1 记忆系统的容量规划
    • 9.2 Skill 的质量控制
    • 9.3 Nudge Engine 的频率调优
    • 9.4 安全与权限控制
  10. 性能优化:让自进化更快、更稳
    • 10.1 Prompt Cache 策略
    • 10.2 记忆检索的向量化
    • 10.3 Skill 加载的懒加载机制
  11. 未来展望:自进化 Agent 的下一个前沿
  12. 总结

1. 背景与痛点:为什么传统 AI Agent 无法"成长"?

1.1 传统 AI Agent 的三大困境

在 Hermes Agent 出现之前,主流 AI Agent 框架(如 LangChain、AutoGPT、OpenClaw)都面临三个核心困境:

困境一:会话即遗忘(Session Amnesia)

传统 Agent 的记忆仅限于当前会话的 Context Window。一旦会话结束,所有的上下文、用户偏好、项目背景全部丢失。下次对话时,Agent 就像失忆一样,需要用户重新介绍项目背景、重复之前的决策。

用户:帮我用 Rust 重构这个 Python 性能瓶颈模块。
Agent:好的,先让我看看代码结构...(分析 10 分钟)
用户:(第二天)继续昨天的重构工作。
Agent:你好!请问你想让我帮你做什么?

困境二:经验无法沉淀(No Experience Accumulation)

传统 Agent 每次执行任务都是"从零开始"。它不会从之前的成功/失败案例中学习,不会积累领域知识,更不会形成可复用的技能模块。这导致:

  • 同样类型的错误会重复犯
  • 同样类型的任务每次都要重新探索
  • Agent 的能力上限被模型训练数据锁死

困境三:无法主动优化(No Proactive Improvement)

传统 Agent 是纯粹的"反应式"系统——用户下达指令,Agent 执行。它不会主动反思"我刚才的执行过程有没有可以优化的地方?",更不会自动调整自己的 Prompt、工具选择策略或决策逻辑。

1.2 自进化:AI Agent 的"奇点"能力

自进化(Self-Improving) 是指 AI Agent 具备以下能力:

  1. 记住:跨会话持久化记忆系统
  2. 学习:从执行经验中提取可复用的技能(Skill)
  3. 反思:定期复盘并优化自己的决策逻辑
  4. 适应:根据用户反馈动态调整行为模式

Hermes Agent 的核心突破在于:它不是通过 Fine-tuning 模型来实现进化(那样成本太高),而是通过软件工程手段(记忆架构 + Skill 生成 + Nudge Engine)在模型外部构建了一套"元认知系统"。

类比:传统 Agent 就像是"每次考试都不带笔记的天才"——智商高但容易忘;Hermes Agent 就像是"会做笔记、会复习、会总结错题本的学习者"——智商或许相同,但长期来看能力差距会越来越大。


2. Hermes Agent 核心理念:自进化闭环系统

2.1 自进化闭环的三大子系统

Hermes Agent 的自进化能力由三大子系统协同实现:

┌─────────────────────────────────────────────────────────┐
│                 自进化闭环系统                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐        ┌─────────────┐               │
│  │  执行任务   │───────▶│  记录经验   │               │
│  └─────────────┘        └─────────────┘               │
│         │                      │                         │
│         ▼                      ▼                         │
│  ┌─────────────┐        ┌─────────────┐               │
│  │  Tool/模型  │◀──────│  Skill 生成 │               │
│  │   调用      │        │  (提炼技能) │               │
│  └─────────────┘        └─────────────┘               │
│         │                      │                         │
│         ▼                      ▼                         │
│  ┌─────────────┐        ┌─────────────┐               │
│  │  用户反馈   │───────▶│ Nudge Engine│               │
│  └─────────────┘        │  (定时复盘) │               │
│                          └─────────────┘               │
│                                 │                       │
│                                 ▼                       │
│                          ┌─────────────┐               │
│                          │  优化决策   │               │
│                          │   逻辑      │               │
│                          └─────────────┘               │
└─────────────────────────────────────────────────────────┘

子系统一:三层记忆架构(Memory System)

负责"记住"——将执行过程中的关键信息分层存储,并在合适的时间注入到上下文。

子系统二:Dynamic Skill Generation(动态技能生成)

负责"学习"——从成功的执行案例中自动提炼出可复用的 Skill(本质上是结构化的 Prompt + 工具调用模板),并在后续类似任务中自动加载。

子系统三:Nudge Engine(定时复盘引擎)

负责"反思"——在后台定期回顾近期的执行记录,生成"改进建议"(Nudge),并注入到后续会话的 System Prompt 中。

2.2 为什么这三者的协同如此重要?

单独看,每个子系统都不是全新发明:

  • 记忆系统:OpenClaw 也有 MEMORY.md / USER.md
  • Skill 生成:LangChain 的 Tool 也有类似概念
  • 定时复盘:人类程序员也会定期 code review

但 Hermes Agent 的革命性在于:这三者形成了一个正反馈闭环

记忆系统积累原始素材
        ↓
Skill 生成提炼可复用模式
        ↓
Nudge Engine 发现系统性问题
        ↓
优化决策逻辑 → 更好的执行 → 更高质量的记忆
        ↓
      (循环迭代)

这种闭环使得 Hermes Agent 的能力曲线不是线性的(像传统 Agent 那样每次都是从零开始),而是指数级的(每次执行都在为未来赋能)。


3. 三层记忆架构深度解析

Hermes Agent 的记忆系统是其自进化能力的基石。它不是简单地"把所有对话存进数据库",而是精妙地分为三层,每层有明确的职责边界、存储格式和注入策略。

3.1 会话记忆(Session Memory):工作记忆的工程化

定义:会话记忆是指当前会话(Session)内的所有上下文信息,包括用户消息、Agent 回复、工具调用记录、错误堆栈等。

技术挑战

  1. Context Window 有限:即使是最先进的模型(如 Claude Opus 4.5),Context Window 也是有限的(200K tokens ≈ 15 万英文单词)。长会话必然超出。
  2. 简单截断会丢失关键信息:如果直接丢弃早期对话,可能导致 Agent "忘记"用户之前强调的重要约束。
  3. 多轮工具调用的依赖关系:Agent 可能在第 5 轮调用了某个 API,第 15 轮需要引用该 API 的返回值。如果中间的上下文被压缩,依赖关系就断了。

Hermes 的解决方案:动态摘要 + 关键帧保留

Hermes Agent 在 run_agent.py 中实现了一套动态摘要策略

# 伪代码:Hermes 的上下文管理逻辑
def manage_context(session_history, max_tokens=200000):
    current_tokens = count_tokens(session_history)
    
    if current_tokens <= max_tokens * 0.7:
        # 70% 以下:完整保留
        return session_history
    
    elif current_tokens <= max_tokens * 0.9:
        # 70%-90%:对早期消息进行摘要
        early_messages = session_history[:-10]  # 保留最近 10 条
        summary = llm.summarize(early_messages)
        return [summary] + session_history[-10:]
    
    else:
        # 90% 以上:激进压缩 + 关键帧提取
        key_frames = extract_key_frames(session_history)
        summary = llm.summarize(session_history[:-5])
        return [summary] + key_frames + session_history[-5:]

关键帧(Key Frame)提取算法

Hermes 定义了几类"关键帧",即使在对上下文进行压缩时也会保留:

  1. 用户明确标记的偏好:如"以后所有 Python 代码都用 Flask 而不是 Django"
  2. 工具调用的返回值:特别是那些后续被引用的 API 响应
  3. 错误与修复记录:Agent 犯了错误 → 用户纠正 → 这个"错误-修复"对极具学习价值
  4. 决策节点:Agent 在多个可选方案中做了选择,并给出了理由

代码示例:关键帧提取

def extract_key_frames(messages):
    """从会话历史中提取关键帧"""
    key_frames = []
    
    for i, msg in enumerate(messages):
        # 规则 1:用户消息含"记住"、"以后"等关键词
        if msg.role == "user" and contains_preference_keywords(msg.content):
            key_frames.append({
                "type": "user_preference",
                "content": msg.content,
                "timestamp": msg.timestamp
            })
        
        # 规则 2:工具调用并返回了复杂对象
        if msg.role == "tool" and is_complex_return_value(msg.content):
            key_frames.append({
                "type": "tool_return",
                "tool_name": msg.tool_name,
                "content": compress_tool_return(msg.content),
                "index": i
            })
        
        # 规则 3:错误消息 + 后续修复
        if is_error_message(messages[i]):
            if i + 1 < len(messages) and is_fix_message(messages[i + 1]):
                key_frames.append({
                    "type": "error_fix_pair",
                    "error": messages[i].content,
                    "fix": messages[i + 1].content
                })
    
    return key_frames

会话记忆的生命周期

  • 创建:会话开始时创建空的 session.db(SQLite)
  • 写入:每轮对话结束后,异步写入(不阻塞主流程)
  • 压缩:当 tokens 接近阈值时触发动态摘要
  • 归档:会话结束后,会话记忆不会立即删除,而是转移到"持久记忆"层(如果内容有价值)

3.2 持久记忆(Persistent Memory):跨会话的事实库

定义:持久记忆是指需要跨会话保留的信息,包括:

  • 用户画像(偏好、习惯、技术栈)
  • 项目背景(技术选型、架构决策、代码规范)
  • 领域知识(特定行业的术语、流程、约束)
  • 历史决策("为什么我们放弃了 MySQL 选了 PostgreSQL")

技术底座:SQLite + FTS5

Hermes Agent 选择 SQLite 作为持久记忆的存储引擎,而非向量数据库(如 Pinecone、Weaviate)。这个选择背后有深刻的工程考量:

为什么不用向量数据库?

维度向量数据库SQLite + FTS5
部署复杂度需要独立服务,运维成本高单文件,零配置
查询灵活性只支持相似度搜索支持全文搜索、SQL 查询、混合检索
成本通常按向量数收费免费
性能高维向量检索快文本搜索更快(特别是对于短文本)
事务支持完整 ACID

SQLite 表结构设计

-- 用户画像表
CREATE TABLE user_profile (
    id INTEGER PRIMARY KEY,
    key TEXT UNIQUE NOT NULL,  -- 如 "preferred_language", "code_style"
    value TEXT NOT NULL,
    confidence REAL DEFAULT 1.0,  -- 置信度(0-1)
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    source TEXT  -- 来源: "explicit"(用户明确说)/"inferred"(推理得到)
);

-- 项目背景表
CREATE TABLE project_context (
    id INTEGER PRIMARY KEY,
    project_name TEXT NOT NULL,
    key TEXT NOT NULL,  -- 如 "architecture", "dependencies"
    value TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 会话摘要表(用于跨会话检索)
CREATE TABLE session_summaries (
    id INTEGER PRIMARY KEY,
    session_id TEXT NOT NULL,
    summary TEXT NOT NULL,
    topics TEXT,  -- 逗号分隔的主题标签
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- FTS5 虚拟表(启用全文搜索)
CREATE VIRTUAL TABLE memory_fts USING fts5(
    content,  -- 指向原始表的某一列
    content_rowid,  -- 原始表的 ROWID
    tokenize = 'porter unicode61'  -- 英文词干提取 + Unicode 支持
);

注入策略:何时将持久记忆注入上下文?

Hermes Agent 采用懒加载 + 相关性排序策略:

  1. 会话开始时:只注入"高优先级"记忆(如用户明确说"记住这个"的内容)
  2. 用户提问后:根据问题的关键词,异步搜索持久记忆,将 top-3 相关记忆注入
  3. 工具调用前:检查工具描述中是否引用了某些记忆(如"使用项目标准的错误处理模式")

代码示例:持久记忆的检索与注入

def retrieve_relevant_memories(current_query, db_path="~/.hermes/memory.db"):
    """根据当前查询检索相关持久记忆"""
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    
    # 步骤 1:FTS5 全文搜索
    cursor.execute("""
        SELECT content, rank 
        FROM memory_fts 
        WHERE memory_fts MATCH ?
        ORDER BY rank
        LIMIT 10
    """, (tokenize(current_query),))
    
    fts_results = cursor.fetchall()
    
    # 步骤 2:如果是技术相关问题,还查询 user_profile
    if is_technical_query(current_query):
        cursor.execute("""
            SELECT key, value 
            FROM user_profile 
            WHERE key LIKE '%tech%' OR key LIKE '%language%' OR key LIKE '%framework%'
        """)
        profile_results = cursor.fetchall()
    else:
        profile_results = []
    
    # 步骤 3:合并、去重、排序
    all_memories = merge_and_rank(fts_results, profile_results)
    
    return all_memories[:3]  # 只返回 top-3


def inject_memories_to_context(memories, system_prompt):
    """将检索到的记忆注入到 System Prompt"""
    if not memories:
        return system_prompt
    
    memory_section = "\n\n## 相关记忆(自动注入)\n"
    for i, mem in enumerate(memories, 1):
        memory_section += f"{i}. {mem}\n"
    
    # 注入到 System Prompt 的末尾(这样模型更容易"看到")
    return system_prompt + memory_section

3.3 技能记忆(Skill Memory):从经验到可复用代码

定义:技能记忆是 Hermes Agent 最具革命性的创新。它指的是:从成功的执行案例中自动提炼出的、可复用的"技能模块"

Skill 的本质

在 Hermes 的语境中,一个 Skill 不是简单的"代码片段",而是一个结构化的知识包:

# 示例 Skill:调用 GitHub API 时自动处理 Rate Limit
name: "github_api_rate_limit_handler"
description: "当调用 GitHub API 遇到 403 Rate Limit 时,自动等待并重试"
trigger_conditions:
  - "用户要求调用 GitHub API"
  - "之前的工具调用返回了 403 错误"
required_tools:
  - "web_fetch"
  - "exec"
execution_steps:
  1. "检查 HTTP 响应头中的 X-RateLimit-Remaining"
  2. "如果 ≤ 5,计算 X-RateLimit-Reset 时间,等待对应秒数"
  3. "重试请求"
code_template: |
  import time
  import requests
  
  def github_api_call(url, token):
      headers = {"Authorization": f"token {token}"}
      resp = requests.get(url, headers=headers)
      
      if resp.status_code == 403 and "rate limit" in resp.text.lower():
          reset_time = int(resp.headers.get("X-RateLimit-Reset", 0))
          wait_seconds = reset_time - time.time()
          if wait_seconds > 0:
              print(f"Rate limit hit. Waiting {wait_seconds} seconds...")
              time.sleep(wait_seconds + 1)
          return requests.get(url, headers=headers)  # 重试
      
      return resp
test_cases:
  - "输入:GitHub API 返回 403;输出:等待后重试"
  - "输入:GitHub API 返回 200;输出:直接返回响应"

Skill 的自动生成流程

Hermes Agent 的 Skill 生成不是"一次性"的,而是一个持续迭代的过程:

步骤 1:检测"可学习效果"
  ↓
  Agent 成功完成一个任务后,Nudge Engine 会问:
  "这个任务的执行过程是否可以被抽象成通用模式?"
  
步骤 2:提炼 Skill 草稿
  ↓
  将执行过程中的工具调用序列、Prompt 模板、错误处理逻辑
  提取成结构化的 Skill YAML
  
步骤 3:人工审核(可选)
  ↓
  如果 confidence > 0.8,自动激活;
  如果 0.5 < confidence < 0.8,提交给用户审核;
  如果 confidence < 0.5,丢弃
  
步骤 4:注册到 Skill Registry
  ↓
  将 Skill 写入 ~/.hermes/skills/ 目录,并更新 skills/index.json
  
步骤 5:渐进式加载
  ↓
  后续会话中,只有当触发条件匹配时,才加载该 Skill
  (避免所有 Skill 一次性全部注入导致 Context Window 爆满)

代码示例:Skill 生成的简化实现

def generate_skill_from_execution(execution_log):
    """从执行日志中生成 Skill 草稿"""
    
    # 步骤 1:提取工具调用序列
    tool_calls = [log for log in execution_log if log.type == "tool_call"]
    
    # 步骤 2:识别模式
    # 例如:如果连续调用了 web_fetch → 解析 JSON → 存入数据库
    # 这可能是个"爬虫 + 存储"的通用模式
    pattern = identify_pattern(tool_calls)
    
    if pattern.confidence < 0.5:
        return None  # 置信度太低,不生成
    
    # 步骤 3:生成 Skill YAML
    skill_yaml = {
        "name": pattern.name,
        "description": pattern.description,
        "trigger_conditions": pattern.trigger_conditions,
        "required_tools": pattern.required_tools,
        "execution_steps": pattern.steps,
        "code_template": pattern.code_template,
        "test_cases": pattern.test_cases
    }
    
    # 步骤 4:写入文件
    skill_path = f"~/.hermes/skills/{pattern.name}.yaml"
    with open(skill_path, 'w') as f:
        yaml.dump(skill_yaml, f)
    
    # 步骤 5:更新索引
    update_skill_index(pattern.name, skill_path)
    
    return skill_yaml

Skill 的渐进式加载(Progressive Loading)

Hermes Agent 的一个核心优化是:不把所有 Skill 一次性全部加载到 Context Window,而是根据当前任务的"触发条件"动态加载。

这通过 Tool Search 机制实现:

def tool_search(query, skill_registry, limit=5):
    """根据查询动态搜索相关 Skill"""
    
    # 步骤 1:向量化查询
    query_embedding = embed_model.encode(query)
    
    # 步骤 2:计算相似度
    similarities = []
    for skill in skill_registry.list_skills():
        skill_embedding = embed_model.encode(skill.description)
        sim = cosine_similarity(query_embedding, skill_embedding)
        similarities.append((skill, sim))
    
    # 步骤 3:返回 top-k
    similarities.sort(key=lambda x: x[1], reverse=True)
    return [skill for skill, sim in similarities[:limit]]

当用户说"帮我抓取这个 GitHub 仓库的 README"时,Agent 会:

  1. 调用 tool_search("抓取 GitHub 仓库 README")
  2. 发现 github_repo_crawler Skill 的相似度最高
  3. 只加载这个 Skill 的详细描述 + 代码模板
  4. 执行任务

这样,即使你有 1000 个 Skill,每次任务也只需要加载 3-5 个,极大地节省了 tokens。


4. 自进化子系统之一:Dynamic Skill Generation

(本节深入讲解 Skill 自动生成的算法细节、质量评估体系、与 LangChain Tool 的对比)

4.1 Skill 是什么?为什么比 Prompt 更强?

在深入技术细节之前,我们需要厘清:Skill 和传统意义上的"Prompt"有什么区别?

维度PromptSkill
粒度通常是"任务级"描述("你是一个 Python 专家")"步骤级"指导(先调这个 API,再解析那个字段)
可复用性低(每次都要重新写)高(一次生成,处处使用)
可组合性强(Skill A 可以调用 Skill B)
可验证性难(Prompt 的效果很难量化)强(Skill 有 test_cases,可以自动化测试)

核心差异:Prompt 是"软指令"(模型可以不遵守),Skill 是"硬逻辑"(包含代码模板、工具调用序列、错误处理流程)。

4.2 Skill 自动生成的完整流程

Hermes Agent 的 Skill 生成分为离线生成在线生成两种模式。

离线生成(推荐)

在 Nudge Engine 的定时复盘阶段,批量分析过去 N 天的执行日志,识别出高频模式,生成 Skill。

优点:不阻塞用户交互;可以跨多个会话找模式(单个会话可能看不出通用性)
缺点:有延迟(新任务不会立即生成 Skill)

在线生成(实验性)

在任务执行成功后,立即询问用户"是否将这次的执行过程保存为 Skill?"

优点:即时反馈,用户可以选择保留哪些 Skill
缺点:打断用户工作流;单个任务的模式可能不具备通用性

离线生成的算法流程

def offline_skill_generation(execution_logs, min_frequency=3):
    """离线批量生成 Skill"""
    
    # 步骤 1:聚类相似任务
    task_clusters = cluster_tasks(execution_logs)
    
    # 步骤 2:在每个聚类中识别高频工具调用序列
    for cluster in task_clusters:
        common_sequences = find_common_subsequences(
            [log.tool_calls for log in cluster],
            min_frequency=min_frequency
        )
        
        # 步骤 3:为每个高频序列生成 Skill
        for seq in common_sequences:
            skill = generate_skill_from_sequence(seq)
            
            # 步骤 4:质量评估
            quality_score = evaluate_skill_quality(skill)
            
            if quality_score > 0.7:
                register_skill(skill)
    
    return len(registered_skills)


def find_common_subsequences(tool_call_sequences, min_frequency):
    """找出多个执行日志中共同的工具调用子序列"""
    from collections import Counter
    
    # 将所有工具调用序列转换成字符串表示
    # 例如:["web_fetch", "json_parse", "db_insert"]
    seq_strings = ['->'.join([tc.tool_name for tc in seq]) 
                   for seq in tool_call_sequences]
    
    # 统计所有子序列的频率
    subseq_counter = Counter()
    for seq_str in seq_strings:
        subseqs = generate_all_subsequences(seq_str.split('->'))
        for subseq in subseqs:
            subseq_counter['->'.join(subseq)] += 1
    
    # 返回频率 ≥ min_frequency 的子序列
    return [subseq for subseq, count in subseq_counter.items() 
            if count >= min_frequency]

质量评估体系

不是所有"高频序列"都值得变成 Skill。Hermes Agent 用多维度质量评估来过滤低质量 Skill:

def evaluate_skill_quality(skill):
    """评估 Skill 的质量(0-1 分)"""
    scores = {}
    
    # 维度 1:通用性(Generality)
    # 如果 Skill 的 trigger_conditions 太窄(如包含具体的 URL、文件名),
    # 则通用性差
    scores['generality'] = compute_generality(skill.trigger_conditions)
    
    # 维度 2:正确性(Correctness)
    # 运行 test_cases,看通过率
    scores['correctness'] = run_test_cases(skill.test_cases)
    
    # 维度 3:简洁性(Conciseness)
    # 如果 Skill 的 execution_steps 太长(>10 步),可能过于复杂
    scores['conciseness'] = 1.0 / (1.0 + len(skill.execution_steps) * 0.1)
    
    # 维度 4:可组合性(Composability)
    # 如果 Skill 可以调用其他 Skill,则加分
    scores['composability'] = 0.5 if skill.required_tools else 0.0
    for tool in skill.required_tools:
        if tool.startswith('skill:'):
            scores['composability'] += 0.1
    
    # 加权求和
    weights = {'generality': 0.3, 'correctness': 0.4, 
               'conciseness': 0.2, 'composability': 0.1}
    final_score = sum(scores[k] * weights[k] for k in weights)
    
    return final_score

问题:随着使用时间增长,Skill 数量可能达到几百甚至几千个。如果每次任务都将所有 Skill 的描述都注入到 Context Window,会很快耗尽 tokens。

解决方案:Tool Search——按需检索相关 Skill。

Tool Search 的工作原理

# 当用户发送消息后,Hermes Agent 的处理流程

user_message = "帮我分析这个 GitHub 仓库的代码质量"

# 传统模式:将所有 Skill 的描述都注入
# tools_description = format_all_skills(skill_registry)  # 可能 10K tokens

# Tool Search 模式:只注入 3 个最相关的 Skill
relevant_skills = tool_search(
    query=user_message,
    skill_registry=skill_registry,
    limit=3
)
tools_description = format_skills(relevant_skills)  # 只有 500 tokens!

# 将 tools_description 注入到 System Prompt
system_prompt = base_system_prompt + "\n\n可用工具:\n" + tools_description

Tool Search 的实现细节

Hermes Agent 实现了三种检索策略,按优先级依次尝试:

  1. 关键词匹配(Keyword Matching):用户的查询包含 Skill 的 trigger_conditions 中的关键词
  2. 向量相似度(Embedding Similarity):将用户查询和 Skill 的 description 都向量化,计算余弦相似度
  3. LLM Reranking:如果前两步返回了 >10 个候选,用轻量级 LLM(如 GPT-3.5)重新排序
def tool_search(query, skill_registry, limit=5, use_reranking=True):
    """多策略 Tool Search"""
    
    candidates = set()
    
    # 策略 1:关键词匹配
    for skill in skill_registry.list_skills():
        for condition in skill.trigger_conditions:
            if keyword_match(query, condition):
                candidates.add(skill)
    
    # 策略 2:向量相似度
    query_embedding = embed_model.encode(query)
    for skill in skill_registry.list_skills():
        skill_embedding = embed_model.encode(skill.description)
        sim = cosine_similarity(query_embedding, skill_embedding)
        if sim > 0.7:  # 相似度阈值
            candidates.add(skill)
    
    # 策略 3:LLM Reranking(可选)
    if use_reranking and len(candidates) > limit:
        candidates = llm_rerank(query, candidates, top_k=limit)
    
    return list(candidates)[:limit]

5. 自进化子系统之二:Nudge Engine 定时复盘

(本节讲解 Nudge Engine 的设计哲学、复盘内容的生成算法、如何避免"过度 nudging")

5.1 Nudge Engine 的设计哲学

核心洞察:AI Agent 的执行过程会产生大量"隐性知识"——

  • 为什么这次任务成功了,上次却失败了?
  • 用户在哪个环节经常纠正 Agent?
  • Agent 在哪些类型的任务上耗时最长?

这些隐性知识如果能被提炼出来,并" nudging"(轻推)Agent 在后续任务中避免重复错误,就能实现"自进化"。

Nudge 的定义

在 Hermes Agent 中,一个 Nudge 是一条结构化的改进建议,格式如下:

- id: "nudge_20260603_001"
  type: "error_prevention"  # 类型:错误预防 / 效率优化 / 用户偏好适应
  priority: "high"  # high / medium / low
  content: "当用户要求'安装 XXX'时,先检查系统是否已安装(运行 `which XXX`),避免重复安装"
  evidence: "2026-06-01 会话 #1234 中,用户要求安装 ffmpeg,Agent 直接执行 `apt-get install ffmpeg`,但系统已安装,导致冲突"
  applicable_scenarios:
    - "用户要求安装软件包"
    - "用户要求安装 CLI 工具"
  created_at: "2026-06-03T10:30:00Z"

Nudge Engine 的运行频率

Nudge Engine 是一个后台异步进程,不会阻塞用户交互。默认配置:

  • 浅度复盘:每 10 分钟运行一次,只分析最近 10 个工具调用的日志
  • 深度复盘:每天凌晨 3:00 运行一次,分析过去 7 天的所有执行日志

5.2 复盘内容的生成与注入

浅度复盘(Lightweight Review)

浅度复盘关注"即时错误",目标是快速发现并纠正 Agent 的低级错误。

def lightweight_review(recent_logs, max_logs=10):
    """浅度复盘:发现即时错误"""
    nudges = []
    
    # 规则 1:检测重复错误
    error_counts = Counter([log.error_type for log in recent_logs if log.is_error])
    for error_type, count in error_counts.items():
        if count >= 2:
            nudge = {
                "type": "error_prevention",
                "priority": "high",
                "content": f"检测到重复错误:{error_type}。建议:{get_error_fix(error_type)}",
                "evidence": f"最近 {len(recent_logs)} 次调用中出现了 {count} 次"
            }
            nudges.append(nudge)
    
    # 规则 2:检测低效工具调用
    for log in recent_logs:
        if log.duration > 30:  # 单次工具调用超过 30 秒
            nudge = {
                "type": "efficiency_optimization",
                "priority": "medium",
                "content": f"工具 {log.tool_name} 调用耗时 {log.duration} 秒,考虑优化或替换",
                "evidence": f"会话 {log.session_id} 中的调用记录"
            }
            nudges.append(nudge)
    
    return nudges

深度复盘(Deep Review)

深度复盘使用 LLM 来分析长期模式,生成更高质量的 Nudge。

def deep_review(all_logs, time_window_days=7):
    """深度复盘:使用 LLM 发现长期模式"""
    
    # 步骤 1:将日志按"任务"聚合
    tasks = aggregate_logs_by_task(all_logs)
    
    # 步骤 2:为每个任务生成摘要
    task_summaries = []
    for task in tasks:
        summary = llm.summarize(f"""
            任务描述:{task.description}
            执行步骤:{task.steps}
            结果:{task.result}
            用户反馈:{task.user_feedback}
        """)
        task_summaries.append(summary)
    
    # 步骤 3:让 LLM 发现模式
    prompt = f"""
        以下是过去 {time_window_days} 天的任务执行摘要:
        {format_task_summaries(task_summaries)}
        
        请分析并回答:
        1. Agent 在哪些类型的任务上表现最好?为什么?
        2. Agent 在哪些类型的任务上经常失败?根本原因是什么?
        3. 有没有重复出现的用户纠正?这反映了 Agent 的哪些不足?
        4. 生成 3-5 条具体的改进建议(Nudge)。
        
        输出格式:YAML
    """
    
    response = llm.complete(prompt)
    nudges = yaml.safe_load(response)
    
    return nudges

Nudge 的注入策略

生成的 Nudge 不是"立即生效"的,而是存储在 ~/.hermes/nudges/ 目录,并在后续会话中渐进式注入

def inject_nudges_to_prompt(system_prompt, max_nudges=3):
    """将 Nudge 注入到 System Prompt"""
    
    # 步骤 1:读取所有未应用的 Nudge
    pending_nudges = load_pending_nudges()
    
    if not pending_nudges:
        return system_prompt
    
    # 步骤 2:按优先级排序
    pending_nudges.sort(key=lambda n: priority_score(n), reverse=True)
    
    # 步骤 3:只注入 top-N(避免 Context Window 过长)
    nudges_to_inject = pending_nudges[:max_nudges]
    
    # 步骤 4:格式化为 System Prompt 的一部分
    nudge_section = "\n\n## 改进建议(自动生成)\n"
    for i, nudge in enumerate(nudges_to_inject, 1):
        nudge_section += f"{i}. 【{nudge.priority}】{nudge.content}\n"
        # 标记已应用
        mark_nudge_applied(nudge.id)
    
    return system_prompt + nudge_section

5.3 如何避免"过度 nudging"?

问题:如果 Nudge Engine 生成了太多 Nudge,会导致:

  1. Context Window 爆满:System Prompt 中 Nudge 占比过高,挤占了正常任务的上下文
  2. 模型困惑:太多"改进建议"反而让模型无所适从("我到底应该听哪条?")
  3. Nudge 冲突:新旧 Nudge 可能相互矛盾(如旧 Nudge 说"用 Flask",新 Nudge 说"用 FastAPI")

解决方案:Nudge 生命周期管理。

class NudgeLifecycleManager:
    """Nudge 生命周期管理"""
    
    def __init__(self, nudge_db_path="~/.hermes/nudges/db.sqlite"):
        self.db_path = nudge_db_path
        self.init_db()
    
    def apply_nudge(self, nudge):
        """应用 Nudge(注入到 System Prompt)"""
        # 检查是否有冲突的 Nudge
        conflicts = self.find_conflicting_nudges(nudge)
        if conflicts:
            # 如果新 Nudge 的优先级更高,则禁用旧 Nudge
            for old_nudge in conflicts:
                if nudge.priority_score > old_nudge.priority_score:
                    self.disable_nudge(old_nudge.id)
                else:
                    return False  # 不应用新 Nudge
        
        # 应用新 Nudge
        self.mark_applied(nudge.id)
        return True
    
    def find_conflicting_nudges(self, new_nudge):
        """查找与 new_nudge 冲突的已应用 Nudge"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("""
            SELECT * FROM nudges 
            WHERE is_applied = 1 
              AND id != ?
              AND (
                  -- 同一场景下的不同建议
                  json_extract(applicable_scenarios, '$') 
                  LIKE json_extract(?, '$.applicable_scenarios')
              )
        """, (new_nudge.id, json.dumps(new_nudge)))
        
        return cursor.fetchall()
    
    def cleanup_old_nudges(self, days_to_keep=30):
        """清理超过 N 天的已应用 Nudge"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("""
            DELETE FROM nudges 
            WHERE is_applied = 1 
              AND julianday('now') - julianday(applied_at) > ?
        """, (days_to_keep,))
        
        conn.commit()

推荐文章

Mysql允许外网访问详细流程
2024-11-17 05:03:26 +0800 CST
如何在Vue3中处理全局状态管理?
2024-11-18 19:25:59 +0800 CST
JavaScript设计模式:观察者模式
2024-11-19 05:37:50 +0800 CST
基于Flask实现后台权限管理系统
2024-11-19 09:53:09 +0800 CST
纯CSS绘制iPhoneX的外观
2024-11-19 06:39:43 +0800 CST
宝塔面板 Nginx 服务管理命令
2024-11-18 17:26:26 +0800 CST
php curl并发代码
2024-11-18 01:45:03 +0800 CST
程序员茄子在线接单