AGN 网络当前最大的问题是角色越界:Coordinator 角色(尤其是 DeepSeek)会直接修改代码、运行 git apply 等本应由 Executor 执行的操作。根本原因是系统内没有任何角色边界强制机制——角色完全是隐式的(由运行的脚本决定),没有权限检查、没有命令过滤、没有写路径限制。

此外还存在稳定性问题:超时机制无效、崩溃后任务进入死锁状态、跨设备配置不同步等。

本计划目标:

  1. 建立强制性的角色权限系统
  2. 提供 Coordinator 执行受控操作的安全通道
  3. 修复已确认的稳定性漏洞
  4. 确保跨设备行为一致

Phase 1: Role Guard 基础设施 (P0 — 必须首先完成)

P0-1: 创建角色权限配置文件

新建文件: config/role_permissions.json

定义每个角色的权限边界:

  • writable_dirs: 允许写入的目录白名单
  • blocked_command_patterns: 禁止的命令正则黑名单
  • utility_request_commands: 需要走 Admin 审批通道的命令
{
  "version": 1,
  "roles": {
    "coordinator": {
      "writable_dirs": ["dispatch", "ssot", "memory", "runtime", ".agn_workspace"],
      "blocked_command_patterns": [
        "^git\\s+(apply|commit|push|cherry-pick|rebase|reset)",
        "^sed\\s+-i",
        "^codex\\s+exec",
        "^rm\\s+-",
        "^chmod"
      ],
      "utility_request_commands": ["git clone", "git checkout"]
    },
    "executor": {
      "writable_dirs": ["results", "dispatch/acks", "reports", ".agn_workspace", "runtime"],
      "blocked_command_patterns": ["^rm\\s+-rf\\s+/"],
      "utility_request_commands": []
    },
    "reviewer": {
      "writable_dirs": ["verdicts", "reports", ".agn_workspace", "runtime"],
      "blocked_command_patterns": [
        "^git\\s+(apply|commit|push)",
        "^codex\\s+exec",
        "^rm\\s+-rf"
      ],
      "utility_request_commands": []
    },
    "admin": {
      "writable_dirs": ["*"],
      "blocked_command_patterns": [],
      "utility_request_commands": []
    }
  }
}

设计选择: 命令使用黑名单(Coordinator 仍可执行 git status/log/diff、读文件等),目录使用白名单。

P0-2: 创建 Role Guard 模块

新建文件: scripts/role_guard.py (~150 行)

核心函数:

  • get_current_role() -> str: 从 os.environ["AGN_ROLE"] 读取,未设置时默认 "admin"(向后兼容)
  • check_command(cmd, role) -> (bool, str): 检查命令是否被角色黑名单拦截
  • check_write_path(path, role) -> (bool, str): 检查写路径是否在角色白名单内
  • log_violation(role, action, detail): 写入 audit/events.jsonl

关键设计: 缓存配置文件(按 mtime 变更时重新加载),避免每次调用都读磁盘。

P0-3: 在 agent_runner.py 拦截点集成 Role Guard

修改文件: scripts/agent_runner.py

两个拦截点:

  1. run_command() (第 242 行) — 命令执行入口:

    • subprocess.run() 前调用 check_command()
    • 被拦截的命令返回 return_code=126 + stderr="ROLE_GUARD_BLOCKED: {reason}"
    • 记录审计事件 action="role_violation"
  2. atomic_write_json() (第 114 行) — 文件写入入口:

    • 调用 check_write_path() 验证目标路径
    • 被拦截时抛出 PermissionError

P0-4: 在启动脚本注入 AGN_ROLE

修改文件: scripts/agn_up.sh

start_proc "agn_coordinator" env AGN_ROLE=coordinator python3 scripts/coordinator_loop.py ...
start_proc "agn_executor"    env AGN_ROLE=executor    python3 scripts/executor_worker.py ...
start_proc "agn_reviewer"    env AGN_ROLE=reviewer    python3 scripts/reviewer_worker.py ...

