dsh-stata
在 DSH(DeepSeek Harness) 里直接控制 Stata:一个常驻的 Stata 会话,执行命令、运行 .do 文件、读回真实控制台输出,数据与估计结果在多次调用之间保留。
English documentation: README.en.md
为什么不用 PyStata / COM
Stata 官方自动化入口在不少环境下不可用:
| 方案 | 前提 | 实测问题 |
|---|
PyStata / StataSO_Main | 有效许可、无头初始化 | 许可过期即 SystemError: failed to initialize Stata |
批处理 /e do file.do | 有效许可 | 进程直接退出,不产生 .log |
| OLE Automation(COM) | StataOLEApp 写入 HKLM | Stata 17 默认不注册;每用户 HKCU 注册在部分环境下对 ole32 不可见,需管理员权限 |
| dsh-stata | 只要能打开 Stata GUI | ✅ 纯文件队列,不需要 COM、不需要管理员、不需要批处理模式 |
dsh-stata 不绕过 Stata 的许可校验,它只是在 可见的 GUI 窗口 里跑一个轮询 do 文件,因此只要你能手动在 Stata 里执行命令,它就能工作。
功能
| 工具 | 用途 |
|---|
stata_run | 在常驻会话里执行 Stata 命令(可多行,支持循环 / program define / 注释),返回控制台输出 |
stata_do | 直接运行磁盘上的 .do 文件(绝对路径或相对工作目录) |
stata_status | 查看 Stata 可执行文件、会话目录、进程号、就绪状态与交换文件 |
stata_reset | 停止后台命令循环、恢复 Stata 窗口交互;卡死或超时后用它恢复 |
同时注册技能 stata:会话模型、常用工作流与排错指引,让 agent 知道“一个会话就是一个数据集”的正确用法。
- 会话跨调用保留:当前数据集、宏、
program define、estimation 结果、return list/r() 之外的 e() 都还在。
- 输出是真实
Results 窗口内容:命令回显 + 结果 + 错误码,例如 r(111);。
- Stata 报错不会终止会话,下一条命令照常执行。
- 单条命令串行执行,带超时与互斥锁;超时可用
stata_reset 恢复。
环境要求
- Windows(当前版本显式支持;其他平台的 PR 欢迎)。
- 已安装 Stata GUI(Stata 14 及以上,MP / SE / BE 均可)。
- DSH,且 profile 具备插件管理能力(Web 侧栏 插件 页,或 Creator 模式下的
plugin_manager 工具)。
- Node.js
^22.19.0 || >=24.0.0(DSH 自带的运行时即可)。
安装
方式一:DSH 插件页(推荐)
- 侧栏打开 插件 / Plugins。
- 点 添加插件 / Add plugin。
- 填入其一:
- Git 地址:
https://github.com/SoldierNo4/dsh-stata
- 本地绝对路径:解压/克隆后包含
package.json 的目录
- 点 安装,随后 启用。
启用了 HMR 的 profile 会立即重组,无需重启。
方式二:Creator 模式工具
{ "action": "install_bundle", "target": "https://github.com/SoldierNo4/dsh-stata" }
方式三:手动加入 profile
在 profile 的 package.json 依赖里加入本包(link: 本地目录或 git 地址),并把 dsh-stata 追加到 dsh.profile.bundles,然后重启 DSH。插件自身的 cordis.patch.yml 会注册工具与技能,无需再改 profile 的 patch 文件。
快速开始
直接对 DSH 说:
用 Stata 载入 auto 数据,跑一下 price 对 mpg、weight 的回归
对应的工具调用与输出:
stata_run: sysuse auto, clear
regress price mpg weight
. sysuse auto, clear
(1978 automobile data)
. regress price mpg weight
Source | SS df MS Number of obs = 74
-------------+---------------------------------- F(2, 71) = 14.74
Model | 186321280 2 93160639.9 Prob > F = 0.0000
Residual | 448744116 71 6320339.67 R-squared = 0.2934
-------------+---------------------------------- Adj R-squared = 0.2735
Total | 635065396 73 8699525.97 Root MSE = 2514
------------------------------------------------------------------------------
price | Coefficient Std. err. t P>|t| [95% conf. interval]
-------------+----------------------------------------------------------------
mpg | -49.51222 86.15604 -0.57 0.567 -221.3025 122.278
weight | 1.746559 .6413538 2.72 0.008 .467736 3.025382
_cons | 1946.069 3597.05 0.54 0.590 -5226.245 9118.382
------------------------------------------------------------------------------
下一次调用直接接着用内存里的数据:
stata_run: display _N " obs in memory"
→ 74 obs in memory
跑已有脚本:
stata_do: analysis/table1.do
工具参考
stata_run
| 参数 | 类型 | 必填 | 说明 |
|---|
code | string | ✅ | Stata 命令,多行或分号分隔,语法与在 Stata 里键入完全一致 |
timeoutSeconds | integer | ❌ | 最长等待秒数,默认 180,上限 3600 |
返回 { ok, output, durationMs, error? };output 为清理掉日志头尾后的控制台文本。
stata_do
| 参数 | 类型 | 必填 | 说明 |
|---|
path | string | ✅ | .do 文件路径,绝对或相对当前工作目录 |
timeoutSeconds | integer | ❌ | 同上 |
stata_status
无参数。返回会话目录、可执行文件、进程号、processAlive / ready / idle / lockHeld 以及交换文件列表。
stata_reset
| 参数 | 类型 | 必填 | 说明 |
|---|
force | boolean | ❌ | 无法优雅退出命令循环时,直接结束 Stata 进程 |
优雅路径会让 Stata 回到可交互提示符(数据保留);force 路径会丢弃内存中的数据。下一次 stata_run 自动开启新会话。
配置(可选)
默认零配置即可工作。需要覆盖时,在 profile 的 patch 层里给该行加 config:
- id: dsh-stata
config:
stataPath: C:\Program Files\Stata17\StataMP-64.exe
sessionDir: E:\stata-session
defaultTimeoutMs: 180000
| 键 | 默认 | 说明 |
|---|
stataPath | 自动探测 | Stata 可执行文件。探测顺序:本项 → 环境变量 DSH_STATA_EXE / STATA_EXE → %ProgramFiles%\Stata* 下最新的 StataMP-64.exe、StataSE-64.exe、StataBE-64.exe 等 |
sessionDir | %LOCALAPPDATA%\dsh-stata | 命令与日志的交换目录;不可写时回退到系统临时目录 |
defaultTimeoutMs | 180000 | 单条命令默认超时,上限 3600000 |
工作原理
stata_run(code)
│ 1. 写入 <sessionDir>/cmd.do(前面加一行 log using response.log)
│ 2. 删除 <sessionDir>/done.txt
▼
<sessionDir>/cmd.do ──轮询 60ms──► Stata GUI 里运行的 daemon.do
▲ │ capture noisily do cmd.do
│ │ 关闭日志、写 done.txt
│ 3. 轮询到 done.txt ◄─────────────────┘
└─ 4. 读取 response.log → 去掉日志头尾 → 作为工具结果返回
daemon.do 以 quietly { while (1) { ... quietly sleep 100 } } 常驻轮询,检测到 stop.txt 就退出循环并写 idle.txt(这就是 stata_reset 的优雅路径)。
- 会话目录里的
.lock 保证同一会话串行执行;state.json 记录 pid 与启动时间,用于判断会话是否仍然存活。
- 实测延迟(Stata 17 MP,Windows 11):冷启动首条命令 1.5–3.0 秒,之后 0.2–1.1 秒。
排错
| 现象 | 处理 |
|---|
stata_run 超时 | 命令可能仍在跑;stata_status 看 processAlive,用 stata_reset 恢复(长任务在调用时调大 timeoutSeconds) |
| Stata 窗口被手动关闭 | 下一次 stata_run 会发现会话失效并重新启动一个 |
报错 Stata was not found | 设置 stataPath 配置项或 DSH_STATA_EXE 环境变量 |
| 会话目录不可写 | 用 sessionDir 指到一个可写目录(沙箱环境里 %LOCALAPPDATA% 可能被限制) |
| 想彻底重置 | stata_reset(force: true)后重新调用 stata_run |
限制与注意事项
- Windows only:可执行文件探测与路径处理按 Windows 写死,其他平台尚未支持。
- 串行:一个会话同一时刻只跑一个命令块;需要并行请开多个 Stata 实例(不同
sessionDir)。
- 不要在命令里执行
exit / quit,那会关闭 Stata 并中断会话。
- 不要在会话进行中手工编辑会话目录里的
daemon.do / cmd.do。
- 图形导出请用
graph export out.png, replace;直接 graph 会在 Stata 窗口弹窗。
- 插件会在本机启动 Stata 进程并按你的命令读写文件,请只运行你信任的脚本。
开发
# 无需安装 Stata 即可运行的单元测试(注册、schema、配置解析)
npm test
# 需要真实 Stata 的端到端验证:在 DSH 里安装本目录后依次调用
# stata_run "display 2 + 2"
# stata_run "sysuse auto, clear"
# stata_run "summarize mpg"
# stata_do <任意 .do 文件>
# stata_reset
目录结构:
stata-plugin/
├── cordis.patch.yml # 注册一行:id dsh-stata
├── lib/
│ ├── index.js # 插件入口:注册 tools + skill,处理 dispose
│ ├── session.js # StataSession:目录/进程/锁/日志/优雅收尾
│ ├── tools.js # 4 个工具的 schema 与执行逻辑
│ └── skill.js # 加载 skills/stata/SKILL.md 并注册技能
├── locale/{en,zh}.json # 插件页显示名与描述
├── skills/stata/SKILL.md # 技能正文
├── test/ # node:test 单元测试
└── icon.svg
贡献
欢迎 Issue 与 PR。提交前请跑 npm test,并尽量附上当前环境的 Stata 版本、DSH 版本与复现步骤。不要提交 node_modules/ 或会话交换文件。
许可证
MIT © 2026 SoldierNo4
本项目与 StataCorp 无关,未包含 Stata 的任何代码;使用需自备合法 Stata 许可。