默认 13 入口 · 12 套状态机 · 166 测试文件全绿 · 10 语言

码龙·六合工具

MALONG · LIUHE

LLM-native 原生工具

不是给人类的工具套一层 API。

「LLM 为什么要学着用人类的工具?
人类工具假设使用者有手、有眼、有记忆。LLM 三者皆无。 与其教模型迁就工具,不如把工具改成它本来就该有的形状。
13
默认入口(六合线)
12
套状态机落盘
4.86k
tokens/轮 目录税
0
假「完成」
The Wrong Assumption

人类工具,是给有手、有眼、有记忆的人设计的

不是「给工具加个 API」的问题。人类工具的每一条交互假设,放在 LLM 身上都不成立。

人类工具的前提
LLM 的现实
六合线的机制
有一双手:点、拖、确认、试错
一次调用定生死,错了要能退
事务 + journal + 24h undo
有一双眼:扫一眼就知道结构
只看得见塞进上下文的文本
代码图 + 预算截断
有一个脑子:记得刚才改了什么
上下文会截断,会话会重开
12 套状态机落盘 + 重新投影
会自己判断「做完了」
它会非常自信地宣布完成
工具只报机械事实,判断交还宿主

所以六合线做的不是「更强的工具」,而是三套补偿机制 + 一条会交还的任务线。

The Graph

「图」不是比喻,是索引里的数据结构

不是画给投资人看的示意图。索引里存的是符号、引用和文件依赖——nodes、edges、cycles,可以查询、可以算影响面。

test cycle config.js change point resolve.js env.js parse.js index.js cli.js test-parse.js util.js 变更点 / 影响面 依赖边 环(resolve ↔ parse) 测试 示意图:一次改动的爆炸半径,由 dep_graph / impact_analysis / call_chain 从索引现算

一个入口,13 种问法

默认档不需要记忆 44 个工具名。只读快查全部收进 code_query:

outline · symbols
body · search
map · impact
references · callers/callees
tests · validate
capabilities · capability
dep_graph(scope=project) → 返回值结构
{
  "nodes": [ ... ],
  "edges": [ ... ],
  "cycles": [
    ["resolve.js", "parse.js"]
  ]
}

边界写清楚

动态调用、反射、跨语言引用不可见——这是静态分析边界,写在每次返回的 limitations 里,不装作全知。

宁可如实说“看不见”,也不让模型基于一张假图做决策。

The Six-He Line

默认档只有一条线:从一句话,到一份证据

五步主链、四个回退环、六个侧出口。每一步都写明走到了哪个 node,下一步由 next_action 给出。

cancelled → code_control(action="reopen") ① ORIENT code_task goal → 候选 / 结构 / 影响面 定 route,自己不改不验 next: code_read / 模板 ② READ code_read 符号 / 文件正文 + 版本 发 read_ref 作为写前锚点 预算截断如实标出 ③ EDIT code_patch · code_batch code_maintain 单文件 / N 处一次往返 / import 修复 默认 dry_run · 落盘写 journal ④ CHECK code_check test / typecheck / lint 自动选 落 evidence,skipped ≠ passed 长套件 async 后台 600s ⑤ CONTROL code_control status / resume / cancel / reopen switch_route / expand / job validated → 交还 node=orient node=read node=edit node=check done / cancelled failed → 先修,再重跑(指针仍指 code_check 同 scope) stale:file_change / goal_change → 证据过期 indeterminate:崩溃 / 取消未确认 → 先重读核对 每次调用都返回同一份只读投影:task.lifecycle · route · node · observation.progress · next_action
侧出口 code_query · 只读快查 code_status · 随时看态 code_audit · 审查路线 code_worktree · 隔离改动 invoke_capability · 深度出口 edit_transaction · 恢复出口
读 · 4
code_task · code_read
code_status · code_query

做任何事前,先知道自己在哪、能读什么、影响多大。

写 · 8
code_patchcode_batch code_maintaincode_check code_controlcode_audit code_worktreeinvoke_capability

每一个都能预览、留证据、可撤销;失败不会自动变成「完成」。

共享 · 1
edit_transaction

跨文件事务与 undo,同时属于 classic 44——目录逐字节冻结,所以刻意不改。