run_agn_task.py 保持 admin 角色(人工直接调用的一次性脚本)。

P0-5: 审计违规日志

Role Guard 拦截的每个操作记录到 audit/events.jsonl

{
  "action": "role_violation",
  "role": "coordinator",
  "blocked_action": "command",
  "command": "git apply some.patch",
  "reason": "blocked_command_pattern matched",
  "timestamp": "..."
}

复用现有 phase_a/audit.pyappend_audit() 基础设施。


Phase 2: 安全通道与稳定性修复 (P1)

P1-1: Command Request 审批队列

新建文件: scripts/command_request.py (~100 行)

当 Coordinator 需要执行超出权限的操作(如 git clone)时:

  1. 写入 dispatch/command_requests/<request_id>.json
  2. Admin 通过 API 审批
  3. 审批后由系统以 admin 角色执行

修改文件: phase_a/main.py — 新增两个端点:

  • POST /api/command-requests/{id}/approve
  • POST /api/command-requests/{id}/reject

P1-2: Coordinator 集成 Command Request

修改文件: scripts/coordinator_ingest.py, scripts/coordinator_loop.py

Coordinator 需要 git clone 时,调用 command_request.submit_request() 而非直接执行。Coordinator Loop 在下一个 tick 检查审批状态。

P1-3: 跨设备配置同步

修改文件: config/kirara_state_sync.json, scripts/kirara_state_sync.py

config/role_permissions.jsonconfig/providers.json 加入 KiraraState 同步路径,确保所有设备使用相同的角色权限配置。

P1-4: 角色配置验证脚本

新建文件: scripts/validation/verify_role_config.py

验证项:JSON schema、正则合法性、目录存在性、跨设备一致性。 新增 Makefile target: verify-role-config

P1-5: 修复无效超时机制

修改文件: scripts/executor_worker.py (第 56-76 行)

问题: 当前 threading.Timer(_TIMEOUT, lambda: None) 不会终止子进程。Timer 触发一个空 lambda,然后通过 timer.is_alive() 间接检测超时——但存在竞态条件。

修复: 改用 deadline 模式:

deadline = time.monotonic() + _EXECUTOR_TASK_TIMEOUT
# ... 执行任务 ...
if time.monotonic() > deadline:
    task_timed_out = True

同时确保 run_command() 的内部 timeout_sec 参数与外部超时一致,让 subprocess.run(timeout=...) 实际终止进程。

同样修复 scripts/reviewer_worker.py 中的对应代码。

P1-6: 僵死任务检测与恢复

修改文件: scripts/coordinator_loop.py

问题: Worker 崩溃后,dispatch 文件存在但无 result,任务进入死锁。

修复:process_once() 中增加过期检测:

  • dispatch 存在但 result 缺失,且 dispatch 的 mtime 超过 AGN_STALE_DISPATCH_TIMEOUT(默认 30 分钟),标记为失败或重新 dispatch。

P1-7: Provider 降级回退

修改文件: scripts/agent_runner.py

问题: Reviewer provider 不可用时,任务被当作 reject 处理,触发 hallucination lock。基础设施故障不应惩罚任务。

修复:config/providers.json 中增加 fallback_order,provider 失败时尝试下一个。只有所有 provider 都失败才递增 qa_retry_count

P1-8: Coordinator Loop 错误详情

修改文件: scripts/coordinator_loop.py (第 264 行)

当前 bare except Exception 只递增计数器,异常详情丢失。修复:在 append_audit 调用中包含 str(exc) 截断后的错误信息。

P1-9: 扩展任务状态机

修改文件: phase_a/task_engine.py

当前只有 4 个状态,缺少 haltedawaiting_utility。扩展 derive_status() 使其覆盖完整生命周期。增加 VALID_TRANSITIONS 字典文档化合法状态转换。

