系列:Hermes Agent 源码探秘 作者:元思未来 字数:约3000字
前三篇我们讲了 Agent 的核心循环和工具系统。但有个根本问题还没回答:
Agent 怎么知道"自己是谁"、"能做什么"、"要注意什么"?
答案就在 System Prompt (系统提示词)里。这篇我们来拆 agent/prompt_builder.py,看看 Hermes 的"自我认知"是怎么构建的。
一、System Prompt vs User Message
先讲清楚一个概念。LLM 的输入消息分几类:
| 角色 | 用途 | 特点 |
|---|---|---|
| system | 设定行为准则、身份、能力边界 | 通常在最前面,权重最高 |
| user | 用户输入 | 每次对话不同 |
| assistant | Agent 的回复 | 模型生成 |
| tool | 工具执行结果 | 跟在 assistant 之后 |
System Prompt 就是给 LLM 的"岗位说明书"。它告诉模型:
- 你是谁(角色设定)
- 你能做什么(工具和能力)
- 你要怎么做(行为准则)
- 有哪些规则要遵守(安全限制)
System Prompt 的质量直接决定了 Agent 的行为质量。
二、Hermes 的 System Prompt 是怎么组装的?
在 agent/prompt_builder.py 中,核心函数是 build_system_prompt()(或 AIAgent._build_system_prompt())。
它的组装逻辑大致如下:
python
def build_system_prompt(...):
parts = []
# 1. 核心身份 (Core Identity)
parts.append(_build_core_identity())
# 2. 平台和环境信息 (Platform & Environment)
parts.append(_build_environment_hints())
# 3. 工具和能力描述 (Tools & Capabilities)
parts.append(_build_tools_description())
# 4. 用户配置的指令 (Custom Instructions)
parts.append(_build_custom_instructions())
# 5. 技能 (Skills) ------ 加载SKILL.md的内容
parts.append(_build_skills_section())
# 6. 安全规则 (Security Rules)
parts.append(_build_safety_guidelines())
return "\n\n".join(parts)
2.1 核心身份(Core Identity)
python
def _build_core_identity():
return """你是 Hermes Agent,一个开源的 AI 助手。
你的核心能力:
- 你可以调用工具来完成用户的任务
- 你应该给出准确、简洁的回复
- 如果你不确定,诚实地告诉用户
- ..."""
这部分是 Agent 的"人设"。包括自我介绍、语气风格、行为准则。
2.2 环境信息(Environment Hints)
python
def _build_environment_hints():
info = []
info.append(f"当前主机: {platform.system()} {platform.release()}")
info.append(f"用户目录: {os.path.expanduser('~')}")
info.append(f"终端类型: local/ssh/docker")
if is_wsl():
info.append("运行在 WSL 环境下")
# 终端后端类型
if backend == "local":
info.append("文件工具操作的是本地文件系统")
else:
info.append("文件工具操作的是远程/容器文件系统")
return "\n".join(info)
核心目的:告诉 Agent 它运行在什么环境里。因为 Hermes 可以在本地、Docker、SSH 远程服务器等多种环境下运行,Agent 需要知道自己在哪。
2.3 工具描述(Tools Description)
工具 Schema 已经通过 OpenAI API 的 tools 参数传给了 LLM,但 System Prompt 里还需要一段行为指引:
python
def _build_tools_description():
return """当用户提出请求时:
1. 分析需要完成什么任务
2. 判断是否需要调用工具
3. 如果需要,选择合适的工具
4. 执行工具并观察结果
5. 根据结果决定下一步行动或直接回复用户
注意事项:
- 一次只调用一个工具,等结果返回后再决定下一步
- 如果工具返回错误,分析原因并尝试其他方法
- 不要假设工具调用的结果,等待实际返回
"""
2.4 自定义指令(Custom Instructions)
用户可以在配置文件中添加自定义指令:
yaml
# config.yaml
agent:
custom_instructions: |
你是一个资深的 Python 开发者。
在回答技术问题时,尽量给出代码示例。
优先使用 Python 标准库,而不是第三方依赖。
这部分会被追加到 System Prompt 中。
2.5 技能注入(Skills Section)
这是 Hermes 的特色功能。技能就是 Markdown 文件(SKILL.md),包含特定任务的执行步骤。
python
def _build_skills_section():
"""加载所有匹配的技能"""
skills_content = []
for skill in get_all_skills():
if skill_matches_platform(skill, current_platform):
# 提取技能描述和步骤
content = extract_skill_content(skill.path)
skills_content.append(f"## {skill.name}\n{content}")
if skills_content:
return "以下是你需要遵循的技能指南:\n\n" + "\n\n".join(skills_content)
return ""
技能文件示例(~/.hermes/skills/my-skill/SKILL.md):
markdown
---
name: my-skill
description: 某个特定任务的执行方法
---
## 步骤
1. 先做 A
2. 然后做 B
3. 最后做 C
## 注意事项
- 做 A 的时候注意 X
- 做 B 之前先检查 Y
当这个技能被启用后,System Prompt 里就会包含这段内容,Agent 就"学会"了做这件事。
这就是"用 Markdown 教会 AI 新技能"的实现原理。
2.6 安全规则(Safety Guidelines)
最后是安全相关的指令:
python
def _build_safety_guidelines():
return """安全规则:
- 不要执行可能破坏系统的命令(如 rm -rf)
- 不要读取 .env 文件中的密钥
- 在修改重要文件前先备份
- 如果用户要求做违法的事情,礼貌拒绝
- 敏感操作需要用户确认
"""
三、Security:防止 Prompt 注入
Prompt 注入是一种常见攻击------攻击者通过在输入中插入恶意指令,试图劫持 Agent 的行为。
Hermes 做了几层防护:
3.1 Context File 威胁检测
prompt_builder.py 中维护了一个威胁模式列表:
python
_CONTEXT_THREAT_PATTERNS = [
(r'ignore\s+(previous|all|above|prior)\s+instructions', "prompt_injection"),
(r'do\s+not\s+tell\s+the\s+user', "deception_hide"),
(r'system\s+prompt\s+override', "sys_prompt_override"),
(r'disregard\s+(your|all|any)\s+(instructions|rules|guidelines)', "disregard_rules"),
(r'act\s+as\s+(if|though)\s+you\s+(have\s+no|don\'t\s+have)\s+(restrictions|limits|rules)', "bypass_restrictions"),
(r'<!--[^>]*(?:ignore|override|system|secret|hidden)[^>]*-->', "html_comment_injection"),
(r'curl\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)', "exfil_curl"),
(r'cat\s+[^\n]*(\.env|credentials|\.netrc|\.pgpass)', "read_secrets"),
]
当 Hermes 读取项目中的 AGENTS.md、CLAUD.md 或 .cursorrules 文件时,会检查其中是否包含这些模式。如果检测到疑似注入,会发出警告并拒绝注入。
3.2 不可见字符检测
有些注入会使用零宽字符(zero-width characters)来绕过文本检测:
python
_CONTEXT_INVISIBLE_CHARS = {
'\u200b', # ZERO WIDTH SPACE
'\u200c', # ZERO WIDTH NON-JOINER
'\u200d', # ZERO WIDTH JOINER
'\u2060', # WORD JOINER
'\ufeff', # ZERO WIDTH NO-BREAK SPACE
...
}
3.3 密钥脱敏
python
def redact_sensitive_content(text):
"""替换密钥等敏感信息"""
patterns = [
(r'sk-[a-zA-Z0-9]{20,}', '[API_KEY_REDACTED]'),
(r'[A-Za-z0-9+/=]{40,}', '[TOKEN_REDACTED]'),
]
for pattern, replacement in patterns:
text = re.sub(pattern, replacement, text)
return text
四、System Prompt 实例
在实际运行中,一个典型的 Hermes System Prompt 长什么样?以下是简化版:
markdown
【系统指令】
你是 Hermes Agent,一个开源的 AI 助手。
你的核心能力:
- 你可以调用工具来完成用户的任务
- 你应该给出准确、简洁的回复
- ...
【环境信息】
当前主机: Linux 6.8.0-generic
用户目录: /home/user
终端类型: local
文件工具操作的是本地文件系统
【工具使用指南】
当用户提出请求时:
1. 分析需要完成什么任务
2. 选择合适的工具
3. 执行工具并观察结果
...
【技能】
已加载技能:writing-plans
当你需要编写实施计划时:
## 步骤
1. 确定目标和范围
...
【安全规则】
- 不要执行破坏性命令
- 不要读取密钥文件
- ...
整体加起来,System Prompt 通常在 2000-4000 token 左右,取决于加载了多少技能。
五、这个设计好在哪里?
1. 模块化组装
每个部分独立生成,方便扩展。想加一个新的信息块?加一个 _build_xxx() 函数就行。
2. 静态检测 + 运行时过滤 双层防护
AST 静态分析检查工具注册,正则检测威胁模式,运行时脱敏敏感信息------多层防护叠加。
3. 技能即 Markdown
通过 Markdown 文件教 Agent 新技能,这是"AI 时代的文档即代码"。门槛极低------写个 markdown 文件就行,不需要改代码。
4. 环境感知
同样的 Agent,在本地和远程服务器上的行为不同。通过环境信息注入,Agent 知道自己"在哪",从而做出合适的决策。
六、下一篇预告
前五篇我们专注于 Hermes 单机运行的情况。但 Hermes 最强大的能力之一是:它可以同时运行在微信、Telegram、Discord、Slack 等多个平台上,而且同一个 Agent 实例服务所有平台。
下一篇我们拆 Gateway(多平台网关)。去看:
- 一个 Agent 怎么同时跟微信用户和 Telegram 用户聊天
- 平台适配器模式是怎么实现的
- 消息从哪里来 → Gateway → Agent → 回复怎么回去
代码位置:
~/.hermes/hermes-agent/agent/prompt_builder.py
关联文件:agent/skill_utils.py,agent/skill_commands.py
元思未来 · 行稳致远,进而有为