《为什么 MCP、Skill、RAG 能工作?从 Conversation Loop 看现代 Agent 的底层架构》

作 者:吴佳浩Alben
撰稿时间:2026.7.10
更新时间:2026.7.13
前言
很多文章介绍 Agent 时,都会分别讲 MCP、Skill、Function Calling、RAG,却很少回答一个更关键的问题:
MCP、Skill、RAG 为什么能够协同工作?它们究竟是如何融入 Agent 的?
答案,其实都藏在 Agent 的执行主线------Conversation Loop 中。
本文将结合 Hermes 与 OpenClaw 的真实源码,从 Conversation Loop 出发,逐步串联 Tool Calling、Tool Registry、MCP、Skill、Memory(RAG) 等核心模块,完整解析现代 Agent 的底层架构,解释一个 Agent 为什么能够不断思考、调用工具、检索知识,并最终完成复杂任务。
MCP、Skill、Memory(RAG)并不是独立工作的能力,它们都是 Conversation Loop 中可被 LLM 调度的组成部分。
Conversation Loop 负责持续执行 「LLM 决策 → Tool 调用 → 结果反馈 → 再决策」 ;Tool Registry 提供工具目录;MCP 接入外部能力;Skill 封装可复用的执行流程;Memory(RAG) 提供长期记忆与知识检索。
它们共同构成了现代 Agent 的工具生态,而 Conversation Loop 则将这些能力串联起来,驱动整个 Agent 持续运行。理解了这条执行主线,也就理解了 MCP、Skill、RAG 为什么能够真正融入 Agent。
一、整体架构总览
先看全局,建立坐标感。

从这张图可以直接看出:所有的箭头最终都回到 Conversation Loop。消息网关是入口,工具生态是手臂,LLM 是外脑,但 Loop 才是心跳。
二、Conversation Loop:Agent 心脏
2.1 一句话讲清楚
Agent 本质上就是一个会自己循环思考的程序。每一轮都让 LLM 判断:「我是直接回答,还是调用工具?」如果调用工具,就把执行结果放回上下文,再让 LLM 思考下一步。
代码层面就是:
bash
while 预算没花光:
LLM 看一遍历史消息和所有工具列表 → 决定下一步做什么
如果 LLM 说「回答完毕」 → 退出循环,返回用户
如果 LLM 说「调用工具 X(参数 Y)」 → 执行工具 X → 结果贴回对话 → 继续循环
这就是现代 Agent 的核心执行模型。本质上,它只是一个不断进行「决策 → 执行 → 反馈 → 再决策」的循环。
2.2 循环流程