深度出口:invoke_capability(name=…) 一次只调一个 classic 工具;清单走 code_query(mode="capabilities")。Success ≠ verified。
换档:MALONG_INTERFACE=classic | diagnostic。默认档下,响应里的经典工具名会被改写成可调用的 graph 调用。
One Real Run

一次真实任务,十次往返

下面每个箭头都是实际会发生的调用。中间有一次失败的检查、一次修复和一次重验——没有一步靠模型自认为完成。

宿主 / LLM 决定意图与业务判断 六合线入口 默认 13 个 状态层 task · evidence · journal 工作区 文件 · git 索引 SQLite · Rust/tree-sitter 1 code_task(goal="给 parseConfig 加 .env 回退") task_id · route=change · node=orient · progress=open 2 候选 / 结构 / 影响面 7 candidates · 3 affected files 3 建任务 goal_revision=1 task 落盘 4 code_read(locator) —— 写之前必须先拿锚点 body + read_ref@v1(写前基准) 5 code_patch(dry_run=true, read_ref=@v1) diff 预览 · 未落盘 6 code_patch(dry_run=false) 写文件 committed · undo(24h) · progress=partial 7 code_check(run=true) 跑 pytest 1 failed · evidence(result=failed) · progress=partial next_action = code_check(同 scope) · 备注:先修,再重跑 —— 不把失败说成完成 8 code_patch(fix) committed · progress=partial 9 code_check(run=true) —— 同 scope 重验 passed · result_kind=validated · progress=done 10 code_status(task_id) —— 收口前再确认一次 next_action: null · mechanical_requirements_met: true goal_judgment: "host_required" —— 工具侧只报机械事实,业务判断交还宿主 progress open → partial → partial → done 第 1 / 6 / 7 / 9 步
写前有锚点

没有 read_ref 就不给写;文件在中间被外部改过,会直接 VERSION_CONFLICT,不静默覆盖。

失败不会被说成完成

测试失败后 progress 仍是 partial,next_action 指回同一个检查范围——先修,再证明。

收口是一个空动作

next_action:null + host_required:工具宣布「机械要求已满足」,业务是否达标由宿主判断。

When Is It Done

跑过了,不等于改对了

「完成」不是一个感觉,是一个可以检查的投影。所有状态机汇成同一个只读结论,按固定优先级取值——第一个命中的就是事实。

常见的「完成」
问题
六合线的判定
测试退出码 = 0
✕ 测试通过只证明测试通过
✓ validated + 未陈旧 + 无在途,才给 done
skipped 也报「全绿」
✕ 没跑等于没验证
✓ skipped ≠ passed,不计入 validated
超时 = 代码有 bug
✕ 模型会去改本来正确的代码
✓ 超时是预算截断,转 async 600s,注明非缺陷
模型自己说「做完了」
✕ 它会非常自信
✓ goal_judgment: host_required,判断交还

progress 优先级瀑布

从上往下,第一个命中的状态就是当前事实。只读,不可写。

cancelled任务被取消 → 只能 reopen
terminal已终结(completed / done / abandoned)
in_flight有在途 job / operation → 先等
indeterminate崩溃 / 取消未确认 → 交宿主核对
stalegoal_change > file_change > pending_work
partial写了但没验 → 去 code_check
donevalidated 且不陈旧、无在途
open刚开任务,按 route 给第一步

收口时,模型真正拿到的字段

{
  "progress": "done",
  "next_action": null,
  "mechanical_requirements_met": true,
  "goal_judgment": "host_required"
}

这四行是合同,不是文案:next_action:null 表示工具侧没有下一步;是否真的达成业务目标,工具不替你盖章。

review 路线是例外

测试全绿也不算完成——完成态由 audit 驱动。仅当审计证据适用于当前 goal 和文件时,才进入 awaiting_decision。

Twelve State Machines

模型会失忆,盘不会

12 套状态机落在工作区里,每次调用重新投影。上下文被截断、会话被重开,任务也不会丢——这是「无记忆」真正的补法。

