让 AI 真正完成开发任务:Windows 上的 DSH Headless、Git Worktree 与自动编译实践
AI-CODING 实践系列(二)
关键词:AI 自动编码、DeepSeek Harness、Git Worktree、Python、Scheduler、MSBuild、C++、Windows
前言
在上一篇文章中,我介绍了如何通过 DeepSeek Harness(DSH)和飞书机器人建立一个远程开发入口。
但能通过手机与 AI 讨论代码,并不意味着拥有一套真正的自动开发系统。
要让 AI 从聊天走向实际开发,还需要解决几个工程问题:
- 如何让 AI 自动进入正确的项目?
- 如何避免多个任务修改同一个工作目录?
- 如何自动编译 AI 生成的 C++ 代码?
- 如何知道任务是成功了,还是只得到一个看起来合理的回答?
- 如何取消异常任务,避免后台残留进程?
- 如何在程序重启后恢复任务状态?
这些问题最终通过一套轻量的 Python Runner + Scheduler 得到了解决。
本文记录这部分工程实现和真实验证结果。
一、先明确目标:不是让 AI 写代码,而是让它完成任务
AI 编码工具已经能够根据自然语言生成和修改代码。
但对于无人值守开发,我希望最终得到的不只是代码,还包括:
- 实际的文件修改。
- 可复现的构建结果。
- 可检查的测试结果。
- 可追踪的执行记录。
- 与原始开发分支隔离的修改。
- 明确的成功、失败或取消状态。
因此,我把一次自动开发定义为:
接收开发任务
↓
创建独立工作区
↓
启动 AI 编码 Agent
↓
检查代码修改
↓
执行 MSBuild
↓
运行验证
↓
保存结果与日志
这才是可以交给后台系统调度的完整任务单元。
二、为什么使用 Git Worktree?
如果直接让 AI 修改原始工作目录,风险比较明显。
一旦任务中断、修改失败或者同时存在多个任务,就可能影响开发者正在使用的代码。
为此,我使用 Git Worktree 为每个任务创建独立的工作目录。
例如:
repositories/
└── demo-project/ # 原始 Git 仓库
workspaces/
├── task-001/ # 任务 1
├── task-002/ # 任务 2
└── task-003/ # 任务 3
每个任务拥有自己的分支和工作目录。
一个典型的操作是:
git worktree add -b ai/task-001 ../workspaces/task-001 master
这样 AI 可以在独立目录内修改代码,而不需要切换原始仓库的当前分支。
在实际验证中,Runner 自动创建了独立 Worktree 和任务分支,DSH Headless 只修改了测试任务中的目标源文件,原仓库 HEAD 和工作状态在执行前后保持一致。
这里需要特别说明:
Git Worktree 提供的是版本管理层面的隔离,而不是完整的操作系统安全沙箱。
它能有效隔离不同任务的 Git 工作状态,但不能单独限制进程读取其他目录或访问网络。
三、DSH Headless:将编码 Agent 变成可调度程序
交互式 Agent 适合讨论需求,而自动化系统更需要可独立启动、执行和退出的进程。
DSH Headless 正好可以承担这个角色。
它能够通过命令行接收任务,并将执行过程输出为结构化事件。
在实际工程中,我主要利用以下能力:
- 为任务指定独立工作目录。
- 通过 Headless Profile 调用已经授权的模型。
- 使用 JSON 事件流记录运行过程。
- 获取模型 Session ID。
- 通过进程退出码判断执行状态。
- 在任务完成后结束进程。
Runner 不依赖用户一直打开 DSH Web 页面。
它会根据任务配置启动一个独立 Headless 进程,完成当前任务后退出。
这也意味着,飞书聊天中的 Session 与实际编码 Session 可以完全分离。
前者保存讨论上下文,后者专门负责某一次开发任务。
四、Runner v0.1:最小自动编码执行器
第一阶段实现了一个 Python Runner。
核心模块包括:
| 模块 | 主要职责 |
|---|---|
models.py |
解析和校验任务规格 |
gitops.py |
创建、检查和清理 Worktree |
dsh.py |
启动 DSH Headless,解析事件 |
build.py |
执行受信任的构建与验证命令 |
process.py |
管理子进程、超时和终止 |
orchestrator.py |
串联完整执行流程 |
Runner 提供统一的 CLI:
python -m runner run --spec task.json --task-id task-001
这里的 task.json 不只是自然语言需求,还包含经过校验的仓库、构建、超时和允许修改路径等配置。
一个重要原则是:
模型负责修改源代码,不负责随意决定系统应该执行什么构建命令。
构建命令来自受信任配置。
这可以减少 AI 因为理解错误而选择错误工具链、构建路径或系统命令的风险。
当然,受信任的构建命令并不意味着构建过程本身完全安全,因为参与编译的源码可能由 Agent 修改。
五、Windows 进程管理比想象中重要
自动化 Agent 可能启动多个子进程。
例如:
Python Runner
└── Node.js / DSH
└── PowerShell
└── 其他开发工具
如果只终止最外层进程,内部启动的子进程可能继续存在。
这对于长期运行的开发机尤其麻烦。
Runner 使用 Windows Job Object 管理任务进程树,并提供 taskkill /T /F 作为经过身份校验后的回退方式。
这里有一个非常重要的约束:
不能通过结束所有 **node.exe** 进程来清理任务。
因为长期运行的 DSH Web Host 本身也依赖 Node.js。
错误的清理操作可能把整个远程开发入口一起关闭。
因此,执行器只针对当前任务所属的进程树进行清理。
测试中验证了任务超时后的进程树终止,真实 Runner 端到端执行结束后也未发现新增的残留 DSH Headless 进程。
六、真实 C++ 编译验证
自动编码的关键验收,不应该是 AI 说“代码已经修改完成”,而应该是编译和测试确实通过。
本次验证使用 Windows 11、Visual Studio 2026、MSVC 和 Windows SDK。
测试任务要求 DSH Headless 创建一个简单的 C++ 加法程序,并通过 MSBuild 编译。
核心逻辑类似:
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(2, 3);
if (result == 5) {
std::cout << result << std::endl;
return 0;
}
return 1;
}
执行结果:
- DSH Headless 成功生成源代码。
- MSBuild 成功编译。
- 生成的 EXE 正常运行。
- 程序输出
5。 - 任务退出码为
0。 - 原始仓库没有被修改。
- 结果与日志成功保存。
最终 Runner v0.1 的真实端到端测试耗时约 17 秒,10 项自动化测试全部通过。
当然,这只是一个简单 C++ 工程的验证,不代表复杂 CAD 插件工程也能在相同时间内完成。
它证明的是底层执行链路可行,而不是 AI 对复杂工程的开发质量已经得到充分验证。
七、为什么还需要 Scheduler?
Runner 解决的是如何执行一个任务。
但真正从飞书远程发起任务,还需要处理多个任务之间的关系。
例如:
- 用户连续提交两个需求。
- 第一个任务仍在执行。
- 用户希望取消第二个任务。
- 开发机突然重启。
- 聊天消息重复投递。
- 某个任务执行失败,需要查看历史记录。
如果把这些逻辑全部交给 Agent 自己处理,很容易产生状态混乱。
因此,我增加了独立的 Python Scheduler。
它只负责几个确定性的功能:
任务提交
↓
SQLite 持久化
↓
FIFO 队列
↓
单 Worker
↓
调用 Runner
↓
保存结果
为什么只采用单 Worker?
因为当前使用的是一台资源有限的 Windows 开发机,而且 C++ 编译本身会消耗较多 CPU、内存和磁盘资源。
在这种环境下,与其提前开发复杂的并发调度,不如先保证任务依次完成。
八、Scheduler v0.2 的实现
Scheduler 使用 SQLite 保存任务状态、执行记录和任务事件。
任务状态包括:
queued
running
succeeded
failed
cancelled
interrupted
一次正常执行的状态变化为:
queued → running → succeeded
→ failed
→ cancelled
它还提供了几个基础能力:
FIFO 排队
按照数据库中的任务序号依次执行,防止任务插队。
幂等提交
相同幂等键重复提交时,返回已有任务,避免重复运行。
单实例运行
通过 Windows 文件锁和 SQLite 原子领取机制,防止多个 Worker 重复执行同一个任务。
任务取消
可以取消尚未执行的任务,也可以向正在运行的 Runner 发送取消请求。
异常恢复
程序重启后,排队中的任务继续保留;原先处于运行状态的任务标记为 interrupted,默认不自动重复执行,避免出现未知状态下的二次修改。
清理预览
能够检查哪些历史 Worktree 符合清理条件,但不自动删除正在运行或身份不明确的工作区。
这些能力并不复杂,但对无人值守系统的可靠性非常重要。
九、Scheduler 的真实验证结果
Scheduler v0.2 完成后,使用同一个 C++ 测试仓库提交了两个任务。
每个任务均通过 Scheduler 调用真实的 Runner、DSH Headless 和 MSBuild。
验证结果如下:
| 验证项目 | 结果 |
|---|---|
| Scheduler 自动化测试 | 18/18 通过 |
| Runner 回归测试 | 11/11 通过 |
| 管理模块 Python 回归测试 | 142 项通过 |
| 管理插件 Node 测试 | 24 项通过 |
| 真实编码任务 | 2 个,全部成功 |
| 执行顺序 | 严格串行 |
| 独立 Worktree | 通过 |
| 独立 DSH Session | 通过 |
| MSBuild 编译 | 两次成功 |
| EXE 运行验证 | 两次通过 |
两次真实任务的 Runner 执行时间分别约为 21 秒和 21 秒。
需要注意,本轮真实验证使用了两次独立的单任务 Worker 进程,证明了串行领取和执行链路有效。长期常驻 Worker 的稳定性、真实长任务取消和异常重启恢复,还需要进一步验证。
这也是开发报告中必须坚持的一点:
模拟测试通过、真实任务通过和生产长期运行稳定,是三个不同层级的结论。
十、最不能忽视的问题:安全边界
在自动编码系统中,安全不仅仅是防止 Agent 修改错误文件。
更大的风险来自自动执行 AI 生成的代码。
本次验证发现:
- DSH 的
workspace-write模式能够拒绝部分工作区外写入。 - 工作区外文件读取并未受到同等限制。
- Git Worktree 本身不提供操作系统隔离。
- MSBuild 构建和生成的 EXE 运行不在 DSH 沙箱内。
- 路径白名单检查主要发生在代码修改之后。
尤其需要注意:如果自动编译的源代码包含恶意行为,最终生成的程序可能以 Runner 用户权限运行。
因此,当前版本仍然属于受控环境中的自动化开发原型。
后续接入真实项目时,需要考虑:
- 使用独立的低权限执行账号。
- 限制构建进程可以访问的目录和凭据。
- 明确网络访问策略。
- 限制 CPU、内存、磁盘及执行时间。
- 在必要时使用独立虚拟机执行不可信构建。
- 对自动修改的代码保留人工审核。
这些问题比一味追求更多 Agent 并行更重要。
十一、从自动编码到实际开发助手
随着 Runner 和 Scheduler 完成,系统的三个核心部分逐渐清晰:
Control
管理项目、话题和权限
Scheduler
管理任务、队列与执行状态
Runner
管理 Worktree、Agent、编译和验证
它们分别解决不同的问题。
而飞书和 DSH Host 负责将自然语言需求转化为可以提交的任务。
目前系统已具备通过飞书驱动开发的能力,形成了从远程交互到本地自动执行的初步闭环。
但这只是阶段性成果。
接下来真正重要的,不是继续增加架构层次,而是把它用于实际开发项目。
例如使用现有 C++ 工程完成一次功能修改、缺陷修复或构建问题排查,并观察它在需求理解、代码质量和编译修复方面的表现。
十二、阶段性反思
这次实践最大的收获,并不是写出了一套 Python 任务调度程序。
而是逐渐认识到:AI 自动开发系统的难点,不只是模型能否生成代码,更在于如何将不确定的模型行为纳入可靠的软件工程流程。
在这个过程中,几个设计原则逐渐明确:
- 对话 Agent 和编码 Agent 可以分离。
- 每个任务应拥有独立的工作区和执行上下文。
- 任务状态应该由确定性程序管理。
- 构建与测试结果比模型的文字总结更可信。
- 自动化系统必须支持失败、取消和恢复。
- 安全隔离不能仅依赖 Agent 的提示词或沙箱声明。
- 不应为了未来可能出现的需求,过早开发复杂架构。
当前系统已经完成了第一阶段的主要目标。
下一阶段,我更希望它真正参与日常开发,而不是继续围绕基础设施进行迭代。
毕竟,衡量 AI 开发助手价值的最终标准,不是它拥有多少 Agent、多少插件或多少架构模块,而是:
它是否能够可靠地完成实际开发任务,并真正减少开发者的工作量。
本文中的性能和测试数据来自特定 Windows 测试环境,不构成其他机器或复杂业务工程的性能保证。阶段性验证结果不代表已经达到生产环境的安全性与可靠性要求。