【免费下载链接】flexprice
Usage-based pricing and billing for developers 🔓 Cloud or self-hosted ⚙️ No-code UI 💰 Realtime usage metering 🎟 Credits & top-ups 🔑 Control feature access
本文讲解 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.yml | CI 参考配置,把过期检查接入 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.md | internal/domain/invoice/** | 发票领域模型与 Repository 接口契约 |
| internal/service/AGENTS.md | internal/service/** | 全部业务逻辑编排层 |
| internal/api/v1/AGENTS.md | internal/api/v1/** | Gin HTTP 处理器层 |
internal/temporal/workflows/invoice/AGENTS.md | internal/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.mdcmd_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 明确列出三条禁止事项,它们是这套机制保持"低噪音"的关键:
- 不要每次同步都重写整个 AGENTS.md——只做最小 diff;
- 不要把批评、TODO、"待改进项"写进 AGENTS.md——这些应放入
.context/findings/目录(根目录 AGENTS.md 同样声明:"Improvement notes →.context/findings/(never in this file)"); - 不要运行
--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
相关推荐
redis-py 仓库 AGENTS.md 同步审计工作流:基于 sync-claude-md 命令的规范文档维护实践
redis py 仓库 AGENTS.md 同步审计工作流:基于 sync claude md 命令的规范文档维护实践 导读 本文面向 redis py 仓库的
后端数据库客户端缓存Sails 资产管线中的 sync 任务:基于 grunt-sync 的增量文件同步机制详解
Sails 资产管线中的 sync 任务:基于 grunt sync 的增量文件同步机制详解 导读 在 Sails 框架的默认资产管线中, tasks/conf
后端EthStorage node vs 传统存储方案:为什么它是Rollups长期数据可用性的终极选择
EthStorage node vs 传统存储方案:为什么它是Rollups长期数据可用性的终极选择 在区块链技术快速发展的今天,Rollups 作为以太坊扩容
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考