任务生命周期task
active → cancelled ⇢ reopen 回到 active
取消是终态,不会自动恢复;别名 completed / done / abandoned
看 task.lifecycle
路线route
understand change diagnose review maintain document isolate system
不再复制八套流程,只决定推荐与完成判据
看 task.route
节点node
orient read edit check maintain worktree review cancelled
由 operation / evidence 事件推进
看 task.node
进度投影projection
cancelled > terminal > in_flight > indeterminate
> stale > partial > done > open
只读,不可写;第一个命中即事实
看 observation.progress
证据新鲜度freshness
goal_change > file_change > pending_work
旧证据不会压过新事实
看 result_stale · stale_reason
结果种类result_kind
validated partial analysis_only
检查通过且确有 patch,才叫 validated
看 observation.result_kind
检查结果evidence
passed failed skipped unavailable unknown
skipped / unavailable 都不算通过
看 evidence[].result
后台 jobjob
running → cancelling → cancelled | succeeded failed
孤儿进程 → indeterminate,绝不假称完成
看 code_control(job)
操作operation
prepared succeeded failed indeterminate abandoned
每次写 / 检查落一条,带哈希
看 observation.operations
写事务 journaljournal
created → staged → committed / committed_recovered
rolled_back · needs_review · abandoned · undone
崩溃后按三方哈希对账
看 undo.token(24h)
符号冲突conflict
CLEAN · FILE_CHANGED_SYMBOL_STABLE
FILE_CHANGED · SYMBOL_CHANGED · SYMBOL_SIGNATURE_CHANGED
SYMBOL_DELETED · AMBIGUOUS_SYMBOL · NO_BASE
→ allow / allow_warn / reject
看 写错误里的 conflict
委派账本ledger
prepared succeeded failed indeterminate
request_id 幂等,重放不盲跑
看 receipt

journal 与委派账本主要用于崩溃对账与幂等;其余状态都会出现在 LLM 拿到的响应字段里。

Architecture

一层入口,四层底座

入口可以很少,是因为复杂度被放到了它该在的地方:状态落盘、索引与解析在进程外。

宿主 / LLM 任意 MCP 客户端 · 决定意图与业务判断 JSON-RPC over stdio · 120s 硬限 MCP server · 默认 13 个入口 读 ×4 写 ×8 共享 ×1 状态层 · 落盘 task.json · evidence · journal jobs · operation ledger · write locks 崩溃对账:intended / pre / post 三方哈希 索引层 SQLite(better-sqlite3 / sql.js) symbols · refs · files · deps WAL · integrity_check · 按 workspace 隔离 写后同步 工作区 文件 · git · 原子写 temp+rename · 单写者 Rust malong-parse tree-sitter · 10 语言 · catch_unwind 崩溃隔离 异步任务另开 600s 预算;MCP 的 120s 是协议硬限,超时不等于代码缺陷。
状态在进程外

MCP 进程崩了,任务不会跟着丢。重启后按 journal 对账,能恢复就恢复,不能就标 indeterminate。

解析在进程外

tree-sitter 跑在 Rust 服务里,panic 被 catch_unwind 兜住,返回错误码而不是带走整个 MCP 进程。

写入是单写者

写锁 + 批次锁 + 原子写。sql.js 后端下第二个写进程会被明确拒绝,而不是悄悄覆盖。

NOTE

六合线不是把任务画成一张图。任务侧只有 route、node 和每次现算的 next_action,没有持久化的边;真正的图在代码侧——{nodes, edges, cycles}。这个区分我们写进文档,不混着说。

Toolbox

13 是默认,不是上限

三个档位是并列目录,不是包含关系。全部纯正则 / AST 实现,零 LLM 调用。

