agentmemory commit-context 技能实战:从一行代码回溯到产生它的 Agent 会话
【免费下载链接】agentmemory#1 Persistent memory for AI coding agents based on real-world benchmarks项目地址: https://gitcode.com/GitHub_Trending/age/agentmemory
导读
当你在代码库里看到一行"来路不明"的代码、一个被反复修改的函数,或是一次不清楚动机的提交时,commit-context技能可以帮你把这段代码精确关联回产生它的那次 Agent 会话——包括当时的会话摘要、观察记录数量与高价值决策。本文基于 plugin/skills/commit-context/SKILL.md 与配套的 EXAMPLES.md,结合 agentmemory 的 MCP 服务端、REST API 与 post-commit 钩子源码,完整讲解"从 git 定位 SHA → 调用memory_commit_lookup→ 呈现会话上下文"的实战链路,读完即可在任意支持 agentmemory 的编码 Agent(Claude Code、Codex、Cursor 等)中熟练使用这一追溯能力。
为什么需要 commit-context:解决"这段代码为什么在这里"
Agent 会话产生的代码修改会被记录在 agentmemory 的观察(observation)体系中,但 git 提交本身并不会自动携带"这是哪个会话写的"这一信息。当用户问出以下问题时,正是commit-context的适用场景:
- "Why is this code here"(这段代码为什么在这里)
- "What was the agent doing when this changed"(改动发生时 Agent 在做什么)
- "Who wrote this"(这是谁写的)
- 希望了解代码库中某个具体位置的背景上下文
技能定义(plugin/skills/commit-context/SKILL.md 的 frontmatter)将其描述为:Trace a file, function, or line back to the agent session that produced its current commit,即"把一个文件、函数或行号,追溯到产生其当前提交的那次 Agent 会话"。它是一个user-invocable: true的独立技能,参数提示为[file, function, or line],意味着它可以接收文件路径、函数名或行区间三种输入形态。
值得强调的核心原则是:只报告 git 与查询接口返回的事实。当查询返回commit: null时,说明该提交早于"会话关联"机制,此时绝不能根据 diff 凭空编造 Agent 意图——这是整个技能的行为底线,也是它与普通git blame的核心差异:它只回答"是否有记录在案的会话",而不是"代码看起来在做什么"。
前提:会话与提交的自动关联(post-commit 钩子 + KV.commits)
memory_commit_lookup之所以能查到结果,是因为 agentmemory 在提交发生时就已经自动建立了"提交 → 会话"的关联。这一环节由 src/hooks/post-commit.ts 实现:
- 读取 stdin 中的钩子载荷(直接由
.git/hooks/post-commit调用时可能没有 stdin,会走兜底逻辑); - 通过
git rev-parse HEAD取得当前提交 SHA(也支持AGENTMEMORY_COMMIT_SHA环境变量覆盖); - 依次抓取分支(
git rev-parse --abbrev-ref HEAD)、远端仓库地址(git config --get remote.origin.url)、提交信息(git log -1 --pretty=%B)、作者(git log -1 --pretty=%an <%ae>)、提交时间(git log -1 --pretty=%aI)以及变更文件列表(git diff-tree --no-commit-id --name-only -r); - 将这些字段连同
sessionId(来自载荷的session_id或AGENTMEMORY_SESSION_ID)POST 到AGENTMEMORY_URL(默认http://localhost:3111)的/agentmemory/session/commit接口,全程 best-effort,失败不阻断提交。
服务端 src/triggers/api.ts 中的api::session::commit函数收到请求后,会在KV.commits命名空间下按完整 SHA键写入CommitLink记录,并在重复关联时合并sessionIds(用Set去重)、保留首次linkedAt时间戳;同时反向更新Session.commitShas[],形成双向引用。写入过程使用withKeyedLock("commit:<sha>")与withKeyedLock("session:<sessionId>")加锁,避免并发提交时的竞态。
因此,commit-context能正常工作的前提是:你的工作目录已安装并启用了 agentmemory 的 post-commit 钩子(见 plugin/hooks/post-commit.mjs 等钩子脚本与 plugin/plugin.json 的配置方式),且那次提交发生在会话关联机制启用之后。启用之前的历史提交,memory_commit_lookup会返回commit: null——这正是技能中反复强调要如实上报的情况。
快速上手:三步完成一次提交上下文追溯
第一步:用 git 定位 SHA
commit-context不自己解析 git,而是要求先通过标准 git 命令拿到目标提交的完整 SHA。三种输入形态对应三条命令:
| 输入形态 | 命令 | 说明 |
|---|---|---|
| 行区间 | git blame -L <start>,<end> <file> | 定位某几行当前归属的提交,如git blame -L 40,52 src/auth/refresh.ts |
| 函数 | git log -L :<function>:<file> | 按函数名追踪其提交历史,可加-n 1只取最近一次 |
| 裸路径 | git log -n 1 -- <file> | 获取该文件最近一次变更的提交 |
第二步:调用 memory_commit_lookup
拿到完整 SHA 后调用 MCP 工具memory_commit_lookup:
memory_commit_lookup { "sha": "9a1b2c3d4e5f60718293a4b5c6d7e8f901234567" }注意:必须传完整 SHA(40 位),工具契约要求sha为必填字段(见下文"工具契约")。
第三步:呈现结果
查询返回后,按以下结构向用户呈现(参考技能 Workflow 的第 3 步):
- 提交信息:完整 SHA、短 SHA、分支、作者、提交信息;
- 关联会话:会话 ID、所属项目、起止时间、观察记录数量、会话摘要;
- 高价值观察:当可用时,通过
memory_recall(即memory_smart_search)补充该会话中importance >= 7的观察记录,作为"当时到底在做什么"的证据。
预期的标准输出形态如下:
9a1b2c3 on main by dev: "rotate refresh tokens" Linked session 7f3a9c2 "Auth refresh rework", 14 obs.工具契约:memory_commit_lookup 的入参与返回结构
工具定义位于 src/mcp/tools-registry.ts:
- 名称:
memory_commit_lookup - 描述:Look up the agent session(s) that produced a specific git commit, given its SHA. Returns the commit metadata and linked sessions.
- 入参:对象类型,唯一字段
sha(string,完整 git 提交 SHA),必填。
返回值包含两部分:
commit:CommitLink记录(SHA、短 SHA、分支、仓库、提交信息、作者、提交时间、变更文件、会话 ID 列表、关联时间);sessions:与该提交关联的会话对象数组(ID、项目、工作目录、起止时间、状态、观察记录数量、模型、摘要、commitShas反向引用等)。
值得注意的边界行为:当 SHA 在KV.commits中不存在时,服务端返回的不是错误,而是{ "commit": null, "sessions": [] }(HTTP 200)。这与 REST 接口GET /agentmemory/session/by-commit返回 404 的行为不同,是 MCP 层特意设计的"可预期的空结果",技能层据此输出"该提交早于会话关联"。
底层实现:MCP 服务端如何响应 memory_commit_lookup
从 src/mcp/server.ts 可以看清完整的调用链:
case "memory_commit_lookup": { const sha = asNonEmptyString(args.sha); if (!sha) return { status_code: 400, body: { error: "sha required" } }; const link = await kv.get(KV.commits, sha); if (!link) { return { status_code: 200, body: { content: [{ type: "text", text: JSON.stringify({ commit: null, sessions: [] }, null, 2) }] }, }; } const linkRecord = link as { sessionIds?: string[] }; const fetched = await Promise.all( (linkRecord.sessionIds ?? []).map((sid) => kv.get(KV.sessions, sid)), ); const sessions = fetched.filter((s) => s !== null); return { status_code: 200, body: { content: [{ type: "text", text: JSON.stringify({ commit: link, sessions }, null, 2) }] }, }; }实现要点:
- 空 SHA 直接 400:
sha缺失或为空字符串时返回参数错误; - 一次 KV 点查:以完整 SHA 为键在
KV.commits命名空间查询CommitLink; - 并行拉取会话:
Promise.all并发按sessionIds逐个读取KV.sessions,过滤掉已不存在的会话(例如被清理的记录),保证不会返回悬空引用; - 响应统一走 MCP 文本内容协议:结果以
{ type: "text", text: JSON.stringify(...) }包装返回给调用方。
配套的 REST 实现api::commits(src/triggers/api.ts)与 MCP 的memory_commits(src/mcp/server.ts)则提供了批量视角:按branch/repo过滤、limit默认 100 上限 500、按linkedAt倒序输出——它们与commit-context形成"单点深入 + 批量概览"的互补关系。
数据模型:CommitLink 与 Session.commitShas 的双向关联
CommitLink的类型定义位于 src/types.ts:
export interface CommitLink { sha: string; shortSha: string; branch?: string; repo?: string; message?: string; author?: string; authoredAt?: string; files?: string[]; sessionIds: string[]; linkedAt: string; }同时Session接口(src/types.ts)新增了commitShas?: string[]反向引用。这意味着:
- 正向:给定提交 → 通过
CommitLink.sessionIds找到会话(commit-context使用的方向); - 反向:给定会话 → 通过
Session.commitShas找到它产出过的所有提交(可配合commit-history技能列出某会话的全部产出)。
关联的合并逻辑在 src/triggers/api.ts 中可见:重复对同一 SHA 上报不同会话时,sessionIds做并集合并、首次linkedAt保持不变、其余字段"新值优先、旧值兜底"。也就是说,一次提交可能关联多个会话(例如人工修正 + Agent 修改混合产生),呈现时必须把sessions数组完整列出,而不是只取第一个。
REST 回退方案
当 MCP 工具不可用但守护进程在运行时,plugin/skills/_shared/TROUBLESHOOTING.md 给出了 REST 直连方案。commit-context对应的回退接口是:
GET /agentmemory/session/by-commit?sha=<sha>要点:
- 设置
AGENTMEMORY_URL为守护进程地址(默认http://localhost:3111); - 仅当设置了
AGENTMEMORY_SECRET时才附带Authorization: Bearer <secret>——默认的本机守护进程是开放的,带上多余请求头反而会被拒绝; - 该接口在找不到关联时返回404,与 MCP 层的
{ "commit": null }语义不同,回退时需要区分处理。
服务端实现见 src/triggers/api.ts:校验 auth → 校验sha参数 → 查询KV.commits→ 并行拉取会话 → 返回{ commit, sessions }。批量的GET /agentmemory/commits(支持branch、repo、limit查询参数)则对应commit-history技能的回退;注意给 commit-history 技能 的明确警告:拼 REST URL 时必须用URLSearchParams/encodeURIComponent对branch、repo等值做 URL 编码,否则含?、&、#的分支名会破坏查询串。
三个完整实战示例
以下示例来自 EXAMPLES.md,覆盖了行区间、函数、裸路径三种输入形态以及"有会话/无会话"两种结果。
示例一:行区间 + 已关联会话
用户指着refresh.ts的 40-52 行问:"Why is this retry loop here?"
第 1 步,定位 SHA:
git blame -L 40,52 src/auth/refresh.ts # 9a1b2c3d (dev 2026-06-07) ... retry on revoked token第 2 步,查询:
memory_commit_lookup { "sha": "9a1b2c3d4e5f60718293a4b5c6d7e8f901234567" }第 3 步,得到响应:
{ "commit": { "sha": "9a1b2c3d...", "short": "9a1b2c3", "branch": "main", "author": "dev", "message": "rotate refresh tokens" }, "sessions": [ { "id": "7f3a9c21", "project": "app", "observationCount": 14, "summary": "Reworked refresh rotation" } ] }向用户呈现:
9a1b2c3onmainby dev: "rotate refresh tokens". Linked to session7f3a9c2"Auth refresh rework" (14 obs). The retry loop handles a token revoked mid-flight, per the session's high-importance observations.
注意最后一句的措辞:它明确标注"per the session's high-importance observations"——这是从高价值观察中得到的证据,而非 Agent 从 diff 里猜测的意图。
示例二:函数追溯 + 早于会话关联的提交
用户问:"What was the agent doing when it wrote validateScope?"
git log -L :validateScope:src/auth/scope.ts -n 1 # 1122aabb ...memory_commit_lookup { "sha": "1122aabbccddeeff00112233445566778899aabb" }响应:
{ "commit": null }正确的呈现方式:
1122aabpredates agent session linking, so there is no recorded session. Fromgit show: it addedvalidateScopeto enforce per-token scopes. I can show the full diff if useful.
这里的关键是:如实说明"该提交早于会话关联,没有记录的会话",然后用git show的事实(而非编造)补充 diff 层面的信息。
示例三:裸路径
用户说:"Give me context on src/middleware/limit.ts."
git log -n 1 -- src/middleware/limit.ts取出 SHA,运行memory_commit_lookup,再按示例一的形态呈现提交 + 关联会话。
反模式:绝不编造意图
技能明确给出了对错对照,这是所有使用者的行为红线:
错误示范(WRONG):查询返回{ "commit": null },Agent 却仅凭 diff 叙述"the agent was refactoring auth"(当时在重构鉴权)。
正确示范(RIGHT):"This commit predates session linking, so there is no recorded agent session. Fromgit show: it changed token rotation in refresh.ts."
同理,在呈现会话细节时,必须逐字引用查询接口返回的内容(会话 ID、观察数量、摘要),不做转述、不四舍五入、不补充观察里不存在的意图。这一纪律与配套的 recall 技能 完全一致——recall 同样强调"只呈现工具返回的结果,绝不捏造观察、会话 ID 或重要度分数"。
输出自检清单
每次使用commit-context后,按下述清单自检(来自 SKILL.md 的 Checklist 节):
- SHA 来自
git blame/git log,而非猜测; commit: null被如实报告为"早于会话关联",未编造会话;- 会话详情逐字引用查询响应;
- 未声称超出观察记录所陈述范围的意图。
配套技能与排障
- 批量视角:commit-history 技能(
memory_commits)一次性列出多个已关联 Agent 会话的提交,支持branch、repo、limit(裸数字视为 limit,默认 100、上限 500)过滤,输出按时间倒序; - 深度挖掘:recall 技能(
memory_smart_search)搜索关联会话背后的观察记录,用importance >= 7的高价值观察补充"为什么"; - 排障:若
memory_commit_lookup不可用,先执行TROUBLESHOOTING.md的标准流程——确认宿主/plugin list中agentmemory已启用、重启宿主(.mcp.json仅在启动时读取)、检查/mcp连接状态,最后再走 REST 回退 的GET /agentmemory/session/by-commit?sha=<sha>。
至此,从"git 定位 SHA"到"呈现关联会话与高价值观察"的完整链路已经打通:commit-context负责单点追溯,commit-history负责批量概览,recall负责纵深检索,三者共同构成了 agentmemory 面向"代码 → 会话 → 决策"这一完整溯源链的查询体系。而这一切的基础,是 post-commit 钩子在每次提交时自动写入的KV.commits关联记录——理解这条写入链路,是正确解读查询结果的前提。
【免费下载链接】agentmemory#1 Persistent memory for AI coding agents based on real-world benchmarks项目地址: https://gitcode.com/GitHub_Trending/age/agentmemory
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考