第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug
本文用一个可复现的 Java Bug,完整演示 Pi 与 DeepSeek Harness 的安装、只读分析、最小修改和测试验收,并记录权限边界、Hash 校验、Diff 审查与实际踩坑,帮助新手建立一套可复核的 Agent 工作流程。
上一篇《Agent Harness 到底是什么?从 Pi 和 DeepSeek Harness 讲起》讲清了基本概念、两个工具的区别。这篇直接动手:从一个会报错的 Java 小项目开始,安装 Pi 和 DeepSeek Harness,再让它们分别完成一次任务。
在这次实测之前,我大概理解 Agent Harness 的概念,但还没有亲手走完整个过程:应该在哪个目录启动,怎样登录,怎样要求它先别改文件,任务结束后又该怎样验收。
这次我把这条路线完整跑了一遍:先在 IDEA 里复现一个 Java Bug,再让 Pi 和 DeepSeek Harness 分别读取项目、修改代码、运行检查,最后回到 IDEA 做人工验收。
这不是模型能力评测,也不预设哪个工具更好用。文中只写这次真实发生的操作、报错和结果。
这次要完成的路线
IDEA 跑出 Bug
→ 安装并启动 Pi
→ Pi 只读分析
→ Pi 修改并验证
→ 恢复 Bug
→ 启动 DSH,完成同一任务
→ 回到 IDEA 和 Diff 人工验收
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 1
除了按目录完成操作,过程中还要持续记录四件事:
- 它先读了哪些文件。
- 它有没有遵守只改一个文件的要求。
- 它有没有真的运行检查。
- 最后的代码和测试结果,能不能由我自己复核。
四项都有可检查的记录,这次练习才算跑通。
动手前,准备环境和安全边界
本文在 Windows 上实验,所有需要读者执行的命令都按 Windows PowerShell 编写。如果使用 IDEA 底部的 Terminal,也请先确认当前 Shell 是 PowerShell。开始前先检查环境。
node --version
npm --version
npx --version
java --version
javac --version
git --version
需要准备:
- Node.js 和 npm,用来安装、启动 Pi 与 DSH
- Git for Windows,用来提供 Git 和 Pi 在 Windows 运行时需要的 Git Bash;本文给读者执行的命令仍统一使用 Windows PowerShell
- JDK 21 和 IntelliJ IDEA,用来运行 Java 练习
- Pi、DSH 支持的模型账号或 API Key。API Key 就是模型服务发给你的访问凭证
- 一个没有真实数据、随时可以删掉的练习目录
本次实测使用 Node.js 24.19.0。Pi 0.84.3 的本地安装包声明 Node.js 需要 >=22.19.0;DSH 官方快速开始只写明需要安装 Node.js,没有在页面上固定更细的版本区间。想尽量复现本文,可以先使用 24.19.0;如果 npm 报 Unsupported engine,按安装提示升级 Node,再把 PowerShell 和 IDEA Terminal 全部关掉重开,然后重新检查 node、npm、npx。
如果升级后显示的仍是旧版本,用下面两条确认命令来自哪个目录。
where.exe node
where.exe npm
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 2
第一次练习只在下面这些边界内操作:
- 只使用可丢弃的练习目录
- 不放真实业务代码、生产配置、私钥和客户数据
- API Key 只填在登录或模型设置处,不写进项目、提示词和截图
- Agent 说完成了以后,仍然要自己看代码、跑测试、审 Diff
本地终端和本地 Web 页面不代表代码一定只留在本机。只要连接外部模型服务,完成任务所需的上下文就可能发送给对应服务。第一次不要拿公司的支付、结算、对账项目练手。
第 1 步:先在 IDEA 里复现 Bug
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 3
这次使用的练习目录是:
D:\Documents\Agent\agent-harness-lab
你可以换成自己的路径。文末附有完整示例文件,没有现成项目也能自己创建。
项目结构很简单。
agent-harness-lab/
├─ AGENTS.md
├─ TASK.md
├─ README.md
├─ baseline/OrderService.java
├─ src/main/java/lab/OrderService.java
└─ src/test/java/lab/OrderServiceSelfTest.java
AGENTS.md 保存给 Agent 看的项目规则,TASK.md 写本次任务,README.md 保存检查命令,baseline 保存最初的故障代码。
OrderService 只有一个方法。
public String normalizeStatus(String status) {
return status.trim().toUpperCase(Locale.ROOT);
}
它应该满足三个条件:
" paid "返回"PAID"null返回"UNKNOWN"- 空白字符串返回
"UNKNOWN"
当前代码没有处理 null,空白字符串的返回结果也不对。先保留这个故障,交给 Agent 修。
在 IDEA 中运行基线
- 用 IDEA 打开
D:\Documents\Agent\agent-harness-lab。 - 打开
File → Project Structure → Project。 - 给
Project SDK选择本机已经安装的 JDK 21,具体路径以自己的电脑为准。 - 打开
src/test/java/lab/OrderServiceSelfTest.java。 - 点击
main方法左侧的绿色三角,选择Run 'OrderServiceSelfTest.main()'。
如果代码能正常编译,程序会在 normalizeStatus(null) 这里触发空指针。具体文件行号以 IDEA 的真实输出为准,跑完以后再记录。
如果 IDEA 提示找不到 OrderService
这个问题来自练习项目的模块依赖,不是要交给 Agent 修的业务 Bug。
当前本地项目把生产代码和测试代码拆成了 main、test 两个模块,test 需要依赖 main。
- 打开
File → Project Structure → Modules。 - 选择
test → Dependencies。 - 点击
+ → Module Dependency。 - 只选择
main,Scope保持Compile。 - 点击
Apply → OK,再运行测试类的main方法。
不要再给 test 添加 root 依赖。root 里放着用于恢复现场的 baseline/OrderService.java,它和生产代码同名,同时引入会出现重复类。
如果暂时不想调整 IDEA 模块,也可以打开 IDEA 底部的 Terminal,确认当前 Shell 是 PowerShell,再运行下面两条命令作为保底。
javac -encoding UTF-8 -d out src/main/java/lab/OrderService.java src/test/java/lab/OrderServiceSelfTest.java
java -ea -cp out lab.OrderServiceSelfTest
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 4
给不允许修改的文件留一个指纹
Agent 动手前,先给四个受保护文件计算 SHA-256。它相当于文件内容的指纹,只要内容变化一个字符,Hash 就会变化。
在 Windows PowerShell 中运行下面这段,并保持窗口不要关闭。如果使用 IDEA Terminal,先确认其 Shell 已切换为 PowerShell。
$guardedFiles = @(
'AGENTS.md',
'TASK.md',
'README.md',
'src/test/java/lab/OrderServiceSelfTest.java'
)
$guardedBefore = Get-FileHash -Algorithm SHA256 -LiteralPath $guardedFiles
$guardedBefore | Select-Object Path, Hash
Pi 和 DSH 每完成一次任务,都用这份指纹复查。这样既能通过 Diff 查看 OrderService.java 的变化,也能确认规则、任务、README 和测试有没有被改过。
先跑出失败基线很重要。Agent 稍后会报告它已经修好;有了修复前的失败记录,再从同一个入口重跑,结果才不只是听它自己汇报。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 5
第 2 步:安装并第一次打开 Pi
Pi 当前的官方安装命令是:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
pi --version
包名要看清。旧教程里常见的 @mariozechner/pi-coding-agent 已经不是当前官方包,本文使用 @earendil-works/pi-coding-agent。
如果安装完成后提示无法识别 pi,先关闭并重新打开 Windows PowerShell,或已切换到 PowerShell 的 IDEA Terminal,不要立刻重复安装。仍然找不到时运行下面三条,确认它们来自当前使用的 Node 目录。
where.exe node
where.exe npm
where.exe pi
第一轮先尝试通过启动参数只提供读取工具,不把写入和命令工具交给 Pi。
$labRoot = 'D:\Documents\Agent\agent-harness-lab'
Set-Location $labRoot
pi --tools 'read,grep,find,ls'
--tools 后面的逗号列表要整体加引号。本次最初实测漏掉了这对引号;Pi 虽然仍能进入交互界面,但这不代表四个工具已经加载成功。
本次实测中,pi --version 返回 0.84.3。启动页列出了当前目录的 AGENTS.md 和 4 个 Skills。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 6
启动页还出现了两类黄色提示,含义不同:
Warning: Failed to download fd: GitHub API error: 403和Warning: Failed to download ripgrep: GitHub API error: 403:Pi 已经打开,失败的是缺失辅助搜索工具的自动下载。403 的具体原因仅凭这张图无法判断;第 3 步真正调用grep、find时,还要继续确认读取工具能否使用。No models available:当前会话还没有可用模型。先用/login登录模型服务,再用/model确认本次实际使用的模型。
接下来完成两项配置:
- 输入
/login,选择模型服务并完成登录。 - 输入
/model,确认本次实际使用的模型。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 7
登录并确认模型后,在 Pi 里输入 /quit,退出第一次打开的会话。看到 PowerShell 的 PS ...> 提示符后,第 2 步结束。
第 3 步:让 Pi 先做只读分析
第 3 步从退出 Pi 后的 PowerShell 开始。先运行下面的启动命令,不要急着发送任务提示词。
Set-Location 'D:\Documents\Agent\agent-harness-lab'
pi --tools 'read,grep,find,ls'
/quit 要在 Pi 里输入,pi --tools 'read,grep,find,ls' 要在 PowerShell 里运行。单引号不能省略。等新的 Pi 界面打开后,再继续下面的只读分析。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 8
把下面这段提示词发给 Pi。
先只读分析当前目录,不要修改文件,也不要运行命令。
请阅读 AGENTS.md、TASK.md、README.md 和 Java 源码,然后告诉我
1. 当前失败的根因是什么
2. 哪个文件允许修改
3. 应该运行什么检查
4. 如果允许修改,你准备怎样做最小修复
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 9
这一次只读分析成功。会话日志显示,Pi 实际调用 read 读取了 AGENTS.md、TASK.md、README.md、生产代码、测试代码和 baseline,并用 ls 查看了项目目录。没有出现 write、edit、bash 或 powershell 调用。
| 要看什么 | 本次实测结果 |
|---|---|
| 当前失败的根因 | 找对了。null 会在 status.trim() 处触发空指针;空白字符串会变成 "",而不是期望的 "UNKNOWN" |
| 哪个文件允许修改 | 只允许修改 src/main/java/lab/OrderService.java |
| 应该运行什么检查 | 准确复述了 README.md 中的 javac 编译、自测和 git diff --no-index 命令 |
| 最小修复方案 | 在方法入口统一处理 null 和空白字符串并返回 "UNKNOWN",其余转换逻辑保持不变 |
| 是否修改文件或运行命令 | 没有。本轮只调用了读取类工具,修改与测试要等下一步授权 |
Pi 根据源码和测试用例判断:" paid " 这条正常路径会通过,null 和空白字符串两条会失败。这里是静态分析结论,不是测试执行结果。当前目录已经有 out/;如果在全新目录复现且还没有它,应先运行 New-Item -ItemType Directory -Force out,再执行 README.md 中的编译命令。
pi --tools 'read,grep,find,ls' 在本机正确启用了四个读取类工具,同时没有提供 write、edit、bash 和 powershell。这能限制 Pi 本轮可调用的内置工具,但不等于系统隔离;Pi 仍以当前 Windows 用户的权限读取文件,练习目录里不能放敏感内容。
记录完成后,在 Pi 中输入 /quit 或连续按两次 Ctrl+C 退出。
实测记录 03|完成。
deepseek-v4-pro实际读取了项目规则、任务、README、生产代码、测试和 baseline,正确给出了失败根因、唯一可修改文件、检查命令和最小修复方案。本轮没有修改文件,也没有运行编译或测试。
第 4 步:让 Pi 修改代码并验证
第 3 步已经确认 Pi 实际读取了项目文件并给出四项结论。退出只读会话后,用默认模式重新启动 Pi。
Set-Location 'D:\Documents\Agent\agent-harness-lab'
pi
下面的任务提示词会原样交给 Pi 和 DSH。两边面对同一份代码、同一套规则,后面的观察才有参考价值。
请先阅读 AGENTS.md、TASK.md、README.md 和相关 Java 源码。
目标
修复 TASK.md 描述的问题,并运行 README.md 中的检查。
要求
1. 修改前先解释根因和最小修复计划
2. 只允许修改 src/main/java/lab/OrderService.java
3. 不修改测试、任务说明和 README
4. 不访问网络,不安装依赖,不提交 Git
5. 修改后实际运行编译和自测命令
6. 使用下面的命令展示生产代码与基线的差异
git diff --no-index -- baseline/OrderService.java src/main/java/lab/OrderService.java
7. 最后报告根因、修改内容、测试结果、是否触碰边界和剩余风险
Agent 工作时留在旁边观察:
- 它先读规则,还是先改代码
- 它实际用了哪些工具
- 它有没有动测试文件
- 它有没有执行 README 中的命令
- 命令失败以后,它怎样处理
如果第一次没有成功,就把报错、补充提示和第二次尝试一起记下来,不为文章效果删掉失败过程。
Pi 的实际执行记录
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 10
本次会话最初选中 deepseek-v4-pro,在发送任务前切换为 deepseek-v4-flash,推理档位为 high。后续读取、修改、验证和最终报告都发生在切换之后。
会话日志里的真实工具轨迹是:
- 用
read读取AGENTS.md、TASK.md、README.md、当前生产代码、baseline 和自测代码。 - 只对
src/main/java/lab/OrderService.java发起一次edit。 - 用
bash编译并运行自测,得到All self-tests passed.。 - 再用
bash执行git diff --no-index,结果只新增下面 3 行,退出码为预期的1。
+ if (status == null || status.trim().isEmpty()) {
+ return "UNKNOWN";
+ }
| 检查项 | Pi 本轮实测 |
|---|---|
| 根因 | null 会触发空指针;空白字符串处理后得到 "",不符合返回 "UNKNOWN" 的要求 |
| 修改范围 | 方法签名和原有大写转换逻辑不变,只新增 3 行入口守卫 |
| 编译与自测 | 已实际执行并输出 All self-tests passed. |
| Diff | 只有 OrderService.java 的 3 行新增;退出码 1 表示发现预期差异 |
| 网络、安装、提交 | Pi 的工具轨迹中没有相关命令 |
| 受保护文件 | 没有针对它们的 edit 调用;是否保持原 Hash 仍由下面的人工检查确认 |
这个修复延续了项目原有的 trim() 行为,覆盖当前三个自测用例。Unicode 空白字符等更宽泛场景不在本次测试范围内,是否扩展语义要另行确认,不能为了“更通用”擅自扩大改动。
回到 IDEA 验收 Pi
Pi 报告完成后,先自己验收。
- 回到
OrderServiceSelfTest.java。 - 点击 IDEA 的
Rerun,或按Shift+F10。 - 正确结果应该是
All self-tests passed.。 - 再到 IDEA 的 PowerShell Terminal 查看修改前后的差异。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 11
本次人工复跑已经通过。IDEA 的运行窗口输出 All self-tests passed.,随后显示“进程已结束,退出代码为 0”。画面同时显示了三个自测用例和 OrderService.java 中新增的 3 行守卫,因此 Pi 终端里的成功结果在 IDEA 中得到了再次验证。
git diff --no-index -- baseline/OrderService.java src/main/java/lab/OrderService.java
git diff --no-index 发现两个文件不同时会返回退出码 1。这里的 1 表示确实存在差异,不是命令执行失败。
Diff 只说明 OrderService.java 改了什么。受保护文件还要用之前保存的 Hash 检查。
$guardedAfter = Get-FileHash -Algorithm SHA256 -LiteralPath $guardedFiles
Compare-Object $guardedBefore $guardedAfter -Property Path, Hash
没有任何输出,表示这四个文件的内容没变,前提是 $guardedBefore 确实保存于 Pi 修改之前。如果出现结果,先查清 Agent 改了什么,不要直接进入下一步。最后再检查项目树,确认没有新增与任务无关的文件。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 12
本次截图里重新计算了 $guardedBefore,因此仅凭紧接着的 Compare-Object 空输出,不能单独证明 Pi 修改前后没有变化。继续与第 1 步的 011-sha256.png 逐项核对后,AGENTS.md、TASK.md、README.md 和 OrderServiceSelfTest.java 的四个当前 Hash 与原始 Hash 完全一致。结合会话中唯一的 edit 目标是 OrderService.java,可以确认这四个受保护文件未被修改。
实测记录 04|完成。Pi 使用
deepseek-v4-flash只对OrderService.java发起修改,真实执行编译与自测并输出All self-tests passed.;IDEA 人工复跑再次输出相同结果,退出码为0;git diff --no-index只显示 3 行入口守卫;四个受保护文件的当前 SHA-256 与第 1 步保存的原始值完全一致。
第 5 步:恢复 Bug
Pi 验收结束后,把生产代码恢复成最初的故障版本。
Set-Location 'D:\Documents\Agent\agent-harness-lab'
Copy-Item -LiteralPath baseline/OrderService.java -Destination src/main/java/lab/OrderService.java -Force
回到 IDEA,再运行一次 OrderServiceSelfTest.main()。
只有空指针重新出现,才能确认 DSH 接下来拿到的是同一个起点。两边面对的代码不同,比较就失去了意义。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 13
本次恢复验证符合预期。IDEA 显示 normalizeStatus 已回到没有入口守卫的单行实现;运行自测时,normalizeStatus(null) 在 OrderService.java:8 再次触发 NullPointerException,调用位置是 OrderServiceSelfTest.java:9,进程退出码为 1。
随后计算当前生产文件与 baseline 的 SHA-256,两者都是 D8C2F3DEB9D5FFC938C8DE010101DD369A800C0BBA8791CE8C53DA7096E3C208。这说明 OrderService.java 已经字节级恢复,DSH 将面对与 Pi 修改前相同的故障代码。
向 DSH 发送第 7 步任务前,再执行一次下面这条,为 DSH 单独保存受保护文件的指纹。只启动本地页面不会修改练习代码,因此即使服务已经打开,现在补做仍然有效。
$guardedBefore = Get-FileHash -Algorithm SHA256 -LiteralPath $guardedFiles
实测记录 05|完成。当前
OrderService.java与 baseline 的 SHA-256 完全一致;IDEA 复跑重新出现预期的NullPointerException,退出码为1。第 7 步继续用第 1 步保存的原始 Hash 核对四个受保护文件。
第 6 步:启动并配置 DeepSeek Harness
下文把 DeepSeek Harness 简称为 DSH。
在练习目录运行官方启动命令。
Set-Location 'D:\Documents\Agent\agent-harness-lab'
npx @deepseek-ai/dsh web
第一次运行时,npx 可能需要下载所需内容。如果它询问是否安装,先核对包名确实是 @deepseek-ai/dsh,再确认继续。
本次首次运行提示安装 @deepseek-ai/dsh@0.1.1-rc.2,确认后终端长时间只显示旋转符。进程和 npm 日志显示它仍在解析较大的依赖树,并非立即卡死;与此同时,本机已有同版本 DSH 服务监听 3080,页面返回 200 OK。后来重复的 npx 进程自行退出,只剩一个服务进程。
遇到类似情况时,先直接访问下面的地址。页面能打开就不要重复执行 npx,否则可能同时启动多个安装或服务进程。
默认页面地址是:
http://127.0.0.1:3080
如果只想启动服务,不让它自动打开浏览器,可以使用下面的命令。
npx @deepseek-ai/dsh web --no-open
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 14
打开页面后,按下面的顺序配置。
- 进入
Settings → Models。 - 配置要使用的模型服务。
- 点击
Choose workspace。Workspace 就是这次允许 DSH 使用的工作目录。 - 只选择
D:\Documents\Agent\agent-harness-lab。 - 权限预设选择
workspace-write。它内部对应文件沙箱workspace-write和审批策略ask,不要为了少点确认就切到danger-full-access。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 15
本次页面配置为:
| 配置项 | 实测值 |
|---|---|
| 页面地址 | http://127.0.0.1:3080 |
| Workspace | agent-harness-lab |
| Provider | DeepSeek,状态正常 |
| 模型 | DeepSeek-V4-Flash ,High |
| Agent 模式 | 标准模式 |
| 权限 | Workspace Write |
| 界面语言 | 中文 |
这个预设也不是完整隔离。它主要约束文件写入,不限制文件读取、网络和进程可见性;官方还标明 Windows ACL 后端可能只有 partial,也就是部分生效。练习目录之外仍然不要放希望它绝对看不到的敏感内容。
如果想尽量比较 Harness,而不是比较模型,就让 Pi 和 DSH 使用同一个模型。做不到也没关系,只要在实测记录里写清两边的模型,不把结果包装成严格评测。
实测记录 06|完成。DSH
0.1.1-rc.2已在127.0.0.1:3080打开,Workspace 为agent-harness-lab,使用DeepSeek-V4-Flash High、标准模式、中文界面和Workspace Write;截图没有暴露 API Key。Workspace Write允许修改练习目录,第 7 步的“只读分析”仍只是提示词约束。
第 7 步:把同一个任务交给 DSH
先把第 3 步那段要求不修改文件的提示词发给 DSH,观察它能不能找到项目规则、任务和测试入口。
这里有一个需要单独说明的差别:Pi 第一轮通过 --tools 去掉了写入和命令工具;DSH 这一轮主要依靠提示词要求它只读观察。两者不是同一种技术限制。发出任务后仍要看真实工具轨迹,不能只看它口头答应了什么。
DSH 的只读分析记录
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 16
本轮使用 DeepSeek-V4-Flash High。页面展示的真实轨迹是:先注入 Workspace 下的 AGENTS.md、DSH 系统提示和 Skill Catalog,再进入 Think;随后用 Glob 查找 **/*.java,依次读取 AGENTS.md、TASK.md、README.md、当前生产代码、baseline 和自测代码。没有出现编辑工具,也没有执行编译、测试或 Diff 命令。
它给出的四项结论与项目内容一致。
| 检查项 | DSH 本轮分析 |
|---|---|
| 根因 | null 会在 status.trim() 处触发空指针;空白字符串处理后得到 "",不符合返回 "UNKNOWN" 的要求 |
| 修改边界 | 只允许修改 src/main/java/lab/OrderService.java,禁止改任务、README 和测试 |
| 验证办法 | 正确列出了 README 中的 javac、java 和 git diff --no-index 命令 |
| 最小修复 | 保持方法签名和原有大写逻辑,只在入口增加 null、空白字符串守卫 |
DSH 最后问“需要我继续执行这个最小修复吗?”,说明它在分析阶段后停下来等待确认。到这里能确认的是:它完成了只读静态分析,结论正确,且可见轨迹中没有修改或执行命令。这里还不能写“修复成功”,因为代码、编译、自测和 IDEA 复跑都没有发生。
截图之后再次核对,当前 OrderService.java 仍与 baseline 的 SHA-256 一致,四个受保护文件的 Hash 也没有变化。不过,页面底部仍是 Workspace Write,所以这次没有写文件是它遵守了提示词,不是系统撤掉了写入能力。
现在先回到此前保持打开的 PowerShell,为 DSH 的修改阶段单独保存一次受保护文件指纹。
$guardedBefore = Get-FileHash -Algorithm SHA256 -LiteralPath $guardedFiles
$guardedBefore | Select-Object Path, Hash
保存完成后,不要只回复“是”。把第 4 步的完整任务提示词原样发送给 DSH,其中包含允许修改的文件、禁止事项、验证命令和最终报告要求。这样 Pi 与 DSH 接到的是同一份修改任务,也方便事后检查它有没有越界。
DSH 的修改与自测记录
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 17
本次发送的是第 4 步的完整提示词。DSH 没有直接跳到修改,而是先复述根因和最小修复计划,然后按下面的顺序执行:
- 用
Edit修改src/main/java/lab/OrderService.java。 - 用 PowerShell 运行
javac编译生产代码和自测代码。 - 用 PowerShell 运行
git diff --no-index,展示生产代码与 baseline 的差异。 - 用 PowerShell 运行
java -ea -cp out lab.OrderServiceSelfTest。 - 汇总根因、修改内容、测试结果、边界和剩余风险。
页面中的最终报告显示,唯一的源码变化是下面 3 行入口守卫。
+ if (status == null || status.trim().isEmpty()) {
+ return "UNKNOWN";
+ }
| 检查项 | DSH 本轮实测 |
|---|---|
| 修改范围 | 只修改 src/main/java/lab/OrderService.java,方法签名、Locale import 和原有大写逻辑不变 |
| 编译 | javac 成功,退出码为 0 |
| 自测 | 输出 All self-tests passed.,退出码为 0 |
| Diff | 只有 3 行新增;git diff --no-index 退出码为预期的 1 |
| 边界 | 可见轨迹中没有网络访问、依赖安装或 Git 提交 |
| 剩余风险 | 当前判断基于 trim(),没有把更宽泛的 Unicode 空白字符扩展进任务语义 |
随后又在项目根目录独立运行相同的编译、自测和 Diff 命令,得到相同结果。Diff 同时出现了 LF/CRLF 提示,它表示 Git 以后接触这些文件时可能转换行尾,不影响本次 3 行逻辑差异。接下来回到 IDEA,从同一个入口再做一次不依赖 DSH 报告的人工复跑。
回到 IDEA 验收 DSH
验收办法和 Pi 相同。
- 回到 IDEA 运行
OrderServiceSelfTest.main()。 - 确认实际输出,不只看 DSH 的总结。
- 在 IDEA 的 PowerShell Terminal 运行同一条 Diff 命令。
- 检查
TASK.md、README.md和测试文件有没有被改动。
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 18
本次 IDEA 人工复跑通过。运行窗口输出 All self-tests passed.,随后显示“进程已结束,退出代码为 0”;画面同时显示三个自测用例和 OrderService.java 中新增的入口守卫。这说明 DSH 页面里的成功结果已经从 IDEA 的同一测试入口得到复核。
git diff --no-index -- baseline/OrderService.java src/main/java/lab/OrderService.java
再检查受保护文件的 Hash。
$guardedAfter = Get-FileHash -Algorithm SHA256 -LiteralPath $guardedFiles
Compare-Object $guardedBefore $guardedAfter -Property Path, Hash
本次当前 Hash 与第 1 步保存的四个原始值逐项一致:
| 受保护文件 | 当前 SHA-256 |
|---|---|
AGENTS.md | 3411EFBABAB2C5E54F1F19C88EAC6A1B8922658D277249D019441755A4D8AF67 |
TASK.md | 10102B6A31479A6E289C0045A73E0895921232E7958703AFD44A5E6440E43295 |
README.md | 21E72787FED95437AFC82258CC5EA8BDA6FC83161BDCCA880889BBC1F4CC5B54 |
OrderServiceSelfTest.java | F418BE36AE30EE90D1E0B8C9329845ACF022821A203D6EAB2E9839141CD9325B |
截图中的最后一次 Compare-Object 没有输出,但画面里又重新计算了 $guardedBefore,所以不能只凭这次空输出证明 DSH 修改前后没有变化。有效依据是:上表四个当前 Hash 与第 1 步保留的原始 Hash 逐项一致。结合 DSH 的唯一 Edit 目标和真实 Diff,可以确认这四个受保护文件没有被修改。
完成后,关闭浏览器标签并不会停止 DSH。回到启动 npx 的 PowerShell 窗口,按 Ctrl+C 停止服务。
实测记录 07|完成。DSH 先完成只读分析,再只修改
OrderService.java;页面记录和项目根目录独立复核都显示编译成功、自测输出All self-tests passed.,Diff 只有预期的 3 行入口守卫;IDEA 人工复跑再次通过,退出码为0;四个受保护文件的当前 SHA-256 与第 1 步原始值一致。
用同一套证据记录两个 Harness
第一次上手 Agent Harness:用 Pi 和 DeepSeek Harness 修一个 Java Bug image 19
第一次体验先不急着打分,把实际记录填齐。
| 观察项 | Pi 实际记录 | DSH 实际记录 |
|---|---|---|
| 安装和第一次启动 | 0.84.3 ,在 PowerShell 中进入终端界面 | 0.1.1-rc.2 ,在 127.0.0.1:3080 打开本地工作台 |
| 实际使用的模型 | 只读分析为 deepseek-v4-pro;修改阶段为 deepseek-v4-flash high | DeepSeek-V4-Flash High |
| 是否找到项目规则 | 是,读取了 AGENTS.md、任务、README、生产代码、baseline 和自测 | 是,读取范围与 Pi 相同 |
| 第一个读取的文件 | AGENTS.md | AGENTS.md |
| 工具调用怎样展示 | 终端中按时间顺序显示 read、edit 和 bash | 页面同时展示 Think、任务进度、Read、Edit 和 PowerShell 调用 |
| 是否尊重文件边界 | 是,真实 Diff 和原始 Hash 复核均通过 | 是,真实 Diff 和原始 Hash 复核均通过 |
| 是否主动运行检查 | 是,实际编译、自测并展示 Diff | 是,实际编译、自测并展示 Diff |
| IDEA 人工验收结果 | All self-tests passed. ,退出码 0 | All self-tests passed. ,退出码 0 |
| 中途遇到的问题 | 首次 --tools 参数漏加引号;fd、ripgrep 自动下载遇到 GitHub API 403 | 首次 npx 长时间显示旋转符;确认 3080 页面可用后没有重复启动服务 |
| 本次主观感受 | 命令行里的工具轨迹集中,但第一次使用没有图形页面直观 | 模型、计划、任务和工具调用都在图形页面里,第一次操作更直观 |
这次两边最终都新增了相同的 3 行守卫,编译、自测和 IDEA 人工复跑也都通过。差别主要出现在交互方式:Pi 把过程集中显示在终端里,DSH 把模型、计划、任务和工具调用放在同一个图形页面中。
这份记录可以回答开始时的三个问题:
| 问题 | 本次答案 |
|---|---|
| 它做过什么,我看得见吗? | 能。Pi 和 DSH 都留下了读取、编辑和命令调用轨迹 |
| 它有没有越过我给出的边界? | 没有发现。真实 Diff 只有生产代码的 3 行新增,四个受保护文件的 Hash 与原始值一致 |
| 它说完成以后,我能独立验证吗? | 能。两次都在 IDEA 从同一个测试入口复跑成功,退出码为 0 |
这仍然不是严格对比评测:只跑了一个小任务、每个 Harness 只有一次修改记录,而且 Pi 的只读阶段和修改阶段使用了不同模型。上面的结论只描述这次操作,不能据此判断谁的能力更强。
走完七步之后
七步走完,我留下了一套可以复用的验收顺序:先复现问题,保存受保护文件的 Hash,让 Agent 读取、修改和运行检查,再回到 IDEA 复跑并审 Diff。插件、子 Agent 和复杂工作流不在这次练习范围内,这次也没有测试它们。
下一步,尝试做一个 DSH 插件
下一步,我打算参考 Russell 的《万字长文,DeepSeek Harness 一文全看懂!!》,亲手做一个 DSH 插件。
不过现在只定了方向,具体做什么展示还没定。它最后是一个新工具、一块 Web 界面,还是另一种更适合演示的能力,要等选题确定以后再说。我不想为了让文章结尾显得完整,先编一个插件名,再把还没做过的功能和结果写上去。
选题时我会看三件事。读者能不能一眼看出装插件前后的变化,结果能不能独立验证,实验范围能不能控制在一个小目录里。等展示内容真正定下来,再补功能边界、权限、操作步骤和验收记录。
所以这里先留一个预告。下一篇从选题、搭插件骨架、加载运行,到失败、修改和最终验证,重新完整走一遍。现在能确认的只有一件事,下一站是 DSH 插件,具体做什么,尚未决定。
DSH 仍处于 Developer Preview,真正动手前还要重新核对官方仓库里的 web-cordis 示例和插件配置文档。
附录|没有练习项目时,自己创建一份
先建立目录。
$labRoot = 'D:\Documents\Agent\agent-harness-lab'
New-Item -ItemType Directory -Path "$labRoot\src\main\java\lab" -Force | Out-Null
New-Item -ItemType Directory -Path "$labRoot\src\test\java\lab" -Force | Out-Null
New-Item -ItemType Directory -Path "$labRoot\baseline" -Force | Out-Null
New-Item -ItemType Directory -Path "$labRoot\out" -Force | Out-Null
Set-Location $labRoot
src/main/java/lab/OrderService.java
package lab;
import java.util.Locale;
public final class OrderService {
public String normalizeStatus(String status) {
return status.trim().toUpperCase(Locale.ROOT);
}
}
src/test/java/lab/OrderServiceSelfTest.java
package lab;
public final class OrderServiceSelfTest {
public static void main(String[] args) {
OrderService service = new OrderService();
assertEquals("PAID", service.normalizeStatus(" paid "), "normal value");
assertEquals("UNKNOWN", service.normalizeStatus(null), "null value");
assertEquals("UNKNOWN", service.normalizeStatus(" "), "blank value");
System.out.println("All self-tests passed.");
}
private static void assertEquals(String expected, String actual, String scenario) {
if (!expected.equals(actual)) {
throw new AssertionError(
scenario + ": expected <" + expected + "> but was <" + actual + ">"
);
}
}
}
AGENTS.md
# Agent Harness Lab Rules
- 只允许修改 src/main/java/lab/OrderService.java
- 不修改测试、TASK.md 和 README.md
- 不安装依赖,不访问网络,不操作练习目录之外的文件
- 修改后必须运行编译和自测命令
- 最后报告原因、改动、测试结果和剩余风险
TASK.md
# Task
OrderService.normalizeStatus 需要满足下面三条
- " paid " 返回 "PAID"
- null 返回 "UNKNOWN"
- 空白字符串返回 "UNKNOWN"
保持现有 public 方法签名,只做最小、可读的生产代码修改,不要改测试。
README.md
# Agent Harness First Lab
在 Windows PowerShell 中运行
```powershell
javac -encoding UTF-8 -d out src/main/java/lab/OrderService.java src/test/java/lab/OrderServiceSelfTest.java
java -ea -cp out lab.OrderServiceSelfTest
正确结果是 All self-tests passed.
最后保存一份故障基线。
```powershell
Copy-Item -LiteralPath src/main/java/lab/OrderService.java -Destination baseline/OrderService.java -Force
手工创建项目后,再让 IDEA 认识源码目录。
- 用 IDEA 打开
agent-harness-lab。 - 在项目树中右键
src/main/java,选择Mark Directory as → Sources Root。 - 右键
src/test/java,选择Mark Directory as → Test Sources Root。 baseline保持普通目录;如果 IDEA 把它识别成源码,改成Mark Directory as → Excluded。- 在
Project Structure → Project中选择 JDK 21。
这条是自行创建项目的路线,不会沿用前文随附项目里的 root、main、test 三模块结构,也不用添加模块依赖。
官方资料
- Pi Quickstart[4]
- Pi Windows 使用说明[5]
- Pi Security[6]
- DeepSeek Harness 官方介绍[7]
- DSH Web UI 快速开始[8]
- DSH 模型配置[9]
- DSH 权限预设[10]
- DSH Sandbox 边界[11]
- DSH web-cordis 动态插件示例[2]
- DSH 插件配置文档[3]
- DeepSeek Harness 官方仓库[12]
延伸阅读(非官方):
- Russell:万字长文——DeepSeek Harness 一文全看懂[1]
资料核对时间为 2026 年 8 月 27 日。DSH 目前仍处于 Developer Preview,官方明确提醒可能发生破坏兼容性的变化。Pi 与 DSH 的安装命令、版本和界面也可能更新,实际使用前请再查看上面的官方文档。
引用链接
[1] 《万字长文,DeepSeek Harness 一文全看懂!!》: https://x.com/Russell3402/status/2092535898034630816
[2] `web-cordis` 示例: https://github.com/deepseek-ai/deepseek-harness/tree/master/examples/web-cordis
[3] 插件配置文档: https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/config.md
[4] Pi Quickstart: https://pi.dev/docs/latest/quickstart
[5] Pi Windows 使用说明: https://pi.dev/docs/latest/windows
[6] Pi Security: https://pi.dev/docs/latest/security
[7] DeepSeek Harness 官方介绍: https://www.deepseek.com/harness/en/
[8] DSH Web UI 快速开始: https://deepseek-harness.github.io/deepseek-harness/en/guide/quickstart
[9] DSH 模型配置: https://deepseek-harness.github.io/deepseek-harness/en/guide/providers
[10] DSH 权限预设: https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/permission-presets
[11] DSH Sandbox 边界: https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/sandbox
[12] DeepSeek Harness 官方仓库: https://github.com/deepseek-ai/deepseek-harness