reasonix 0.10.0 → 0.11.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.
@@ -0,0 +1,721 @@
1
+ <p align="center">
2
+ <img src="docs/logo.svg" alt="Reasonix — DeepSeek 原生的 agent 框架" width="640"/>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <em>为 DeepSeek V4(flash + pro)打造的缓存优先 agent 循环 — Ink TUI、原生 MCP、不依赖 LangChain。</em>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="./README.md">English</a> · <strong>简体中文</strong> · <a href="https://esengine.github.io/reasonix/">官方网站</a>
11
+ </p>
12
+
13
+ # Reasonix
14
+
15
+ [![npm version](https://img.shields.io/npm/v/reasonix.svg)](https://www.npmjs.com/package/reasonix)
16
+ [![CI](https://github.com/esengine/reasonix/actions/workflows/ci.yml/badge.svg)](https://github.com/esengine/reasonix/actions/workflows/ci.yml)
17
+ [![license](https://img.shields.io/npm/l/reasonix.svg)](./LICENSE)
18
+ [![downloads](https://img.shields.io/npm/dm/reasonix.svg)](https://www.npmjs.com/package/reasonix)
19
+ [![node](https://img.shields.io/node/v/reasonix.svg)](./package.json)
20
+
21
+ **DeepSeek 原生的终端 AI 编程代理。** 编辑以可审查的 SEARCH/REPLACE 块呈现,
22
+ 落盘前必须确认。Ink TUI、原生 MCP、不依赖 LangChain。
23
+
24
+ ---
25
+
26
+ ## 60 秒快速上手
27
+
28
+ **1. 获取 DeepSeek API Key。** 注册即送免费额度:
29
+ <https://platform.deepseek.com/api_keys>
30
+
31
+ **2. 切到项目目录运行。** 无需安装。
32
+
33
+ ```bash
34
+ cd my-project
35
+ npx reasonix code
36
+ ```
37
+
38
+ 首次运行会走 30 秒向导(粘贴 API key → 选预设 → 多选 MCP 服务器)。
39
+ 之后每次直接进入。
40
+
41
+ **3. 让它改代码。** 模型会以 SEARCH/REPLACE 块的形式提出编辑——
42
+ 你不 `/apply`,磁盘不会被改。
43
+
44
+ ```
45
+ reasonix code › users.ts 里 findByEmail 对大小写敏感导致登录失败,帮我改
46
+
47
+ assistant
48
+ ▸ tool<search_files> → src/users.ts, src/users.test.ts
49
+ ▸ tool<read_file> → (src/users.ts, 412 chars)
50
+ ▸ 找到了。findByEmail 直接用 === 比对。改成小写规范化并补一条测试。
51
+
52
+ src/users.ts
53
+ <<<<<<< SEARCH
54
+ return users.find(u => u.email === email);
55
+ =======
56
+ const needle = email.toLowerCase();
57
+ return users.find(u => u.email.toLowerCase() === needle);
58
+ >>>>>>> REPLACE
59
+
60
+ ▸ 1 处待应用编辑(1 个文件)— /apply 写入 · /discard 丢弃
61
+
62
+ reasonix code › /apply
63
+ ▸ ✓ applied src/users.ts
64
+ ```
65
+
66
+ 要求 Node ≥ 20.10。支持 macOS、Linux、Windows(PowerShell · Git Bash ·
67
+ Windows Terminal)。任何时候按 `Esc` 中断;`/help` 查看完整命令列表。
68
+
69
+ ---
70
+
71
+ ## `reasonix code` — 终端里的结对编程
72
+
73
+ 作用域为启动目录。模型自带 `read_file` / `write_file` / `edit_file` /
74
+ `list_directory` / `search_files` / `directory_tree` / `get_file_info` /
75
+ `create_directory` / `move_file`,全部在沙箱内 —— 任何解析后落到启动根目录之外
76
+ 的路径(包括 `..` 或符号链接逃逸)都会被拒绝。再加上带只读白名单的
77
+ `run_command`;任何会修改状态的命令(`npm install`、`git commit` 等)都要走
78
+ 确认弹窗。
79
+
80
+ ### 流程演示:先看再改
81
+
82
+ 对于"这段代码做什么用?"这类问题,模型会用读取类工具,然后用散文回答 ——
83
+ 不会出 SEARCH/REPLACE 块、也不会写文件。只有你明确要求修改时它才动手:
84
+
85
+ ```
86
+ reasonix code › 这个项目的路由是怎么组织的?
87
+ assistant
88
+ ▸ tool<directory_tree> → (src/ tree, 47 entries)
89
+ ▸ tool<read_file> → (src/router.ts, 1.2 KB)
90
+ ▸ 路由分三层:顶层 AppRouter 注册 tab,每个 tab 用 React Router 的
91
+ nested routes 写子路径,最后 …
92
+ ```
93
+
94
+ `edit_file` 的 SEARCH 块如果没有按字节精确匹配文件内容,编辑会被显式拒绝
95
+ 而不是模糊匹配。模型能看到错误并自行重试 —— 比起"静默改错","显式拒绝"
96
+ 是更安全的失败方式。
97
+
98
+ ### Plan 模式 —— 执行前先审阅
99
+
100
+ 任何比"改个 typo"更大的改动,模型会被引导先提交一份 markdown 计划。
101
+ 你会看到 **批准 / 重做 / 取消** 三选一:
102
+
103
+ ```
104
+ reasonix code › 把 auth 从 JWT 迁移到 session cookies
105
+
106
+ ▸ plan submitted — awaiting your review
107
+ ────────────────────────────────────────
108
+ ## Summary
109
+ Swap JWT middleware for session cookies, keep user table intact.
110
+
111
+ ## Files
112
+ - src/auth/middleware.ts — replace `verifyJwt` with `readSession`
113
+ - src/auth/session.ts — new file, in-memory store + signed cookie
114
+ - src/routes/login.ts — return Set-Cookie instead of a token
115
+ - tests/auth/*.test.ts — update fixtures
116
+
117
+ ## Risks
118
+ - Existing logged-in users get logged out (no migration).
119
+ - Session store is in-memory; restart clears sessions.
120
+ ────────────────────────────────────────
121
+ ▸ Approve and implement
122
+ Refine — explore more
123
+ Cancel
124
+ ```
125
+
126
+ **强制启用** 用 `/plan` —— 进入显式只读阶段:模型必须先提交计划,否则任何
127
+ 编辑或非白名单 shell 调用都不会执行。适合那些你想先审一遍再让模型动手的
128
+ 高风险改动。`/plan off` 或在弹窗里选 Approve/Cancel 退出。
129
+
130
+ ### 输入前缀 —— `!cmd` 与 `@path`
131
+
132
+ 两个不需要斜杠的内联快捷方式:
133
+
134
+ **`!<cmd>` —— 在沙箱里跑 shell 命令并把结果喂给模型。** 像 bash 一样在
135
+ prompt 里直接打。输出既会进入可见日志,也会进入会话 —— 模型下一轮会基于它
136
+ 推理:
137
+
138
+ ```
139
+ reasonix code › !git status --short
140
+ ▸ M src/users.ts
141
+ ▸ M src/users.test.ts
142
+
143
+ reasonix code › 把这两个文件的改动说明一下
144
+ assistant
145
+ ▸ tool<read_file> → src/users.ts, src/users.test.ts
146
+ ▸ …
147
+ ```
148
+
149
+ 无白名单门 —— 用户主动输入的 shell 命令就是显式同意。60 秒超时、32k 字符
150
+ 上限,0.5.14 起会话恢复后依然保留。
151
+
152
+ **`@path/to/file` —— 内联一个文件作为 "Referenced files"。** 输入 `@`
153
+ 弹出选择器(↑/↓ 切换、Tab/Enter 插入)。比让模型 `read_file` 后再问更省事:
154
+ "@src/users.ts 这个文件做什么用?"。沙箱限定:仅相对路径、不允许 `..`、单
155
+ 文件 64KB 上限。最近用过的文件排在前面。
156
+
157
+ ### `/commit` —— 一步暂存 + 提交
158
+
159
+ ```
160
+ reasonix code › /commit "fix: findByEmail case-insensitive"
161
+ ▸ git add -A && git commit -m "fix: findByEmail case-insensitive"
162
+ [main a1b2c3d] fix: findByEmail case-insensitive
163
+ ```
164
+
165
+ ### 一些值得试试的命令
166
+
167
+ - `/tool 1` —— 打印最后一次工具调用的完整输出(当 400 字内联剪辑不够看时)。
168
+ - `/think` —— 看上一轮模型的完整推理(思考模式:v4-flash / v4-pro / reasoner 别名)。
169
+ - `/undo` —— 回滚上一批已应用的编辑。
170
+ - `/new` —— 在同一目录开新会话,但不丢失旧会话文件。
171
+ - `/effort high` —— 从默认 `max` agent 推理强度降一档,简单任务更省更快。
172
+ - `npx reasonix code --preset max` —— v4-pro + 三路 self-consistency 分支,
173
+ 适合疑难重构。
174
+ - `npx reasonix code src/` —— 更窄的沙箱(只有 `src/` 可写)。
175
+ - `npx reasonix code --no-session` —— 临时会话,什么都不存。
176
+
177
+ ### `reasonix stats` —— 你到底省了多少?
178
+
179
+ 每次 `reasonix chat|code|run` 跑完都会往
180
+ `~/.reasonix/usage.jsonl` 追加一条精简记录(token + 成本 + Claude Sonnet
181
+ 4.6 同负载下的等价价格)。`reasonix stats` 不带参数会把日志聚合成今日 /
182
+ 本周 / 本月 / 历史 四个窗口:
183
+
184
+ ```
185
+ Reasonix usage — /Users/you/.reasonix/usage.jsonl
186
+
187
+ turns cache hit cost (USD) vs Claude saved
188
+ ----------------------------------------------------------------------
189
+ today 8 95.1% $0.004821 $0.1348 96.4%
190
+ week 34 93.8% $0.023104 $0.6081 96.2%
191
+ month 127 94.2% $0.081530 $2.1452 96.2%
192
+ all-time 342 94.0% $0.210881 $5.8934 96.4%
193
+ ```
194
+
195
+ 隐私:日志里只有 token、成本和你自己取的 session 名。没有 prompt、没有
196
+ completion、没有工具参数。`reasonix stats <transcript>` 仍兼容老用法
197
+ (按文件出 assistant turn + tool call 摘要)。
198
+
199
+ ### 保持最新
200
+
201
+ 面板顶栏在 `Reasonix` 旁边显示当前版本(如
202
+ `Reasonix v0.5.21 · deepseek-v4-pro · harvest · max …`,最后那个 `max` 是
203
+ 推理强度徽章 —— `/effort high` 可降一档)。后台每 24 小时静默查询一次 npm
204
+ registry,发现新版会在同一行右侧显示黄色 `update: X.Y.Z`。不阻塞、不烦人,
205
+ 每天最多查一次,离线 / 防火墙下静默失败。
206
+
207
+ ```bash
208
+ reasonix update # 打印当前 vs 最新,并执行 `npm i -g reasonix@latest`
209
+ reasonix update --dry-run # 只打印计划,不实际安装
210
+ ```
211
+
212
+ 通过 `npx` 用?命令会识别这种情况并改为打印 cache 刷新提示 —— npx 在下次
213
+ 调用时会自动取最新版本。
214
+
215
+ ### 项目约定 —— `REASONIX.md`
216
+
217
+ 把 `REASONIX.md` 放到项目根目录,每次启动都会被钉进 system prompt。可提交
218
+ 的团队记忆 —— 房间约定、领域词表、模型老忘的事情:
219
+
220
+ ```bash
221
+ cat > REASONIX.md <<'EOF'
222
+ # Notes for Reasonix
223
+ - Use snake_case for new Python modules; legacy camelCase modules keep their style.
224
+ - `cargo check` is in the auto-run allowlist; full `cargo test` needs confirmation.
225
+ - The `api/` dir mirrors `backend/` — keep schemas in sync.
226
+ EOF
227
+ ```
228
+
229
+ 重启(或 `/new`)后生效;前缀每会话只哈希一次,让 DeepSeek 的缓存保持热。
230
+ `/memory` 打印当前已钉的内容。`REASONIX_MEMORY=off` 在 CI / 离线复现时关闭
231
+ 所有记忆来源。
232
+
233
+ ### 用户记忆 —— `~/.reasonix/memory/`
234
+
235
+ 第二层 **私人按用户** 的记忆放在你的 home 目录。不像 `REASONIX.md` 会被
236
+ 提交,模型自己也能通过 `remember` 工具往里写。两个作用域:
237
+
238
+ - `~/.reasonix/memory/global/` —— 跨项目(你的偏好、工具链)。
239
+ - `~/.reasonix/memory/<project-hash>/` —— 限定一个 `reasonix code` 沙箱
240
+ 根目录(决策、本地事实、按仓库的快捷方式)。
241
+
242
+ 每个作用域都维护一个总是加载的 `MEMORY.md` 索引(一行一条)+ 零或多个
243
+ `<name>.md` 详情文件(按需通过 `recall_memory` 加载)。写入立刻生效;钉进
244
+ system prompt 在下一次 `/new` 或重启时生效,以保证当前会话的缓存前缀稳定。
245
+
246
+ ```
247
+ reasonix code › 我用 bun 而不是 npm,请以后都用 bun 跑构建
248
+
249
+ assistant
250
+ ▸ tool<remember> → project/bun_build saved
251
+ "Build command on this machine is `bun run build`"
252
+ ```
253
+
254
+ **斜杠**:`/memory` · `/memory list` · `/memory show <name>` ·
255
+ `/memory forget <name>` · `/memory clear <scope> confirm`。
256
+ **模型工具**:`remember(type, scope, name, description, content)` ·
257
+ `forget(scope, name)` · `recall_memory(scope, name)`。
258
+
259
+ 项目作用域只在 `reasonix code` 里可用(需要真实的沙箱根来计算哈希);纯
260
+ `reasonix` 只有全局作用域。
261
+
262
+ ### Skills —— 用户自定义的 prompt 包
263
+
264
+ Skill 就是你写在磁盘上的散文指令块。Reasonix 把它们的名字 + 一行描述钉
265
+ 进 system prompt;模型可以在合适时机自动调 `run_skill({name: "..."})`,
266
+ 你也可以打 `/skill <name> [args]` 手动触发。
267
+
268
+ 两个作用域,与用户记忆同样的布局:
269
+
270
+ - `<project>/.reasonix/skills/` —— 按项目(提交进 git 给团队,或加入
271
+ `.gitignore` 做个人草稿)。
272
+ - `~/.reasonix/skills/` —— 全局,到处可用。
273
+
274
+ 两种文件结构都行:`<name>/SKILL.md`(推荐 —— 可同时打包附带资源)或扁平
275
+ `<name>.md`。
276
+
277
+ ```markdown
278
+ ---
279
+ name: review
280
+ description: Review uncommitted changes and flag risks
281
+ ---
282
+
283
+ Run `git diff` on staged and unstaged changes. Summarize what each
284
+ hunk does, call out potential regressions, and list files that might
285
+ need additional tests. Don't propose edits unless I ask.
286
+ ```
287
+
288
+ 用法:
289
+
290
+ ```
291
+ reasonix code › /skill review
292
+ ▸ running skill: review
293
+ assistant
294
+ ▸ tool<run_command> → git diff --cached
295
+ ▸ 3 改动,1 个需要回归测试 …
296
+ ```
297
+
298
+ 或者让模型自己挑 —— 因为 skill 名字 + 描述都在前缀里,问 "帮我看下未提交
299
+ 的改动有没有风险" 就会触发 `run_skill({name: "review"})`,不用你打斜杠。
300
+
301
+ **斜杠**:`/skill`(列出) · `/skill show <name>` · `/skill <name>
302
+ [args]`(把 body 作为用户轮注入)。
303
+
304
+ **故意不绑死** 任何其他客户端的目录约定(`.claude/skills` 等等)——
305
+ Reasonix 在对话层是模型无关的。任何 SKILL.md 都能用;body 是散文,所以
306
+ 为别的工具写的 skill 多半能直接用(Reasonix 工具名不同 —— `filesystem`
307
+ / `shell` / `web` —— 但模型读到指令后会挑我们的等价工具)。
308
+
309
+ ### Hooks —— 围绕工具调用与轮次自动化
310
+
311
+ 在 `.reasonix/`(项目或 `~/`)放一个 `settings.json`,Reasonix 会在
312
+ 循环里四个常见的点触发 shell 命令:工具运行前、工具返回后、prompt 到达
313
+ 模型前、轮次结束后。
314
+
315
+ ```json
316
+ // <project>/.reasonix/settings.json ← 可提交
317
+ // ~/.reasonix/settings.json ← 按用户
318
+ {
319
+ "hooks": {
320
+ "PreToolUse": [{ "match": "edit_file|write_file", "command": "bun scripts/guard.ts" }],
321
+ "PostToolUse": [{ "match": "edit_file", "command": "biome format --write" }],
322
+ "UserPromptSubmit": [{ "command": "echo $(date +%s) >> ~/.reasonix/prompts.log" }],
323
+ "Stop": [{ "command": "bun test --run", "timeout": 60000 }]
324
+ }
325
+ }
326
+ ```
327
+
328
+ 每个 hook 都是 shell 命令。Reasonix 调用时通过 stdin 传一份 JSON 信封,
329
+ 描述事件:
330
+
331
+ ```json
332
+ { "event": "PreToolUse", "cwd": "/path/to/project",
333
+ "toolName": "edit_file", "toolArgs": { "path": "src/x.ts", "..." } }
334
+ ```
335
+
336
+ 退出码决定走向:
337
+
338
+ - **0** —— 通过;循环正常继续
339
+ - **2** —— 阻断(仅对 `PreToolUse` / `UserPromptSubmit` 生效);hook 的
340
+ stderr 会作为合成工具结果让模型看到,或者整条 prompt 直接被丢弃
341
+ - **其他** —— 警告;循环继续,stderr 渲染为黄色行内提示
342
+
343
+ `match` 是对工具名的锚定正则;`*` 或省略匹配所有工具。项目 hook 先于全局
344
+ hook 触发。默认超时:阻断类事件 5 秒,日志类事件 30 秒;可在每条 hook 里
345
+ 用 `timeout` 覆盖。
346
+
347
+ **斜杠**:`/hooks`(列出当前生效的 hook)· `/hooks reload`(不丢会话从磁盘
348
+ 重读 `settings.json`)。
349
+
350
+ ### 在 TUI 里保持最新
351
+
352
+ `/update` 在运行中的会话里会显示当前版本、最近一次后台 24h 检查取到的最新
353
+ 版本,以及实际安装命令。这条斜杠 **不会** 直接 spawn `npm install` ——
354
+ stdio:inherit 进入正在跑的 Ink 渲染器会把显示搞乱。要真正升级请退出会话,
355
+ 在新 shell 里执行 `reasonix update`。
356
+
357
+ ---
358
+
359
+ ## `reasonix` —— 也能当通用聊天
360
+
361
+ 同一套 TUI,没有文件系统工具(除非你通过 MCP 主动接入)。适合起草、问答、
362
+ schema 设计、架构讨论,或者驱动你自己的 MCP 服务器。会话按名字保存在
363
+ `~/.reasonix/sessions/`。
364
+
365
+ ```bash
366
+ npx reasonix # 用已保存配置 + 向导选过的 MCP
367
+ npx reasonix --preset smart # 这次跑用 reasoner + R1 harvest
368
+ npx reasonix --session design # 命名会话 — 之后 --session design 续聊
369
+ ```
370
+
371
+ 临时挂 MCP 服务器:
372
+
373
+ ```bash
374
+ npx reasonix \
375
+ --mcp "fs=npx -y @modelcontextprotocol/server-filesystem /tmp/safe" \
376
+ --mcp "kb=https://mcp.example.com/sse"
377
+ ```
378
+
379
+ MCP 工具走和原生工具一样的 Cache-First + 修复 + 上下文安全管线 —— 32k
380
+ 结果上限、实时进度通知渲染、自动重试。
381
+
382
+ ---
383
+
384
+ ## 会话内命令
385
+
386
+ **核心**
387
+
388
+ | 命令 | 作用 |
389
+ |---|---|
390
+ | `/help` · `/?` | 完整命令参考(带提示) |
391
+ | `/status` | 当前模型 · flag · 上下文 · 会话 |
392
+ | `/new` · `/reset` | 同会话内开新对话 |
393
+ | `/clear` | 仅清空可见 scrollback(日志保留) |
394
+ | `/retry` | 截断并重发上一条消息(重新采样) |
395
+ | `/exit` · `/quit` | 退出 |
396
+
397
+ **模型**
398
+
399
+ | 命令 | 作用 |
400
+ |---|---|
401
+ | `/preset <fast\|smart\|max>` | 一键预设(model + harvest + branch) |
402
+ | `/model <id>` | 切换 DeepSeek 模型(`deepseek-v4-flash`、`deepseek-v4-pro`,加上 `deepseek-chat` / `deepseek-reasoner` 兼容别名) |
403
+ | `/models` | 列出 DeepSeek `/models` 端点的可用模型 |
404
+ | `/harvest [on\|off]` | 切换 R1 plan-state 提取 |
405
+ | `/branch <N\|off>` | 每轮跑 N 路并行采样,挑最佳(N ≥ 2) |
406
+ | `/effort <high\|max>` | reasoning_effort 上限 —— `max` 是 agent 默认,`high` 更便宜更快 |
407
+ | `/think` | 打印上一轮的完整 thinking-mode 推理 |
408
+
409
+ **上下文与工具**
410
+
411
+ | 命令 | 作用 |
412
+ |---|---|
413
+ | `/mcp` | 列出已挂载的 MCP 服务器及其 tool / resource / prompt |
414
+ | `/resource [uri]` | 浏览 + 读取 MCP resource(无参 → 列 URI;`<uri>` → 拉取) |
415
+ | `/prompt [name]` | 浏览 + 拉取 MCP prompt |
416
+ | `/tool [N]` | 打印第 N 次工具调用的完整输出(1 = 最近) |
417
+ | `/compact [tokens]` | 压缩日志里超大的工具结果(默认每条结果 4000 tokens) |
418
+ | `/context` | 看上下文 token 都花在哪(system / tools / log) |
419
+ | `/stats` | 跨会话成本仪表盘(today / week / month / all-time) |
420
+ | `/keys` | 键盘快捷键 + 输入前缀(`!` / `@` / `/`)速查 |
421
+
422
+ **记忆与 Skill**
423
+
424
+ | 命令 | 作用 |
425
+ |---|---|
426
+ | `/memory` | 显示已钉记忆(REASONIX.md + ~/.reasonix/memory) |
427
+ | `/memory list` · `show <name>` · `forget <name>` · `clear <scope> confirm` | 管理记忆库 |
428
+ | `/skill` · `/skill list` | 列出已发现的 skill(项目 + 全局) |
429
+ | `/skill show <name>` | 打印某个 skill 的 body |
430
+ | `/skill <name> [args]` | 运行 skill(把 body 作为用户轮注入) |
431
+
432
+ **会话**
433
+
434
+ | 命令 | 作用 |
435
+ |---|---|
436
+ | `/sessions` | 列出已保存的会话(当前用 `▸` 标记) |
437
+ | `/forget` | 从磁盘删除当前会话 |
438
+ | `/setup` | 重新配置(退出后跑 `reasonix setup`) |
439
+
440
+ **只在 code 模式下** (`reasonix code`)
441
+
442
+ | 命令 | 作用 |
443
+ |---|---|
444
+ | `/apply` | 把待应用的 SEARCH/REPLACE 块写入磁盘 |
445
+ | `/discard` | 丢弃待应用的编辑块 |
446
+ | `/undo` | 回滚最后一批已应用的编辑 |
447
+ | `/commit "msg"` | `git add -A && git commit -m "msg"` |
448
+ | `/plan [on\|off]` | 切换只读 plan 模式 |
449
+ | `/apply-plan` | 强制批准当前待审计划 |
450
+
451
+ **键盘**
452
+
453
+ - `Enter` —— 提交
454
+ - `Shift+Enter` / `Ctrl+J` —— 换行(多行粘贴也行;
455
+ `\` + Enter 是跨平台兜底)
456
+ - `↑` / `↓` —— 空闲时翻 prompt 历史;在斜杠自动补全里上下选
457
+ - `/foo` 前缀下按 `Tab` / `Enter` —— 接受高亮的建议
458
+ - `Esc` —— 中断当前轮(停 API 调用、取消进行中的工具、拒绝待响应的 MCP 请求)
459
+ - 确认弹窗里的 `y` / `n` —— 快捷接受 / 拒绝
460
+
461
+ ---
462
+
463
+ ## 会话与安全网
464
+
465
+ - 会话以 JSONL 形式保存在 `~/.reasonix/sessions/<name>.jsonl`(`reasonix
466
+ code` 按目录分)。每条消息都原子追加;`Ctrl+C` 不会丢上下文。
467
+ - 工具结果每次调用最多 32k 字符。超大会话加载时自动自愈(缩小并重写文件)。
468
+ - 每次出站 API 调用前会校验 `assistant.tool_calls` / `tool` 的配对,损坏的
469
+ 会话不会一直 400。
470
+ - 上下文仪表 50% 变黄、80% 变红并提示 `/compact`。接近 1M token 上限
471
+ (V4 flash + pro)会先尝试自动压缩,再退化为强制总结。
472
+ - `reasonix code` 沙箱拒绝任何解析后落到启动目录之外的路径(含符号链接
473
+ 逃逸和 `..` 穿越)。
474
+
475
+ ### 故障排查:重复行 / 鬼影渲染
476
+
477
+ 有些 Windows 终端(Git Bash / MINTTY / winpty 包装的 shell)没完整实现
478
+ Ink 用的 ANSI cursor-up 转义。表现:spinner、流式预览、工具结果行不在原地
479
+ 覆盖刷新,反而在 scrollback 里印出多份。
480
+
481
+ 遇到了就用 plain 模式:
482
+
483
+ ```bash
484
+ REASONIX_UI=plain npx reasonix code
485
+ ```
486
+
487
+ Plain 模式抑制了实时 / 动画行,关掉了内部 tick 计时器。代价是失去流式预览
488
+ 和 spinner,但 scrollback 稳定。Windows Terminal、Windows Terminal 里的
489
+ PowerShell 7、WezTerm 不需要这个开关。
490
+
491
+ ---
492
+
493
+ ## Web 搜索 —— 默认开
494
+
495
+ 模型一启动就有两个 web 工具:`web_search` 和 `web_fetch`。无 flag、无 API
496
+ key、无注册。当你问到模型训练时没见过的事(新发布、最近事件、冷门 API),
497
+ 它会自己决定调 `web_search`;snippet 不够就跟一个 `web_fetch`。
498
+
499
+ 底层是 **Mojeek** 的公开搜索页 —— 独立 web 索引,bot 友好,无 cookie /
500
+ session。冷门或非常新的查询覆盖率比 Google/Bing 薄一些,但脚本里很可靠。
501
+ (DDG 是原始后端,但 2026 年开始上反机器人页。)
502
+
503
+ **关掉**(离线 / 隐私 / CI):
504
+
505
+ ```json
506
+ // ~/.reasonix/config.json
507
+ { "apiKey": "sk-…", "search": false }
508
+ ```
509
+
510
+ ```bash
511
+ REASONIX_SEARCH=off npx reasonix code
512
+ ```
513
+
514
+ **接自己的**(Kagi、SearXNG、内部缓存):实现 `WebSearchProvider` 接口
515
+ 然后自己调 `registerWebTools(registry, { provider })`,或通过 `--mcp`
516
+ 桥接已有的 MCP 搜索服务器。
517
+
518
+ ---
519
+
520
+ ## MCP —— 自带工具
521
+
522
+ 任意 [MCP](https://spec.modelcontextprotocol.io/) 服务器都能用。向导内置
523
+ 目录可选,或用 flag 直接挂:
524
+
525
+ ```bash
526
+ # stdio(本地子进程)
527
+ npx reasonix --mcp "fs=npx -y @modelcontextprotocol/server-filesystem /tmp/safe"
528
+
529
+ # 同时挂多个
530
+ npx reasonix \
531
+ --mcp "fs=npx -y @modelcontextprotocol/server-filesystem /tmp/safe" \
532
+ --mcp "demo=npx tsx examples/mcp-server-demo.ts"
533
+
534
+ # HTTP+SSE(远程 / 托管)
535
+ npx reasonix --mcp "kb=https://mcp.example.com/sse"
536
+ ```
537
+
538
+ `reasonix mcp list` 显示精选目录。`reasonix mcp inspect <spec>` 不开聊天,
539
+ 直接连一次并打印服务器的 tool / resource / prompt。长任务的进度通知
540
+ (2025-03-26 spec)会以进度条形式实时渲染在 spinner 里。
541
+
542
+ 支持的传输:**stdio**(本地命令)和 **HTTP+SSE**(远程,MCP 2024-11-05
543
+ spec)。
544
+
545
+ ---
546
+
547
+ ## CLI 参考
548
+
549
+ ```bash
550
+ npx reasonix code [path] # 编程模式,作用域 path(默认 cwd)
551
+ npx reasonix # 聊天(用已保存配置)
552
+ npx reasonix setup # 重跑配置向导
553
+ npx reasonix chat --session work # 命名会话
554
+ npx reasonix chat --no-session # 临时会话
555
+ npx reasonix run "ask anything" # 一次性运行,结果流到 stdout
556
+ npx reasonix stats session.jsonl # 总结一份 transcript
557
+ npx reasonix replay chat.jsonl # 从 transcript 重建成本/缓存视图
558
+ npx reasonix diff a.jsonl b.jsonl --md # 对比两份 transcript
559
+ npx reasonix mcp list # 精选 MCP 目录
560
+ npx reasonix mcp inspect <spec> # 探测单个 MCP 服务器
561
+ npx reasonix sessions # 列已保存会话
562
+ ```
563
+
564
+ 常用 flag:
565
+
566
+ ```bash
567
+ --preset <fast|smart|max> # 预设组合(model + harvest + branch)
568
+ --model <id> # 显式指定模型 ID
569
+ --harvest / --no-harvest # R1 plan-state 提取
570
+ --branch <N> # self-consistency 预算
571
+ --mcp "name=cmd args…" # 挂 MCP 服务器(可重复)
572
+ --transcript path.jsonl # 同时旁路写一份 JSONL transcript
573
+ --session <name> # 命名会话(code 模式默认按目录)
574
+ --no-session # 临时会话
575
+ --no-config # 忽略 ~/.reasonix/config.json(CI 友好)
576
+ ```
577
+
578
+ 环境变量(覆盖 config):
579
+
580
+ ```bash
581
+ export DEEPSEEK_API_KEY=sk-...
582
+ export DEEPSEEK_BASE_URL=https://... # 可选的备用端点
583
+ export REASONIX_MEMORY=off # 关闭 REASONIX.md + 用户记忆
584
+ export REASONIX_SEARCH=off # 关闭 web_search / web_fetch
585
+ export REASONIX_UI=plain # 关闭实时行(鬼影绕开)
586
+ ```
587
+
588
+ ---
589
+
590
+ ## 库用法
591
+
592
+ ```ts
593
+ import {
594
+ CacheFirstLoop,
595
+ DeepSeekClient,
596
+ ImmutablePrefix,
597
+ ToolRegistry,
598
+ } from "reasonix";
599
+
600
+ const client = new DeepSeekClient(); // 从环境变量读 DEEPSEEK_API_KEY
601
+ const tools = new ToolRegistry();
602
+
603
+ tools.register({
604
+ name: "add",
605
+ description: "Add two integers",
606
+ parameters: {
607
+ type: "object",
608
+ properties: { a: { type: "integer" }, b: { type: "integer" } },
609
+ required: ["a", "b"],
610
+ },
611
+ fn: ({ a, b }: { a: number; b: number }) => a + b,
612
+ });
613
+
614
+ const loop = new CacheFirstLoop({
615
+ client,
616
+ tools,
617
+ prefix: new ImmutablePrefix({
618
+ system: "You are a math helper.",
619
+ toolSpecs: tools.specs(),
620
+ }),
621
+ harvest: true,
622
+ branch: 3,
623
+ });
624
+
625
+ for await (const ev of loop.step("What is 17 + 25?")) {
626
+ if (ev.role === "assistant_final") console.log(ev.content);
627
+ }
628
+ console.log(loop.stats.summary());
629
+ ```
630
+
631
+ `ChatOptions.seedTools` 接受一个预置好的 `ToolRegistry`,如果你只想要
632
+ `reasonix code` 的 loop 接线、不要 CLI 包装。详见
633
+ [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
634
+
635
+ ---
636
+
637
+ ## 性能对比 —— 缓存命中率自己也能验证
638
+
639
+ 这里每个抽象都对应 DeepSeek 的一个具体特性 —— 极低 token 价、R1 推理轨
640
+ 迹、自动前缀缓存、JSON 模式。通用框架把这些机会全留在桌上。
641
+
642
+ | | Reasonix 默认 | 通用框架 |
643
+ |---|---|---|
644
+ | 前缀稳定的循环(→ 85–95% 缓存命中) | 是 | 否(每轮重建 prompt) |
645
+ | 自动展平深层 tool schema | 是 | 否(DeepSeek 会丢参数) |
646
+ | 带 jitter 退避的 429/503 重试 | 是 | 否(要自己写回调) |
647
+ | 抢救泄到 `<think>` 里的 tool call | 是 | 否 |
648
+ | 同参数重复爆发熔断 | 是 | 否 |
649
+ | 实时缓存命中 / 成本 / vs Claude 面板 | 是 | 否 |
650
+
651
+ 同一 τ-bench-lite 负载(8 个多轮工具调用任务 × 3 次重复 = 每边 48 次运行),
652
+ 实测 DeepSeek `deepseek-chat`,唯一变量是前缀稳定性:
653
+
654
+ | 指标 | 基线(缓存敌对) | Reasonix | 差值 |
655
+ |---|---:|---:|---:|
656
+ | 缓存命中 | 46.6% | **94.4%** | +47.7 pp |
657
+ | 单任务成本 | $0.002599 | $0.001579 | **−39%** |
658
+ | 通过率 | 96% (23/24) | **100% (24/24)** | — |
659
+
660
+ **无需消耗 API 额度即可复现:**
661
+
662
+ ```bash
663
+ git clone https://github.com/esengine/reasonix.git && cd reasonix && npm install
664
+ npx reasonix replay benchmarks/tau-bench/transcripts/t01_address_happy.reasonix.r1.jsonl
665
+ npx reasonix diff \
666
+ benchmarks/tau-bench/transcripts/t01_address_happy.baseline.r1.jsonl \
667
+ benchmarks/tau-bench/transcripts/t01_address_happy.reasonix.r1.jsonl
668
+ ```
669
+
670
+ 提交进仓库的 JSONL 文件每轮带 `usage`、`cost`、`prefixHash`。Reasonix
671
+ 的前缀哈希在每次模型调用时都字节稳定;基线则每轮都变。缓存差距是从日志稳
672
+ 定性 **机械可推导** 的,不依赖于 prompt 写得不一样。
673
+
674
+ 完整 48 次运行报告:
675
+ [`benchmarks/tau-bench/report.md`](./benchmarks/tau-bench/report.md)。
676
+ 用自己的 API key 复现:`npx tsx benchmarks/tau-bench/runner.ts --repeats 3`。
677
+
678
+ MCP 参考运行(即使两个 MCP 子进程并发,整 5 轮也只有一个前缀哈希):
679
+
680
+ | 服务器 | 轮次 | 缓存命中 | 成本 | vs Claude |
681
+ |---|---:|---:|---:|---:|
682
+ | 内置 demo(`add` / `echo` / `get_time`) | 2 | **96.6%**(第 2 轮) | $0.000254 | −94.0% |
683
+ | 官方 `server-filesystem` | 5 | **96.7%** | $0.001235 | −97.0% |
684
+ | **两者并发** | 5 | **81.1%** | $0.001852 | −95.9% |
685
+
686
+ ---
687
+
688
+ ## Non-goals(明确不做)
689
+
690
+ - **多 agent 编排 / 子 agent**(用 LangGraph)。
691
+ - **工作流 DSL / DAG 调度 / 并行分支引擎** —— skill 是散文,模型靠正常的
692
+ tool-use 循环串起来。这样保住了单循环 + append-only + cache-first 三大
693
+ 不变量。
694
+ - **多供应商抽象**(用 LiteLLM)。Reasonix 故意只做 DeepSeek —— 每个支柱
695
+ (cache-first 循环、R1 抢救、tool-call 修复)都针对 DeepSeek 的具体行为
696
+ 和经济性做了调优。绑死一家后端是特性。
697
+ - **RAG / 向量库**(用 LlamaIndex)。
698
+ - **Web UI / SaaS。**
699
+
700
+ Reasonix 只做 DeepSeek,做到底。
701
+
702
+ ---
703
+
704
+ ## 开发
705
+
706
+ ```bash
707
+ git clone https://github.com/esengine/reasonix.git
708
+ cd reasonix
709
+ npm install
710
+ npm run dev code # 用 tsx 直接从源码跑 CLI
711
+ npm run build # tsup 打包到 dist/
712
+ npm test # vitest(1007 个测试)
713
+ npm run lint # biome
714
+ npm run typecheck # tsc --noEmit
715
+ ```
716
+
717
+ ---
718
+
719
+ ## License
720
+
721
+ MIT