skill定义
结合anthropic,microsoft的前沿定义,可以将Skill总结如下:
Agent Skill 是一种可复用、可发现、可组合的能力单元,它将领域知识、执行流程(Workflow)、工具调用策略和资源封装在一起,使 Agent 能够在特定任务场景下稳定地复现专家级行为。
写成公式可以表示为
其中Skill正在成为Agent stack中独立于tool和memory的第4层抽象
其中的resource包含的类别很多,可以是独立的python脚本,专家知识,配置及依赖环境,比如第三方api或者mcp服务配置
Skill内部处理逻辑
从具体的Hermes agent的源码看skill部分的具体逻辑
Skill文件夹的结构
在Hermes的路径/.hermes/skills中可以找到hermes的skill包
常见skill文件夹中结构如下
html
~/.hermes/skills/
└── <category>/
└── <skill-name>/
├── SKILL.md ← 必须,主文件
├── references/ ← 可选,按需加载的深度文档
├── templates/ ← 可选,模板文件(LaTeX、YAML 等)
├── scripts/ ← 可选,辅助脚本
└── assets/ ← 可选,图片等资源
这个skill的文件夹设计和一般的文件夹设计不同,其中包含的reference文件夹是一般skill不具备的,框架会自动发现 references/、templates/、assets/、scripts/ 四个目录,并在 skill_view() 返回值的 linked_files 字段中列出,供 LLM 按需调用。
Skill文件结构
以YAML Frontmatter作为文件开头的,作为描述skill的元数据,被两个---包围,作为LLM在系统提示词中看到的skill元描述内容,复合渐进式披露的原则
通常内容如下:

正文结构
| 顺序 | 章节 | 作用 |
|---|---|---|
| 1 | # 标题 |
skill 名称 |
| 2 | ## When to Use |
触发条件,最关键 |
| 3 | ## Overview / Key Concepts |
核心概念速查 |
| 4 | ## Steps / Workflow |
具体操作步骤 |
| 5 | ## Examples |
可运行的代码示例 |
| 6 | ## Common Pitfalls / Red Flags |
反面教材 |
| 7 | ## Checklist / Verification |
收尾核查清单 |
| 8 | ## References |
指向 references/ 子文件 |
示例
以市场预测的Polymarket Skill为例子,他的文件夹结构如下
XML
skill/
├── SKILL.md ← 2.9KB:概念、典型流程、3个API、注意事项
├── references/
│ └── api-endpoints.md ← API 细节(按需加载)
└── scripts/
└── polymarket.py
Polymarket 是一个预测市场平台,用户可以用真实金钱对未来事件的结果下注,比如"特朗普会赢得2028年大选吗?"、"比特币年底会超过10万美元吗?"。市场价格本身就代表概率------某个结果的价格是 0.65,就意味着市场认为这件事发生的概率是 65%。
其中SKILL.md内容为背景知识和操作规范
api-endpoints.md保存实际api的请求使用方式,是对SKILL.md中的补充

scripts/polymarket.py是一个可以直接运行的命令行工具,零依赖,全程用 Python 标准库。LLM可以直接进行调用
python
python3 polymarket.py search "bitcoin" # 搜索比特币相关市场
python3 polymarket.py trending # 看今天交易量最大的事件
python3 polymarket.py history 0xabc... # 看某个市场的价格历史
python3 polymarket.py book TOKEN_ID # 看 orderbook 深度
Hermes agent中代码逻辑
1.提取skill元数据
/.hermes/hermes-agent/agent/skill_utils.py
python
# skill_utils.py L532-544
def iter_skill_index_files(skills_dir: Path, filename: str):
"""Walk skills_dir yielding sorted paths matching *filename*."""
matches = []
for root, dirs, files in os.walk(skills_dir, followlinks=True):
# 剔除 .git / venv / node_modules / __pycache__ 等
dirs[:] = [d for d in dirs if d not in EXCLUDED_SKILL_DIRS]
if filename in files:
matches.append(Path(root) / filename)
# 按相对路径字典序排列,保证结果稳定
for path in sorted(matches, key=lambda p: str(p.relative_to(skills_dir))):
yield path
提取每个skill的元数据,并在60字符处截断
python
# prompt_builder.py L990-1006
def _parse_skill_file(skill_file: Path) -> tuple[bool, dict, str]:
raw = skill_file.read_text(encoding="utf-8")
frontmatter, _ = parse_frontmatter(raw) # ← 解析 YAML frontmatter
if not skill_matches_platform(frontmatter):
return False, frontmatter, "" # ← 平台过滤(macos/linux/windows)
return True, frontmatter, extract_skill_description(frontmatter)
# skill_utils.py L518-526
def extract_skill_description(frontmatter: Dict[str, Any]) -> str:
raw_desc = frontmatter.get("description", "")
desc = str(raw_desc).strip().strip("'\"")
if len(desc) > 60:
return desc[:57] + "..." # ← 这就是系统提示词里看到的内容
return desc
2.检查两级缓存
/.hermes/hermes-agent/agent/prompt_builder.py
第一层(进程内 LRU)
_SKILLS_PROMPT_CACHE: OrderedDict 最多 8 个 entry
cache_key = (skills_dir, external_dirs, tools, toolsets, platform, disabled_skills)
第二层(磁盘 snapshot)
~/.hermes/.skills_prompt_snapshot.json
含 manifest(每个 SKILL.md 的 mtime + size),用于校验缓存是否过期
校验snapshot逻辑,如果命中则直接复用,而非重新扫描skill
python
# prompt_builder.py L919-934
def _load_skills_snapshot(skills_dir: Path) -> Optional[dict]:
snapshot = json.loads(snapshot_path.read_text(encoding="utf-8"))
# 版本号不匹配(代码升级后)→ 缓存失效
if snapshot.get("version") != _SKILLS_SNAPSHOT_VERSION:
return None
# manifest 不匹配(有 SKILL.md 文件被修改)→ 缓存失效,重新扫描
if snapshot.get("manifest") != _build_skills_manifest(skills_dir):
return None
return snapshot # 命中:直接用预解析的 metadata,跳过全量扫描
snapshot内容

