news 2026/10/9 2:10:46

flexprice Context Sync 实践:基于 git-SHA 水印的 AGENTS.md 上下文同步机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
flexprice Context Sync 实践:基于 git-SHA 水印的 AGENTS.md 上下文同步机制

【免费下载链接】flexprice

Usage-based pricing and billing for developers 🔓 Cloud or self-hosted ⚙️ No-code UI 💰 Realtime usage metering 🎟 Credits & top-ups 🔑 Control feature access

项目地址:https://gitcode.com/gh_mirrors/fl/flexprice
点击查看免费下载

本文讲解 flexprice 仓库内置的 Context Sync(上下文同步)机制:它如何用 git-SHA 作为"水印",持续检测并驱动各层AGENTS.md与代码库保持同步。读完本文,你将掌握sync.py的--check / --sync / --advance三阶段命令流、manifest 注册表的结构与新增层的方法、LLM 更新上下文的最小化原则,以及如何把过期检查接入 CI 作为合并门禁。

flexprice 是一个用 Go 编写的用量计费与账单后端(Gin + Uber FX + Ent + PostgreSQL + ClickHouse + Kafka + Temporal),其代码库规模庞大(internal/下按 domain / repository / service / api / temporal / integration 分层)。为了让 AI 编程助手(Claude、Cursor、Codex 等)在每一层都能拿到与当前代码"不过期"的上下文,flexprice 在.context/sync/目录中实现了一套工具无关(tool-agnostic)的上下文同步引擎:它把每个AGENTS.md文件登记进 manifest,用上一次同步时的 HEAD SHA 作为水印,通过git diff精确判定哪些层受到了代码变更影响。本文以 SKILL.md 为骨架,结合 sync.py 的源码实现与 ci-job.yml 的 CI 参考配置,完整还原这套机制的设计与操作流程。

一、问题背景:为什么 AGENTS.md 会"过期"

AI 编程助手在修改代码时,依赖AGENTS.md这类上下文文件来理解项目结构、分层约束与常见陷阱。但这些文件是手工维护的静态文本:当internal/service/invoice.go新增了一个函数、某个"Key files"表格里的文件名被重命名,或某条编码规约被废弃时,AGENTS.md不会自动更新。

如果不加约束,会出现两种坏结果:

  • 上下文过期:LLM 拿着过时的文件清单与模式去改代码,产生与现状不符的修改;
  • 重写风暴:每次同步都把整个AGENTS.md重写一遍,diff 巨大、噪音高、评审困难。

Context Sync 的解法是:用 git 提交 SHA 作为水印(watermark)。每个AGENTS.md的 frontmatter 里记录"我上一次是基于哪个提交同步的"(synced_sha),同步时用git diff synced_sha..HEAD判断这一段时间里哪些路径发生了变更,从而只更新受影响的层、只产生最小 diff。正如 SKILL.md 所写:"Keeps all Plane-2AGENTS.mdfiles in sync with the codebase using git-SHA watermarks."(SKILL.md)。

二、核心架构:manifest 注册表 + owns globs + SHA 水印

整套机制由三个文件构成:

文件角色
.context/manifest.yaml所有上下文节点的注册表,记录每个AGENTS.md的owns路径范围与synced_sha水印
.context/sync/sync.py无外部依赖(仅需 git + Python 3.8+ + PyYAML)的同步引擎,负责 diff、判定、出提示包、推进水印
.context/sync/SKILL.md面向 LLM 的"技能说明书",定义完整操作流程与禁止事项
.context/sync/ci-job.ymlCI 参考配置,把过期检查接入 push / PR 门禁

2.1 manifest 注册表的结构

.context/manifest.yaml 的顶层是version: '1'与一个nodes列表,每个节点包含三个关键字段:

