代号 Kestrel(红隼):体型最小的猛禽之一,悬停精准、俯冲高效——小而精,不是玩具。 (代号可换,不影响本文档任何设计。)
| 版本 | 0.1(创始版) |
| 日期 | 2026-07-02 |
| 状态 | 待评审 |
| 定位 | 专为本地部署模型(llama.cpp / LM Studio)设计的轻量高效 agent,开源项目 |
| 语言 | Rust |
| 目标平台 | Windows 优先,架构上不排斥 Linux/macOS |
市面上的 agent 是"为云端 API 设计、顺便兼容本地";Kestrel 反过来:把本地推理的物理约束(prefill 慢、上下文小、KV 缓存宝贵)当一级设计约束,所有云端 agent 不在乎的地方,正是我们的主场。
- 前缀稳定性是一级架构约束。 消费级显卡 prefill 仅数百 t/s(实测 7900XTX 生成 ~65 t/s 但 prefill 仅 ~600 t/s),20k token 历史每轮重算即 30s+。凡是打碎 KV 前缀缓存的设计(动态 system prompt、改写历史、工具列表顺序漂移)一律禁止。
- 固定 token 开销 ≤ 2.5k。 Claude Code 的固定开销约 14.3k token(系统提示 + 27 个工具 schema),在云端 200k 窗口里占 7%,在本地 32k 窗口里占 45%——是致命的。我们:系统提示 ≤800 + 工具 schema ≤1400 + 记忆文件 ≤300。
- 循环要薄,外壳要厚。 Claude Code 的代码里仅 ~1.6% 是 AI 决策逻辑,98.4% 是确定性基础设施(权限门、截断、压缩、恢复)。工程价值全在循环外——这正是 Rust 的用武之地。
- 单线程主循环 + append-only 事件日志。 OpenHands 亲手废除了自己的 pub/sub 事件总线("引发各种线程/异步问题,消息顺序几乎无保证");Claude Code 全部押注单线程循环 + 扁平历史。不重蹈覆辙。
- 权限系统不是可选项。 OpenClaw 2026 年初的安全危机(CVE-2026-25253 一键 RCE、4 万实例裸奔公网、技能市场 820+ 恶意技能)是反面教材。即使第一版是单机个人版,权限门也进第一版。
- 边界即 crate。 模块边界由 Cargo workspace 的 crate 边界强制,编译器兜底,不靠代码规范自觉。依赖方向单向,core 不知道任何 IO 细节。
- 可测试性内建。 事件日志天然是回放测试基座;agent 的全部确定性行为可以无模型、毫秒级进 CI。
| 项目 | 规模 | 架构路线 | 对我们的价值 | 不采纳的部分 |
|---|---|---|---|---|
| OpenAI codex-rs (95k star) | ~100 crates | SQ/EQ 协议分层 + OS 级沙箱纵深 | protocol crate 思想(Op/Event 解耦前后端);SandboxPolicy/AskForApproval 分档设计;工具 spec+handler 双文件模式 |
100 个 crate 的规模;Bazel 双构建;实时语音等全家桶 |
| Block goose (50k star) | 13 crates | library-first + 纯 MCP 扩展 | 库优先结构(CLI/server 都是薄壳);provider trait 以 stream() 为主方法;toolshim(给无原生 FC 的本地模型做工具模拟) |
内置工具也走 MCP(多一层进程内协议开销);Electron 桌面端;50+ provider |
| rig (7.8k star) | core+集成 | 轻量库地基 | Tool trait 设计(const NAME + 关联类型);依赖控制的榜样 |
不直接依赖(见 §3.4) |
| swiftide (0.7k star) | 分层库 | 强类型状态机 | ToolExecutor trait(执行环境可插拔:本机/Docker);FeedbackRequired 内建人类审批停机点 |
indexing/query 管线(非我们的场景) |
| OpenHands | 巨型 | V0 事件总线 → V1 事件溯源 | V1 的修正即我们的起点:append-only EventLog + 单一状态源;沙箱 opt-in;SecurityAnalyzer 风险分级 |
V0 的一切:pub/sub、140+ 字段配置、10GB 镜像 |
| Claude Code | 闭源 | 单线程主循环 | 循环设计、权限七模式(deny 优先)、上下文五级流水线、JSONL transcript、子代理只回摘要 | 27 个工具 14.3k 固定开销(云端奢侈品) |
| aider | Python | 编辑格式实证派 | SEARCH/REPLACE 为主 + 按模型配格式 + 整文件回退;repo-map 1k token 预算 | Python 生态 |
- AutoGPT:无约束自治 → 超过 4-5 步的目标基本达不成。→ 硬性迭代/预算上限。
- OpenHands V0:事件总线 + 万能配置 → 自己废除。→ 单循环 + 单文件 TOML 配置。
- OpenClaw:插件市场 + 默认联网监听 → 2026 年首个 agent 安全危机。→ 无插件市场;默认只监听 localhost;权限门第一版就有。
- 本地用户的真实抱怨(HN/GitHub issues):prefill 100 秒级、忘开
--jinja模型看不见工具、"工具调用哪怕 5% 失败率就毁掉整个体验"、大 harness 的 system prompt 在本地窗口里是纯负担。→ 这四条各自对应我们的一个设计决策(§5.4、§6.3、§5.3、原则 2)。
库核心 + 薄适配器 + 事件流输出(library core + thin adapters + event stream)。 端口(trait)只建在确定会有第二个实现的四个边界上;其余地方写朴素、直接、可读的代码。
| 方案 | 否决理由 |
|---|---|
| 教科书式六边形架构 | 六边形为"领域模型复杂、外部系统多变"的企业应用设计。我们恰好相反:外部边界少而稳定,复杂度在运行时行为(loop 状态机、token 预算、KV 联动)而非领域模型。全套 DTO 转换/依赖注入容器只会稀释代码密度。取其魂(依赖单向、core 无 IO),弃其形(每层仪式)。 |
| 事件总线(pub/sub) | OpenHands 亲手废除的路线,消息顺序无保证、调试地狱。我们用单消费者的有序事件通道(tokio::sync::mpsc):core 产出事件,前端消费,永远单向、有序。 |
| Actor 框架(AutoAgents/Ractor 路线) | Actor 解决的是大规模并发实体的问题。我们只有一个 agent 循环 + 少量后台任务,tokio::select! + channel 足够,引入 actor 框架是拿大炮打蚊子,还引入一整套监督树的学习成本。 |
| 纯 MCP 扩展(goose 路线) | 内置工具也走 MCP 意味着每个工具调用多一层 JSON-RPC 序列化,且工具 schema 不受我们控制(MCP 工具 schema 常常比用户提示还费 token)。内置工具用原生 trait(零开销、schema 逐 token 手工优化),MCP 作为 v2 的外接桥——这也是 rig/swiftide/codex 的共识做法。 |
| 依赖 rig-core 做地基 | rig 的抽象质量不错,但我们的差异化恰好在它抽象掉的那一层:llama.cpp 的 slot 管理、cache_prompt、/props 探测、GBNF 注入都需要直接控制 HTTP 请求体。为省 2 千行代码引入一个把关键层遮住的依赖,不值。自研 backend 层,trait 设计参考 rig/goose。 |
/// 1. LLM 后端 —— 第二实现明确存在:llama.cpp / LM Studio
trait LlmBackend {
async fn stream(&self, req: CompletionRequest) -> Result<CompletionStream>;
async fn probe(&self) -> Result<BackendCapabilities>; // /props 或 /api/v0/models
async fn save_cache(&self, session: &SessionId) -> Result<()>; // 不支持则 no-op
}
/// 2. 工具 —— 每个内置工具是一个实现
trait Tool {
const NAME: &'static str;
fn spec(&self) -> &ToolSpec; // 静态 schema(前缀稳定性!)
fn risk(&self, args: &Value) -> RiskLevel; // 权限门用
async fn call(&self, args: Value, ctx: &ToolCtx) -> Result<ToolOutput>;
}
/// 3. 前端 —— 第二实现已规划:CLI 现在 / WebUI 以后
/// 形态:core 暴露 `run(op) -> impl Stream<Item = Event>`,前端只是事件的渲染器
/// (codex 的 SQ/EQ 思想,砍掉它的 JSON-RPC 仪式)
/// 4. 存储 —— 事件日志 / 会话 / 模型 profile
trait Store {
async fn append(&self, session: &SessionId, event: &Event) -> Result<()>;
async fn replay(&self, session: &SessionId) -> Result<Vec<Event>>;
}agent 外壳的性能瓶颈永远在模型推理,Python 外壳也不慢——性能不是理由。真正的理由:
- 边界由编译器强制:crate 边界的私有泄露编译不过,开源多人协作时比 code review 可靠。
- 单二进制分发:
cargo install或下载一个 exe,没有 Python 环境地狱;对将来"给朋友用"关键。 - 赛道最强先例都是 Rust(codex-rs、goose),tokio + reqwest + ratatui + axum 恰好覆盖全部需求。
- 常驻进程内存小、无 GC——本地机器的内存要省给模型。
备选 Go(开发更快)被放弃:类型系统弱一档(边界表达力)、且本赛道无同级先例可抄。
graph TB
subgraph frontends["前端层(薄壳,只渲染事件)"]
CLI["kestrel-cli<br/>(ratatui TUI, v1)"]
WEB["kestrel-server + console<br/>(axum SSE + React WebUI)"]
end
subgraph core["kestrel-core(纯逻辑,零 IO 依赖)"]
LOOP["Agent Loop<br/>单线程 turn 状态机"]
CTX["Context Ledger<br/>token 预算 + KV 联动"]
PERM["Permission Engine<br/>deny 优先 + 风险分级"]
PROTO["Event / Op 类型<br/>(kestrel-protocol)"]
end
subgraph adapters["适配器层"]
BACKEND["kestrel-backend<br/>llama.cpp / LM Studio"]
TOOLS["kestrel-tools<br/>shell / fs / search / browser"]
STORE["kestrel-store<br/>JSONL 事件日志 / profile"]
end
subgraph external["外部世界"]
LLAMA["llama-server<br/>(--jinja, slots)"]
LMS["LM Studio<br/>(REST /api/v1)"]
OS["PowerShell / 文件系统 / CDP 浏览器"]
end
CLI -->|Op| LOOP
WEB -->|"Op (POST /api/ops)"| LOOP
LOOP -->|"Event (mpsc, 有序)"| CLI
LOOP -->|"Event (SSE /api/events)"| WEB
LOOP --> CTX
LOOP --> PERM
LOOP -->|trait LlmBackend| BACKEND
LOOP -->|trait Tool| TOOLS
LOOP -->|trait Store| STORE
BACKEND --> LLAMA
BACKEND --> LMS
TOOLS --> OS
依赖方向铁律:前端 → core ← 适配器,core 不依赖任何适配器 crate;适配器之间互不依赖;所有共享类型下沉到 kestrel-protocol。
kestrel/
├── Cargo.toml # workspace 根:统一 lints、profile、依赖版本
├── README.md / LICENSE-MIT / LICENSE-APACHE / CONTRIBUTING.md
├── deny.toml / rustfmt.toml # 依赖白名单、格式化配置
├── .github/workflows/ci.yml # fmt + clippy(-D warnings) + test + cargo-deny
├── docs/
│ ├── architecture.md # 本文档
│ ├── adr/ # 架构决策记录(0001-语言、0002-风格、...)
│ └── research/ # 竞品与技术调研报告(结论的证据基础)
├── crates/
│ ├── kestrel-protocol/ # Event/Op/ToolSpec/RiskLevel 等纯类型 + serde
│ │ # 依赖仅 serde;被所有 crate 依赖
│ ├── kestrel-core/ # agent loop、context ledger、permission engine、
│ │ # crew 路由 + ports/;只依赖 protocol + tokio(sync)
│ ├── kestrel-backend/ # LlmBackend 实现:llamacpp.rs / lmstudio.rs /
│ │ # openai_compat.rs(兜底)+ probe.rs(能力探针)
│ ├── kestrel-tools/ # 内置工具:shell.rs fs.rs search.rs(browser M4)
│ ├── kestrel-store/ # JSONL 事件日志、模型 profile、TOML 配置
│ ├── kestrel-cli/ # 前端:极简 REPL 起步,M2 引入 ratatui TUI
│ └── kestrel-server/ # 前端适配器:axum,SSE 推事件 + POST 收 Op(ADR-0007)
├── console/ # WebUI(React+Vite+Tailwind),纯前端,不进 Rust 依赖图
├── profiles/ # 内置模型 profile(qwen3-8b.toml 等,探针可覆盖)
└── tests/replays/ # 录制的回放测试 fixture(.jsonl)
7 个 Rust crate + 1 个前端应用(对比:codex ~100、goose 13)。每个 crate 的 lib.rs 顶部用模块级文档注释声明其职责边界与禁止依赖,CI 用 cargo-deny 强制依赖白名单。console/ 通过 HTTP 契约与 kestrel-server 通信,不在 Rust 依赖图内。
主循环是一个单线程、单消费者、可取消的状态机。一个 turn = 一次"组装 prompt → 流式推理 → 解析工具调用 → 权限门 → 执行工具 → 追加结果"的闭环,直到模型不再请求工具或触达迭代/预算上限。
enum TurnState {
Assembling, // context ledger 组装 prompt(前缀稳定!动态信息置尾)
Streaming, // backend.stream(),边收边解析 tool_call
AwaitingApproval, // 命中风险动作 → 事件通知前端,挂起等 Op::Approve/Deny
ExecutingTools, // 只读工具并行、写状态工具串行(codex/Claude Code 共识)
AppendingResults, // 结果 head-tail 截断后 append 进事件日志
Done { reason }, // 无工具调用 / 迭代上限 / 预算耗尽 / 用户中断
}铁律:
- 消息历史 append-only,永不原地改写——这是 KV 前缀缓存能命中的前提(原则 1)。
- 迭代上限(默认 10)+ token 预算双闸,治 AutoGPT 式无限循环。
- 取消信号(
CancellationToken)贯穿到子进程:本地推理慢,可中断性比云端更关键。 - 工具执行失败时,把具体错误(最近似片段、行号)append 回历史让模型自纠错,而非静默重试(aider 的 reflection 经验)。
不叫 "manager" 叫 ledger(账本),因为它的核心职责是按服务器上报的真实上下文长度记账,而非拍脑袋截断。
- 启动时
probe()拿到后端真实n_ctx(llama.cpp/props、LM Studio/api/v0/models),预算按此算,不硬编码。 - 摄入即截断:工具输出是头号 token 杀手(ReAct 循环里常占 70-80%),在 append 那一刻就 head-tail 截断(保头保尾,中间折叠成
… [省略 N 行] …),而不是等压缩时再处理。 - 压缩少而狠:任何改写历史的压缩都会打碎 KV 前缀 → 触发全量 re-prefill。所以绝不每轮微调,只在逼近预算(~85%)时做一次大压缩——而且这次压缩由副手模型异地完成(见第 6 章),主脑 KV 不受影响。
- 压缩点与 llama.cpp
/slots/{id}?action=save联动:旧状态落盘,配合状态树分支(未来)可秒级回溯。
即使第一版是单机个人版,权限门也进第一版(OpenClaw 的教训)。
enum RiskLevel { ReadOnly, Mutating, Destructive, External }
enum Decision { Allow, AskUser, Deny }- deny 优先求值:命中全局 deny 规则的工具,在模型看到之前就从工具列表里预过滤掉(Claude Code 做法)——既安全又省 token schema。
- 每个工具自报
risk(args):rm -rf、删文件、写系统目录 →Destructive;联网 →External。风险等级驱动确认策略。 - 确认策略分档(对齐 codex
AskForApproval):auto(只读自动放行)/on-request/strict(所有写动作都问)。 - 审校模型是这一层的可视化:高危动作触发时,14B 审校给出"稳/险 + 一句理由",权限确认从冷冰冰的 y/n 变成有依据的第二意见(第 6 章)。
kestrel-backend 是唯一碰 HTTP 的地方,也是本地专项优化的集中地。
- 首跑能力探针:接入新模型/后端时跑一组 ~30 秒微基准(原生工具调用格式可靠性、SEARCH/REPLACE 编辑成功率、指令遵循),自动判定该模型走哪条工具调用路线(原生 FC / Hermes-XML 提示词 / GBNF 约束兜底)与编辑格式,存成
profiles/<model>.toml。本地用户换模型像换灯泡一样频繁——这把"每换个模型手调半天"变成零配置。 - 前缀稳定发送:system prompt + 工具定义完全静态、顺序固定;
cache_prompt默认开;长会话切换用/slots/{id}?action=save|restore。 - llama.cpp 专项:强制
--jinja(否则模型看不见工具,最高频事故);KV 缓存告警 Q8 以上(Q4 显著劣化工具调用);GBNF/json_schema作为约束兜底。 - LM Studio 专项:JIT 冷启动 + TTL 逐出的重试逻辑;利用其上报的 TTFT/tok-s 做自适应调度。
这是 Kestrel 对用户的第一眼记忆点,也是所有本地优化的可视化外壳。
整个架构的中心张力:原则 1(前缀稳定,永不改写历史) 与 上下文管理(必须压缩,压缩即改写) 直接冲突。
解法:让压缩、摘要、预读、检索这些"会污染/改写上下文"的活,全部交给跑在独立进程、独立 KV 缓存的辅助模型。主脑(35B)的 KV 前缀一个字节都不动,辅助模型只在轮次边界递进一份干净的产物。
于是每一个原本"看不见的舒服"都变成了一个看得见的机组成员在干活:压缩=副手、检索=书记、复核=审校、预热=影子槽。剧场不是给算力加皮肤,它是让 §5.2 的"异地压缩"、§7 的影子槽预热这些机制长出一张脸。这就是它同时"眼前一亮"和"经得起追问"的根本原因。
| 成员 | 模型 | 职责 | 底层机制 |
|---|---|---|---|
| 主脑 Lead | Qwen3-35B-A3B | 主循环:规划、写码、决定工具调用 | §5.1 agent loop |
| 副手 Copilot | Qwen3-8B | 后台:压缩历史、摘要长工具输出、预读文件、起草 commit | §5.2 异地压缩 + §7 影子槽 |
| 书记 Librarian | Qwen3-Embedding-4B | 静默:索引项目+记忆,按需递相关片段 | 本地语义记忆检索 |
| 审校 Critic | Qwen3-14B(按需) | 高危动作前独立复核主脑计划,给"稳/险+理由" | §5.3 权限门的可视化 |
为什么角色映射到真实活、而非表演:机组成员之间绝不做表演式对话(那是多 agent 框架烧 token 的教训)。所有"协作"都是真实并行工作的可视化,不是为了演戏多调一次模型。
┌ Kestrel · 会话: 重构 auth 模块 ································· 机组 4/4 [####]
│
│ 你 重构登录逻辑,拆掉那个 800 行的 God 函数
│
│ 主脑 我先看整体结构,读 auth.rs (812 行)
│ └ 副手 812 行 -> 190 token 摘要 [ok]
│ └ 书记 检索到 2 处相似重构
│ 规划拆成 3 块:session.rs / token.rs / guard.rs
│
│ 主脑 [!] 即将删除 verify_legacy()
│ └ 审校 险 — 还有 3 处引用未迁移,建议先加 @deprecated
│ 采纳审校意见,改为标记弃用 [ok]
│
├─ 机组账本 ─────────────────────────────────
│ 副手省 3,020 tok · 书记命中 2 · 审校拦截 1 险动作
│ 主脑 62 tok/s · KV 缓存 [########·] 87% 命中
└─
- 主脑独占主车道,其余成员是缩进的暗色"低语"——不与主对话抢注意力,可一键折叠。
- 交接时刻可见:副手完成时,一条细线从副手车道流进主脑,显示摘要吞吐(
3,200 tok → 180 tok)——价值转移看得见。 - 机组账本:滚动统计"副手替主脑省下多少 token / prefill 秒数、书记命中几次、审校拦下几个险动作"。把无形优化变成可炫耀的数字——这是"说不出来的舒服"变"说得出来"的关键。
- 可命名/换肤:允许用户给自己的模型起名——本地用户对自己的机器有归属感,个性化=情感黏性。
- 成员间零表演式对话;所有协作是真实并行工作的渲染。
- 编排是确定性代码(固定 job-type → 角色路由),不是让主脑花一次昂贵推理去"决定谁来干"。
- 副手永不阻塞主脑:摘要没好,主脑照常推进,产物就绪时才在下一轮边界并入。
- 低语而非喧哗:副手/书记活动默认暗色小字、可折叠。
机组规模随加载的模型数自动伸缩,用户按显存决定跑几个:
- 1 个模型 → 独奏模式,剧场坍缩成单车道,一切照常工作。
- 2 个 → 主脑 + 副手(核心剧场成立)。
- 3–4 个 → 全机组。
降级是零配置的:没加载审校模型,高危确认就回退成普通 y/n;没加载书记,就退回关键词检索。
- 模型池(
kestrel-backend):N 个LlmBackend实例,各指向一个 llama-server 进程/slot 或 LM Studio 模型;逐模型健康/负载探测。 - 作业路由(
kestrel-core):把 job-type(Turn/Compact/Summarize/Prefetch/Retrieve/Review)确定性地映射到机组角色——纯代码,非 LLM 决策。 - 事件带 actor 标签(
kestrel-protocol):每个Event携带actor: CrewRole,前端据此渲染车道。对事件枚举只是小增量。 - 并发与 KV 隔离:副手/书记作业跑在独立 tokio 任务;产物仅在轮次边界并入主脑上下文,绝不中途插入(保前缀稳定)。这正是影子槽机制,现在有了一张脸。
- 配置:机组编制是一张 TOML 表(哪个模型担任哪个角色,或
auto按已加载模型自动分配)。
这些是让机组跑得动的底层机制——用户不直接看见,但它们是账本上那些数字的来源。
| 创新 | 作用 | 归属 |
|---|---|---|
| 影子槽预热(Shadow-Slot Prewarming) | 压缩后的新前缀在备用 slot 后台预填充,轮次边界原子切换——压缩从"卡 30 秒"变"无感知" | 副手的底层实现 |
| 能力探针(Capability Probing) | 接入新模型自动微基准 → 生成协议档位与编辑格式 profile | §5.4,零配置换模型 |
| 确定性回放测试(Replay Harness) | 事件日志录制 → LLM 响应变 fixture → 确定性外壳无模型毫秒级进 CI | 兑现"工程质量" |
| 异地压缩 | 压缩在副手进程完成,主脑 KV 前缀零扰动 | §5.2 + §6.1 的解 |
- ≤10 个内置工具起步(每个 schema 都吃前缀预算):
shell、read、edit、search(grep+glob 合一)、browser(CDP,非视觉)、process(系统管理)。 - 函数名注册表校验:小模型爱幻觉函数名,执行前查注册表,未命中返回可操作错误。
- 编辑工具为弱模型设计:默认 SEARCH/REPLACE 块(最贴训练分布);解析宽容(容忍空白/fence 漂移);匹配失败返回最近似片段;最弱模型留整文件重写回退;编辑格式 per-model 由探针配置。强制编辑前先 Read(防盲改)。
- 工具输出摄入即截断(§5.2);返回高信号信息、语义化而非 UUID。
| 阶段 | 交付 | 机组形态 |
|---|---|---|
| M1 骨架(已交付) | workspace + protocol + core loop + OpenAI 兼容 backend(覆盖 llama.cpp/LM Studio)+ read/search/edit/shell + 权限门 + JSONL 事件日志 + REPL CLI + 回放冒烟测试 | 独奏(仅主脑) |
| WebUI 个人版(进行中) | kestrel-server(axum SSE 事件 + POST Op)+ console(React 扁平深色壳):聊天回合流式 + 权限模态 + 会话回放;单人本机、无认证(ADR-0007) |
独奏,可视化 |
| M2 剧场核心 | 模型池 + 作业路由 + 副手(异地压缩/摘要)+ actor 事件 + 机组账本 | 主脑 + 副手 |
| M3 全机组 | 书记(记忆检索)+ 审校(高危复核)+ 能力探针 + 回放测试进 CI | 全机组 |
| M4 扩展 | browser/process 工具 + 状态树分支(slot save/restore)+ MCP 外接桥 + 投机代理(ADR-0004)+ Loadout 装备编组(声明式能力编组 + 成本感知编译器,ADR-0006) | + 时间旅行、秒回 |
| M5 夜班 | 闲时自主家务:记忆蒸馏、索引、探针复跑、草稿起草 + 夜班报告(默认只读,ADR-0004) | + 夜班 |
| M6 睡眠周期 | 本地 LoRA 自我进化:偏好数据管道 -> 深睡训练 -> 探针考试上岗(北极星,见 docs/vision.md,落地前另立 ADR) | 会做梦的机组 |
| 朋友版(原 v2) | 在 kestrel-server 上叠:认证 + 多会话隔离 + TLS——给朋友用 |
不改 core 一行 |
- 双许可 MIT + Apache-2.0(Rust 生态惯例);
README/CONTRIBUTING/CODE_OF_CONDUCT。 - CI 硬门槛:
cargo fmt --check+cargo clippy -D warnings+cargo test+ 回放测试 +cargo-deny(依赖白名单,强制 §4.1 依赖方向)。 - 每个 crate
lib.rs顶部模块文档声明职责边界与禁止依赖;边界由编译器兜底,非靠自觉。 - 语义化版本;
CHANGELOG遵循 Keep a Changelog;核心类型 crate(kestrel-protocol)稳定后谨慎破坏性变更。
重大选型的备选方案、否决理由与重开条件已拆分为独立文件,见 adr/:
- ADR-0001 语言选型:Rust
- ADR-0002 架构风格:库核心 + 薄适配器 + 事件流
- ADR-0003 已否决方案速查
- ADR-0004 成本模型反转:为"token 免费、延迟贵"设计
- ADR-0005 能力披露分层:渐进式披露,但只许向尾部追加
- ADR-0006 Loadout:声明式能力编组与分发
- ADR-0007 WebUI 个人版:浏览器 + axum
- ADR-0008 本地化:表现层 i18n + 语言中立的事件日志
- ADR-0009 数据存储位置与布局:OS 标准目录 + 版本化迁移
- ADR-0010 模型启动器:把模型作为 agent 的一部分来启动/监督
本文档中的调研结论(§2、§5.4 等)的完整证据见 research/。
本文档为创始版(v0.1)。设计决策均附调研依据;代号 Kestrel 可换,不影响任何技术选型。