











"有用户在场"和"无用户在场"不是同一个东西的开启/关闭状态,是两种完全不同的模式。
很多 agent 系统把"有用户"和"无用户"当作同一个运行实例的两个状态:有用户时弹框确认,无用户时自动审批。好像是同一个引擎,只是改一个 flag。
这是第一个危险的想法。
第二个危险的想法是:无人值守就是"把所有权限预授权了然后让 agent 自己跑"。听起来简单,但用户能做的事远不止批准权限——用户可以回答问题、澄清需求、提供补充信息,甚至说"换个思路,从另一边试试"。这些能力在无用户模式下全部消失了。
无用户模式不是"把权限放开就行了"。它是一种完全不同的交互模型,需要从架构上重新设计。
有用户模式下,agent 和用户之间是一条双向通道:
用户 → agent:指令、澄清、否定
agent → 用户:进度、问题、确认请求
这条通道最重要的特性是:agent 可以追问。 它遇到不确定的事,可以问。它可以确认用户的意图,可以请求额外的上下文。用户也可以主动打断——"不对,换个方向做"——此时 agent 调整计划。
无用户模式下,这条双向通道变成了单向:
agent → 系统:工具调用
系统 → agent:工具结果
没有追问的余地。agent 遇到不确定的事,唯一的办法是在现有的信息范围内做最佳决策。它不能用"请确认一下"这种操作——因为没有人可问。
这带来一个设计推论:无用户模式的 agent 必须在启动前,把所有需要决策的点预判完。
比如,一个代码重构任务——有用户时,agent 可以中途问"这个模块要不要保留旧接口做兼容"。无用户时,agent 必须一开始就知道"保留旧接口做兼容"这个策略,或者写成一条规则写在指令里。它不能边做边问。
权限模型是最明显的差异——但也是最容易被误认为"唯一差异"的地方。
有用户时,CodeCoder 的权限模型走 Ask 模式:agent 要执行 run_command:git,弹窗问用户;用户说 yes 或 no,系统记录这次选择的权限范围(Once / ThisSession / ThisProject)。
无用户时,没有窗口可弹。所以模型切换为:
codecoder.json 中声明了哪些工具的哪些 key 可以直接执行。比如 run_command:git 走预授权,不用问任何人ToolFinished{is_error: true} 事件。注意——它不阻塞 agent 的执行。Agent 收到拒绝后,应该设计绕过方案:比如换一个不需要该工具的实现方式这种"拒绝但不阻塞"的设计很关键。如果拒绝导致整个 agent 卡住,那无用户模式就只适合那些"所有需要的东西都能提前预见到"的极简单场景。实际上,优秀的无用户 agent 会在被拒绝后自动换一条路径:不能用 curl 那就用 read_file 读本地缓存的版本;不能直接写文件那就输出到 stdout 让调度器捕获。
CodeCoder 在无用户模式下还有一个熔断机制:k 次连续失败后主动终止。 这防止了 agent 在同一个死胡同里反复兜圈子。退出码报告是"StuckNeedsFix"(2),上层调度器可以根据这个码决定是重试、还是通知人工介入。
有用户模式下,退出很简单:用户关终端。Session 自动保存,下次 /resume 继续。
无用户模式下,退出必须有明确的契约。因为调用者不是人,是一个调度脚本或 CI 系统,它们需要从退出码判断下一步做什么。
CodeCoder 的无用户退出码契约是:
崩溃恢复靠两个机制:
重要的是:退出码只告诉调用者"完成状态",不告诉"原因"。 原因在 BgObserver 写下的 .ccd.bg.ndjson 文件里——每行一个 JSON 事件,记录了每一步的工具调用、结果、错误。调度器可以 tail -f 实时观察,离线读取完整记录。
把这两种模式当作同一个东西的两个状态,会导致设计上的盲区:
ask_user 工具——但 agent 可能不知道"当前没有用户"。它可能会尝试弹窗然后卡住。解决方案是:启动时通过 CODECODER_BG_TASK 或 CODECODER_BG_WORKGRAPH 环境变量明确告知 agent 进入无用户模式,并在 system prompt 中声明"你当前没有用户通道,不能追问"codecoder.json 的项目级 allowlist把这些差异全部考虑进去后,两种模式的架构图是这样的:
有用户模式:TUI/Socket → agent → 工具(Ask 弹窗)→ 工具结果 → 用户
无用户模式:环境变量 → agent → 工具(预授权/自动拒绝)→ 工具结果 → ndjson → 退出码
两条路径共用一个 agent 内核,但交互层、权限层、退出层完全不同。它们不是同一个代码路径里的 if-else 分支——它们是两个不同的入口。
有/无用户双模式的支持不是无代价的工程选择。
首先,两条入口路径意味着两倍的维护复杂度。 权限系统需要同时工作在 "Ask → 弹窗 → 用户选择" 和 "预授权 → 自动拒绝" 两条路径上。如果权限模块内部重构,两边的行为都需要验证。CodeCoder 用同一个权限内核 + 不同的前端策略(内存中的 SessionAllowlist vs. 文件中的 ProjectAllowlist)来缓解这个问题,但测试矩阵扩大一倍是事实。
其次,无用户模式的预授权列表存在过度授权风险。 你在 codecoder.json 中写下 run_command:git 允许——但 agent 可以用 git 推送到远程、删除分支、修改提交信息。预授权的粗粒度意味着:只要你给了某个 key,agent 就能用它做这个 key 允许范围内的任何事情,而 CI 场景下没有人在循环里叫停。这是无用户模式固有的信任敞口——你必须在便利性和安全性之间做取舍。
第三,两种模式之间的切换成本被低估了。 开发阶段用交互式模式写好和调试好 workgraph,切换到无用户模式部署到 CI 环境时,你可能会遇到"交互式模式下正常工作,无用户模式下在某一步卡住了"的情况,因为交互模式下用户可能在不知不觉中批准了一些东西,而无用户模式下同样的操作因为缺少预授权被拒绝。逐条对比交互式 session 和无用户配置是一个繁琐但必要的部署步骤。
收尾部分的设计检查清单就是为了覆盖这些盲区——但即使如此,从交互式到无用户的第一轮切换几乎总会发现遗漏的预授权权限。
如果你设计的 agent 要支持无用户模式,用这份清单检查:
这六个问题中任何一个答不上来,无用户模式就不应该上线。
下一篇,我们聚焦一个具体的问题——当 agent 说"做完了",你怎么知道它真的做完了?
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。