2.3 简化版实现示意
以下是 conversation_loop.py(约 5300 行生产代码)的核心逻辑简化示意,真实实现有更多的重试、回退、压缩、安全检查等处理:
python
# 简化版示意 --- 源自 Hermes: agent/conversation_loop.py
def run_conversation(agent, user_message):
"""一个用户 Turn 的完整处理流程"""
# 1. 构建 Turn 上下文(system prompt + 历史 + 用户消息)
messages = build_turn_context(agent, user_message)
# 2. 从 Tool Registry 获取所有可用工具的定义(JSON Schema)
tools, checks = agent.tool_registry.get_all_visible_tools()
# 3. 迭代预算控制(默认最多 60 轮 tool call → API call)
budget = IterationBudget(max_turns=agent.max_turns)
while not budget.is_exhausted():
# 4. 上下文太长就压缩
messages = compress_if_needed(agent, messages)
# 5. 调用 LLM API(带重试/fallback/provider 切换)
response = call_llm_with_retry(agent, messages, tools, budget)
# 6. 解析 LLM 返回
if response.finish_reason == "stop" and not response.tool_calls:
break # 纯文本 → 完成任务
if response.tool_calls:
for tool_call in response.tool_calls:
result = execute_tool(agent, tool_call)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
continue # ← 回到循环顶部,LLM 重新审视最新状态
# 7. Turn 结束:持久化 session
save_session(agent, messages)
# ⚠️ 以上是简化示意,不是可直接运行的源码。
# 真实文件 ~5300 行,额外包含:
# - 多层重试(API 错误 / 限流 / 超时)
# - Provider 自动切换(fallback)
# - 消息压缩/截断/修复
# - 工具参数消毒 & 安全检查
# - 上下文长度估算 & 动态调整
# - 异常恢复 & 审计日志
关键点:
- 每次 LLM 调用时,所有可用工具的定义都以 Function Calling JSON Schema 形式传给 API。LLM 不是「查询数据库」选工具,而是根据工具描述做语义匹配
- 工具结果原样追加到 messages 数组。LLM 下一轮能「看到」之前的工具执行结果,这形成了情境感知链
- 预算耗尽(60 轮)是硬上限,防止死循环烧 Token
2.4 源码中的注释印证
以下注释直接来自 agent/conversation_loop.py 文件头部(非虚构):
python
"""
Hermes agent/conversation_loop.py 的架构注释(真实文档字符串)
核心处理流程:
1. build_turn_context() - 构建 System Prompt + 历史消息
2. conversation_compression - 超长上下文压缩
3. 调用 LLM API(带重试/fallback/provider 切换)
4. 解析 response: text → 结束 / tool_calls → 执行
5. tool_executor 执行工具调用(分发到 Registry / MCP / Skill)
6. 结果追加到 messages,回到第 3 步
7. 预算耗尽或完成任务 → save + post_hooks
"""
现在你理解了心脏怎么跳。接下来看三个问题:
- LLM 怎么知道有哪些工具可以调? → System Prompt(第三章)
- 这些工具从哪来的? → Tool Registry(第四章)
- 外部工具和技能包怎么接进来? → MCP(第五章)和 Skill(第六章)
说得直接一点------后面介绍的所有模块(System Prompt、Tool Registry、MCP、Skill、Memory),本质上都在做同一件事:让这个 Conversation Loop 更聪明、更安全、更高效地运行。
三、System Prompt:LLM 怎么知道有哪些工具?
3.1 构建流程
Agent 并不是「知道」有什么工具,而是把所有的能力写成一个开放式菜单让 LLM 去理解。
注意:这个构建过程不是在循环内部每次都做的。
- 静态部分(人格、工具定义、Skill 目录)在 Agent 启动时构建好,每次 API 调用时直接引用
- 动态部分(Memory 检索结果、Skill 完整指令)在 LLM 输出对应的 function_call 后才加载并注入
3.2 Hermes:工具定义如何传给 LLM
python
# Hermes: agent/conversation_loop.py --- 每次 API 调用前(简化版)
def _build_api_request(agent, messages, tools_entries):
api_tools = []
for entry in tools_entries:
api_tools.append({
"type": "function",
"function": {
"name": entry.name,
"description": entry.description, # ← LLM 就是靠这个字段做语义匹配
"parameters": entry.parameters, # JSON Schema
}
})
return client.chat.completions.create(
model=agent.model,
messages=messages,
tools=api_tools, # ← 所有可用工具的菜单,一次性传给 LLM
)
3.3 OpenClaw:Skill 目录注入
xml
<!-- OpenClaw System Prompt 中注入的 Skill 目录 -->
<available_skills>
<skill>
<name>weather</name>
<description>Current weather and forecasts with web_fetch, falling back to wttr.in...</description>
<location>/opt/homebrew/lib/node_modules/openclaw/skills/weather/SKILL.md</location>
</skill>
<skill>
<name>github</name>
<description>GitHub CLI for issues, PRs, CI/check logs, comments, reviews, releases...</description>
<location>/opt/homebrew/lib/node_modules/openclaw/skills/github/SKILL.md</location>
</skill>
<skill>
<name>hackernews</name>
<description>Hacker News API for stories and comments. Use when user mentions HN...</description>
<location>~/.agents/skills/hackernews/SKILL.md</location>
</skill>
</available_skills>
LLM 看到 hackernews 的 description 里有「Hacker News API for stories」,当用户问「HN 今天有什么热门」时,LLM 做语义匹配 → 输出 read(skills/hackernews/SKILL.md) → Agent 加载完指令后再继续循环。
核心原理 :LLM 不「知道」该用什么工具,它只是看到工具的 description 字段和用户的意图相似,就决定调用。所以 description 写得越好,Agent 越「聪明」。
四、Tool Registry:工具的「货源」
4.1 工具的来源与注册

