AI 编程中的两个真实问题:多项目任务管理与多人协作隔离

在多项目并行与多人协作的 AI 编程实践中,任务状态的连贯性与个人配置的隔离性是影响效率的关键痛点。本文提出一套基于“子项目 Source of Truth”与“本地规则隔离”的工程化方案,旨在解决跨项目任务断点管理与团队配置污染问题,并提供一套可复制的目录结构、读写边界与备份策略。


一个工程师开始高频使用 AI agent 写代码后,很快遇到的问题并不是“AI 会不会写函数”,而是另一类更现实的问题。

他同时维护多个项目:有的项目在做功能开发,有的项目在做配置迁移,有的项目只是偶尔修 bug。每天打开 AI agent 时,他都要重新解释:这个项目做到哪一步,哪个任务已经完成,哪个任务还在进行,哪个任务只是计划中。时间一长,任务状态散落在各个对话、各个项目和零散文档里。AI 很容易重复安排一个已经完成的任务,也可能忽略某个正在进行但还没收尾的任务。

随后第二个问题出现了:其中一些项目并不是个人项目,而是多人共享协作的项目。每个人使用 AI agent 的习惯不同,有人喜欢临时草稿,审阅之后再生成正式文档;有人则反感这种方式,直接让 AI 一次性生成详细任务文件。但这些个人偏好不应该写进团队共享的 AGENT.md,也不应该污染 .gitignore 或项目源码。

这两个问题可以归纳为:

  1. 单用户多个项目的管理问题。
  2. 单项目被多用户共同管理时的协作隔离问题。

本文讨论的不是某个具体工具的用法,而是在一次真实 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 -. "本地忽略" .-> TMP
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 -. "本地忽略" .-> TMP
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 -. "本地忽略" .-> TMP

这里的关键不是文件名本身,而是责任边界:

  • 子项目的 tasks-status/ 是任务状态的 source of truth。
  • 根项目的 planned.mddoing.mdcompleted.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,主项目只是一个聚合视图。

子项目暴露统一结构:

1
2
3
4
tasks-status/
  planned/
  doing/
  completed/

每个任务是一个独立 Markdown 文件,并放在对应状态目录里。例如:

1
2
3
4
5
6
7
tasks-status/
  planned/
    2026-05-09-planned-example-api-cleanup.md
  doing/
    2026-05-09-doing-example-auth-refactor.md
  completed/
    2026-05-09-completed-someuser-onboarding-configuration.md

主项目读取这些状态后,生成自己的汇总文件:

1
2
3
planned.md
doing.md
completed.md

汇总文件不是新的任务源,只是当前视图。每条汇总信息都保留 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" .-> C
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" .-> C
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" .-> C

这里的重点不是目录命名,而是责任划分:

  • 子项目负责维护真实任务状态。
  • 主项目负责聚合和展示。
  • 主项目不能替子项目修复、移动、重命名任务文件。
  • 如果子项目缺少 tasks-status/,主项目只能报告“未配置”,不能替它创建。

这个边界让 AI agent 的行为更可预测。

问题一继续:任务状态靠人工维护,如何保证信息准确?

任务状态结构解决了“在哪里读”的问题,但没有解决“状态是否新鲜”的问题。

如果一个任务已经完成,但子项目没有把它从 doing/ 移到 completed/,主项目看到的状态仍然会过期。这个问题不能完全靠主项目解决,因为主项目不是 source of truth。

因此需要对子项目 agent 增加状态维护纪律:

  • 安排新任务前,先扫描 planned/doing/completed/
  • 至少检查三个目录下的任务文件名。
  • 如果文件名看起来相关,或无法判断是否重复,就读取具体任务文档。
  • 状态变化时,立即移动任务文件到对应目录。
  • 移动任务时,同步重命名文件中的状态段。
  • doing 任务发生大量变更时,更新任务文档的时间、摘要、当前状态和下一步。
  • 标记 completed 前,确认文档包含完成说明、完成时间、剩余风险或阻塞项。

任务文件名也需要有强约束:

1
YYYY-MM-DD-<status>-<short-task-name>.md

其中 <status> 必须和所在目录一致:

1
2
3
tasks-status/doing/2026-05-09-doing-example-task.md
tasks-status/planned/2026-05-09-planned-example-task.md
tasks-status/completed/2026-05-09-completed-example-task.md

