把 AI 会话链接直接写进提交信息,看似方便,却可能同时引入隐私泄漏、链接失效和历史污染。更稳妥的做法是让提交只保留低敏感索引,把详细证据放进 Git Notes,并由 CI 将测试结果和证据摘要绑定到构建产物。
为什么不应直接提交会话链接
团队使用 AI 编程工具后,通常希望回答三个问题:代码是否由 AI 辅助、使用了哪个模型或提示词模板、最终由谁确认。最直接的方案是在提交信息里附上会话 URL,但提交信息属于长期历史,一旦推送便很难彻底清除。
会话链接还可能携带账号、组织或会话标识。即使链接本身需要登录,未来的权限配置变化也可能扩大可见范围。部分服务会删除旧会话或调整 URL,几年后留下的可能只是无法访问的字符串。
| 方案 | 是否进入提交对象 | 可独立更新 | 主要风险 |
|---|---|---|---|
| 提交完整会话链接 | 是 | 否,修改会改变提交哈希 | 泄漏、失效、污染历史 |
| 仓库内保存会话原文 | 是 | 可以,但仍进入历史 | 提示词和业务上下文扩散 |
| Git Notes 保存证据摘要 | 否 | 是 | 需要额外同步和权限治理 |
| CI 构建证明 | 否 | 由 CI 生成 | 依赖 CI 身份和制品存储 |
这里的关键不是保存完整对话,而是保存足以审计来源的最小证据:模型标识、提示词模板版本、会话在受控系统中的内部编号、人工确认者,以及测试和构建结果。密码、源码片段、客户数据和公开可访问的会话 URL 都不应进入证据。
设计分层的证据模型
可以把留痕拆成三层。第一层是提交 Trailer,只声明该提交使用了 AI,并指出证据位于哪个 notes ref。Trailer 会永久进入提交,因此只放稳定、低敏感的信息:
Implement invoice validation
AI-Assisted: true
Evidence-Ref: refs/notes/ai-evidence
Reviewed-by: Xu Kang <xu@example.com>
Git 自带的 interpret-trailers 可以规范地追加字段:
git interpret-trailers --in-place \
--trailer 'AI-Assisted: true' \
--trailer 'Evidence-Ref: refs/notes/ai-evidence' \
--trailer 'Reviewed-by: Xu Kang <xu@example.com>' \
.git/COMMIT_EDITMSG
第二层是 Git Notes 中的结构化摘要。它附着在提交哈希上,但不改变提交对象。第三层由 CI 生成,把提交、notes 内容摘要、测试结果和最终制品绑定起来,并由 CI 身份签发构建证明。
这三层的职责不同:Trailer 负责发现,Notes 负责描述,构建证明负责验证某次 CI 确实针对这些输入产出了指定制品。
用 Git Notes 记录最小证据
下面的脚本依赖 Git 和 jq,把证据作为 JSON 附加到当前提交。提示词只记录模板版本和 SHA-256,不保存正文;会话编号也应使用组织内部的不可公开解析标识。
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ne 4 ]; then
echo "usage: $0 MODEL PROMPT_VERSION PROMPT_SHA256 SESSION_ID" >&2
exit 2
fi
model=$1
prompt_version=$2
prompt_sha256=$3
session_id=$4
commit=$(git rev-parse HEAD)
reviewer=$(git config user.email)
created_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
jq -n \
--arg schema_version "1" \
--arg commit "$commit" \
--arg model "$model" \
--arg prompt_version "$prompt_version" \
--arg prompt_sha256 "$prompt_sha256" \
--arg session_id "$session_id" \
--arg reviewer "$reviewer" \
--arg created_at "$created_at" \
'{
schema_version: $schema_version,
commit: $commit,
ai: {
model: $model,
prompt_version: $prompt_version,
prompt_sha256: $prompt_sha256,
session_id: $session_id
},
human_review: {
confirmed: true,
reviewer: $reviewer
},
created_at: $created_at
}' > "$tmp"
git notes --ref=ai-evidence add -f -F "$tmp" "$commit"
git notes --ref=ai-evidence show "$commit"
保存为 scripts/add-ai-note.sh 并赋予执行权限后,可以这样调用:
chmod +x scripts/add-ai-note.sh
./scripts/add-ai-note.sh \
'gpt-5' \
'invoice-review/v3' \
'b7e23ec29af22b0b4e41da31e868d57226121c84' \
'ai-session-01842'
git push origin refs/notes/ai-evidence
Notes 默认不会随分支自动推送或拉取。团队需要显式同步,也可以配置抓取规则:
git config --add remote.origin.fetch \
'+refs/notes/ai-evidence:refs/notes/ai-evidence'
git fetch origin
在 CI 中生成构建证明
以下是一个 Node.js 项目的 GitHub Actions 示例。项目需要在 package.json 中提供 test 和 build 脚本,构建结果位于 dist/。流程先读取 note,再运行测试,最后把代码提交、AI 摘要和测试结果写入制品清单。
name: verified-build
on:
push:
branches: [main]
permissions:
contents: read
id-token: write
attestations: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch AI evidence
run: |
git fetch origin \
refs/notes/ai-evidence:refs/notes/ai-evidence
git notes --ref=ai-evidence show "$GITHUB_SHA" > ai-note.json
jq -e \
'.commit == env.GITHUB_SHA and
.human_review.confirmed == true and
(.ai.prompt_sha256 | length == 40 or length == 64)' \
ai-note.json
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Test and build
run: |
npm ci
npm test
npm run build
jq -n \
--arg status passed \
--arg runner "$RUNNER_NAME" \
'{status: $status, runner: $runner}' > test-result.json
- name: Assemble artifact
run: |
mkdir -p release
cp -R dist release/dist
jq -n \
--arg commit "$GITHUB_SHA" \
--slurpfile ai ai-note.json \
--slurpfile tests test-result.json \
'{commit: $commit, ai_evidence: $ai[0], tests: $tests[0]}' \
> release/evidence.json
tar -czf release.tgz release
- uses: actions/attest-build-provenance@v2
with:
subject-path: release.tgz
- uses: actions/upload-artifact@v4
with:
name: verified-release
path: release.tgz
actions/attest-build-provenance 会针对 release.tgz 生成构建来源证明。下载制品后,可以使用 GitHub CLI 验证其证明与仓库身份:
gh attestation verify release.tgz --repo OWNER/REPOSITORY
验证成功只能说明制品由指定仓库的受信工作流产生,并不自动证明 AI 输出正确。代码审查、测试质量和发布审批仍然是独立控制项。
权限、更新与审计边界
Git Notes 可以被覆盖或删除,所以它本身不是不可篡改日志。应限制 refs/notes/ai-evidence 的推送权限,禁止普通开发者强制更新,并让 CI 只接受结构符合约定的记录。多人同时写 notes 时还要在推送前拉取远端 ref,处理并发更新。
即使 Notes 不在普通提交历史里,也不能把它当作秘密存储。能够读取该 ref 的用户仍能看到内容。敏感对话应保存在有访问控制和保留期限的审计系统中,note 只记录内部编号或内容摘要。
审计时应同时检查四项:提交 Trailer 是否指向约定 ref;note 中的提交哈希是否匹配;人工确认是否存在;发布制品的证明是否由受信 CI 身份签发。若 note 后续被修改,旧制品内的 evidence.json 和制品证明仍保留当时使用的证据副本,可用于识别差异。
总结
可追溯不等于永久保存完整 AI 对话。更合适的方案是按敏感度和生命周期分层:提交 Trailer 只承担发现入口,Git Notes 保存可更新的最小证据,CI 把模型、提示词版本、人工确认和测试结果写入制品,并为制品生成构建证明。
落地时需要记住四点:不要提交公开会话链接或提示词原文;显式同步并保护 notes ref;把测试结果与最终制品一起证明;把 AI 留痕视为来源审计信息,而不是代码正确性的替代品。这样既能保留调查线索,也不会让敏感上下文永久固化在仓库历史中。
评论