version: '1' nodes: - file: internal/domain/invoice/AGENTS.md owns: - internal/domain/invoice/** synced_sha: 8a1b776e6230d469e02f453f16cc54b5d7596a1a synced_at: 2026-06-09 00:00:00+00:00

字段语义:

  • file:AGENTS.md相对仓库根目录的路径,也是该节点的唯一标识(--file参数就按它定位);
  • owns:该层"拥有"的源码路径 glob 列表。只有命中这些 glob 的文件变更,才会判定该层过期;
  • synced_sha:上次同步时的 HEAD SHA,即水印。与当前git rev-parse HEAD不一致就说明有变更待同步;
  • synced_at:上次同步的 ISO 8601 时间戳。

2.2 当前已注册的节点

flexprice 目前注册了 4 个上下文节点(其余AGENTS.md如 internal/ee/e2eprobe/AGENTS.md 等未登记,不受本机制管理):

节点文件owns 范围说明
internal/domain/invoice/AGENTS.mdinternal/domain/invoice/**发票领域模型与 Repository 接口契约
internal/service/AGENTS.mdinternal/service/**全部业务逻辑编排层
internal/api/v1/AGENTS.mdinternal/api/v1/**Gin HTTP 处理器层
internal/temporal/workflows/invoice/AGENTS.mdinternal/temporal/workflows/invoice/**、internal/temporal/activities/**发票相关 Temporal 工作流与活动(一个节点可拥有多个 glob)

注意internal/temporal/workflows/invoice这个节点同时 owns 了两个路径范围——owns是列表,这正是为了让"活动变更也触发工作流上下文更新"这种跨目录依赖可以被表达。

2.3 各层 AGENTS.md 的 frontmatter 水印

被管理的AGENTS.md文件在 YAML frontmatter 中携带与 manifest 一致的水印信息。以 internal/service/AGENTS.md 为例:

--- layer: service owns: - "internal/service/**" synced_sha: 8a1b776e6230d469e02f453f16cc54b5d7596a1a synced_at: 2026-06-09T00:00:00Z ---

这里比 manifest 多了一个layer字段,用于标识层名;owns、synced_sha、synced_at与 manifest 保持一致。根目录的 AGENTS.md(constitution 层)同样携带synced_sha: 8a1b776e6230d469e02f453f16cc54b5d7596a1a水印。同步完成后,--advance会把这两处水印同时推进到新 HEAD。

三、五步工作流:从过期检测到同步完成

SKILL.md 将一次完整的同步拆成 5 个步骤,每一步都有对应的sync.py子命令:

Step 1 — 检查哪些层过期了

cd /path/to/flexprice # 仓库根目录 pip install pyyaml --break-system-packages -q python3 .context/sync/sync.py --check
  • 全部最新:退出码 0,输出All context nodes are current.;
  • 存在过期:退出码 1,并在 stderr 列出每个过期节点及其变更文件(每个节点最多显示前 5 个变更文件)。

Step 2 — 同步(生成更新提示包)

python3 .context/sync/sync.py --sync

引擎对每个节点做两件事(见sync.py的cmd_sync):

  • 没有 owned 变更的节点:不需要 LLM 参与,直接自动把 manifest 中该节点的synced_sha推进到当前 HEAD(输出[ADVANCE] <file> — no owned changes, SHA bumped to HEAD),并写入synced_at;
  • 有 owned 变更的节点:打印一个"提示包(prompt bundle)",内含三部分——--- CURRENT CONTEXT ---(当前AGENTS.md全文)、--- SCOPED DIFF ---(仅 owns 路径范围内的 git diff)、--- INSTRUCTIONS FOR THE LLM ---(给 LLM 的更新指令)。

Step 3 — 让 LLM 最小化更新每个过期的 AGENTS.md

把每个提示包喂给当前会话的 LLM(Claude/Cursor/Codex),产出更新后的AGENTS.md。更新必须遵守四条规则(SKILL.md):

  • 若"Key files"表格涉及文件的新增、删除、重命名,则更新该表格;
  • 若 diff 揭示了新的模式或移除了旧模式,则更新"Patterns"或"Common pitfalls";
  • 不要重写没有变化的章节(minimal diffs only);
  • 不要在AGENTS.md里添加改进建议或 TODO——那些应放入.context/findings/<layer>.md;
  • 在 frontmatter 中把synced_sha推进到当前 HEAD,synced_at更新为当前 ISO 8601 UTC 时间。

Step 4 — 推进 manifest 水印

每保存一个更新后的文件,就执行一次推进:

python3 .context/sync/sync.py --advance --file internal/service/AGENTS.md # 每个更新过的文件重复一次

--advance是必须执行的:SKILL.md 明确警告"不要运行--sync却跳过--advance",否则 manifest 不更新、节点会一直停留在过期状态。

Step 5 — 验证

python3 .context/sync/sync.py --check # 应输出: All context nodes are current.

四、命令参考:三个子命令与 --file 参数

sync.py使用argparse,三个子命令是互斥且必选的(--check/--sync/--advance三者必须且只能选一个),另有一个可选的--file用于定位单个节点:

命令行为退出码
--check全量(或--file指定节点)过期检查有过期节点返回 1,否则 0
--sync打印过期节点的提示包;无 owned 变更的节点直接推进水印0
--advance --file <path>将指定节点synced_sha推进到 HEAD缺--file时报错退出 1
--file <path>与--check/--sync组合,只处理单个节点视子命令而定

只同步单个节点的完整示例:

python3 .context/sync/sync.py --sync --file internal/service/AGENTS.md python3 .context/sync/sync.py --advance --file internal/service/AGENTS.md

cmd_advance的约束值得一提:如果--file指向的路径不在 manifest 中,会输出ERROR: <path> not found in manifest.并以退出码 1 终止(sync.py 的find_node/cmd_advance)。

五、源码级原理:sync.py 的关键实现

.context/sync/sync.py 全文约 230 行,零第三方运行时依赖(PyYAML 仅用于解析 manifest)。它的核心逻辑可以拆成五个函数,理解了它们就理解了整套机制:

5.1 水印读取:head_sha() 与 changed_files()

def head_sha() -> str: return run_git("rev-parse", "HEAD") def changed_files(from_sha: str, to_sha: str = "HEAD") -> list[str]: if from_sha == to_sha: return [] output = run_git("diff", "--name-only", f"{from_sha}..{to_sha}") return [f for f in output.splitlines() if f]

水印比较是纯 git 操作:synced_sha == HEAD时该节点"天然最新";否则用git diff --name-only synced_sha..HEAD拿到这段区间内所有变更文件的路径列表。注意仓库根目录是通过git rev-parse --show-toplevel动态解析的,因此sync.py必须从 git 仓库内执行。

5.2 归属判定:matches_globs()

def matches_globs(filepath: str, globs: list[str]) -> bool: for pattern in globs: if fnmatch.fnmatch(filepath, pattern): return True clean = pattern.rstrip("*").rstrip("/") if filepath.startswith(clean + "/") or filepath == clean: return True return False

这是"哪些节点过期"的判定核心:一个变更文件要么精确命中某个 glob(fnmatch),要么落在该 glob 去掉尾部*后的目录前缀下。后一条路径前缀匹配保证了internal/service/**这类模式能覆盖internal/service/foo.go这类具体文件。

5.3 范围 diff:scoped_diff()

def scoped_diff(from_sha: str, owns: list[str]) -> str: paths = [] for glob in owns: clean = glob.rstrip("*").rstrip("/") if clean: paths.append(clean) args = ["diff", from_sha, "HEAD", "--"] + paths return run_git(*args)

提示包里的 diff 不是全量 diff,而是把ownsglob 转成 git pathspec 后的范围 diff——LLM 只会看到"该层自己代码"的变更,避免被无关模块的改动干扰。这是"最小更新"在输入侧的保证。

5.4 三阶段命令:cmd_check / cmd_sync / cmd_advance

  • cmd_check:遍历所有节点,收集"有 owned 变更"的节点列表;空则输出All context nodes are current.返回 0,否则列出过期节点与变更文件并返回 1——这个退出码正是 CI 门禁的判定依据;
  • cmd_sync:对"已是最新"的节点输出[OK];对"无 owned 变更"的节点直接推进水印(node["synced_sha"] = current_head,写入 UTC 时间戳)且不调用 LLM;对"有 owned 变更"的节点调用print_prompt_bundle()打印提示包;最后统一save_manifest()并输出后续步骤指引(喂给 LLM → 保存文件 →--advance→ 以 PR 形式评审合并);
  • cmd_advance:定位节点、把synced_sha与synced_at写入 manifest 并落盘,输出[ADVANCED] <file> → synced_sha = <HEAD>。

5.5 提示包的格式契约

print_prompt_bundle()输出的"给 LLM 的指令"本身也是一段自包含的协议,直接约束了 LLM 的输出行为:

Output: the COMPLETE updated AGENTS.md content, nothing else. After the LLM produces the file, run: python3 .context/sync/sync.py --advance --file <path>

即 LLM 应输出"完整的更新后 AGENTS.md",除此之外什么都不输出——这一契约保证了 LLM 输出可以直接落盘为文件。

六、新增一个上下文层:注册到 manifest

当你在某个目录新建AGENTS.md并希望纳入同步机制时,按 SKILL.md 的示例在 .context/manifest.yaml 中新增节点:

# .context/manifest.yaml — add a new node: - file: internal/repository/AGENTS.md owns: - "internal/repository/**" synced_sha: <current HEAD SHA from: git rev-parse HEAD> synced_at: <ISO 8601 now>

要点:

  • synced_sha初始值取自当前git rev-parse HEAD,确保注册那一刻节点就是"最新"的;
  • owns的 glob 必须与该目录的实际源码路径严格对应,否则会漏判或误判过期;
  • 同步时引擎不会自动创建 manifest——load_manifest()在 manifest 缺失时会直接报ERROR: .context/manifest.yaml not found. Run from repo root.并以退出码 1 退出(sync.py),所以新层文件必须先登记、再同步。

七、红线清单:What NOT to do

SKILL.md 明确列出三条禁止事项,它们是这套机制保持"低噪音"的关键:

  1. 不要每次同步都重写整个 AGENTS.md——只做最小 diff;
  2. 不要把批评、TODO、"待改进项"写进 AGENTS.md——这些应放入.context/findings/目录(根目录 AGENTS.md 同样声明:"Improvement notes →.context/findings/(never in this file)");
  3. 不要运行--sync却跳过--advance——否则 manifest 不更新,节点永远停留在过期状态。

值得注意的设计是:findings与AGENTS.md在内容职责上是分离的——AGENTS.md只承载"当前为真的约束与模式",而改进建议流向独立目录,避免同步时把主观意见混入客观上下文。

八、接入 CI:把过期检查变成合并门禁

.context/sync/ci-job.yml 提供了一段可直接复制的参考 CI 配置(复制到.github/workflows/context-sync.yml即可)。其要点:

on: push: branches: [main] pull_request: branches: [main]
  • actions/checkout必须设置fetch-depth: 0(全量历史),因为git diff synced_sha..HEAD依赖完整提交历史;
  • Python 3.11 +pip install pyyaml准备运行环境;
  • 核心步骤python3 .context/sync/sync.py --check:有任一已注册节点落后于 HEAD 时退出码为 1,直接让 CI 失败;
  • 失败后的处理路径写在注释里:本地跑--sync→ 把提示包喂给 LLM → 提交更新后的AGENTS.md→ 逐文件执行--advance。

这套配置把"上下文是否过期"变成了与编译、单测同级的合并门禁:任何合并到 main 的 PR,只要触碰了已注册层却未同步上下文,就会在 CI 中被拦下。

九、完整示例会话:一次真实的同步过程

SKILL.md 用一个完整的对话示例展示了这套机制在 AI 会话中的典型用法:

You: sync context Claude: → runs python3 .context/sync/sync.py --check → finds internal/service/AGENTS.md is stale (3 files changed) → runs --sync, reads the prompt bundle → produces updated internal/service/AGENTS.md (minimal changes only) → runs --advance --file internal/service/AGENTS.md → runs --check again → "All context nodes are current."

这个示例把前文的所有环节串成了一条流水线:检测(--check发现 1 个过期节点)→ 取包(--sync读取范围 diff 与当前上下文)→ 生成(LLM 产出最小更新)→ 推进(--advance更新水印)→ 验证(--check全绿)。这也正是 LLM 触发词("sync context"、"update context"、"is context stale" 等)被设计成可识别意图的原因——SKILL.md 的 frontmatter 里就列出了这些触发词,便于各类 AI 客户端按语义唤起该技能。

十、与各层 AGENTS.md 的配合:上下文分层的落地形态

Context Sync 管理的是"分层上下文",各层AGENTS.md本身也体现了严格的分层纪律,同步时 LLM 需按各层的固定章节结构做最小更新:

  • internal/domain/invoice/AGENTS.md:纯 Go 领域模型层,强调"零 DB 依赖"、金额一律decimal.Decimal、发票状态机draft → open → paid / void,且 Repository 接口变更必须与 internal/repository 实现协同更新;
  • internal/service/AGENTS.md:业务编排层,突出ComputeInvoice的幂等约束、事务走WithTx、Temporal 工作流必须经StartWorkflow启动,以及GetPreviewInvoice(只读)与ComputeInvoice(写入)的易混陷阱;
  • internal/api/v1/AGENTS.md:HTTP 层,只做"解析 → 校验 → 委托 → 响应",并强调 Swagger 注解与@x-scope(如FinalizeInvoice标记delete、GetPreviewInvoice标记read)的规范。

正是因为有这套分层上下文,同步引擎才能把 diff 精确地"路由"到受影响的层,而不是让根目录的宪法级 AGENTS.md(Stack、目录地图、硬性不变量、loglint 合并门禁等)频繁变动。

总结

flexprice 的 Context Sync 是一套把"AI 上下文维护"工程化的轻量方案:manifest 注册 + SHA 水印 + glob 归属 + 范围 diff + LLM 最小更新 + 水印推进 + CI 门禁。它没有引入任何重型工具链(仅 Python 3.8+、git、PyYAML),却精确解决了"AGENTS.md 何时过期、该更新哪一层、怎么保证最小 diff"三个核心问题。对于任何维护多层AGENTS.md并深度依赖 AI 编程助手的代码库,这套以 .context/sync/sync.py 为引擎、.context/sync/SKILL.md 为操作规程、.context/manifest.yaml 为注册中心的模式,都值得直接借鉴。

【免费下载链接】flexprice

Usage-based pricing and billing for developers 🔓 Cloud or self-hosted ⚙️ No-code UI 💰 Realtime usage metering 🎟 Credits & top-ups 🔑 Control feature access

项目地址:https://gitcode.com/gh_mirrors/fl/flexprice
点击查看免费下载

相关推荐

上一篇:13种音频加密格式终极解决方案:QMCDecode深度解析与技术实践
下一篇:深蓝词库转换:终极跨平台输入法词库转换工具完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 2:07:40

Deep-Live-Cam换脸模型配置:2个文件3步跑通

Deep-Live-Cam换脸模型配置:2个文件3步跑通 【免费下载链接】Deep-Live-Cam real time face swap and one-click video deepfake with only a single image 项目地址: https://gitcode.com/GitHub_Trending/de/Deep-Live-Cam Deep-Live-Cam 做实时换脸&#xff0c;整套模…

作者头像 李华
网站建设 2026/10/9 2:07:23

Java手机APP信息统计分析系统:从埋点采集到看板聚合的完整实现

简介&#xff1a;这是一套面向Java后端与大数据方向学习者的手机APP信息统计分析系统源码&#xff0c;围绕用户行为数据的采集、存储、分析与可视化展开&#xff0c;适合作为课程设计、毕业设计或大数据入门项目的参考实现。资源包共57个文件&#xff0c;约56.75MB&#xff0c;…

作者头像 李华
网站建设 2026/10/9 2:04:27

模板消息错误消息优化:从错误码规范到链路追踪的工程实践

做了快十年的模板消息平台&#xff0c;我最大的体会是&#xff1a;模板这玩意儿&#xff0c;看着简单&#xff0c;真出起问题来能把人逼疯。尤其是错误消息——用户那边只收到一句"发送失败"&#xff0c;后台日志里躺着一串又臭又长的堆栈&#xff0c;模板ID、参数名…

作者头像 李华