Claude Code Agent Harness工程拆解:Skills、Session与记忆系统的实现骨架
Claude Code Agent Harness工程拆解:Skills、Session与记忆系统的实现骨架
这篇不再讲“agent harness 是什么”,而是直接讲:一个 agent 系统里,
skills、session、memory在工程上分别落在哪里,又是怎么彼此串起来的。
先说明一下边界。
本文基于一份公开暴露出来、被认为和 Claude Code 相关的仓库做分析。它并不是我能确认的“官方完整源码”,而更像一份暴露了大量表面结构的研究样本。因此下面的结论分两类:
- 可以直接从代码确认的事实
- 基于文件名、模块边界和命令设计做出的工程推断
我会尽量把两者分开说。
零、先给结论:如果只看一张图,Claude 的 harness 更像什么
如果你希望看完这篇以后,对 RAG / skill / memory / session / tool 的关系一眼就有图感,那我先给结论:
Claude Code 这类 harness,本质上不是“聊天 + 工具”,而是一个围绕 任务执行 组织起来的运行时系统。
它的核心不是某条 prompt,而是五条咬合在一起的链路:路由、检索、执行、记忆、压缩。
1. 总体架构图
flowchart LR
U[User Request] --> C[Command / Route Layer]
C --> S[Skill Layer]
C --> Q[Query Engine / Turn Loop]
S --> Q
M1[Static Memory\nCLAUDE.md / instructions] --> Q
M2[Dynamic Memory\nmemdir relevant memories] --> Q
M3[Session Memory\nsummary markdown] --> Q
R1[Skill Retrieval\nloadSkillsDir / skillSearch] --> Q
Q --> T[Tool Runtime]
T --> MCP[MCP / External Tools]
T --> FS[Local Tools\nRead Edit Bash]
T --> SR[Structured Session]
Q --> SR
SR --> CP[Compaction / Session Summary]
CP --> M3
SR --> EX[Resume / Export / Inspect]
这张图里最关键的不是方块多,而是你能看到:
skill不是跟tool并列的“另一个功能”memory不是一个孤立黑盒RAG也不是一个单独向量库
它们实际上都在服务同一件事:让 query engine 在每一轮都拿到“这次该怎么做、该用什么、该记住什么”。
2. 一次任务的执行流程图
flowchart TD
A[用户输入任务] --> B[命令路由 / 意图识别]
B --> C{是否命中 skill / command}
C -->|是| D[装配对应 workflow]
C -->|否| E[走通用 agent 路径]
D --> F[读取长期规则\nCLAUDE.md / local instructions]
E --> F
F --> G[检索相关 skills / memories]
G --> H[构造当前回合上下文]
H --> I[调用模型决定是否使用工具]
I --> J[执行工具 / MCP]
J --> K[写入结构化 session]
K --> L{上下文是否过长}
L -->|否| M[继续下一轮]
L -->|是| N[compact / session memory 更新]
N --> M
如果你把这条流程看懂了,后面再看 skill、memory、RAG 就不会散。
一、先看系统入口:它到底把什么当成一等公民
一个仓库的入口,往往最能暴露作者眼里的系统边界。
Python 侧的 src/main.py 暴露了这些命令:
summarymanifestcommandstoolsroutebootstrapturn-loopflush-transcriptload-sessionremote-modessh-modeteleport-modedirect-connect-modedeep-link-mode
这说明在这个系统里,至少有几块被视作一等公民:
- 命令表面
- 工具表面
- 路由
- bootstrap
- 多轮 turn loop
- transcript / session
- remote / direct / deep link 等运行模式
也就是说,它并没有把 agent 理解成“拿到 prompt 然后直接答”,而是理解成一个会启动、会路由、会持久化、会切换模式的 runtime。
这就是工程味。
二、Skills:在这个仓库里,它更像“工作流层”
1. 代码里能直接确认的事实
Python 侧有一个 skills 子包,但它本身只是 placeholder。也就是说,当前 Python 实现并没有真正把 skill runtime 完整搬过来。
不过,src/reference_data/subsystems/skills.json 保存了暴露出来的技能子系统快照,其中能看到这些样例文件:
1 | skills/bundled/batch.ts |
同时,命令快照里还存在 commands/skills/index.ts 和 commands/skills/skills.tsx 这样的条目。
另外,src/command_graph.py 还专门把 command 分成了三类:
builtinsplugin_likeskill_like
这说明在原始设计里,skill 并不是完全附属物,而是一个足够独立、足够成体系的命令来源。
2. 我对这套 skill 机制的工程推断
只看这些表面,已经能推断出几点很关键的设计思想。
skill 是“按任务类型组织”的
从 remember、verify、stuck 这种名字看,skill 不是按模型能力分的,而是按任务阶段与执行语义分的。
这跟很多人做 agent 的方式很不一样。
很多人会这样组织:
- 一个技能负责画图
- 一个技能负责写代码
- 一个技能负责搜索
而这里暴露出来的命名方式更像:
- 当 agent 需要记住某些东西时怎么办
- 当 agent 做完事情后怎么验证
- 当 agent 卡住时如何脱困
这是一种更高层的抽象。
skill 很可能支持“目录加载 + 内置捆绑”
bundledSkills.ts 和 loadSkillsDir.ts 的组合很像一套双层机制:
- 系统一开始自带一批 bundled skills
- 同时允许从某个 skills 目录动态加载额外技能
如果这个推断成立,那它的价值非常大,因为这意味着 skill 系统不是写死在二进制里的,而是支持扩展。
skill 可能和工具生态深度耦合
mcpSkillBuilders.ts 这个名字特别有意思。
它暗示 skill 不只是 prompt 文本,还可能和 MCP tool schema、tool registry 甚至自动生成逻辑有关。
换句话说,skill 很可能是“任务方法 + 工具约束 + 执行偏好”的组合体。
这也是很多 agent 工程后面会自然走到的一步:单纯写提示词不够了,必须把工具调用规则也一起编进任务模板里。
三、Session:真正的会话系统,不是简单聊天记录
这部分是这份仓库里最能直接学的地方。
1. Python 侧:最小闭环版本
Python 的会话设计很朴素,但很适合拿来当教学样本。
TranscriptStore
src/transcript.py 干的事情非常克制:
append(entry)compact(keep_last)replay()flush()
它没有做花哨的 event sourcing,也没有做复杂索引,就是一个轻量的会话缓存层。
但它已经告诉你一个重要的工程事实:
transcript 和 session 不一定是一回事。
transcript 更像当前运行期暂存的消息轨迹,而 session 则是最终需要落盘和恢复的状态对象。
SessionStore
src/session_store.py 则把会话持久化成 JSON:
1 | { |
这个结构有两个值得学的点。
第一,它把消息历史和usage 统计放在一起。
第二,它默认把 session 存在 .port_sessions/ 下。
这说明在作者眼里,会话不是临时副产品,而是可恢复、可分析、可追踪的工件。
2. QueryEngine:会话状态如何流动
src/query_engine.py 把这套 session 骨架真正串了起来。
核心逻辑非常清楚:
- 每次
submit_message(prompt, matched_commands, matched_tools, denied_tools) - 生成一次当前 turn 的摘要输出
- 更新 token usage
- 把 prompt 记入
mutable_messages - 把 prompt 追加到
TranscriptStore - 必要时执行 compact
- 最后支持
persist_session()
这一层的作用,不是生成“聪明答案”,而是管理多轮状态机。
对 harness 来说,这是关键分工:
- 模型负责生成内容
- query engine 负责管理回合
如果这层没分出来,系统后面会非常难维护。
四、Rust Session:为什么我说它更接近真实 harness
Rust 里的 Session 模型,明显已经进入“可运行 runtime”的阶段。
它不是简单保存一串字符串,而是:
1. 显式区分消息角色
MessageRole 包括:
SystemUserAssistantTool
这是 agent 系统和普通聊天系统最大的分水岭之一。
一旦有工具调用,消息流就不再只是:
1 | 用户 -> 模型 -> 用户 -> 模型 |
而更像:
1 | 用户 -> assistant 推理 -> tool_use -> tool_result -> assistant 继续推理 |
如果会话模型不能表达这条链,后面所有的 replay、resume、debug、audit 都会变得很弱。
2. ContentBlock 不是纯文本
会话里的 block 分三类:
TextToolUseToolResult
这意味着 session 记录的是结构化执行轨迹,而不是单纯的人类可读 transcript。
为什么这点重要?
因为当你要恢复 session 时,真正有价值的不只是用户说过什么,而是:
- 模型在第几步决定调用了什么工具
- 传了什么参数
- 工具有没有报错
- 后续回答是否依赖这次工具结果
这些都必须写进 session schema。
3. slash command 直接把 session 当操作对象
Rust 的 /compact、/resume、/session、/export 命令都不是围绕“当前聊天框”设计的,而是围绕 Session 这个数据结构设计的。
比如:
/compact:压缩当前 session/resume:加载某个 session 文件/session list:列出当前管理的本地 session/export:导出当前 session transcript
这说明在作者眼里,会话不是 UI 附属品,而是 runtime 的核心对象。
五、Memory:这里最值得学的是“文件化记忆”
很多人做 agent,第一反应就是往向量数据库上想。
但这份仓库对我最大的启发反而是:
在工程实践里,最先落地、也最稳的 memory 往往不是 embedding,而是 instruction files。
1. Prompt 构建器会主动发现 instruction files
Rust 的 prompt.rs 会从当前工作目录向上扫描,并尝试读取:
CLAUDE.mdCLAUDE.local.md.claude/CLAUDE.md.claude/instructions.md
然后把这些文件内容放进 system prompt 里。
这套机制有几个很强的工程特征。
可组合
目录层级越深,规则可以越具体。
比如:
- 仓库根目录有全局开发规范
- 某个子目录有该模块的局部规则
- 本地
.claude目录再补充个人环境约束
这就像配置继承,而不是一次性写死。
可审计
memory 变成了文件,就能:
- 看 diff
- 做 review
- 做版本管理
- 知道到底是什么规则在影响模型
可显式暴露
/memory 命令会把当前发现的 instruction files 列出来,包括路径、行数和预览内容。
也就是说,memory 不是神秘黑盒,而是可检查的上下文输入。
2. memdir 快照暴露了另一层 memory 能力
除了 instruction files,这份仓库还暴露出一个 memdir 子系统,样例文件包括:
1 | memdir/findRelevantMemories.ts |
这非常像一套“目录化记忆检索系统”。
我对它的推断是:
memoryScan:扫描可用记忆findRelevantMemories:按当前任务检索相关记忆memoryAge:考虑记忆新鲜度memoryTypes:给记忆分类型teamMem*:支持团队级共享记忆
如果这个推断是对的,那么它和前面的 instruction files 很可能形成了一个双层 memory 体系:
第一层:静态长期规则
就是 CLAUDE.md 这类明确文件,稳定、可读、可版本化。
第二层:动态相关记忆
就是根据当前任务,从 memdir 里找最相关的内容补进来。
这个组合很合理:
- 静态规则负责稳定约束
- 动态记忆负责任务适配
六、Compaction:session 和 memory 之间的胶水层
compact.rs 是这份仓库里一个很有“工程启发”的模块。
它做的事情不是简单删除旧消息,而是:
- 先估算当前 session token 数
- 如果超过阈值,就保留最近若干条消息
- 把更早的消息做 summary
- 再把这段 summary 作为新的
Systemmessage 放回去
从结构上看,这一步几乎就是把“短期对话历史”重新编码成“中期可恢复记忆”。
所以我会把 compact 看成连接 session 和 memory 的一层胶水:
- session 太长了,不能全留
- 但也不能直接删
- 所以用 compact 把旧 session 转成 summary memory
这非常像一个成熟 agent 系统必须拥有的能力。
七、这套骨架怎么串起来
如果把这个仓库里暴露出来的设计压成一张流程图,大概会是这样:
1 | 用户输入 |
从这里你会看到一个很关键的点:
skill、session、memory 不是三块孤立能力,而是三条互相咬合的基础设施链路。
skills负责“怎么走”session负责“走到哪了”memory负责“走的时候要遵守什么长期规则”compact负责“路太长时怎么压缩”
这就是 harness 的真正骨架。
八、看完源码后,我对 RAG 的理解是什么
这是你这次最想要补清楚的地方。
很多人一说 RAG,脑子里立刻只剩下:
- chunk
- embedding
- vector db
- top-k recall
但看完 Claude 这类 harness 的源码以后,我更愿意把 RAG 理解成:
在任务执行前,把“当前这轮真正需要的上下文”从各种候选知识源里检出来,再装配进模型上下文。
这个定义比“向量检索”更大,也更接近工程现实。
1. 在 harness 里,RAG 其实至少有四层
| 层 | 来源 | 作用 | 典型实现感觉 |
|---|---|---|---|
| 静态规则检索 | CLAUDE.md / instructions |
提供长期约束 | 文件扫描 + 注入 system prompt |
| skill 检索 | loadSkillsDir / skillSearch |
找到该走哪套 workflow | frontmatter + 目录索引 + 搜索 |
| memory 检索 | memdir/findRelevantMemories |
找到与当前任务真正相关的经验或提醒 | 扫描 memory header,再让模型做选择 |
| session 检索 | SessionMemory / compact summary |
找回当前会话已经形成的阶段性结论 | 会话摘要 markdown / compact result |
也就是说,RAG 在这里不是“外接知识库模块”,而是整个 harness 的上下文装配机制。
2. 这份源码里最有启发的一点:它并不迷信向量库
findRelevantMemories.ts 的思路非常值得你注意。
它不是一上来就 embedding 全量召回,而是:
- 先扫描 memory 文件头部和描述
- 把候选 memory manifest 组织出来
- 再让一个侧路模型去判断“哪些 memory 明确有用”
这说明一个很重要的工程判断:
很多内部知识检索问题,先做结构化候选缩小,再做模型选择,已经足够好。
这对你后面在公司里做提效非常关键,因为它意味着:
- 你不一定要先上向量数据库
- 你可以先把知识组织成可枚举、可筛选、可解释的文件或卡片
- 然后再用模型做轻量 rerank
这条路线往往更容易落地。
3. 所以我现在对 RAG 的实际理解是
对 agent 系统来说,RAG 不是单独的一层,而是下面这套动作的统称:
- 识别这轮任务需要什么上下文
- 从多个来源里检索候选信息
- 过滤噪音和重复信息
- 把最相关的内容注入当前回合
- 在回合结束后决定是否沉淀成新的 memory
这才是你后面做公司级 agent 时真正该做的 RAG。
九、看完源码后,我对 skill 的理解是什么
看完 loadSkillsDir.ts 之后,我觉得 skill 最不该被理解成“一段提示词模板”。
更准确一点说:
skill 是 workflow + policy + tool contract 的组合。
为什么这么说?
因为从这份源码暴露出来的字段看,一个 skill 不只是正文说明,它还包含:
descriptionwhen_to_useallowed-toolsargumentshooksmodeleffortpathsexecutionContext
这意味着 skill 的本质不是“多写几句提示词”,而是把下面这些东西一起打包:
- 什么时候触发
- 允许用什么工具
- 约束在哪些路径生效
- 用什么执行强度
- 卡住时有没有 hooks 或补救动作
1. 这对你做公司 skill 的直接启发
如果你后面在公司里做 skill,别把它写成知识点百科。
更好的写法应该是:
- 触发场景是什么
- 这个场景要先查什么
- 哪些工具必须优先用
- 哪些输出格式是标准产物
- 成功标准是什么
- 遇到失败时要怎么降级
比如一个“线上问题排查 skill”,就不该只是:
请分析日志并定位问题。
而应该更像:
- 先拿 traceId / issue key
- 查询日志 / 调用链 / 配置
- 识别异常归属模块
- 输出结论、证据、影响面、下一步建议
- 如果证据不足,再补查哪类信息
这就是从“提示词”升级到“workflow skill”。
2. skill 和 agent 的边界
这是很多团队容易混淆的地方。
skill更像“做事的方法包”agent更像“执行这个方法的人”
一个 agent 可以调用多个 skill;
一个 skill 也可以被多个 agent 复用。
所以如果你在公司里要做体系,建议不要把 skill 和 agent 绑死。
更合理的分层是:
- skill 负责沉淀流程
- agent 负责调度执行
- tool 负责具体动作
十、看完源码后,我对 memory 的理解是什么
这块我现在的理解会比以前更清晰:
memory 不是“记住你说过的话”这么简单,而是让系统在多轮和跨轮执行中保持稳定工作方式的上下文层。
而且从 Claude 这类 harness 的实现来看,memory 不是一层,而是分层的。
1. 第一层:静态规则型 memory
也就是 CLAUDE.md、.claude/instructions.md 这种文件。
它的作用不是记录临时事实,而是保存长期有效的约束,比如:
- 项目规范
- 工具偏好
- 提交方式
- 测试命令
- 安全边界
这一层最大的优点是:
- 可见
- 可 review
- 可 diff
- 可协作
对企业环境来说,这一层比“神秘长期记忆”更重要。
2. 第二层:会话摘要型 memory
SessionMemory/sessionMemory.ts 很有意思。
它不是让主线程每次都自己总结,而是:
- 在后台周期性提取当前会话中的关键结论
- 维护成 markdown 文件
- 作为后续上下文的一部分继续供系统使用
这意味着 session memory 的本质是:
把高噪声的多轮对话,提纯成可复用的阶段性状态。
这一步对企业 agent 特别重要,因为公司里的任务往往不是一轮完成,而是:
- 需求澄清
- 查代码
- 查日志
- 修改
- 测试
- 复盘
如果没有这层摘要型 memory,系统很容易在长链路里越跑越散。
3. 第三层:动态相关记忆
也就是 memdir/findRelevantMemories.ts 这一层。
它不是把所有 memory 全塞进去,而是只挑当前 query 真有帮助的那几个。
这说明动态 memory 的重点不是“存更多”,而是“选更准”。
4. 所以 memory 最终应该怎么设计
如果让我压成一句话:
- 静态规则 memory 负责稳定约束
- 会话摘要 memory 负责阶段状态
- 动态相关 memory 负责当前命中
这三层加在一起,才是一个 agent 真正可用的 memory 系统。
十一、对你后面做公司提效、做 skill 或 agent,有哪些可借鉴的地方
这一段我尽量只讲可落地的。
1. 不要一开始就做“大而全平台”,先做最小闭环
看完这种源码后,一个很容易犯的错是:
想一步到位做出一个“公司版 Claude Code”。
但更现实的做法是先跑通一个高价值闭环,比如:
- Jira / 需求单 -> 技术方案草稿
- Trace / 日志 -> 问题分析报告
- 代码变更 -> 风险点和测试建议
- 发布单 -> 发布前核对清单
先把一个链路打深,比做十个半成品 agent 有价值。
2. skill 优先沉淀高频、稳定、可复用的流程
适合先 skill 化的事情,通常有三个特征:
- 高频发生
- 步骤相对稳定
- 输出格式容易标准化
比如你公司里很适合先做的,往往是:
- 问题排查 skill
- 技术方案生成 skill
- CR / review skill
- 集成测试设计 skill
- 发布校验 skill
3. 先做文件化 memory,不要急着上复杂“企业记忆”
对公司提效来说,第一批最值钱的 memory 很可能不是知识图谱,而是这些:
- 项目规范文件
- 常见故障处理手册
- 领域术语定义
- 默认链路和边界说明
- 组件/模块 owner 约定
这些内容先文件化、结构化、可搜索,收益就已经很高。
4. 先做“检索装配”而不是“万能问答”
很多内部 agent 做不起来,不是因为模型不够强,而是因为输入上下文太乱。
你真正该做的,不是让 agent 什么都懂,而是让它每次执行前能拿到:
- 当前任务所属域
- 相关技能
- 相关 memory
- 相关文档或日志
- 允许使用的工具
这就是 harness 思维。
5. 把 session 当资产,而不是临时聊天记录
如果你后面要在公司里做 agent,session 一定要可:
- 回放
- 审计
- 恢复
- 摘要
- 导出
因为这会直接决定它能不能进入真实业务环境。
十二、如果你现在就要做一个公司内提效 agent,我建议的最小架构
你完全可以先做成下面这个版本:
flowchart LR
A[用户请求\n需求/故障/改动] --> B[任务分类器]
B --> C[选择 skill]
C --> D[加载静态规则\n项目规范/流程文件]
C --> E[检索动态记忆\n案例/故障经验/FAQ]
D --> F[组装当前上下文]
E --> F
F --> G[Agent 执行]
G --> H[工具层\n日志/代码/配置/发布/数据库]
H --> I[产出结果\n方案/分析/修复建议]
I --> J[写入 session / summary]
J --> K[可复用经验是否沉淀为 memory]
这个架构里最应该先补的不是“更强模型”,而是:
- 分类器
- skill 选择
- 静态规则装配
- 动态记忆检索
- session summary
只要这五件事顺起来,你的 agent 就会从“偶尔有用”变成“逐渐稳定”。
十三、如果你自己做 agent,我建议直接借鉴什么
1. 先把 session schema 做对
至少要有:
- role
- content blocks
- tool_use
- tool_result
- usage
如果这层做错,后面的 resume、audit、export 都会很难看。
2. 把 skill 看成 workflow template
不要把 skill 做成一堆漂亮话,而要做成:
- 触发条件
- 任务步骤
- 工具偏好
- 完成标准
- 失败或卡住时的回退路径
3. 先用文件化 memory,再考虑复杂检索
CLAUDE.md 这种模式特别适合落地,因为它:
- 简单
- 可解释
- 便于团队协作
- 易于与代码仓库一起管理
很多时候,这比你一上来就搞复杂长期记忆系统更靠谱。
4. 把 compact 当正式能力来设计
context window 再大,也会有上限。
真正长期运行的 agent,迟早要面对:
- 如何保留最近上下文
- 如何压缩历史
- 如何让压缩后仍然可恢复工作状态
这不是锦上添花,而是 runtime 核心能力。
十四、我的一句判断
如果只从工程角度评价,这份仓库最值得学的地方,不是某个模型调用细节,而是它暴露出了一套很像样的基础设施分层:
- 命令层
- 工具层
- 技能层
- prompt / memory 装配层
- session 持久化层
- compaction 层
- resume / export / inspect 层
这正是一个 agent harness 从“好玩 demo”走向“可持续运行系统”时必须长出来的骨架。
下一篇我会把这份仓库当成研究对象,不再只讲实现,而是尝试回答一个更有意思的问题:
仅凭这份“暴露出来的仓库表面”,我们到底能反推出 Claude Code 怎样的设计哲学?
延伸阅读
- 如果你是先看到这篇,建议先回到 Claude Code Agent Harness入门:从Skills、Session到Memory说起 把整体脉络串起来。
- 如果你想把工程骨架和模型能力差异放在一起看,可以读 GPT-5.5 vs GPT-6 vs Claude Opus 4.7 编码推理与记忆的全面对比。
- 如果你更关心这些能力最后如何融入日常开发协作,也可以读 关于AI的思考与未来方向。