4.2 真实代码:Hermes 的 Tool Registry
python
# Hermes: tools/registry.py(简化版)
class ToolRegistry:
def __init__(self):
self._tools: Dict[str, ToolEntry] = {}
self._toolset_checks: Dict[str, callable] = {}
def register(self, name, func, description, parameters, toolset):
"""注册一个工具到全局注册表"""
self._tools[name] = ToolEntry(
name=name,
func=func,
description=description, # ← LLM 做语义匹配的核心字段
parameters=parameters, # JSON Schema
toolset=toolset,
)
def get_all_visible_tools(self):
"""
返回当前上下文下所有可见工具。
只返回 toolset check 通过的工具(否则不注入到 System Prompt)。
"""
visible = []
for name, entry in self._tools.items():
check = self._toolset_checks.get(entry.toolset)
if check is None or check():
visible.append(entry)
return visible
def lookup(self, name: str) -> Optional[ToolEntry]:
"""按名称查找工具"""
return self._tools.get(name)
# 工具自动发现:扫描 tools/ 目录下所有 Python 模块
def discover_builtin_tools(tools_dir: Path) -> List[str]:
discovered = []
for path in tools_dir.glob("*.py"):
if _module_registers_tools(path):
discovered.append(path.stem)
return discovered
4.3 工具可用性检查(环境感知)
并非所有工具在所有上下文中都可用。每个 toolset 可以注册一个 check_fn:
python
# 示例:只有当 DISCORD_BOT_TOKEN 存在时,discord 工具才可见
tool_registry.register_toolset_check(
"discord",
lambda: bool(os.getenv("DISCORD_BOT_TOKEN"))
)
在 errors.log 中的实际日志:
bash
WARNING tools.registry: check_fn check_discord_tool_requirements returned False
WARNING tools.registry: check_fn _browser_cdp_check returned False
设计含义:不可用的工具不会出现在 System Prompt 的 tools 数组中,LLM 根本看不到它,也就不会调用它。这是最干净的工具屏蔽方式------不需要 LLM 知道什么「不该用」,它压根感知不到。
五、MCP(Model Context Protocol):外部工具的标准化接入
MCP 解决的核心问题:Agent 不可能为每一个业务系统单独开发 Tool,因此需要一种统一的协议来接入外部能力。
5.1 架构