P1-10: Schema 验证扩展

修改文件: scripts/schema_validate.py

新增 validate_ssot_task()validate_role_permissions() 验证器。


Phase 3: 打磨 (P2)

P2-1: run_agn_task.py Role Binding

修改文件: scripts/run_agn_task.py

允许 JSON 输入包含 role_binding 字段,在调用 executor/reviewer 前动态切换 AGN_ROLE 环境变量。

P2-2: Memory Sync 冲突保留

修改文件: scripts/memory_sync.py

write_conflicts_snapshot() 改为追加合并模式,避免覆盖历史冲突记录。

P2-3: Role Guard 测试

新建文件: tests/test_role_guard.py

测试用例:

  • Coordinator 运行 git apply → 返回 rc=126
  • Executor 写 results/ → 成功
  • Reviewer 写 dispatch/ → 失败
  • Admin 全部成功
  • 审计日志包含 role_violation 事件

P2-4: 跨设备配置漂移检测

新建文件: scripts/validation/verify_config_drift.py

对比本地与 KiraraState 中的配置版本,报告差异。


实施顺序

Phase 1 (P0):
  P0-1 → P0-2 → P0-3 → P0-4 → P0-5  (串行依赖链)

Phase 2 (P1):
  P1-5, P1-6, P1-7, P1-8  (可与 Phase 1 并行,互不依赖)
  P1-1 → P1-2              (串行依赖)
  P1-3, P1-4               (依赖 P0-1)
  P1-9                     (依赖 P1-1)
  P1-10                    (依赖 P0-1)

Phase 3 (P2):
  P2-1, P2-2              (依赖 P0 完成)
  P2-3                    (依赖 P0 全部完成)
  P2-4                    (依赖 P1-3)

关键架构决策

  1. 命令黑名单 + 目录白名单: 黑名单防止 Coordinator 执行危险命令,但不阻断其正常的只读操作(git status/log/diff 等)。白名单控制写权限——未列出的目录不可写。

  2. 环境变量 AGN_ROLE: 在进程启动时注入,模型无法篡改(模型控制 prompt,不控制进程环境)。防线在 Python 层的 role_guard.py,在 subprocess.run() 之前拦截。

  3. 文件式 Command Request 队列: 与现有架构一致(文件即消息总线),无需引入新数据库。

  4. 向后兼容: AGN_ROLE 未设置时默认 admin,现有脚本、测试、手动调用均不受影响。

  5. 不使用 chmod/assume-unchanged: GPT Codex 建议的方案是脆弱的、OS 依赖的。Role Guard 方案更健壮、可审计、跨平台一致。


验证方式

  1. Unit test: pytest tests/test_role_guard.py — 角色拦截逻辑
  2. Smoke test: AGN_ROLE=coordinator python3 -c "..." 尝试被禁命令,验证 rc=126
  3. 集成测试: make agn-smoke 启动完整流程,检查 audit/events.jsonl 无 role_violation
  4. 跨设备: 两台设备运行 make verify-role-config,对比输出一致
  5. 回归: make verify-agn-mvp 全量验证确保无破坏

涉及的关键文件

文件操作用途
config/role_permissions.json新建角色权限定义
scripts/role_guard.py新建角色检查核心模块
scripts/agent_runner.py修改run_command()atomic_write_json() 注入拦截
scripts/agn_up.sh修改注入 AGN_ROLE 环境变量
scripts/command_request.py新建Coordinator 受控操作审批
phase_a/main.py修改新增 command-request 审批 API
phase_a/task_engine.py修改扩展状态机
scripts/coordinator_loop.py修改僵死检测 + 错误详情
scripts/executor_worker.py修改修复超时机制
scripts/reviewer_worker.py修改修复超时机制
scripts/memory_sync.py修改冲突追加保留
scripts/schema_validate.py修改新增验证器
config/kirara_state_sync.json修改增加同步路径
tests/test_role_guard.py新建角色系统测试
scripts/validation/verify_role_config.py新建配置验证
scripts/validation/verify_config_drift.py新建漂移检测