六合线(默认)13
graph + 状态层。日常唯一入口:code_task → code_read → code_patch/batch/maintain → code_check → code_control,加 6 个侧出口。
MALONG_INTERFACE 未设
classic(全量)44
经典原语全集:符号读写、索引搜索、影响分析、事务编辑、质量门禁、测试编排。目录逐字节 golden 冻结。
MALONG_INTERFACE=classic
diagnostic(审计)18
六合线 13 + 5 个分析器:security_review / sweep_dead_code / dependency_gatekeeper / config_drift / repo_map。
MALONG_INTERFACE=diagnostic
读 · 4
code_task
任何改动的起点:goal → 候选 / 结构 / 影响,定路线
code_read
读符号 / 文件,发 read_ref 版本锚点
code_status
任务态、材料、证据一览
code_query
无任务只读快查,13 种 mode
写 · 8
code_patch
单文件 patch / replace_symbol,默认 dry_run
code_batch
N 处编辑一次调用(全有或全无)——少 N−1 次往返
code_maintain
import 修复;plan 预览默认,死代码不自动删
code_check
test/typecheck/lint 并落证据;async 跑长套件
code_control
status / resume / cancel / reopen / switch_route / expand / job / cancel_job
code_audit
只读分析器 + 假阳性账本
code_worktree
隔离工作树:preview / apply / status
invoke_capability
一次调一个 classic 工具的深度出口
共享 · 1
edit_transaction
多文件事务 + rollback / undo,同时属于 classic 44(目录逐字节冻结)
展开:classic 44 深度目录(7 组)▸
I/O 原语 ×3 — read_symbol · write_symbol · write_symbols
索引与搜索 ×5 — reindex · symbol_search · code_search · repo_map · read_outline
分析理解 ×8 — impact_analysis · call_chain · references · dep_graph · inspect · trace_symbol · active_todos · code_quality
编辑与重构 ×7 — edit_batch · edit_transaction · edit_collision_guard · git_worktree · rename_symbol · fix_imports · sandbox_validate
质量与安全 ×8 — code_review · security_review · sweep_dead_code · guard_patterns · exception_guard · config_drift · mock_sync · naming_consistency
工程与验证 ×8 — test_bridge · verify_pipeline · debug_runner · patch_parser · diff_facts · find_tests · spec_gen · style_sniffer
依赖与系统 ×5 — dependency_gatekeeper · tsc_check · health · gc · feedback
Proof

上下文,是 LLM 最贵的资源

工具目录每一轮都会随上下文重发。先算这笔账,再谈吞吐。

默认档目录税

16.5KB ≈ 4.86k tokens/轮
描述 2.0 KB ≈ 0.60k inputSchema 14.5 KB ≈ 4.26k(88%)

结论有点反直觉:砍描述最多只能省 0.6k。要降目录税只能砍参数面——这就是 13 个入口把 44 个工具的 schema 收进来的意义。

固定编辑路径:6 步 → 3 步

同一脚本化路径,非宿主对照,非 agentic 对话结果。

旧六步链6 调用 · 7,673 chars
新原语3 调用 · 2,662 chars
token ↓65.3% 调用 ↓50.0%
BATCH EDITS

一次多处编辑,省下的是九整轮上下文

code_batch 把 N 处编辑收进一次调用(全有或全无)。 逐处改,每一轮都要重发系统提示、工具目录、对话历史和已读文件;批量改只发一次—— 少 N−1 轮,乘的是整段上下文,不是目录税——也少 N−1 次模型往返的等待。

省下 ≈ 9 × 单轮完整上下文(10 处编辑)
单轮 20k / 50k / 100k → ≈ 180k / 450k / 900k tokens
估算;目录税 4.86k/轮 只是其中固定的一小块
逐处编辑 10 轮 · 每轮重发完整上下文
code_batch 1 轮 · 只发一次
省下 9 次整段上下文重发
同样的 10 处编辑;批量失败则整批回滚,不会留下半套改动。
1ms
小文件 read P95
7ms
小文件 write P95
98ms
repo_map 全库
588/s
单线程吞吐
134MB
RSS @ 512MB cgroup
128
并发零 OOM
166
JS 测试文件
6000+
全链断言
347
classic-44 合同断言(逐字节 golden)
0
失败 · 4 平台产物
覆盖范围 写链与事务 崩溃恢复 kill -9 双进程并发 MCP 协议半帧 10 语言解析 平台差异 合同 golden 失败路径同样有独立用例
30+ 轮「码龙查码龙」· r12–r30 dogfood 端到端
每一轮审查都伴随断言增长——测试盲区就是 bug 潜伏区
Get Started

现在就可以接上

不需要构建产物。clone、装依赖,把 MCP 客户端指向它。

bash标准安装(better-sqlite3 完整后端)
# 1) 克隆
git clone https://github.com/wulun811/LiuHe.git

# 2) 安装(含 better-sqlite3)
cd LiuHe/malong && npm ci

# 3) 启动 MCP server
node --max-old-space-size=512 mcp-server.js \
  --workspace /path/to/project
opencode.json
{
  "mcp": {
    "malong": {
      "type": "local",
      "command": ["node", "--max-old-space-size=512",
                  "malong/mcp-server.js",
                  "--workspace", "."],
      "enabled": true
    }
  }
}
tree-sitter · 10 语言
解析器在 Rust 进程里
JavaScript TypeScript TSX Python Go Rust C C++ Java Bash