3.组装系统提示词
/.hermes/hermes-agent/agent/system_prompt.py
python
# system_prompt.py L179-195
has_skills_tools = any(
name in agent.valid_tool_names
for name in ['skills_list', 'skill_view', 'skill_manage']
)
if has_skills_tools:
skills_prompt = _r.build_skills_system_prompt(
available_tools=agent.valid_tool_names,
available_toolsets=avail_toolsets,
)
if skills_prompt:
stable_parts.append(skills_prompt) # 加入系统提示词的 stable 层
stable层属于系统提示词的一部分,并且系统提示词不参与上下文压缩,以保持kv缓存的命中率,
系统提示词被分成三层:
stable = 身份 + 工具指引 + skills 索引 ← 本会话不变,prefix cache 保持有效
context = AGENTS.md 等项目上下文 ← 按 cwd 变化
volatile = 记忆 + 时间戳 ← 每次对话都可能变
skills 索引放在 stable 层,意味着一旦 LLM API 缓存了这个 prefix,后续每轮对话不用重新计算这些 token,显著降低成本和延迟。这也是为什么 description 只截断到 60 字符而不是完整加载所有 SKILL.md 内容------索引要尽量短,以便 prefix cache 命中率更高。
最终注入格式
python
# prompt_builder.py L1235-1262
result = (
"## Skills (mandatory)\n"
"Before replying, scan the skills below. If a skill matches or is even "
"partially relevant to your task, you MUST load it with skill_view(name) "
"and follow its instructions. ...\n"
"\n"
"<available_skills>\n"
" research:\n"
" - polymarket: Query Polymarket: markets, prices, orderbooks...\n"
" - research-paper-writing: End-to-end academic paper writing ...\n"
" software-development:\n"
" - spike: Run a time-boxed experiment to validate a technolog...\n"
" ...\n"
"</available_skills>\n"
"\n"
"Only proceed without loading a skill if genuinely none are relevant."
)
4.LLM决策调用
如果LLM在这个用户输入的语句中满足某个skill的触发条件,LLM返回一个tool_call
bash
{
"tool": "skill_view",
"arguments": {
"name": "polymarket"
}
}
agent后端接受到tool_call指令,解析并执行工具
python
# tool_executor.py L542 / L110
def execute_tool_calls_sequential(agent, assistant_message, messages, ...):
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name # = "skill_view"
function_args = json.loads(tool_call.function.arguments)
# 安全门:guardrail 检查、plugin block 检查
guardrail_decision = agent._tool_guardrails.before_call(function_name, function_args)
if not guardrail_decision.allows_execution:
... # 拦截,不执行
# 最终分发执行
result = agent._invoke_tool(function_name, function_args, task_id, ...)
messages.append(make_tool_result_message(function_name, result, tool_call.id))
skill_view通过注册表连接到真实的工具函数
python
# skills_tool.py L1517-1524
registry.register(
name="skill_view",
toolset="skills",
schema=SKILL_VIEW_SCHEMA,
handler=_skill_view_with_bump, # ← 真正执行的函数
check_fn=check_skills_requirements,
emoji="📚",
)
LLM收到返回示例
python
{
"success": true,
"name": "polymarket",
"content": "---\nname: polymarket\n...(SKILL.md 全文 2.9KB)...",
"linked_files": {
"references": ["references/api-endpoints.md"],
"scripts": ["scripts/polymarket.py"]
},
"usage_hint": "To view linked files, call skill_view(name, file_path=...)"
}
5.阅读SKIll
LLM 读完 SKILL.md 后,如果需要实际查询数据,会调用 terminal 工具直接运行脚本:
python
# LLM 的 tool_call:
{
"tool": "terminal",
"arguments": {
"command": "python3 ~/.hermes/skills/research/polymarket/scripts/polymarket.py search 'bitcoin'"
}
}
如果需要查看api细节
python
# LLM 的 tool_call:
{
"tool": "skill_view",
"arguments": {
"name": "polymarket",
"file_path": "references/api-endpoints.md" // ← file_path 参数
}
}
LLM获取足够信息后则会按照SKILL内容做下一步操作。