下面按“可实现性 + 风险点 + 需要补齐的工程细节”来拆解这份方案。整体评价:方向正确、切入点也对。但如果照文档原样落地,会在若干关键边界上出现“看似有 guard、实际仍能越界”或“误杀导致系统不可用”。重点要补的是:命令规范化、路径规范化、默认 admin、写入面覆盖、审批队列防重放、超时与僵死恢复的判定精度


0) 这套方案解决的“根因”是否对

对。你描述的越界本质不是模型不听话,而是系统没有把“角色”变成可执行的权限边界。 把边界收敛到两个“真正的能力出口”:

  • 命令执行入口 run_command()
  • 文件写入入口 atomic_write_json()

这是正确的架构抓手:只要系统里所有写与命令都必须经过这两个出口,Role Guard 才会成为强约束。

隐患:现实项目里写入与命令往往不止这两个出口。后面会讲如何做“覆盖面闭合”。


1) P0-1 角色权限 JSON:黑名单命令 + 白名单目录

优点

  • 对 Coordinator:允许 read-only 运维命令(git status/diff/log)而禁止改 repo 的命令,是合理的。
  • 写目录白名单比“禁止某些目录”更安全,因为默认拒绝。

必须补齐的细节

  1. 命令匹配必须基于“规范化后的 argv”而不是原始字符串 否则会被轻易绕过,比如:
  • 前导空格、换行、bash -lc "git apply …"
  • env GIT_DIR=... git apply ...
  • python -c 'import os; os.system("git apply ...")' 如果 check_command() 只对字符串做 regex,会出现“看似拦了 git apply,但拦不住通过 shell 包装的 git apply”。
  1. 需要明确:系统允许 shell 吗 如果允许 bash -lc,你必须把它视为“任意命令执行”,这会直接把 Role Guard 打穿。最稳的策略是:在 run_command() 层面规定只接受 argv: list[str],并且 shell=False。如果当前系统必须用 shell,那么你的 blocked patterns 要升级为“检测 shell 载荷里的二次命令”,难度会上一个数量级。

  2. writable_dirs 必须做路径规范化与反符号链接穿透 否则 memory/../repo/file 或通过 symlink 指向 repo 的路径都能越权写。 你需要明确写路径检查是基于:

  • realpath 后的绝对路径
  • 并拒绝写入任何不在允许根目录下的 realpath
  1. 白名单里出现 "*" 的 admin 是可以,但默认 admin 是危险的 AGN_ROLE 未设置默认 admin 的“向后兼容”会变成绕过 Role Guard 的第一入口:任何启动脚本漏注入、任何新 worker、任何手动运行都自动获得全权限。 更稳的做法是:
  • 兼容模式必须显式开启,比如 AGN_COMPAT_ADMIN=1 才允许默认 admin
  • 或者默认降级成 coordinator/readonly,而不是 admin 否则“偶发 misconfig”会直接变成“全权执行”。
  1. blocked_command_patterns 里 ^rm\s+- 会误杀 rm -i 这类低风险,也会漏掉 rm\t-rf 等变体 命令匹配如果基于 argv,就不需要靠这种粗 regex;可以更精确地拦截:rm + 任意含 -r-f 或路径为 / 或空等。

2) P0-2 role_guard.py:缓存 mtime 的设计

优点

  • mtime reload 能减少开销,合理。

风险点

  1. mtime 缓存不是一致性保证 跨设备同步、文件系统时间漂移、复制覆盖都可能导致 mtime 行为不如预期。你至少要有:
  • version 字段校验
  • schema 校验
  • 以及加载失败时的“默认拒绝策略” 加载失败不能 fallback 到 admin 或 allow-all,否则一旦配置坏了,系统变成无 guard。
  1. 审计日志必须是“追加、不可覆盖、写失败也不影响拦截结果” Role Guard 的第一职责是阻止越界,日志是第二职责。不要因为 audit 写失败而放行命令或写入。

