Hermes Agent 深度实战:自进化 AI Agent 的三层记忆架构与 Skill 自动生成完全指南(上篇)
摘要:Hermes Agent 作为 2026 年 GitHub 增速最快的 AI Agent 框架之一(Star 突破 6 万),其核心创新在于自进化闭环系统——通过三层记忆架构、动态 Skill 生成、Nudge Engine 定时复盘三大子系统,实现了 AI Agent 从"被动执行"到"主动进化"的范式跃迁。本文将从底层原理、架构设计、源码解析、代码实战四个维度,深度拆解 Hermes Agent 的自进化机制,并给出生产级最佳实践。
目录
- 背景与痛点:为什么传统 AI Agent 无法"成长"?
- Hermes Agent 核心理念:自进化闭环系统
- 三层记忆架构深度解析
- 3.1 会话记忆(Session Memory):工作记忆的工程化
- 3.2 持久记忆(Persistent Memory):跨会话的事实库
- 3.3 技能记忆(Skill Memory):从经验到可复用代码
- 自进化子系统之一:Dynamic Skill Generation
- 4.1 Skill 是什么?为什么比 Prompt 更强?
- 4.2 Skill 自动生成的完整流程
- 4.3 Skill 的渐进式加载与 Tool Search
- 自进化子系统之二:Nudge Engine 定时复盘
- 5.1 Nudge Engine 的设计哲学
- 5.2 复盘内容的生成与注入
- 5.3 如何避免"过度 nudging"?
- 自进化子系统之三:Reinforcement Learning from Experience
- 6.1 经验库的构建
- 6.2 奖励信号的设计
- 6.3 与 Model Fine-tuning 的协同
- 代码实战:从零搭建自进化 Hermes Agent
- 7.1 环境准备与安装
- 7.2 配置三层记忆系统
- 7.3 编写一个自进化 Skill
- 7.4 接入 Nudge Engine
- 7.5 完整实战案例:自动化的代码审查 Agent
- 与其他 Agent 框架的深度对比
- 8.1 Hermes vs OpenClaw
- 8.2 Hermes vs LangGraph
- 8.3 Hermes vs AutoGPT
- 生产级最佳实践
- 9.1 记忆系统的容量规划
- 9.2 Skill 的质量控制
- 9.3 Nudge Engine 的频率调优
- 9.4 安全与权限控制
- 性能优化:让自进化更快、更稳
- 10.1 Prompt Cache 策略
- 10.2 记忆检索的向量化
- 10.3 Skill 加载的懒加载机制
- 未来展望:自进化 Agent 的下一个前沿
- 总结
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 具备以下能力:
- 记住:跨会话持久化记忆系统
- 学习:从执行经验中提取可复用的技能(Skill)
- 反思:定期复盘并优化自己的决策逻辑
- 适应:根据用户反馈动态调整行为模式
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 回复、工具调用记录、错误堆栈等。
技术挑战:
- Context Window 有限:即使是最先进的模型(如 Claude Opus 4.5),Context Window 也是有限的(200K tokens ≈ 15 万英文单词)。长会话必然超出。
- 简单截断会丢失关键信息:如果直接丢弃早期对话,可能导致 Agent "忘记"用户之前强调的重要约束。
- 多轮工具调用的依赖关系: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 定义了几类"关键帧",即使在对上下文进行压缩时也会保留:
- 用户明确标记的偏好:如"以后所有 Python 代码都用 Flask 而不是 Django"
- 工具调用的返回值:特别是那些后续被引用的 API 响应
- 错误与修复记录:Agent 犯了错误 → 用户纠正 → 这个"错误-修复"对极具学习价值
- 决策节点: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 采用懒加载 + 相关性排序策略:
- 会话开始时:只注入"高优先级"记忆(如用户明确说"记住这个"的内容)
- 用户提问后:根据问题的关键词,异步搜索持久记忆,将 top-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 会:
- 调用
tool_search("抓取 GitHub 仓库 README") - 发现
github_repo_crawlerSkill 的相似度最高 - 只加载这个 Skill 的详细描述 + 代码模板
- 执行任务
这样,即使你有 1000 个 Skill,每次任务也只需要加载 3-5 个,极大地节省了 tokens。
4. 自进化子系统之一:Dynamic Skill Generation
(本节深入讲解 Skill 自动生成的算法细节、质量评估体系、与 LangChain Tool 的对比)
4.1 Skill 是什么?为什么比 Prompt 更强?
在深入技术细节之前,我们需要厘清:Skill 和传统意义上的"Prompt"有什么区别?
| 维度 | Prompt | Skill |
|---|---|---|
| 粒度 | 通常是"任务级"描述("你是一个 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
4.3 Skill 的渐进式加载与 Tool Search
问题:随着使用时间增长,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 实现了三种检索策略,按优先级依次尝试:
- 关键词匹配(Keyword Matching):用户的查询包含 Skill 的 trigger_conditions 中的关键词
- 向量相似度(Embedding Similarity):将用户查询和 Skill 的 description 都向量化,计算余弦相似度
- 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,会导致:
- Context Window 爆满:System Prompt 中 Nudge 占比过高,挤占了正常任务的上下文
- 模型困惑:太多"改进建议"反而让模型无所适从("我到底应该听哪条?")
- 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()