pi-dsh-mimic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/NOTICE +16 -0
- package/README.md +155 -0
- package/README.zh-CN.md +133 -0
- package/SECURITY.md +41 -0
- package/docs/advanced.md +182 -0
- package/docs/advanced.zh-CN.md +151 -0
- package/docs/project2-evidence.md +230 -0
- package/package.json +70 -0
- package/scripts/session-stats.mjs +117 -0
- package/src/constants.ts +33 -0
- package/src/editor.ts +133 -0
- package/src/index.ts +188 -0
- package/src/protocol.ts +134 -0
- package/src/session-stage.ts +67 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
[English](advanced.md) · [返回使用说明](../README.zh-CN.md) · [完整证据账本](project2-evidence.md)
|
|
2
|
+
|
|
3
|
+
# 用 DSH Minimal 启动 V4 Pro,再回到 Pi
|
|
4
|
+
|
|
5
|
+
`pi-dsh-mimic` 只模拟 DSH 最有价值的一小段:第一次模型请求。真实任务进入 Minimal
|
|
6
|
+
persona 与 `bash/str_replace_editor` 两工具环境,V4 Pro 由此进入已验证的高能力工程
|
|
7
|
+
轨迹;第一次有效响应后,请求恢复为 Pi 原生格式,模型继续使用 Pi 的完整工具目录和其他
|
|
8
|
+
插件。
|
|
9
|
+
|
|
10
|
+
这条路线不需要复刻完整 DSH harness。DSH Minimal 提供首请求的训练接口,Pi 继续负责
|
|
11
|
+
session、工具执行、文件编辑和插件组合。用户既能获得 Project2 中反复出现的 96–98 分
|
|
12
|
+
能力档,也保留了 Pi 作为成熟 Agent runtime 的优势。
|
|
13
|
+
|
|
14
|
+
## 实验口径
|
|
15
|
+
|
|
16
|
+
本地实验使用 Project2 V4.1b,冻结于提交
|
|
17
|
+
`04255b55f16c4439e538239fb9783070c4165081`。同一运行中断后续跑时,只记录最终交卷分;
|
|
18
|
+
错误 project root、零 token 配置失败和重复评分不计作新样本。
|
|
19
|
+
|
|
20
|
+
`We need`、`Let me` 和 reasoning block 数量用于识别推理轨迹。最终判断来自 F3 的
|
|
21
|
+
ambient-session 安全边界、F6 数据库迁移、F8 ESP 契约、hidden tests 和完整交付。
|
|
22
|
+
Evaluator IDs 与排除项见[证据账本](project2-evidence.md)。
|
|
23
|
+
|
|
24
|
+
## DSH Minimal 证明能力可达
|
|
25
|
+
|
|
26
|
+
外部同题结果给出了清楚的校准:DSH Minimal 在相同 WSL/max/build 条件下得到 99、96,
|
|
27
|
+
Standard 为 91,PTC 为 92。后来的 Anchored Standard 又在首次工具调用后从两个工具恢复
|
|
28
|
+
到 25 个工具,并得到 98、99。
|
|
29
|
+
|
|
30
|
+
这些结果揭示了两个关键事实:首请求的 persona 与 schema 会影响 V4 Pro 的工程轨迹;
|
|
31
|
+
轨迹建立后,完整工具目录并不会自动破坏它。`pi-dsh-mimic` 把这两个事实带进 Pi。
|
|
32
|
+
|
|
33
|
+
## Pi 中的决定性对照
|
|
34
|
+
|
|
35
|
+
同批本地对照把主要差异定位到首请求:
|
|
36
|
+
|
|
37
|
+
| 运行 | 首请求与后续执行 | 分数 | 关键结果 |
|
|
38
|
+
| --- | --- | ---: | --- |
|
|
39
|
+
| 默认 Pi baseline | 完整 Pi prompt 与 5 个工具,全程 native | 92 | F3 11,F6 10,F8 6 |
|
|
40
|
+
| 早期 Minimal 模拟原型 | 原任务进入 Minimal 两工具首包;首次工具调用后恢复 Pi;不重放 Pi context | **98** | F3 16,F6 10,F8 7,hidden 44/45 |
|
|
41
|
+
|
|
42
|
+
98 分轨迹在约第 107 个 assistant response 推翻了自己早期的 ambient fallback:敏感
|
|
43
|
+
context 必须显式携带 `session_id`,voice 路径先取得 current session 再传回。这个自纠
|
|
44
|
+
直接解释了 F3 16/16,比首行短语更接近真正的能力差异。
|
|
45
|
+
|
|
46
|
+
## 负结果如何收窄实现
|
|
47
|
+
|
|
48
|
+
后续实验逐项排除了其他解释:
|
|
49
|
+
|
|
50
|
+
| 问题 | 实验条件 | 结果 | 实现选择 |
|
|
51
|
+
| --- | --- | ---: | --- |
|
|
52
|
+
| 是否需要额外身份轮 | zero-tool Whoami | 93 | 原任务直接进入请求 #1 |
|
|
53
|
+
| 是否只要减少工具数量 | Pi 原生 `bash/read` | 93 | 首请求使用 DSH Minimal schema |
|
|
54
|
+
| 是否应先强迫模型说出轨迹指纹 | Wire Think | 94;F3 16,F6 6 | 不注入或追求 `We need` |
|
|
55
|
+
| 是否应全程模拟 DSH wire | 持续 DSH wire | 94;F3 11 | 第一次有效响应后恢复 Pi 原生请求 |
|
|
56
|
+
| 是否应在恢复后重放 Pi system context | 完整 context 重放 | 90;F6 8 | 保持 Minimal persona,不重放完整 Pi context |
|
|
57
|
+
| 近似的 Minimal 表面是否足够 | 近似 Minimal → Pi 原生 | 93,首行回到 `Let me` | 以实际 98 分首包作为实现基准 |
|
|
58
|
+
|
|
59
|
+
另一次相同流程复现实验得到 96,并保持 F3 16/16;主要波动来自 ESP 静态完成度。
|
|
60
|
+
|
|
61
|
+
## 从实验原型到当前实现
|
|
62
|
+
|
|
63
|
+
同一请求流程随后在两个干净实现中得到完整 Project2 结果:
|
|
64
|
+
|
|
65
|
+
| 实现 | Provider | 分数 | F3 | F6 | F8 |
|
|
66
|
+
| --- | --- | ---: | ---: | ---: | ---: |
|
|
67
|
+
| 首次 package 化实现 | DeepSeek 官方 API | 96 | 16 | 8 | 7 |
|
|
68
|
+
| 当前独立实现 `0.1.0` | OpenCode Go | **98** | 16 | 10 | 7 |
|
|
69
|
+
|
|
70
|
+
OpenCode Go 的初始请求在生成 token 前遇到账户 opt-in 403。同一 session 完成 opt-in 后
|
|
71
|
+
续跑,模型最终自然 `stop` 并得到 98。这次运行验证了当前实现能够通过 OpenCode Go
|
|
72
|
+
进入同一高分档。
|
|
73
|
+
|
|
74
|
+
DeepSeek 官方 API 与 OpenCode Go 都得到 96–98 分,当前能力差异无需再用“OpenCode
|
|
75
|
+
提供的 V4 更弱”解释。
|
|
76
|
+
|
|
77
|
+
## 为什么 Pi 插件仍然可用
|
|
78
|
+
|
|
79
|
+
| | Bootstrap 请求 | 执行阶段请求 |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| System persona | `You are a helpful software engineer assistant.` | 保持不变 |
|
|
82
|
+
| User 历史 | 原始任务,包括 Pi 原生图片 block | 不变 |
|
|
83
|
+
| Provider 可见工具 | `bash`、`str_replace_editor` | Pi 当下完整目录,包括其他插件工具 |
|
|
84
|
+
| Provider payload | DSH Minimal 形状;移除 Pi cache 字段 | Pi 原生编码与排序;cache 随 provider 配置 |
|
|
85
|
+
|
|
86
|
+
扩展从不调用 `setActiveTools`,也不覆盖 Pi 的 `bash`、`read`、`edit` 或 `write`。它只在
|
|
87
|
+
目标新 session 武装时注册一个可执行 `str_replace_editor`。请求 #2 起,Pi 重新掌管
|
|
88
|
+
provider payload 和 active catalog,其他 package 的工具会自然出现。
|
|
89
|
+
|
|
90
|
+
这正是 mimic 与完整 harness port 的区别:它只负责让首请求进入 DSH Minimal 分布,
|
|
91
|
+
任务执行仍由 Pi 负责。
|
|
92
|
+
|
|
93
|
+
## 产品行为与证据
|
|
94
|
+
|
|
95
|
+
| 当前行为 | 主要证据 |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| 只武装目标 provider/model 的新 session | 已有对话无法还原真实 bootstrap;隔离测试覆盖目标与非目标模型 |
|
|
98
|
+
| 原任务直接进入请求 #1 | 早期原型 98;Whoami 93 |
|
|
99
|
+
| 请求 #1 使用 Minimal persona 与 DSH 两项 schema | Pi 原生 `bash/read` 93;近似 Minimal 93 |
|
|
100
|
+
| 第一次有效响应后恢复 Pi 原生执行 | one-shot 96–98;持续 DSH wire 94 |
|
|
101
|
+
| 保持 Minimal persona,不重放完整 Pi system context | context replay 90 |
|
|
102
|
+
| Provider error 或 aborted response 不消耗 bootstrap | 错误重试、阶段持久化和 crash-stale 恢复测试 |
|
|
103
|
+
|
|
104
|
+
## 轨迹与工具规模
|
|
105
|
+
|
|
106
|
+
按不区分大小写统计,默认 Pi baseline 的 78 个 reasoning blocks 中出现了 156 次
|
|
107
|
+
`let me`,98 分原型的 135 个 blocks 中只有 1 次;按精确大小写统计则为 115 对 1。
|
|
108
|
+
这个差异能快速显示首请求是否改变了模型的推理习惯。
|
|
109
|
+
|
|
110
|
+
完整 11 组运行的 assistant、reasoning、`We need / Let me`、tool calls、工具种类和
|
|
111
|
+
stopReason 已整理进[证据账本](project2-evidence.md)。工具量本身不决定能力:持续 DSH
|
|
112
|
+
wire 调用 225 次工具只得到 94,早期原型调用 148 次得到 98;当前实现使用
|
|
113
|
+
`bash/read/edit/write` 共 193 次并得到 98。
|
|
114
|
+
|
|
115
|
+
持续 DSH wire 同样很少出现 `Let me`,Wire Think 也从 `We need` 开始却只有 94。
|
|
116
|
+
插件只观察这些短语,不向 prompt 注入它们。
|
|
117
|
+
|
|
118
|
+
## 实现验证
|
|
119
|
+
|
|
120
|
+
TypeScript typecheck 已通过,13 项自动化测试全部通过:12 项覆盖两个 provider、图片、
|
|
121
|
+
错误重试、文本晋升、session resume、crash-stale 恢复、已有对话隔离和非目标模型隔离;
|
|
122
|
+
另 1 项验证 session 统计工具。
|
|
123
|
+
Editor 测试覆盖 create、view、unique replace、insert、相对路径拒绝和歧义替换拒绝。
|
|
124
|
+
|
|
125
|
+
OpenCode Go bash-first 与 editor-first 离线回环通过 Pi 的真实 package loader 和 provider
|
|
126
|
+
路径捕获两个请求,全程 `realModelCalls=0`。请求 #2 恢复
|
|
127
|
+
`read/bash/edit/write/str_replace_editor`,并携带真实工具结果。归一 user task 后,新请求
|
|
128
|
+
#1 与历史 98 分 capture 在消息、字段顺序、schema、`strict:false` 和序列化结构上逐项
|
|
129
|
+
相等。
|
|
130
|
+
|
|
131
|
+
## 分数细节与重开条件
|
|
132
|
+
|
|
133
|
+
98 分原型和可核验的 DSH Minimal 99 都通过 hidden 44/45、F3 16/16,只错相同的 1 分
|
|
134
|
+
F12 拒绝原因字符串。原型的另一个失分来自 grader 只识别特定 readiness 函数名;实现
|
|
135
|
+
本身已经检查 Wi-Fi、UID、room 和 bed。本文按评分器原分报告。
|
|
136
|
+
|
|
137
|
+
当前 Project2 请求流程已有四次 96–98 分结果。新任务、模型或服务版本变化、provider
|
|
138
|
+
payload 实质变化,或新的完整任务结果,才需要重新运行付费实验。
|
|
139
|
+
|
|
140
|
+
## 来源与归属
|
|
141
|
+
|
|
142
|
+
Minimal persona、bash 描述、editor 描述和 function schema 来自
|
|
143
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的公开 Minimal 协议;
|
|
144
|
+
精确 commit 与许可声明见 [LICENSE](../LICENSE) 和 [NOTICE](../NOTICE)。外部 Project2
|
|
145
|
+
校准来自 [`xiaobright/modeltest`](https://github.com/xiaobright/modeltest) 与
|
|
146
|
+
[`xiaobright/dsh-anchored-standard`](https://github.com/xiaobright/dsh-anchored-standard)。
|
|
147
|
+
|
|
148
|
+
当前源码依据 DeepSeek Harness Minimal 协议、本项目捕获的请求和本项目实验实现,没有
|
|
149
|
+
参考或移植 [`kxh4892636/pi-deepseek-anchor`](https://github.com/kxh4892636/pi-deepseek-anchor)
|
|
150
|
+
的源码。早期探索中曾加载该 extension 验证 Pi 上的两阶段路径,因此证据账本保留这项
|
|
151
|
+
运行 provenance;当前实现、自动化测试、离线回环和 Project2 评分均由本项目完成。
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
[返回实验与设计](advanced.zh-CN.md) · [使用说明](../README.zh-CN.md)
|
|
2
|
+
|
|
3
|
+
# pi-dsh-mimic Project2 证据账本
|
|
4
|
+
|
|
5
|
+
> **Owner:** `pi-dsh-mimic` 维护者
|
|
6
|
+
>
|
|
7
|
+
> **状态:** 当前请求流程的 Project2 实验已收束;满足更新条件时重开
|
|
8
|
+
>
|
|
9
|
+
> **基准:** Project2 V4.1b,冻结提交 `04255b55f16c4439e538239fb9783070c4165081`
|
|
10
|
+
>
|
|
11
|
+
> **最后整理:** 2026-08-17
|
|
12
|
+
>
|
|
13
|
+
> **更新条件:** 新的完整 Pi Project2 评分、同树 ESP-IDF build 证据,或本文引用来源的实质更正。
|
|
14
|
+
|
|
15
|
+
本文记录支持当前实现的完整结果、纳入与排除规则、外部校准和来源关系。原始 run、
|
|
16
|
+
trajectory、provider capture 与 grader 输出由维护者实验归档保存,不随仓库分发。
|
|
17
|
+
|
|
18
|
+
## 计分口径
|
|
19
|
+
|
|
20
|
+
- 同一运行中断后续跑,只保留最终交卷分;续跑前分数属于中间状态。
|
|
21
|
+
- `We need`、`Let me` 和 reasoning block 数量只作轨迹指纹。
|
|
22
|
+
- F3、迁移、ESP 契约、hidden tests 和最终交付决定能力判断。
|
|
23
|
+
- Grader 的词法盲点可以解释,但不改写原分。
|
|
24
|
+
|
|
25
|
+
## 当前结论
|
|
26
|
+
|
|
27
|
+
1. 同批决定性对照是 **默认 Pi 92 → 早期 Minimal 模拟原型 98**。
|
|
28
|
+
2. 相同 one-shot 请求流程的四次最终结果为 **98、96、96、98**,均保持 F3 16/16。
|
|
29
|
+
3. 高分流程是:原任务进入 Minimal 两工具首包;第一次有效响应后恢复 Pi 原生完整目录;
|
|
30
|
+
Minimal persona 保持;不重放完整 Pi system context。
|
|
31
|
+
4. 持续 DSH wire、Whoami、Wire Think、Pi 原生 `bash/read` 和 context 重放都没有提供更好
|
|
32
|
+
的完整任务结果。
|
|
33
|
+
5. DeepSeek 官方 API 与 OpenCode Go 都进入 96–98 分档。
|
|
34
|
+
|
|
35
|
+
## 完整运行
|
|
36
|
+
|
|
37
|
+
本表只列最终且可解释的运行。
|
|
38
|
+
|
|
39
|
+
| 运行 | 首请求与后续执行 | 指纹 | 分数 | 关键分项 | 对实现的意义 |
|
|
40
|
+
| --- | --- | --- | ---: | --- | --- |
|
|
41
|
+
| 默认 Pi baseline | 完整 Pi prompt + 5 工具,全程 native | `Let me` | **92** | F3 11,F6 10,F8 6 | 默认对照;ambient-session 少 5 分 |
|
|
42
|
+
| 早期 Minimal 模拟原型 | 原任务 + Minimal + DSH `bash/editor`;随后 Pi 原生 5 工具;不重放 context | `We need` | **98** | F3 16,F6 10,F8 7 | 首次证明 Pi 能进入高能力轨迹 |
|
|
43
|
+
| 首次 package 化实现 | 同一首包;随后 Pi 原生;同 session 续过一次 402 | `We need` | **96** | F3 16,F6 8,F8 7 | 干净 package 复现目标轨迹;迁移少 2 分 |
|
|
44
|
+
| 当前独立实现 `0.1.0` | 初始首包遇 403;opt-in 后同 session 续跑并恢复 Pi 原生 | `We need` | **98** | F3 16,F6 10,F8 7 | OpenCode Go 再次进入同一高分档 |
|
|
45
|
+
| 持续 DSH wire | 同一首包;后续请求继续 DSH 规范化 | `We need` | **94** | F3 11,F6 10,F8 8 | 指纹保持,关键安全判断没有保持 |
|
|
46
|
+
| Pi context 重放 | 同一首包;随后 Pi 原生,但把完整 system context 作为额外 user message | `We need` | **90** | F3 11,F6 8,F8 6 | 额外 context 增加干扰,迁移退化 |
|
|
47
|
+
| 同流程复现实验 | 同一首包;随后 Pi 原生;不重放 context | `We need` | **96** | F3 16,F6 10,F8 5 | 高分区间复现;ESP 静态完成度较低 |
|
|
48
|
+
| 近似 Minimal → Pi 原生 | 旧的近似 Minimal 首包,随后 Pi 原生 | `Let me` | **93** | F3 11,F6 10,F8 7 | 请求表面近似不足以命中同一轨迹 |
|
|
49
|
+
| Pi 原生 `bash/read` | Minimal persona + Pi 原生 `bash/read`,随后完整 Pi | `Let me` | **93** | F3 11,F6 10,F8 7 | 两个工具本身不是触发条件 |
|
|
50
|
+
| Wire Think | 全工具可见;首轮禁止工具后再执行 | `We need` | **94** | F3 16,F6 6,F8 7 | 指纹和 F3 正确仍不足以保证迁移 |
|
|
51
|
+
| Whoami | 0-tool `你是谁`;随后原任务 + 完整 Pi | `We need` | **93** | F3 11,F6 10,F8 7 | 额外身份轮没有提升完整任务结果 |
|
|
52
|
+
|
|
53
|
+
对应 evaluator IDs(按表中顺序):
|
|
54
|
+
|
|
55
|
+
1. `20260816_194641`
|
|
56
|
+
2. `20260816_194701`
|
|
57
|
+
3. `20260817_122813`
|
|
58
|
+
4. `20260817_161004`
|
|
59
|
+
5. `20260816_224400`
|
|
60
|
+
6. `20260816_234202`
|
|
61
|
+
7. `20260817_002358`
|
|
62
|
+
8. `20260817_045709`
|
|
63
|
+
9. `20260817_064341`
|
|
64
|
+
10. `20260817_071127`
|
|
65
|
+
11. `20260817_074443`
|
|
66
|
+
|
|
67
|
+
## 98 分原型与 DSH Minimal 99
|
|
68
|
+
|
|
69
|
+
- 两者的 hidden 都是 44/45、F3 都是 16/16,只错相同的 1 分 F12 原因字符串:实现返回
|
|
70
|
+
`not_authenticated`,grader 要求 `not_authorized_for_target`。
|
|
71
|
+
- 原型的 F8 唯一失败是 `V4-F8-08`。实现中的
|
|
72
|
+
`device_config_is_network_ready()` 已检查 `wifi_ssid`、`wifi_password`、
|
|
73
|
+
`bemfa_uid`、`room` 和 `bed`;grader 只接受另外三个函数名。
|
|
74
|
+
- 轨迹约在第 107 个 assistant response 推翻早期 ambient fallback,改成敏感 context
|
|
75
|
+
必须显式携带 `session_id`,voice 路径先取 current session 再传回。这个自纠解释了
|
|
76
|
+
F3 16/16。
|
|
77
|
+
|
|
78
|
+
## 当前实现的 OpenCode Go 运行
|
|
79
|
+
|
|
80
|
+
- evaluator `20260817_161004` 为 98:hidden 44/45、F3 16、F6 10、F8 7。
|
|
81
|
+
- 初始 `system,user` 首包与历史 capture 逐字节相等,但在生成 token 前被账户 opt-in 的
|
|
82
|
+
403 拒绝;同一 session 续跑后的成功 bootstrap 为 `system,user,user`。
|
|
83
|
+
- 首个成功工具调用后恢复 `read/bash/edit/write/str_replace_editor` 与 Go provider 原生
|
|
84
|
+
envelope。
|
|
85
|
+
- 同一 session 最终自然 `stop`:160 个成功 assistant responses、193 次工具调用、
|
|
86
|
+
156 个 reasoning blocks,`Let me=0`。
|
|
87
|
+
|
|
88
|
+
这次结果证明当前实现通过 OpenCode Go 得到 98。由于成功 bootstrap 发生在续跑后,不能
|
|
89
|
+
用它单独证明字节完全相同的首次成功请求导致了该分数。
|
|
90
|
+
|
|
91
|
+
## 轨迹、工具与 stopReason
|
|
92
|
+
|
|
93
|
+
统计由只读脚本 [`scripts/session-stats.mjs`](../scripts/session-stats.mjs) 从每个运行最终
|
|
94
|
+
保存的 Pi session JSONL 生成。恢复运行的 session 文件已经包含完整逻辑状态,因此没有
|
|
95
|
+
再叠加 `pi-resume-*` 或 `pi-audit-*` event log,避免重复计数。
|
|
96
|
+
|
|
97
|
+
“成功 assistant”只计 `stopReason=toolUse` 或 `stop` 的 assistant message;reasoning
|
|
98
|
+
blocks 只计 assistant content 中的 `thinking` block。`We need` 与 `Let me` 在 thinking
|
|
99
|
+
文本中按大小写精确匹配,同时保留不区分大小写的对照。
|
|
100
|
+
|
|
101
|
+
| 运行 | 分数 | 成功 assistant | Reasoning blocks | 精确 `We need` | 精确 `Let me` | 不区分大小写 `we need / let me` |
|
|
102
|
+
| --- | ---: | ---: | ---: | ---: | ---: | --- |
|
|
103
|
+
| 默认 Pi baseline | 92 | 113 | 78 | 0 | 115 | 2 / 156 |
|
|
104
|
+
| 早期 Minimal 模拟原型 | 98 | 149 | 135 | 8 | 1 | 10 / 1 |
|
|
105
|
+
| 首次 package 化实现 | 96 | 147 | 147 | 11 | 0 | 11 / 0 |
|
|
106
|
+
| 当前独立实现 `0.1.0` | 98 | 160 | 156 | 7 | 0 | 7 / 0 |
|
|
107
|
+
| 持续 DSH wire | 94 | 196 | 190 | 6 | 2 | 7 / 2 |
|
|
108
|
+
| Pi context 重放 | 90 | 189 | 184 | 6 | 0 | 8 / 0 |
|
|
109
|
+
| 同流程复现实验 | 96 | 160 | 155 | 10 | 0 | 12 / 0 |
|
|
110
|
+
| 近似 Minimal → Pi 原生 | 93 | 159 | 156 | 11 | 10 | 13 / 11 |
|
|
111
|
+
| Pi 原生 `bash/read` | 93 | 184 | 182 | 7 | 12 | 8 / 13 |
|
|
112
|
+
| Wire Think | 94 | 123 | 118 | 10 | 5 | 13 / 5 |
|
|
113
|
+
| Whoami | 93 | 169 | 164 | 7 | 1 | 7 / 1 |
|
|
114
|
+
|
|
115
|
+
早期账本中的 baseline `Let me=156` 使用了不区分大小写统计;精确大小写计数为 115。
|
|
116
|
+
后续引用应标明统计口径。
|
|
117
|
+
|
|
118
|
+
| 运行 | Tool calls | 工具种类与次数 | stopReason 分布 |
|
|
119
|
+
| --- | ---: | --- | --- |
|
|
120
|
+
| 默认 Pi baseline | 157 | `bash` 79,`edit` 32,`read` 30,`write` 16 | `toolUse` 112,`stop` 1,`error` 1 |
|
|
121
|
+
| 早期 Minimal 模拟原型 | 148 | `bash` 83,`edit` 49,`write` 16 | `toolUse` 148,`stop` 1 |
|
|
122
|
+
| 首次 package 化实现 | 189 | `bash` 82,`read` 48,`edit` 42,`write` 17 | `toolUse` 146,`stop` 1,`error` 2 |
|
|
123
|
+
| 当前独立实现 `0.1.0` | 193 | `bash` 98,`read` 49,`edit` 31,`write` 15 | `toolUse` 159,`stop` 1,`error` 1 |
|
|
124
|
+
| 持续 DSH wire | 225 | `bash` 94,`edit` 65,`read` 53,`write` 13 | `toolUse` 195,`stop` 1 |
|
|
125
|
+
| Pi context 重放 | 220 | `bash` 76,`edit` 66,`read` 63,`write` 15 | `toolUse` 188,`stop` 1 |
|
|
126
|
+
| 同流程复现实验 | 176 | `bash` 176 | `toolUse` 159,`stop` 1,`error` 1 |
|
|
127
|
+
| 近似 Minimal → Pi 原生 | 201 | `bash` 93,`read` 46,`edit` 45,`write` 17 | `toolUse` 158,`stop` 1,`error` 1 |
|
|
128
|
+
| Pi 原生 `bash/read` | 204 | `bash` 120,`edit` 48,`read` 19,`write` 17 | `toolUse` 183,`stop` 1 |
|
|
129
|
+
| Wire Think | 134 | `bash` 81,`edit` 37,`write` 16 | `toolUse` 121,`stop` 2 |
|
|
130
|
+
| Whoami | 195 | `read` 67,`bash` 59,`edit` 55,`write` 14 | `toolUse` 167,`stop` 2,`error` 1 |
|
|
131
|
+
|
|
132
|
+
这两张表带来三个直接观察:
|
|
133
|
+
|
|
134
|
+
- 工具调用更多不代表分数更高。持续 DSH wire 调用了 225 次工具,仍只有 94;早期原型
|
|
135
|
+
只调用 148 次工具,得到 98。
|
|
136
|
+
- 高分轨迹能够使用 Pi 原生工具。当前实现调用 `bash/read/edit/write` 共 193 次并得到
|
|
137
|
+
98;同流程复现实验甚至只调用 `bash`,仍得到 96。
|
|
138
|
+
- 完整 Project2 运行没有调用 `str_replace_editor`,因为模型在首包选择了 `bash`。
|
|
139
|
+
Editor-first 离线回环单独验证了该工具可以真实执行。Project2 也没有加载额外 Pi
|
|
140
|
+
package;插件工具在恢复后的可见性由离线组合测试验证。
|
|
141
|
+
|
|
142
|
+
所有最终 session 的 stopReason 分布都包含 `stop`。Wire Think 与 Whoami 各含两个
|
|
143
|
+
`stop` message,分别对应额外思考/身份轮与后续任务。`error` 记录保留了运行中的 provider
|
|
144
|
+
错误或续跑边界,不计入成功 assistant 数量。
|
|
145
|
+
|
|
146
|
+
## 实现选择
|
|
147
|
+
|
|
148
|
+
1. **真实任务直接进入首请求。** 早期原型得到 98;Whoami 只有 93。
|
|
149
|
+
2. **首请求使用 DSH Minimal persona 与 `bash/str_replace_editor` schema。** Pi 原生
|
|
150
|
+
`bash/read` 只有 93。
|
|
151
|
+
3. **第一次有效响应后恢复 Pi 原生执行。** 持续 DSH wire 只有 94,并会妨碍 Pi 原生
|
|
152
|
+
cache、消息编码和插件工具。
|
|
153
|
+
4. **保持 Minimal persona,不重放完整 Pi system context。** Context 重放实验只有 90。
|
|
154
|
+
5. **首包以实际 98 分 capture 为基准。** 近似 Minimal 的运行回到 `Let me` 并得到 93。
|
|
155
|
+
|
|
156
|
+
## 不纳入主表的工件
|
|
157
|
+
|
|
158
|
+
- `20260816_1845_project2_v4pro_pair01` 没有完整 `run.json`。
|
|
159
|
+
- evaluator `20260816_224346` 误把 `workspace/` 当 project root;正确评分为同目录的
|
|
160
|
+
`20260816_224400`。
|
|
161
|
+
- 同流程复现与 Whoami 的较低分是续跑前中间交卷。
|
|
162
|
+
- 近似 Minimal → Pi 原生的两个 93 是同一运行的重复评分。
|
|
163
|
+
- `20260817_131349_opencode_zen_pi_dsh_anchor_0_3_0_project2` 是错误 Zen route 的零
|
|
164
|
+
token、零费用配置失败。名称保留为实验归档定位,不代表公开 package 版本。
|
|
165
|
+
|
|
166
|
+
## 离线实现证据
|
|
167
|
+
|
|
168
|
+
当前 package 不接管 active catalog,也不覆盖 Pi 的 `bash/read/edit/write`。DeepSeek
|
|
169
|
+
官方与 OpenCode Go 两条 provider 路径只在目标新 session 中注册可执行
|
|
170
|
+
`str_replace_editor`:
|
|
171
|
+
|
|
172
|
+
- 请求 #1:`system,user`、原始任务、Minimal persona、`bash/str_replace_editor`,无 Pi
|
|
173
|
+
cache 字段;
|
|
174
|
+
- 请求 #2:恢复 Pi 原生 messages、provider envelope 和
|
|
175
|
+
`read/bash/edit/write/str_replace_editor`;
|
|
176
|
+
- TypeScript typecheck 通过,12 项 package 行为/manifest 测试与 1 项 session 统计工具测试
|
|
177
|
+
全部通过;
|
|
178
|
+
- OpenCode Go bash-first 与 editor-first 离线回环成功,`realModelCalls=0`;
|
|
179
|
+
- editor 的实际文件读取结果进入请求 #2;
|
|
180
|
+
- 归一 user task 后,新请求 #1 与历史 98 分 capture 在字段顺序、消息、schema、
|
|
181
|
+
`strict:false` 和序列化结构上逐项相等。
|
|
182
|
+
|
|
183
|
+
这些检查验证请求机制。完整模型运行提供能力分。
|
|
184
|
+
|
|
185
|
+
## 外部校准与来源关系
|
|
186
|
+
|
|
187
|
+
### DeepSeek Harness 与 Project2
|
|
188
|
+
|
|
189
|
+
当前实现使用 DeepSeek Harness 的公开 Minimal persona、工具说明和 function schemas。
|
|
190
|
+
精确 commit 与 MIT 声明见 package 的 [NOTICE](../NOTICE)。
|
|
191
|
+
|
|
192
|
+
Project2 benchmark、外部分析和 DSH 结果来自:
|
|
193
|
+
|
|
194
|
+
- [`xiaobright/modeltest` V4 Pro harness 分析](https://github.com/xiaobright/modeltest/blob/main/docs/v4.1/DEEPSEEK_V4_PRO_HARNESS_ANALYSIS_20260814.md)
|
|
195
|
+
- [Minimal 99](https://github.com/xiaobright/modeltest/blob/main/evaluator/results/20260813_230337/summary.json)
|
|
196
|
+
- [Standard 91](https://github.com/xiaobright/modeltest/blob/main/evaluator/results/20260814_133328/summary.json)
|
|
197
|
+
- [PTC 92](https://github.com/xiaobright/modeltest/blob/main/evaluator/results/20260814_140756/summary.json)
|
|
198
|
+
- [`dsh-anchored-standard` Issue #60](https://github.com/xiaobright/dsh-anchored-standard/issues/60)
|
|
199
|
+
|
|
200
|
+
两次 Anchored Standard 98/99 实际来自 Windows `pwsh/read → 25 tools`。它们证明首包后
|
|
201
|
+
恢复完整工具可行,不给后来 `bash/str_replace_editor` 实现继承分数。
|
|
202
|
+
|
|
203
|
+
### 早期外部 Pi extension
|
|
204
|
+
|
|
205
|
+
早期探索中有一次 98 分运行加载了
|
|
206
|
+
[`kxh4892636/pi-deepseek-anchor`](https://github.com/kxh4892636/pi-deepseek-anchor)。
|
|
207
|
+
这只记录该次运行使用了什么 extension;当前源码没有参考或移植该仓库的源码。
|
|
208
|
+
|
|
209
|
+
2026-08-17 核对其 HEAD `d369f9664d2c710c259b9a186e69ce4c76e1bf5e` 时,仓库共有
|
|
210
|
+
8 个文件,没有 test/spec 路径或自动化测试文件,也没有公开的 Pi Project2 grader/build
|
|
211
|
+
工件。因此本文不使用该仓库自己的分数主张作为证据。表中的 98 来自本项目保存的运行与
|
|
212
|
+
evaluator。
|
|
213
|
+
|
|
214
|
+
## 当前决定
|
|
215
|
+
|
|
216
|
+
- 产品名为 `pi-dsh-mimic`,首个公开版本为 `0.1.0`。
|
|
217
|
+
- 默认流程固定为 task-bearing one-shot:请求 #1 模拟 DSH Minimal,请求 #2 恢复 Pi。
|
|
218
|
+
- 同时支持 Pi 的 `deepseek` 与 `opencode-go` provider。
|
|
219
|
+
- Minimal persona 保持;完整 Pi system context 不重放。
|
|
220
|
+
- Project2 同一请求流程已有四次 96–98,不继续重复付费运行。
|
|
221
|
+
- 新任务、模型或服务版本变化、provider payload 实质变化,或新的完整任务结果可以重开
|
|
222
|
+
实验。
|
|
223
|
+
|
|
224
|
+
## 工件可用性
|
|
225
|
+
|
|
226
|
+
完整 trajectory、provider capture、grader 输出和构建树保留在维护者实验归档中,没有
|
|
227
|
+
作为仓库的一部分公开。本文提供 evaluator IDs、结果、排除规则和外部来源。
|
|
228
|
+
|
|
229
|
+
若以后公开 capture 或运行摘要,应先移除任务中的私有材料、凭据、请求头和无关源码,
|
|
230
|
+
并保留足以对应本文运行与 evaluator 的标识。
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-dsh-mimic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Unlock DeepSeek V4 Pro's DSH Minimal capability in Pi while keeping Pi's full plugin ecosystem",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi",
|
|
10
|
+
"pi-package",
|
|
11
|
+
"pi-extension",
|
|
12
|
+
"deepseek",
|
|
13
|
+
"deepseek-v4-pro",
|
|
14
|
+
"dsh",
|
|
15
|
+
"dsh-minimal",
|
|
16
|
+
"mimic",
|
|
17
|
+
"pi-plugins"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Utopia-V/mixagents.git",
|
|
22
|
+
"directory": "packages/pi-dsh-mimic"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/Utopia-V/mixagents/tree/main/packages/pi-dsh-mimic#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Utopia-V/mixagents/issues"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"src",
|
|
30
|
+
"scripts/session-stats.mjs",
|
|
31
|
+
"docs/advanced.md",
|
|
32
|
+
"docs/advanced.zh-CN.md",
|
|
33
|
+
"docs/project2-evidence.md",
|
|
34
|
+
"README.md",
|
|
35
|
+
"README.zh-CN.md",
|
|
36
|
+
"SECURITY.md",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"NOTICE"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"pi": {
|
|
44
|
+
"extensions": [
|
|
45
|
+
"./src/index.ts"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=22.19.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@earendil-works/pi-ai": "*",
|
|
53
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
54
|
+
"typebox": "*"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@earendil-works/pi-ai": "0.84.2",
|
|
58
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
59
|
+
"@types/node": "24.12.4",
|
|
60
|
+
"tsx": "4.20.6",
|
|
61
|
+
"typebox": "1.3.7",
|
|
62
|
+
"typescript": "5.9.3"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"test": "tsx --test test/*.test.ts test/*.test.mjs",
|
|
66
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
67
|
+
"check": "npm run typecheck && npm test",
|
|
68
|
+
"pack:check": "npm pack --dry-run"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { basename, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function countOccurrences(source, needle) {
|
|
8
|
+
let count = 0;
|
|
9
|
+
let offset = 0;
|
|
10
|
+
while (true) {
|
|
11
|
+
const index = source.indexOf(needle, offset);
|
|
12
|
+
if (index === -1) return count;
|
|
13
|
+
count += 1;
|
|
14
|
+
offset = index + needle.length;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function increment(counter, key) {
|
|
19
|
+
counter.set(key, (counter.get(key) ?? 0) + 1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function orderedCounts(counter) {
|
|
23
|
+
return Object.fromEntries(
|
|
24
|
+
[...counter.entries()].sort(([leftName, leftCount], [rightName, rightCount]) =>
|
|
25
|
+
rightCount - leftCount || leftName.localeCompare(rightName)),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function summarizeSession(serialized, label = "session.jsonl") {
|
|
30
|
+
const stopReasons = new Map();
|
|
31
|
+
const tools = new Map();
|
|
32
|
+
let assistantMessages = 0;
|
|
33
|
+
let successfulAssistantResponses = 0;
|
|
34
|
+
let reasoningBlocks = 0;
|
|
35
|
+
let exactWeNeed = 0;
|
|
36
|
+
let exactLetMe = 0;
|
|
37
|
+
let normalizedWeNeed = 0;
|
|
38
|
+
let normalizedLetMe = 0;
|
|
39
|
+
let toolCalls = 0;
|
|
40
|
+
let lastSerializedAssistantStopReason = null;
|
|
41
|
+
|
|
42
|
+
const lines = serialized.split(/\r?\n/);
|
|
43
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
44
|
+
const line = lines[index].trim();
|
|
45
|
+
if (line === "") continue;
|
|
46
|
+
|
|
47
|
+
let entry;
|
|
48
|
+
try {
|
|
49
|
+
entry = JSON.parse(line);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(`${label}:${index + 1}: invalid JSON: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (entry?.type !== "message" || entry.message?.role !== "assistant") continue;
|
|
55
|
+
const message = entry.message;
|
|
56
|
+
assistantMessages += 1;
|
|
57
|
+
const stopReason = typeof message.stopReason === "string" ? message.stopReason : "unknown";
|
|
58
|
+
increment(stopReasons, stopReason);
|
|
59
|
+
lastSerializedAssistantStopReason = stopReason;
|
|
60
|
+
if (stopReason === "toolUse" || stopReason === "stop") {
|
|
61
|
+
successfulAssistantResponses += 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const block of Array.isArray(message.content) ? message.content : []) {
|
|
65
|
+
if (block?.type === "thinking") {
|
|
66
|
+
reasoningBlocks += 1;
|
|
67
|
+
const reasoning = typeof block.thinking === "string"
|
|
68
|
+
? block.thinking
|
|
69
|
+
: typeof block.text === "string"
|
|
70
|
+
? block.text
|
|
71
|
+
: "";
|
|
72
|
+
exactWeNeed += countOccurrences(reasoning, "We need");
|
|
73
|
+
exactLetMe += countOccurrences(reasoning, "Let me");
|
|
74
|
+
const normalized = reasoning.toLowerCase();
|
|
75
|
+
normalizedWeNeed += countOccurrences(normalized, "we need");
|
|
76
|
+
normalizedLetMe += countOccurrences(normalized, "let me");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (block?.type === "toolCall") {
|
|
80
|
+
toolCalls += 1;
|
|
81
|
+
increment(tools, typeof block.name === "string" ? block.name : "unknown");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
file: label,
|
|
88
|
+
assistantMessages,
|
|
89
|
+
successfulAssistantResponses,
|
|
90
|
+
reasoningBlocks,
|
|
91
|
+
phrases: {
|
|
92
|
+
exact: { "We need": exactWeNeed, "Let me": exactLetMe },
|
|
93
|
+
normalized: { "we need": normalizedWeNeed, "let me": normalizedLetMe },
|
|
94
|
+
},
|
|
95
|
+
toolCalls,
|
|
96
|
+
tools: orderedCounts(tools),
|
|
97
|
+
stopReasons: orderedCounts(stopReasons),
|
|
98
|
+
lastSerializedAssistantStopReason,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function main(paths) {
|
|
103
|
+
if (paths.length === 0) {
|
|
104
|
+
process.stderr.write("Usage: node scripts/session-stats.mjs <pi-session.jsonl> [...]\n");
|
|
105
|
+
process.exitCode = 2;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const summaries = paths.map((path) =>
|
|
110
|
+
summarizeSession(readFileSync(path, "utf8"), basename(path)));
|
|
111
|
+
process.stdout.write(`${JSON.stringify(summaries, null, 2)}\n`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const invokedPath = process.argv[1] === undefined ? "" : resolve(process.argv[1]);
|
|
115
|
+
if (invokedPath === fileURLToPath(import.meta.url)) {
|
|
116
|
+
main(process.argv.slice(2));
|
|
117
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const TARGET_MODEL_ID = "deepseek-v4-pro";
|
|
2
|
+
|
|
3
|
+
export const TARGET_PROVIDERS = ["deepseek", "opencode-go"] as const;
|
|
4
|
+
|
|
5
|
+
export const MINIMAL_PERSONA = "You are a helpful software engineer assistant.";
|
|
6
|
+
|
|
7
|
+
export const STATE_ENTRY_TYPE = "pi-dsh-mimic/session-stage";
|
|
8
|
+
|
|
9
|
+
export const BOOTSTRAP_TOOL_NAMES = ["bash", "str_replace_editor"] as const;
|
|
10
|
+
|
|
11
|
+
// These descriptions are part of DeepSeek Harness's public Minimal protocol.
|
|
12
|
+
export const MINIMAL_BASH_DESCRIPTION = `Run commands in a bash shell
|
|
13
|
+
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
|
14
|
+
* You don't have access to the internet via this tool.
|
|
15
|
+
* You do have access to a mirror of common linux and python packages via apt and pip.
|
|
16
|
+
* State is persistent across command calls and discussions with the user.
|
|
17
|
+
* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
|
|
18
|
+
* Please avoid commands that may produce a very large amount of output.
|
|
19
|
+
* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.`;
|
|
20
|
+
|
|
21
|
+
export const EDITOR_DESCRIPTION = `Custom editing tool for viewing, creating and editing files
|
|
22
|
+
* State is persistent across command calls and discussions with the user
|
|
23
|
+
* If \`path\` is a file, \`view\` displays the result of applying \`cat -n\`. If \`path\` is a directory, \`view\` lists non-hidden files and directories up to 2 levels deep
|
|
24
|
+
* The \`create\` command cannot be used if the specified \`path\` already exists as a file
|
|
25
|
+
* If a \`command\` generates a long output, it will be truncated and marked with \`<response clipped>\`
|
|
26
|
+
|
|
27
|
+
Notes for using the \`str_replace\` command:
|
|
28
|
+
* The \`old_str\` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
|
|
29
|
+
* If the \`old_str\` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in \`old_str\` to make it unique
|
|
30
|
+
* The \`new_str\` parameter should contain the edited lines that should replace the \`old_str\``;
|
|
31
|
+
|
|
32
|
+
export const EDITOR_TRUNCATED_MESSAGE =
|
|
33
|
+
"<response clipped><NOTE>Search the file for the relevant line numbers, then call view again with a narrower range.</NOTE>";
|