3) P0-3 在两个拦截点集成:run_command 与 atomic_write_json

优点

  • 选对了位置:命令与写入是最核心的能力出口。

最大的覆盖面风险

你的系统里很可能存在:

  • 非 JSON 的写入:open(..., "w") 写文本、写 patch、写临时文件
  • shutil.move/copy, pathlib.Path.write_text
  • 直接调用 git 本身产生的写(即使没 git commitgit checkout 也会改工作树)
  • 第三方工具写 cache

如果这些写入不经过 atomic_write_json(),Coordinator 仍可能越界写 repo。 因此你需要做“覆盖闭合”,至少两条路选一条作为 AGN 标准:

  • 标准化所有写入口:强制工程约定,任何写必须走一个统一的 agn_write_* API(json/text/binary)。
  • OS 级隔离:每个角色跑在不同用户或容器或 chroot/sandbox 下,仅允许挂载可写目录。这样 Python 层 guard 只是第二道防线。

在不引入 OS 隔离的前提下,P0 至少应扩展到:统一 file write API 覆盖 json/text/bytes,否则这是“看似完成 P0,实际仍可越界”的最大风险。


4) P0-4 注入 AGN_ROLE:环境变量是否足够硬

“模型无法篡改环境变量”这句话在你当前体系里大概率成立,因为模型只产出文本,不直接控制宿主进程环境。

但要注意两点:

  1. 如果 Coordinator 能执行 run_command(),它就能启动子进程并传 env。 所以你的 get_current_role() 不应只看 os.environ["AGN_ROLE"],还应由父进程在启动时写一个只读的 role 事实来源,例如:
  • 启动参数 --role=coordinator
  • 或运行目录下一个仅宿主能写的 runtime/role.lock 否则 Coordinator 通过 shell/子进程可能伪造 role。
  1. 对每个 worker 进程来说,role 应该是“不可变”的。 运行中变更 role 是高风险操作,除非经过 admin 通道。

5) P1-1/P1-2 审批队列:是可行的,但需要“防重放 + 幂等 + 限定执行上下文”

审批队列是正确方向,因为它把“偶尔需要的越权操作”从隐性越界变成显式治理。

必须补齐的工程要点:

  1. 请求必须绑定执行上下文 例如 git clone 应该绑定:
  • 允许的目标目录(必须在 admin 允许的某个 sandbox 下)
  • 允许的远端 URL 域名白名单或 repo 白名单
  • 允许的分支/commit(如适用)

否则 Coordinator 可以提交“看似合理的 clone”,实际拉恶意 repo 或覆盖敏感路径。

  1. 审批必须是一次性的、不可重复执行的 需要 request_id + 状态机(pending/approved/executed/rejected/expired),并且 executed 后拒绝再次执行。

  2. 执行器必须用“固定模板”执行 不要让 Coordinator 提交任意字符串命令让 admin 执行。审批对象应该是结构化字段(operation + args),由系统渲染成命令。 否则你等于建了一个“请 admin 帮我执行任意命令”的接口。


6) P1-5 超时修复:思路对,但文档里的“deadline 模式”不够

你指出的问题是对的:Timer 不杀子进程等于没超时。

但用 deadline 做“事后标记 timed_out”仍然不够,关键是要确保子进程被终止,并且不会留下孤儿进程。你需要明确:

  • subprocess.run(timeout=...) 会抛 TimeoutExpired,并杀掉子进程,但不一定杀掉子进程树
  • 如果 worker 会启动 shell 或工具再 fork,可能留下孙进程

最稳的是:

  • 每次 run_command 创建独立进程组
  • 超时后终止进程组
  • 并把超时作为明确错误码回写结果