这个设计看似啰嗦,但它解决的是 AI agent 的实际问题:agent 很依赖明确、重复、可扫描的文本协议。命名越稳定,状态判断越少靠猜。

问题二:多人共享项目中,个人 AI 规则不能污染团队配置

第二个问题来自协作项目。

共享项目通常会有一个 AGENT.md,用于告诉 AI agent 如何在这个项目中工作。但如果每个人都把自己的偏好写进去,文件会很快变成混合体:

  • 有人希望中文对话。
  • 有人希望英文文档。
  • 有人希望保留临时草稿。
  • 有人有自己的任务维护习惯。
  • 有人使用不同的本地自动化。

这些都是真实需求,但不一定是团队规范。

所以共享 AGENT.md 应该保持最小,只放一个 hook:

1
If `SomeUser-agent.local.md` exists in this directory, treat it as optional supplemental personal working preferences for SomeUser; otherwise ignore it.

真正的个人规则放到本地文件:

1
SomeUser-agent.local.md

临时草稿放到:

1
SomeUser-tmp/

这些个人文件通过 .git/info/exclude 忽略:

1
2
SomeUser-agent.local.md
SomeUser-tmp/

这里故意使用 .git/info/exclude,而不是共享 .gitignore。原因是:这些文件是个人工作流的一部分,不一定应该成为团队仓库规范。

更完整的子项目目录约定可以写成这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
shared-project/
  AGENT.md
  SomeUser-agent.local.md
  SomeUser-tmp/
  tasks-status/
    planned/
    doing/
    completed/
  .git/
    info/
      exclude

其中:

  • AGENT.md:团队共享规则,只放项目级约束和个人规则 hook。
  • SomeUser-agent.local.md:当前用户自己的 AI 工作偏好。
  • SomeUser-tmp/:当前用户自己的临时草稿和中间材料。
  • .git/info/exclude:当前用户在当前子项目里的本地忽略规则。
  • tasks-status/:这个子项目自己的任务状态事实来源。

如果同一个项目里有多个协作者,每个人都应该有独立 namespace:

1
2
3
4
user-a-agent.local.md
user-a-tmp/
user-b-agent.local.md
user-b-tmp/

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" .-> G
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" .-> G
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" .-> G

这样做的效果是:

  • 团队共享文件只增加一句最小 hook。
  • 每个人可以有自己的 AI 工作习惯。
  • 个人规则不会进入共享提交。
  • 个人临时文件不会污染正式文档。
  • 没有个人规则文件时,项目仍按原有规则运行。

项目初始化与新用户接入:用 SomeUser 做占位符

这里处理的其实不是单一的“新项目接入”问题,而是模板初始化时的命名问题。通常有两种场景:

  • 同一个用户开始管理一个新项目。
  • 同一个项目里有新的协作者开始使用自己的 AI 规则。

如果这个方案要长期使用,就不能只适配某一个人。否则无论是前一种情况,还是后一种情况,最后都会复制出一堆带有旧名字的规则。

因此交接模板中统一使用 SomeUser 作为占位符。无论是项目初始化,还是同一项目里有新用户加入协作,agent 都应该先问当前用户:

1
The template currently uses `SomeUser`. What personal namespace should replace it?

用户确认后,再做全量替换:

1
2
3
SomeUser-agent.local.md -> <namespace>-agent.local.md
SomeUser-tmp/ -> <namespace>-tmp/
SomeUser personal working preferences -> <namespace> personal working preferences

比如当前用户选择 user-a,则生成:

1
2
user-a-agent.local.md
user-a-tmp/

如果后续同一项目里再有 user-b 加入协作,那么就为 user-b 生成自己的一组本地文件,而不是复用或覆盖 user-a 的那一组:

1
2
user-b-agent.local.md
user-b-tmp/

这个 namespace 最好是短小、稳定、适合文件名的字符串,例如:

1
2
3
user-a
user-b
user-c

不建议包含空格、斜杠或 shell 特殊字符,因为这些会增加脚本和路径处理风险。

实施层:根项目也要有边界

根项目本身也需要规则。否则它会从“管理任务”慢慢变成“能修改所有子项目的控制台”。

