AI 编程中的两个真实问题:多项目任务管理与多人协作隔离
在多项目并行与多人协作的 AI 编程实践中,任务状态的连贯性与个人配置的隔离性是影响效率的关键痛点。本文提出一套基于“子项目 Source of Truth”与“本地规则隔离”的工程化方案,旨在解决跨项目任务断点管理与团队配置污染问题,并提供一套可复制的目录结构、读写边界与备份策略。
一个工程师开始高频使用 AI agent 写代码后,很快遇到的问题并不是“AI 会不会写函数”,而是另一类更现实的问题。
他同时维护多个项目:有的项目在做功能开发,有的项目在做配置迁移,有的项目只是偶尔修 bug。每天打开 AI agent 时,他都要重新解释:这个项目做到哪一步,哪个任务已经完成,哪个任务还在进行,哪个任务只是计划中。时间一长,任务状态散落在各个对话、各个项目和零散文档里。AI 很容易重复安排一个已经完成的任务,也可能忽略某个正在进行但还没收尾的任务。
随后第二个问题出现了:其中一些项目并不是个人项目,而是多人共享协作的项目。每个人使用 AI agent 的习惯不同,有人喜欢临时草稿,审阅之后再生成正式文档;有人则反感这种方式,直接让 AI 一次性生成详细任务文件。但这些个人偏好不应该写进团队共享的 AGENT.md,也不应该污染 .gitignore 或项目源码。
这两个问题可以归纳为:
- 单用户多个项目的管理问题。
- 单项目被多用户共同管理时的协作隔离问题。
本文讨论的不是某个具体工具的用法,而是在一次真实 AI 编程实践中逐步形成的工程化方案。
先看整体结构
这套方案里有两个层次:根项目负责聚合、交接和备份;子项目负责保存真实任务状态和本地个人规则。
flowchart LR
subgraph ROOT["根项目 / 汇总与备份"]
RP["planned.md
doing.md
completed.md"]
DOC["交接文档
new-project-pass-info-to-AGENT-MD.md"]
BK["备份目录
local-user-config-backups/"]
end
subgraph CHILD["子项目 / Source of Truth"]
TS["任务状态
tasks-status/"]
AG["团队规则
AGENT.md"]
LP["个人规则
SomeUser-agent.local.md"]
TMP["临时草稿
SomeUser-tmp/"]
EX["本地忽略
.git/info/exclude"]
end
TS --> RP
DOC -. "复制内容给
子项目 agent" .-> AG
LP --> BK
EX --> BK
TMP -. "默认不备份" .-> BK
RP -. "只读聚合" .-> TS
AG -. "最小 hook" .-> LP
EX -. "本地忽略" .-> LP
EX -. "本地忽略" .-> TMPflowchart LR
subgraph ROOT["根项目 / 汇总与备份"]
RP["planned.md
doing.md
completed.md"]
DOC["交接文档
new-project-pass-info-to-AGENT-MD.md"]
BK["备份目录
local-user-config-backups/"]
end
subgraph CHILD["子项目 / Source of Truth"]
TS["任务状态
tasks-status/"]
AG["团队规则
AGENT.md"]
LP["个人规则
SomeUser-agent.local.md"]
TMP["临时草稿
SomeUser-tmp/"]
EX["本地忽略
.git/info/exclude"]
end
TS --> RP
DOC -. "复制内容给
子项目 agent" .-> AG
LP --> BK
EX --> BK
TMP -. "默认不备份" .-> BK
RP -. "只读聚合" .-> TS
AG -. "最小 hook" .-> LP
EX -. "本地忽略" .-> LP
EX -. "本地忽略" .-> TMPflowchart LR
subgraph ROOT["根项目 / 汇总与备份"]
RP["planned.md
doing.md
completed.md"]
DOC["交接文档
new-project-pass-info-to-AGENT-MD.md"]
BK["备份目录
local-user-config-backups/"]
end
subgraph CHILD["子项目 / Source of Truth"]
TS["任务状态
tasks-status/"]
AG["团队规则
AGENT.md"]
LP["个人规则
SomeUser-agent.local.md"]
TMP["临时草稿
SomeUser-tmp/"]
EX["本地忽略
.git/info/exclude"]
end
TS --> RP
DOC -. "复制内容给
子项目 agent" .-> AG
LP --> BK
EX --> BK
TMP -. "默认不备份" .-> BK
RP -. "只读聚合" .-> TS
AG -. "最小 hook" .-> LP
EX -. "本地忽略" .-> LP
EX -. "本地忽略" .-> TMPflowchart LR
subgraph ROOT["根项目 / 汇总与备份"]
RP["planned.md
doing.md
completed.md"]
DOC["交接文档
new-project-pass-info-to-AGENT-MD.md"]
BK["备份目录
local-user-config-backups/"]
end
subgraph CHILD["子项目 / Source of Truth"]
TS["任务状态
tasks-status/"]
AG["团队规则
AGENT.md"]
LP["个人规则
SomeUser-agent.local.md"]
TMP["临时草稿
SomeUser-tmp/"]
EX["本地忽略
.git/info/exclude"]
end
TS --> RP
DOC -. "复制内容给
子项目 agent" .-> AG
LP --> BK
EX --> BK
TMP -. "默认不备份" .-> BK
RP -. "只读聚合" .-> TS
AG -. "最小 hook" .-> LP
EX -. "本地忽略" .-> LP
EX -. "本地忽略" .-> TMP这里的关键不是文件名本身,而是责任边界:
- 子项目的
tasks-status/是任务状态的 source of truth。 - 根项目的
planned.md、doing.md、completed.md只是聚合视图。 - 团队共享的
AGENT.md只放最小 hook。 - 个人规则、临时草稿、本地 ignore 都留在个人本地。
- 根项目可以备份 allowlist 中的本地配置,但不默认备份临时目录。
为什么要这么麻烦
可以先看几种常见但容易出问题的做法。
| 错误做法 | 直接后果 | 改进后流程 |
|---|---|---|
| 任务状态只留在对话历史里 | 换会话、换项目、换 agent 后状态丢失或过期 | 每个子项目维护 tasks-status/,agent 进入项目后先扫描状态文件 |
| 根项目直接修改子项目任务文件 | 根项目变成跨项目高权限 agent,误改范围扩大 | 根项目只读子项目任务状态,只更新自己的汇总文件 |
每个人都改团队 AGENT.md | 个人偏好污染团队规则,所有人的 agent 都会读到 | AGENT.md 只保留最小 hook,个人规则放进 SomeUser-agent.local.md |
把个人文件写进共享 .gitignore | 个人工作流变成团队规范,协作边界变模糊 | 用每个子项目自己的 .git/info/exclude 忽略个人文件 |
| 备份所有 ignored 文件 | 可能把缓存、密钥、临时草稿一起带走 | 只 allowlist 备份个人规则和 .git/info/exclude |
这里还有一个底层原因:LLM 的上下文窗口既昂贵,也容易被污染。任务状态如果只靠对话历史保存,会越聊越长、越聊越乱;个人规则如果混进共享配置,就会让所有协作者的 agent 都带上同一个人的偏好。本文不展开讨论 RAG、工具隔离或运行时隔离,只讨论这个问题在文件和目录约定上的落地方式。
问题一:一个人同时管理多个项目,如何管理所有任务的状态
最初的直觉是:能不能有一个“主项目”,专门管理所有子项目的任务?
但这里很快出现一个边界问题:如果主项目可以随意修改子项目文件,那它就变成了另一个高权限 agent。它可能为了“整理任务”而修改子项目的文档、配置,甚至误碰源码。这会让风险扩大。
所以第一个关键约束是:
主项目只读子项目任务状态,不直接修改任何子项目文件。
每个子项目自己维护任务状态,主项目只负责读取和汇总。这样,子项目仍然是 source of truth,主项目只是一个聚合视图。
子项目暴露统一结构:
| |
每个任务是一个独立 Markdown 文件,并放在对应状态目录里。例如:
| |
主项目读取这些状态后,生成自己的汇总文件:
| |
汇总文件不是新的任务源,只是当前视图。每条汇总信息都保留 Source path,这样读者可以追溯到原始子项目任务文档。
flowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> Cflowchart TD
A["Child Project A"] --> AS["tasks-status/*.md"]
B["Child Project B"] --> BS["tasks-status/*.md"]
C["Child Project C"] --> CS["tasks-status/*.md"]
AS --> R["Root Task Manager"]
BS --> R
CS --> R
R --> P["planned.md"]
R --> D["doing.md"]
R --> E["completed.md"]
R -. "read-only" .-> A
R -. "read-only" .-> B
R -. "read-only" .-> C这里的重点不是目录命名,而是责任划分:
- 子项目负责维护真实任务状态。
- 主项目负责聚合和展示。
- 主项目不能替子项目修复、移动、重命名任务文件。
- 如果子项目缺少
tasks-status/,主项目只能报告“未配置”,不能替它创建。
这个边界让 AI agent 的行为更可预测。
问题一继续:任务状态靠人工维护,如何保证信息准确?
任务状态结构解决了“在哪里读”的问题,但没有解决“状态是否新鲜”的问题。
如果一个任务已经完成,但子项目没有把它从 doing/ 移到 completed/,主项目看到的状态仍然会过期。这个问题不能完全靠主项目解决,因为主项目不是 source of truth。
因此需要对子项目 agent 增加状态维护纪律:
- 安排新任务前,先扫描
planned/、doing/、completed/。 - 至少检查三个目录下的任务文件名。
- 如果文件名看起来相关,或无法判断是否重复,就读取具体任务文档。
- 状态变化时,立即移动任务文件到对应目录。
- 移动任务时,同步重命名文件中的状态段。
doing任务发生大量变更时,更新任务文档的时间、摘要、当前状态和下一步。- 标记
completed前,确认文档包含完成说明、完成时间、剩余风险或阻塞项。
任务文件名也需要有强约束:
| |
其中 <status> 必须和所在目录一致:
| |
这个设计看似啰嗦,但它解决的是 AI agent 的实际问题:agent 很依赖明确、重复、可扫描的文本协议。命名越稳定,状态判断越少靠猜。
问题二:多人共享项目中,个人 AI 规则不能污染团队配置
第二个问题来自协作项目。
共享项目通常会有一个 AGENT.md,用于告诉 AI agent 如何在这个项目中工作。但如果每个人都把自己的偏好写进去,文件会很快变成混合体:
- 有人希望中文对话。
- 有人希望英文文档。
- 有人希望保留临时草稿。
- 有人有自己的任务维护习惯。
- 有人使用不同的本地自动化。
这些都是真实需求,但不一定是团队规范。
所以共享 AGENT.md 应该保持最小,只放一个 hook:
| |
真正的个人规则放到本地文件:
| |
临时草稿放到:
| |
这些个人文件通过 .git/info/exclude 忽略:
| |
这里故意使用 .git/info/exclude,而不是共享 .gitignore。原因是:这些文件是个人工作流的一部分,不一定应该成为团队仓库规范。
更完整的子项目目录约定可以写成这样:
| |
其中:
AGENT.md:团队共享规则,只放项目级约束和个人规则 hook。SomeUser-agent.local.md:当前用户自己的 AI 工作偏好。SomeUser-tmp/:当前用户自己的临时草稿和中间材料。.git/info/exclude:当前用户在当前子项目里的本地忽略规则。tasks-status/:这个子项目自己的任务状态事实来源。
如果同一个项目里有多个协作者,每个人都应该有独立 namespace:
| |
user-a 不复用 user-b 的 local 文件,user-b 也不覆盖 user-a 的 local 文件。团队共享的 AGENT.md 只需要知道“如果某个用户的 local 文件存在,就把它当作补充偏好读取;不存在就忽略”。
flowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> Gflowchart TD
G["Shared Project Repository"] --> A["AGENT.md"]
A --> H["Minimal hook only"]
H --> U1["user-a-agent.local.md"]
H --> U2["user-b-agent.local.md"]
U1 --> P1["user-a preferences"]
U2 --> P2["user-b preferences"]
E[".git/info/exclude"] --> I1["ignore user-a local files"]
E --> I2["ignore user-b local files"]
T1["user-a-tmp/"] --> C1["user-a drafts"]
T2["user-b-tmp/"] --> C2["user-b drafts"]
U1 -. "local-only" .-> G
U2 -. "local-only" .-> G
T1 -. "local-only" .-> G
T2 -. "local-only" .-> G这样做的效果是:
- 团队共享文件只增加一句最小 hook。
- 每个人可以有自己的 AI 工作习惯。
- 个人规则不会进入共享提交。
- 个人临时文件不会污染正式文档。
- 没有个人规则文件时,项目仍按原有规则运行。
项目初始化与新用户接入:用 SomeUser 做占位符
这里处理的其实不是单一的“新项目接入”问题,而是模板初始化时的命名问题。通常有两种场景:
- 同一个用户开始管理一个新项目。
- 同一个项目里有新的协作者开始使用自己的 AI 规则。
如果这个方案要长期使用,就不能只适配某一个人。否则无论是前一种情况,还是后一种情况,最后都会复制出一堆带有旧名字的规则。
因此交接模板中统一使用 SomeUser 作为占位符。无论是项目初始化,还是同一项目里有新用户加入协作,agent 都应该先问当前用户:
| |
用户确认后,再做全量替换:
| |
比如当前用户选择 user-a,则生成:
| |
如果后续同一项目里再有 user-b 加入协作,那么就为 user-b 生成自己的一组本地文件,而不是复用或覆盖 user-a 的那一组:
| |
这个 namespace 最好是短小、稳定、适合文件名的字符串,例如:
| |
不建议包含空格、斜杠或 shell 特殊字符,因为这些会增加脚本和路径处理风险。
实施层:根项目也要有边界
根项目本身也需要规则。否则它会从“管理任务”慢慢变成“能修改所有子项目的控制台”。
根项目允许管理的内容应该有限,例如:
| |
补充说明: 虽然根项目通常只是由单独个人管理,理论上可以只用一个总的
AGENT.md,临时文件夹也可以直接命名为tmp,但是为了保持整个工程规范的统一性,我们在这里依然采用了与子项目相同的AGENT.md加上SomeUser-agent.local.md以及SomeUser-tmp/的结构。这样的设计在最终效果上与只用一个AGENT.md是一致的,但能让整个工程体系的规范保持一致。
但它不能修改:
| |
如果子项目需要接入这套规则,根项目不直接修改子项目文件,而是提供交接文档:把 new-project-pass-info-to-AGENT-MD.md 里的内容复制出来,粘贴到目标子项目的 Codex 或 Claude 对话框中,让子项目里的 agent 按这份说明自己执行配置。
这条约束非常重要。它让主项目像一个 dashboard 和 harness,而不是一个拥有跨项目写权限的 agent。
周期任务:读报告和写汇总要分开
实际使用中,很自然会想到周期任务:每天或每个工作日生成任务报告。
这里也需要区分两类任务:
report-only task
只读取各项目任务状态,输出一份报告,不写项目文件。aggregation update task
读取各项目任务状态,并更新根项目的planned.md、doing.md、completed.md。
这两类任务的风险不同。前者低风险,后者会写根项目文件。
因此更新型任务执行后,需要写日志,例如:
| |
报告型任务可以引用这个时间:
| |
这样读者可以知道报告到底基于什么时间点的状态。
子项目中被 Git 忽略的个人文件,也需要治理
各个子项目里的个人规则文件不进 Git,这解决了共享污染问题,但引出了另一个问题:这些文件会不会丢?
例如:
| |
这些文件是本地配置,不提交到共享仓库。如果机器迁移或项目重建,它们可能丢失。
解决办法不是“备份所有 ignored 文件”。那样风险太大,因为 ignored 文件里可能有缓存、密钥、构建产物、临时草稿。
更稳妥的做法是 allowlist:
| |
默认不备份:
| |
因为临时草稿目录可能包含未整理内容、中文审阅稿、敏感上下文或过期中间产物。除非显式开启,否则不应该进入备份。
备份脚本的原则是:
- 只扫描直接子项目。
- 只读子项目。
- 只写根项目备份目录。
- 按子项目分目录保存。
- 每个备份目录生成
manifest.md。 - manifest 记录 namespace、来源路径、备份文件、缺失项。
flowchart LR
subgraph SRC["直接子项目"]
S["子项目目录"]
R1["个人规则文件
NAMESPACE-agent.local.md"]
R2["本地忽略规则
.git/info/exclude"]
T["临时目录
NAMESPACE-tmp/"]
end
B["备份脚本
backup-local-user-configs.sh"]
subgraph OUT["根项目备份目录"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "默认不读取" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["直接子项目"]
S["子项目目录"]
R1["个人规则文件
NAMESPACE-agent.local.md"]
R2["本地忽略规则
.git/info/exclude"]
T["临时目录
NAMESPACE-tmp/"]
end
B["备份脚本
backup-local-user-configs.sh"]
subgraph OUT["根项目备份目录"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "默认不读取" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["直接子项目"]
S["子项目目录"]
R1["个人规则文件
NAMESPACE-agent.local.md"]
R2["本地忽略规则
.git/info/exclude"]
T["临时目录
NAMESPACE-tmp/"]
end
B["备份脚本
backup-local-user-configs.sh"]
subgraph OUT["根项目备份目录"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "默认不读取" .-> B
B --> O
O --> F1
O --> F2
O --> Mflowchart LR
subgraph SRC["直接子项目"]
S["子项目目录"]
R1["个人规则文件
NAMESPACE-agent.local.md"]
R2["本地忽略规则
.git/info/exclude"]
T["临时目录
NAMESPACE-tmp/"]
end
B["备份脚本
backup-local-user-configs.sh"]
subgraph OUT["根项目备份目录"]
O["local-user-config-backups/
CHILD_PROJECT/"]
F1["NAMESPACE-agent.local.md"]
F2["git-info-exclude"]
M["manifest.md"]
end
S -. "read-only" .-> B
R1 --> B
R2 --> B
T -. "默认不读取" .-> B
B --> O
O --> F1
O --> F2
O --> M这一步体现了一个关键经验:本地文件虽然不进 Git,但也不能无治理。备份必须精确,而不是贪心。这样处理之后,根项目可以考虑同步到自己的 Git 仓库中,让根项目里的备份目录承担恢复作用。
失效场景与处理方式
这套方案不是零成本方案,关键风险需要提前写清楚。
第一,子项目任务文件长期不更新。
如果子项目没有及时把任务从 doing/ 移到 completed/,根项目汇总就会过期。解决方法不是让根项目越权修改子项目,而是让汇总报告标明数据时间点,并通过周期性 aggregation log 暴露“这份报告基于什么时候的状态生成”。
第二,多人同时修改 doing/ 里的同一个任务。
如果一个任务确实需要多人协作,最好拆成多个有归属的子任务,或者在同一个任务文档里明确 owner 和当前处理人。不要让多个 agent 同时把不同人的状态混写进一个无归属文件。如果发生 Git 冲突,就按普通代码冲突处理,而不是让 agent 自动猜哪一段该保留。
第三,本地配置丢失。SomeUser-agent.local.md 和 .git/info/exclude 不进共享仓库,确实更干净,但机器迁移或项目重建时可能丢。这个风险通过根项目 allowlist 备份缓解:只备份个人规则和本地 ignore,不默认备份 SomeUser-tmp/。
第四,个人临时目录泄漏。SomeUser-tmp/ 可能包含未整理内容、敏感上下文或过期中间产物,所以默认不进入备份,也不进入 Git。如果确实要备份,应该显式开启,而不是让备份脚本自动递归整个 ignored 目录。
效果评价
这套方案的收益主要有四点。
第一,AI agent 更容易获得稳定上下文。
任务状态不再只存在于对话历史里,而是落在每个子项目明确的 tasks-status/ 结构中。
第二,多项目视图更清晰。
根项目可以汇总所有子项目的 planned、doing、completed 状态,但不会反向修改子项目。
第三,多人协作污染更少。
共享 AGENT.md 只保留最小 hook。个人规则、临时草稿、本地 ignore 都留在本地。
第四,风险边界更明确。
哪些文件能写、哪些文件只能读、哪些目录永远不碰,都被写成规则,而不是靠每次对话临时提醒。
但它也不是零成本方案。
最大风险仍然是状态维护依赖人工和 agent 纪律。如果子项目没有及时移动任务文件,根项目汇总就会过期。解决方法不是让根项目强行修复,而是加强子项目状态维护规则,并通过周期性汇总日志暴露状态时间。
另一个风险是本地配置备份。个人文件被 .git/info/exclude 忽略后,确实不会污染团队仓库,但也不会自然进入版本控制。所以需要 allowlist 备份机制,并明确默认不备份临时目录。
这两个风险都不是 bug,而是工程取舍。关键是把取舍显式化。
回到 harness 工程思想
这次实践最后落到的是 harness 思想。
所谓 harness,不只是一个脚本,也不是一个提示词模板。它更像一个工程化外壳,把 AI agent 放进一组明确的约束里:
flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]flowchart LR
I["Input contracts"] --> H["AI Working Harness"]
R["Read boundaries"] --> H
W["Allowed write scope"] --> H
S["Status documents"] --> H
L["Logs and manifests"] --> H
P["Periodic tasks"] --> H
C["Human review points"] --> H
H --> O["Predictable AI operations"]
H --> A["Auditable state"]
H --> B["Lower collaboration risk"]在这个 harness 里:
- 输入协议是
tasks-status/{planned,doing,completed}/。 - 只读边界是主项目不能修改子项目。
- 可写范围是根项目自己的汇总文件和备份目录。
- 状态日志让报告有时间依据。
- allowlist 备份让本地个人配置可恢复。
SomeUser占位符让方案可以被不同用户复用。
如果以后把这套思路扩展到检索层或工具层,也应该沿同样的隔离原则继续做,但那已经超出本文范围。
AI 编程的核心问题,往往不是 AI 能不能写某段代码,而是它在什么边界内写、根据什么状态写、写完之后如何被追踪和恢复。
当项目只有一个人、一个仓库、一个任务时,这些问题不明显。
但当 AI agent 开始参与多个项目,并进入多人共享协作环境时,harness 就变得必要。
它把“让 AI 帮我做事”变成了“让 AI 在工程化边界内稳定协作”。这才是 AI 编程从个人技巧走向实际工程实践时,真正需要补上的一层。
🤖 AI 相关推荐 按语义相似度
想跟进更新? RSS