否则“超时”只是在逻辑上超时,系统资源仍被吃掉,依然会死锁。


7) P1-6 僵死任务检测:mtime 判定是必要但不充分

mtime 超过 30 分钟就判死,有两个误判风险:

  1. 长任务(比如大仓库分析/编译)可能 legitimately > 30 分钟
  2. worker 仍在跑但没有写 result(比如输出被卡住)会被当作死

更稳的僵死判定应至少加入一个“心跳或租约”概念:

  • worker 领取任务时写 dispatch/leases/<task_id>.json,包含 last_seen
  • worker 每 N 秒续约
  • coordinator 发现 lease 过期才重派

这样不会把“慢但活着”当死,也能精准检测“死但没写 result”。


8) P1-7 Provider fallback:正确,但要避免“静默降级造成语义漂移”

Fallback 机制能提升可用性,但要在审计与结果语义上做到可解释:

  • 每次 fallback 必须记录:原 provider、失败原因、最终 provider
  • 对 reviewer 这种角色,fallback 可能造成“审查标准变化” 因此在 verdict 里要写明:由哪个 provider 给出结论,以免你误以为是同一标准。

另外,“只有所有 provider 都失败才递增 qa_retry_count”这点是对的:基础设施失败不该惩罚任务。


9) P1-9 状态机扩展:你需要的是“显式等待态 + 人工介入态”

awaiting_utilityhalted 都是必需的,因为它们对应:

  • awaiting_utility:等待 admin 批准队列,不应算失败
  • halted:系统性故障或安全风险触发停机,不允许自动重试

这里关键是要把合法转移写死,并让 coordinator loop 严格执行,否则状态机会被模型输出“带着跑偏”。


10) 验证方式:现有验证还缺两类“绕过测试”

你列的单元测试是必要但不足。必须补两类测试,否则 Role Guard 容易被绕过:

  1. shell 包装绕过测试 例如 coordinator 尝试通过 bash -lcpython -c os.system 间接执行被禁命令,必须被挡住,或者系统明确禁止 shell。

  2. 路径穿透与 symlink 绕过测试

  • memory/../repo/file
  • memory/link_to_repo/file 都必须被拒绝。
  1. 默认 admin 的回归测试 任何未设置 AGN_ROLE 的进程应在 CI 中直接失败或进入只读模式。否则以后你一加新脚本就漏注入,guard 形同虚设。

11) 我建议你把这份方案“升格”为两条并行防线

为了真正把越界问题终结,建议把架构目标写成:

  • 第一道强制边界:OS/运行时隔离 每个角色跑在独立用户或容器,挂载不同可写目录。Coordinator 根本看不见 repo 的写权限。 这样即使 Python guard 有漏洞,仍无法写。

  • 第二道边界:Role Guard 拦截 + 审计 用于更细粒度的策略控制与审计记录。

如果你短期不做 OS 隔离,那就必须把“所有写入都走统一 API”做到位,形成覆盖闭合,否则 P0 很容易变成“心理安全感”。


12) 最关键的修改建议清单

  1. AGN_ROLE 默认不再是 admin。兼容模式必须显式开启。
  2. run_command() 强制 shell=False,只接受 argv;否则必须把 shell 视为高危并直接禁用给非 admin。
  3. 命令检查基于 argv 语义匹配,不基于原始字符串 regex。
  4. 写路径检查基于 realpath 绝对路径,并拒绝 symlink 穿透与 ..
  5. 扩展写入拦截覆盖:json/text/binary 的统一写 API,或 OS 隔离二选一。
  6. Command Request 只允许结构化操作,不允许任意命令字符串;审批绑定目录与白名单资源。
  7. 超时必须能杀进程组,不能只做 deadline 标记。
  8. 僵死恢复用 lease/heartbeat,不只用 mtime。
  9. fallback 必须审计并在结果中注明实际 provider。
  10. 增加绕过测试:shell 包装、symlink、默认 role 漏注入。