根项目允许管理的内容应该有限,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
AGENT.md
SomeUser-agent.local.md
planned.md
doing.md
completed.md
new-project-pass-info-to-AGENT-MD.md
backup-local-user-configs.sh
local-user-config-backups/
.git/info/exclude
SomeUser-tmp/

补充说明: 虽然根项目通常只是由单独个人管理,理论上可以只用一个总的 AGENT.md,临时文件夹也可以直接命名为 tmp,但是为了保持整个工程规范的统一性,我们在这里依然采用了与子项目相同的 AGENT.md 加上 SomeUser-agent.local.md 以及 SomeUser-tmp/ 的结构。这样的设计在最终效果上与只用一个 AGENT.md 是一致的,但能让整个工程体系的规范保持一致。

但它不能修改:

1
2
3
4
5
6
<child-project>/AGENT.md
<child-project>/*-agent.local.md
<child-project>/.git/info/exclude
<child-project>/*-tmp/**
<child-project>/tasks-status/**
<child-project>/source-code

如果子项目需要接入这套规则,根项目不直接修改子项目文件,而是提供交接文档:把 new-project-pass-info-to-AGENT-MD.md 里的内容复制出来,粘贴到目标子项目的 Codex 或 Claude 对话框中,让子项目里的 agent 按这份说明自己执行配置。

这条约束非常重要。它让主项目像一个 dashboard 和 harness,而不是一个拥有跨项目写权限的 agent。

周期任务:读报告和写汇总要分开

实际使用中,很自然会想到周期任务:每天或每个工作日生成任务报告。

这里也需要区分两类任务:

  1. report-only task
    只读取各项目任务状态,输出一份报告,不写项目文件。

  2. aggregation update task
    读取各项目任务状态,并更新根项目的 planned.mddoing.mdcompleted.md

这两类任务的风险不同。前者低风险,后者会写根项目文件。

因此更新型任务执行后,需要写日志,例如:

1
SomeUser-tmp/aggregation-log-YYYY-MM-DD-HHMMSS.md

报告型任务可以引用这个时间:

1
截止 YYYY-MM-DD HH:mm,本报告基于最近一次任务汇总结果生成。

这样读者可以知道报告到底基于什么时间点的状态。

子项目中被 Git 忽略的个人文件,也需要治理

各个子项目里的个人规则文件不进 Git,这解决了共享污染问题,但引出了另一个问题:这些文件会不会丢?

例如:

1
2
SomeUser-agent.local.md
.git/info/exclude

这些文件是本地配置,不提交到共享仓库。如果机器迁移或项目重建,它们可能丢失。

解决办法不是“备份所有 ignored 文件”。那样风险太大,因为 ignored 文件里可能有缓存、密钥、构建产物、临时草稿。

更稳妥的做法是 allowlist:

1
2
<namespace>-agent.local.md
.git/info/exclude

默认不备份:

1
<namespace>-tmp/

因为临时草稿目录可能包含未整理内容、中文审阅稿、敏感上下文或过期中间产物。除非显式开启,否则不应该进入备份。

备份脚本的原则是:

  • 只扫描直接子项目。
  • 只读子项目。
  • 只写根项目备份目录。
  • 按子项目分目录保存。
  • 每个备份目录生成 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 --> M
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 --> M
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 --> 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"]

在这个 harness 里:

  • 输入协议是 tasks-status/{planned,doing,completed}/
  • 只读边界是主项目不能修改子项目。
  • 可写范围是根项目自己的汇总文件和备份目录。
  • 状态日志让报告有时间依据。
  • allowlist 备份让本地个人配置可恢复。
  • SomeUser 占位符让方案可以被不同用户复用。

如果以后把这套思路扩展到检索层或工具层,也应该沿同样的隔离原则继续做,但那已经超出本文范围。

AI 编程的核心问题,往往不是 AI 能不能写某段代码,而是它在什么边界内写、根据什么状态写、写完之后如何被追踪和恢复。

当项目只有一个人、一个仓库、一个任务时,这些问题不明显。
但当 AI agent 开始参与多个项目,并进入多人共享协作环境时,harness 就变得必要。

它把“让 AI 帮我做事”变成了“让 AI 在工程化边界内稳定协作”。这才是 AI 编程从个人技巧走向实际工程实践时,真正需要补上的一层。


想跟进更新? RSS


相关内容