5.2 配置示例
yaml
# ~/.hermes/config.yaml
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
timeout: 120
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..."
supports_parallel_tool_calls: true
remote_api:
url: "https://my-mcp-server.example.com/mcp"
headers:
Authorization: "Bearer sk-..."
timeout: 180
5.3 真实代码:Hermes 的 MCP 工具模块
python
# Hermes: tools/mcp_tool.py(约 5500 行的生产级实现)
"""
MCP (Model Context Protocol) Client Support
Architecture:
A dedicated background event loop runs in a daemon thread.
Each MCP server runs as a long-lived asyncio Task on this loop.
Tool call coroutines are scheduled via `run_coroutine_threadsafe()`.
Transport modes:
- Stdio: command + args → 启动子进程,stdin/stdout 通信
- HTTP: url → Streamable HTTP POST
- SSE: url + transport: sse → Server-Sent Events
Features:
- Auto-reconnect with exponential backoff(最多 5 次)
- Environment variable filtering(安全过滤敏感 env)
- Credential stripping in error messages(不把 token 泄露给 LLM)
- Parallel tool call opt-in per server
"""
5.4 OpenClaw 的 MCP 实现
javascript
// OpenClaw: agent-bundle-mcp-runtime-BcscePMD.js
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
function loadEmbeddedAgentMcpConfig(params) {
const bundleMcp = loadMergedBundleMcpConfig({
workspaceDir: params.workspaceDir,
cfg: params.cfg,
manifestRegistry: params.manifestRegistry,
});
return {
mcpServers: bundleMcp.config.mcpServers,
diagnostics: bundleMcp.diagnostics,
};
}
// 启动流程:connect → listTools() → 注册到 Tool Catalog → LLM 可调用
5.5 MCP 调用完整时序
六、Skill 系统:可复用的工具组合指令
与 MCP(提供单个函数调用接口)不同,Skill 提供的是执行指令和上下文。它告诉 LLM「遇到某类任务时,按这个步骤组合调用多个工具」。
6.1 Skill 的两种形态
| 形态 | 说明 | 示例 |
|---|---|---|
| 目录式 Skill | SKILL.md + 可执行脚本/配置文件 | weather --- 有 wttr.in 调用逻辑的完整技能包 |
| 内联指令 | 纯 SKILL.md 文本注入 prompt | browser-automation --- 浏览器操作最佳实践指南 |
6.2 加载与触发流程

6.3 真实代码:Hermes 的 Skill 预处理引擎
python
# Hermes: agent/skill_preprocessing.py
# 模板变量替换:${HERMES_SKILL_DIR} → 实际路径
_SKILL_TEMPLATE_RE = re.compile(r"\$\{(HERMES_SKILL_DIR|HERMES_SESSION_ID)\}")
# 内联 Shell:!`date +%Y-%m-%d` → 可在 prompt 中直接执行命令并注入结果
_INLINE_SHELL_RE = re.compile(r"!`([^`\n]+)`")
def substitute_template_vars(content, skill_dir, session_id):
"""替换 SKILL.md 中的模板变量"""
def _replace(match):
token = match.group(1)
if token == "HERMES_SKILL_DIR" and skill_dir:
return str(skill_dir)
return match.group(0)
return _SKILL_TEMPLATE_RE.sub(_replace, content)
def run_inline_shell(command, cwd, timeout):
"""
执行 SKILL.md 中的内联 shell 命令。
例如 `!`date +%Y-%m-%d`` → 注入 "2026-07-14"
失败不抛异常,返回 [inline-shell error: ...] 标记
"""
completed = subprocess.run(
["bash", "-c", command],
capture_output=True, text=True,
timeout=max(1, int(timeout)),
)
return (completed.stdout or "").rstrip("\n")
6.4 Skill 安全扫描(安装前防线)
python
# Hermes: tools/skills_guard.py
"""
Skills Guard --- 外部 Skill 的安全扫描器
Trust levels:
- builtin: 随 Hermes 发布,永不扫描,始终信任
- trusted: 来自 openai/skills、anthropics/skills、NVIDIA/skills
- community: 其他来源。有发现即阻止,除非 --force
"""
TRUSTED_REPOS = {
"openai/skills", "anthropics/skills", "NVIDIA/skills",
}
# 正则威胁检测模式
THREAT_PATTERNS = [
("curl.*\\|.*bash", "remote-pipe", "critical", "exfiltration"),
("crontab\\s+-", "crontab-edit", "high", "persistence"),
("rm\\s+-rf\\s+/", "rm-root", "critical", "destructive"),
("nc\\s+.*-e", "netcat-shell", "critical", "network"),
]
6.5 OpenClaw 的 Skill 选择策略
xml
<!-- OpenClaw System Prompt 中的 Skill 目录 -->
<available_skills>
<skill>
<name>weather</name>
<description>Current weather and forecasts with web_fetch, falling back to wttr.in...</description>
</skill>
</available_skills>
触发流程:用户说「今天天气怎么样」→ LLM 匹配 weather skill → Agent 执行 read(skills/weather/SKILL.md) → 按指令调用 web_fetch(wttr.in) → 返回结果
七、Memory / RAG:长期记忆与知识检索
7.1 记忆的完整生命周期
RAG 不只是「检索」,完整的记忆系统包含写入和检索两个方向:

