这是 Jin(一个 architecture-first 的桌面 coding agent)的核心设计拆解。不吹模型,只讲工程。
0. 问题:为什么"先写代码"的 agent 让人不敢用生产
Codex / OpenCode / Claude Code 这类工具,工作流程是:
需求 → 模型调工具(读文件/写代码/跑测试)→ 甩出 diff → 你 review → 合并
痛点集中在最后两步:diff 越大,review 越贵;review 不完,不敢合;不敢合,就没人敢在真实项目里放开手脚。 而且中途模型很容易"漂移"——加了没要求的东西、改了没授权的地方、忘了最初的意图。
Jin 的思路是把闸门前移到"意图/架构层",而不是"代码层":
需求 → 模型产出【架构变更提案】 → 人审提案(批准/驳回) → 才允许实现 → 自动验证+自修 → 版本化
下面逐个讲关键实现。
1. 架构台账(Ledger):把"这个项目长什么样"变成可查询的事实
Jin 启动时对代码库做扫描,建一张 L0(包/目录)/ L1(模块)/ facet(职责面) 的图,加上 LSP 拿到的符号定义,落成持久化的 snapshot。
它的作用不是"展示",而是当实现的边界约束:
- 提案里的每个 op 都引用台账里的 node(
node.add/node.update/node.remove)。 - 实现阶段,模型只能改提案批准过的
file白名单。越界 → 工具层直接拒。
这一步就压住了漂移的一半:模型没机会在批准范围外动手。
2. 提案 = 你 review 的对象(不是 diff)
需求进来后,planner 产出的不是代码,是一个 JSON 结构:
kind: plan,title / goal / messagetasks[]:每个任务有files[]、acceptance[](验收标准)、dependsOnnodeIntents[]:这次要动哪些 L1/facetops[]:结构变更(增删改 node/edge)
你在 UI 里看的是这个提案 + 影响面,不是几百行 diff。批准后它才进入实现。
关键点:验收标准(acceptance)是自然语言写的,实现后有一步 acceptance review——模型对照"你当初要的效果"逐条检查代码是否满足。这是"意图一致性"的自动兜底。
3. 符号级上下文:别把整仓喂给模型
这是我认为最有价值的一块。传统 agent(OpenCode/Codex)实现时上下文要么是整文件、要么靠模型自己 grep。Jin 的做法:
// 用 LSP 符号索引,只取"这次要动的符号"前后 40 行
for (const path of wantedPaths) {
const symbols = lspSymbols.filter(s => s.path === path);
body = symbols.map(s => slice(lines, s.line - 40, s.line + 40)).mergeOverlaps();
}
wantedPaths由架构台账推出(op 里的node.file+ nodeIntents + 复用候选)。- 节点没写
file时,用 LSP 符号名反查定义所在文件补进去;查不到的,才提示"用 grep/glob 定位"。 - 目标文件里既有的 LSP error/warning 也拼进 prompt 末尾:"这里本来就有问题,顺带修掉"。
效果:模型看到的不是 800 行整文件(其中 700 行无关),而是"精确相关的符号块 + 已知待修问题"。token 省了,漂移少了,还顺带修既有 bug。
4. 实现闭环:写码 → 跑检查 → 失败自修
实现用的是 Eko 引擎(一个 agent 运行时,工具循环)。Jin 在 runEkoCoder 里做的事:
- 工具集:
read_file / write_file / apply_patch / run_shell / delete_file(都是 staging 式,先入虚拟工作区,批准后才落盘)+grep / glob / search_code(RAG) / architecture / todo_write / view_image / fetch_url / web_search / mcp__*。 - 上下文:上文第 3 点的符号级相关代码。
- 收尾前:agent 主动调
run_checks(跑项目自己的test/typecheck/lint)。 - 失败回灌:实现后 Jin 再跑一遍
runProjectChecks,失败的把输出喂回 Eko 自修一轮,再重跑。 - 一致性校验:重扫代码库,检查批准的架构是否真的落成(node/facet 在不在位、文件是否出现)。
也就是说"代码写对"不靠模型自觉,靠自动检查 + 失败自修 + 一致性校验三道。
5. 版本化与撤销:让"改了"可逆
- 每次实现是一个 WAL 版本,可回放、可 diff 两个版本、可重放。
- 实现前
git stash create打一个悬空 commit 当 checkpoint;UI 上"撤销本轮" =git restore --source=<checkpoint>,不动 HEAD。 - 危险操作(删文件、改 git 历史、管道执行远端脚本)在 shell 层有黑名单 + 审计(
.jin/cache/shell-audit.jsonl)。
6. 上下文管理:像 Codex 一样压缩,但多了"本地裁剪"
会话长了会爆上下文。Jin 的压缩策略(compactionMode):
- trim(本地裁剪,无模型):按轮次把旧消息切成"用户请求原文 + 助手结论尾巴",丢掉中间的 reasoning/tool 噪音。即时、离线、确定。
- llm(摘要):让模型把旧半段写成固定结构的 handoff(Goal/Decisions/Work/Open questions/Next steps)。
- auto(默认):先 trim,trim 完还超预算才升级 llm;llm 失败自动回退 trim。
阈值 = 上下文窗口 × 压缩比 × 0.45(架构 prompt 本身占大头,所以提前压)。
7. 其它工程细节(一句话版)
- 多 provider:OpenAI Chat/Responses、Anthropic Messages、Gemini
generateContent、Ollama。模型预设表自动带上下文窗口/价格/vision。 - MCP:
tools/list元数据进工具表(mcp__server__tool),结果序列化为文本,全走审计。 - AGENTS.md:项目级 + 全局两层,规划/实现提示词自动注入,改完即时生效。
- Hooks:
.jin/hooks.json的preToolUse(可拦截)/postToolUse(审计)/beforeImplement;.jin/commands/*.md自定义斜杠命令。 - LLM 诊断日志:JSONL + 控制台,空流/工具不支持/HTTP 降级都有记录,排障不用猜。
8. 它和"裸用 OpenCode/Codex"到底差在哪
不是"多一个工具",是多了一道闸门 + 一套边界约束:
| OpenCode / Codex | Jin | |
|---|---|---|
| review 对象 | 代码 diff | 架构提案(影响面) |
| 漂移控制 | 靠模型自律 + 你 review | 白名单越界即拒 + 一致性校验 |
| 上下文 | 整仓 / 模型自 grep | LSP 符号级 + 既有诊断 |
| 正确性兜底 | 跑测试(手动/工具) | 自动检查 + 失败自修 + 一致性校验 |
| 可逆性 | git(你手动) | WAL 版本 + checkpoint 一键撤销 |
一句话总结:Jin 不让你 review 代码,它让你 review 意图;正确性交给自动闭环。
9. 适用边界
- 适合:中大型、多模块、有架构边界、需要可审计的项目;你当负责人/架构师。
- 不适合:一次性脚本、demo、快速原型——直接 Claude Code 更快。
- 如果你压根不想做"提案审批",Jin 的核心价值就没了。
开源github.com/ZYF93/Jin,欢迎 PR / 拍砖。