关键设计 :写入和检索是解耦的------Agent 不直接在对话中「写记忆」,而是在每次 Turn 结束后通过 post_turn_hook 持久化到文件。后续对话中,LLM 通过 memory_search 工具(或自动注入 context block)检索这些记忆。
7.2 知识库检索架构
7.3 真实代码:Hermes 的 Memory Manager
python
# Hermes: agent/memory_manager.py
def build_memory_context_block(agent, user_message: str, max_chars=8000) -> str:
"""检索相关记忆,构建上下文块注入到对话中"""
results = agent.memory_store.search(query=user_message, top_k=10)
if not results:
return ""
context_lines = ["<memory_context>"]
for result in results:
if len("\n".join(context_lines)) > max_chars:
break
context_lines.append(
f"<memory score={result.score:.2f}>{result.text}</memory>"
)
context_lines.append("</memory_context>")
return "\n".join(context_lines)
7.4 RAG 在 Conversation Loop 中的位置
关键设计 :memory_search 本身也是 Tool Registry 中的一个工具!LLM 在循环中自主决定「我需要先查一下历史记忆」,然后调用它。检索结果作为上下文追加到 messages,下一轮 LLM 就能基于记忆生成回答。
7.5 OpenClaw 的 Memory Search 工具定义
八、全链路端到端:一条消息的完整旅程
以「帮我查一下 GitHub 上 openclaw 项目的最近 3 个 Issue」为例,展示完整的 Conversation Loop 流转:
代码路径对照:
bash
1. 消息到达 → hermes_cli/tui_gateway/server.py
2. 创建 Session + 启动 Loop → agent/conversation_loop.py::run_conversation()
3. 构建上下文 → agent/turn_context.py + agent/memory_manager.py
4. 调用 LLM → agent/conversation_loop.py::call_llm_with_retry()
5. 解析 function_call → tools/registry.py::lookup()
→ Built-in: 直接执行函数
→ MCP: tools/mcp_tool.py → 转发到 MCP Server
→ Skill: agent/skill_utils.py → 加载 SKILL.md
6. 追加结果 + 回到步骤 4(最多 60 轮)
九、核心速查:对比总结 + 关键洞察
9.1 Hermes vs OpenClaw
| 对比维度 | Hermes | OpenClaw |
|---|---|---|
| 定位 | 大而全的 Agent 平台(桌面 GUI + 网关 + 多平台消息) | 轻量 Agent 框架(命令行 + 可嵌入) |
| 语言 | Python 3.11 | TypeScript (Node.js) |
| Conversation Loop | agent/conversation_loop.py (~5300 行) |
embedded-agent-BgF2MOkH.js (dist 打包) |
| Tool 系统 | tools/registry.py --- 类继承 + 装饰器自动注册 |
agent-tools-XUrUI5bQ.js --- Tool Catalog + Compaction |
| Tool 发现 | 自动扫描 tools/*.py + MCP listTools + Skill 元数据 |
显式注册 + MCP listTools + Skill 清单 |
| MCP 支持 | 原生支持(stdio / HTTP / SSE,守护线程) | 原生支持(stdio / HTTP / SSE) |
| Skill 系统 | SKILL.md + 模板变量 + 内联 Shell + 安全扫描 + 信任分级 | SKILL.md + description 注入 |
| Memory / RAG | agent/memory_manager.py --- 向量检索 + 上下文注入 |
memory_search Tool --- 语义搜索 MEMORY.md |
| 消息平台 | 微信 / Discord / Telegram / Slack / Signal / QQ 等 | WebChat / Discord / Telegram / Signal 等 |
| 安全 | Skill 来源信任分级 + MCP 凭证脱敏 + 危险命令拦截 + 可用性检查 | Skill 可用性检查 + 命令审批 |
| 配置复杂度 | 高(~/.hermes/config.yaml 的完整生态配置) | 低(Gateway config + 工作区文件) |
| 适用场景 | 个人全能助理、多平台部署 | 嵌入式 Agent、轻量部署、API 集成 |
9.2 关键洞察速查
| 问题 | 答案 |
|---|---|
| Agent 的本质是什么? | 一个带工具的白盒 while 循环:「LLM 决策 → Tool 调用 → 结果反馈 → LLM 再决策」 |
| Agent 怎么知道该调哪个工具? | 不「知道」。LLM 做语义匹配------工具 description 和用户意图越接近,越容易被调用 |
| MCP 和 Skill 有什么区别? | MCP = 标准化外部函数接口(一个工具一件事),Skill = 指令包(多个工具如何组合) |
| RAG 什么时候触发? | 在 Conversation Loop 内部,LLM 自主决定调用 memory_search 工具 → 检索 → 结果注入上下文 → 继续循环 |
| 记忆从哪来? | 每次 Turn 结束后 Agent 自动写入 memory/YYYY-MM-DD.md,周期性提炼到 MEMORY.md,再通过 Embedding 建立向量索引 |
| 一个 Turn 最多调多少次 LLM? | Hermes 默认 max_turns=60,即最多 60 轮 tool call ↔ API call |
| 工具不可用时怎么办? | check_fn 返回 False → 工具不注入到 tools 数组 → LLM 看不到 → 根本不会调用 |
| Hermes 和 OpenClaw 最大区别? | Hermes 是「大而全的平台」(桌面 + 网关 + N 平台),OpenClaw 是「轻量可嵌入 Agent」 |
十、核心设计模式总结

十一、一张图看懂 Agent

核心公式:
Conversation Loop + Tool Calling + Memory = 现代 Agent 的全部秘密
从上往下读:用户提问进入 Loop → Loop 调用 Prompt(菜单)、Memory(记忆)、Tools(工具)→ 工具分为 Built-in / MCP / Skill → 结果回到 Loop → 完成业务目标。
整篇文章,本质上就是这张图从上到下的逐层展开。
十二、写在最后
无论是 Hermes、OpenClaw,还是 Claude Code、Codex、Gemini CLI、Cursor Agent,它们底层几乎都遵循同一种架构:
Conversation Loop + Tool Calling + Memory
不同产品真正的差异,不在于有没有 MCP,而在于------
- Loop 如何组织:单轮 vs 多轮、同步 vs 异步、预算策略是什么
- Tool 如何管理:装饰器自动注册 vs 显式注册、可用性检查粒度多细
- Memory 如何利用:文件级检索 vs 向量数据库、注入时机在哪一环
- 以及围绕这些能力构建出的工程体系:安全扫描、平台适配、会话持久化、多 Agent 协作
理解了 Conversation Loop、Tool Calling 和 Memory,就理解了现代 Agent 的核心设计;其余能力,如 Planning、多 Agent 协作、Workflow 等,大多是建立在这一基础之上的。
结语:
现代 Agent 看起来能力越来越多:MCP、Skill、Memory、Planning、Workflow......
但当真正读完源码之后会发现,它们并不是彼此独立的能力,而都是围绕 Conversation Loop 这一条执行主线构建出来的。
理解了 Conversation Loop,也就理解了 MCP、Skill、RAG 为什么能够真正融入 Agent;理解了 Tool Registry、Tool Calling 与 Memory 的协作方式,也就理解了现代 Agent 为什么能够持续思考、持续调用工具,并最终完成复杂任务。
源码会不断演进,但底层设计思想往往比代码更加稳定。
希望这篇文章,能够帮助你真正建立现代 Agent 的整体认知。
本文基于 Hermes v0.18.2 与 OpenClaw 当前版本源码分析,部分代码为简化示意,旨在说明整体设计思路。
作者:吴佳浩(Alben)