- 人工智能
- AI Agent
- 代码智能体
- 自主智能体
- MCP Clients
- CLI
【免费下载链接】atomcode
Claude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get Started
本文以 crates/atomcode-kernel/SPIKE.md 为主体,讲清 atomcode 项目中「中立内核(neutral kernel)」平台策略的 Phase A0 设计验证:内核如何用一条双向、可序列化的AgentCommand/AgentEvent句柄驱动一个完整的 agent 运行循环,并通过 14 项可执行论断(claim)逐一证明「审批不进内核、工具选择性挂载、一种原语适配全部前端」等关键设计假设。读完本文,你能理解该内核的 Driver 模型与 Hook 机制(perceive vs inject),并能直接用cargo test -p atomcode-kernel复现全部验证结论。
一、Spike 的定位:只验证 API 形状,不追求实现
SPIKE.md 开篇明确了这个 crate 的性质:
Design-validation spike for the neutral-kernel platform strategy … Internals are minimal/throwaway — the public APIshapeis what Phase A1 carries the proven production hot-paths into.
即:atomcode-kernel是一个设计验证 spike(Phase A0),对应原文引用的中立内核平台策略规格(2026-06-05-atomcode-kernel-platform-strategy.md)。它的内部实现是「最小化、可抛弃」的,真正要证明的是公共 API 的形状(shape)是否成立——Phase A1 将把生产环境已验证的热点路径「原样搬进」这些槽位,而不是重写。
这一点在 Cargo.toml 中也得到印证:
[package] name = "atomcode-kernel" description = "Spike: neutral agent kernel (Phase A0 — design validation, throwaway internals)" publish = false同时该 crate 通过一个test-supportfeature 提供test_support::isolate_home(),供各 crate 的测试隔离~/.atomcode,避免污染开发者的真实数据目录。
二、要证明什么:14 项核心论断一览
SPIKE.md 的核心是一张「论断 → 验证位置」的表。每一项 claim 都对应一个具体的测试函数,跑cargo test -p atomcode-kernel即可全部复现:
| # | 论断(Claim) | 验证位置 |
|---|---|---|
| 1 | 中立内核——一个 turn 可以在没有 persona、没有任何 middleware 的情况下运行 | tests/spike_claims.rsneutral_turn_runs_without_persona_or_middleware |
| 2 | 审批是一种基于 id 关联往返(round-trip)的外部 middleware | 同上approval_middleware_gates_risky_tool_via_id_roundtrip |
| 3 | 工具选择性挂载——未挂载的工具不可见、不生效 | src/tool.rs 测试only_mounted_tools_are_exposed_or_resolvable |
| 4 | 同一个原语同时服务 one-shot 与交互式 driver | 同上one_shot_adapter_auto_answers_and_aggregates+ examples/minimal_specialization.rs |
| 5 | wire 兼容(serde 往返可序列化)→ web/daemon 可复用同一 seam | 同上events_and_commands_are_wire_serializable |
| 6 | LifecycleHooks——turn 级注入(turn_end继续循环)+TurnStarted观察 | 同上lifecycle_hook_injects_and_continues_loop |
| 7 | 完整 LifecycleHooks 面——8 个 turn 级点全部接线且触发 | 同上lifecycle_hooks_complete_surface_all_fire |
| 8 | 执行状态被记录(Message.meta)+ 投影给 LLM(尾部 reminder)+ 缓存安全 | 同上execution_state_recorded_projected_to_llm_and_cache_safe |
| 9 | 轮次预算投影给 LLM("round X/Y")+ 硬上限,记录在meta.round,缓存安全 | 同上round_budget_projected_to_llm_and_hard_capped |
| 10 | on_model_response拿到&mut Message——可以变换响应,变换会落盘(经 Snapshot 验证) | 同上on_model_response_can_transform_response_into_storage |
| 11 | on_model_response对 tool_calls 的删改被内核尊重(被删的调用不执行) | 同上dropping_tool_calls_in_on_model_response_prevents_execution |
| 12 | ToolMiddleware 的before可改写/拦截(不产生"幽灵 ToolStarted")+after变换结果 | 同上tool_middleware_rewrites_blocks_and_transforms |
| 13 | 命令级审批——参数感知的Tool::risk(args);审批中间件拦截危险命令、放行安全命令、按会话缓存授权 | 同上dangerous_command_requires_approval_safe_does_not_and_grant_is_cached |
| 14 | user_prompt_submit可以拦截 prompt(返回Err→ prompt 被拒、不产生 turn、不入库) | 同上user_prompt_submit_can_block_a_prompt |
这张表是整份 spike 文档的骨架:它把一个「内核应该长什么样」的抽象主张,拆成了 14 条可被编译器与测试裁决的具体命题。下文选取几条关键 claim,结合源码看它们到底验证了什么。
三、代表性论断的源码剖析
3.1 Claim 1:没有 persona、没有 middleware,turn 依然能跑
neutral_turn_runs_without_persona_or_middleware 只注册了一个EchoTool,构造 Agent 时不给 hooks、不给 middleware:
let handle = Agent::builder() .provider(provider) .tools(reg.mount(&["echo"])) .build() .spawn();测试断言三件事:安全工具被执行了(echoed)、turn 正常完成(completed)、内核没有发出任何Request事件(!requested)。最后一条是关键——它证明「中立」不是口号:没有任何特殊化配置时,内核自身不会凭空产生审批、提问等外部交互需求。
3.2 Claim 3:选择性挂载——未挂载的工具零影响
这一 claim 的实现在 tool.rs 的ToolRegistry::mount:
/// Select the subset exposed to the LLM. Unmounted tools never produce a /// ToolDef and are not resolvable during a turn → zero effect on the agent. pub fn mount(&self, names: &[&str]) -> MountedTools从源码结构看,mount会先select出名字子集,然后固化成一个MountedToolsSnapshot(含ToolCatalogRevision、选中的工具集合、以及预先渲染好的ToolDef列表)。这意味着模型在某一轮看到的工具定义与该轮实际可解析的工具来自同一个快照,未挂载的工具既不会产生ToolDef供 LLM 看见,也不会在执行阶段被解析到——正是「invisible/inert」两个词的落点。
tool.rs模块头部还写明了内核的信任模型契约,这是理解挂载语义的必要前提:内核不做沙箱,挂载一个工具就是把宿主进程的完整环境权限(文件系统、网络、环境变量)授予其execute;RiskLevel只是建议性元数据而非执行边界;内核在这一高度上唯一内建的安全机制是工具结果字节上限(AgentBuilder::max_tool_result_bytes,默认 64 KiB)。OS 级隔离(seccomp、容器、独立子进程)是嵌入方的责任。
3.3 Claim 2 / 13:审批完全活在 specialization 一侧
approval_middleware_gates_risky_tool_via_id_roundtrip 展示了审批的完整回路:
- 模型发起
risky_write工具调用; ApprovalMiddleware(位于 testkit.rs,即 specialization 侧)经RequestCtx挂起,内核发出AgentEvent::Request { id, kind: "approval", payload };- driver 收到后回发
AgentCommand::Respond { id, value: {"decision": "allow"} }; - 工具执行,结果以
wrote: ...出现。
Claim 13 的 dangerous_command_requires_approval_safe_does_not_and_grant_is_cached 进一步验证了三个层次的行为:
- 参数感知(arg-aware):同一个
bash工具,ls被risk(args)评为 Safe,直接执行、零次审批询问;rm -rf /tmp/x被评 Risky,触发一次审批。Tooltrait 中对应方法签名为fn risk(&self, _args: &str) -> RiskLevel(tool.rs L215-L217),默认Safe——工具自己最清楚哪条命令危险; - 会话级授权缓存:第一次
rm -rf的Respond带上"remember": true后,同一会话中完全相同的危险命令第二次执行时asked仍为 1(只问过一次),ran为 2(两次都执行了)。
这正呼应了 SPIKE.md 的边界事实第 1 条:内核核心文件(agent.rs、event.rs、tool.rs)从不出现 "approval" 一词,审批整个住在testkit::ApprovalMiddleware之上,构建在RequestCtx::request这个通用往返之上。
3.4 Claim 8 / 9:执行状态的「记录 → 投影 → 缓存安全」三段式
这两条 claim 验证的是同一套规则的两侧:
记录侧:on_model_response时,内核把执行统计写入 kernel 原生的Message.metasidecar。当前 message.rs 中的 MessageMeta 携带tokens、elapsed_ms、ctx_window、used_tokens、utilization、round,以及turn_id/request_id等关联 ID。Spike 文档写的是「MessageMeta只持有内核实测事实(tokens、elapsed_ms、ctx_window、used_tokens、utilization、round);cost 等特化关切留在内核之外」——当前字段集是其自然演进(新增的均为#[serde(default)]的增量字段,旧快照仍可反序列化)。
投影侧:pre_requesthook 把当前利用率投影为一条尾部 reminder发给 LLM。测试 execution_state_recorded_projected_to_llm_and_cache_safe 用MockProvider捕获了两次真实外发请求,逐条断言:
- 第 1 次调用只有用户消息,尚无 reminder(历史里还没有 meta);
- 第 2 次调用中,历史用户消息逐字节相同(
b[0] == calls[0][0],前缀缓存安全的直接证据); - assistant 消息文本保持干净(meta 是 sidecar,不烘焙进
text); - 最后一条消息正是投影出的
("[User]", "[ctx 10%]")尾部提醒。
硬上限侧:round_budget_projected_to_llm_and_hard_capped 让 MockProvider 脚本化 4 轮工具调用(模型"永不收手"),而 builder 设了.max_rounds(3)。断言结果:
- 实际 LLM 调用只有 3 次,第 4 轮脚本从未被请求;
- 投影逐轮升级:
[round 1/3]→[round 2/3]→[round 3/3 - final round, wrap up now]; - 触顶时发出含 "max rounds" 的
Error事件; - 每轮 assistant 消息的
meta.round依次为1, 2, 3。
max_rounds在引擎中就是一个普通的Option<u32>(engine.rsAgentBuilder::max_rounds),但 SPIKE.md 强调其角色是「safety fuse」——即使模型无视投影的提醒,循环也必然终止。
3.5 Claim 10 / 11:on_model_response拿到&mut Message
on_model_response_can_transform_response_into_storage 用RedactHook把模型输出的"my password is SECRET"改写成"my password is [redacted]",随后发送AgentCommand::Snapshot命令,从快照消息里验证变换后的文本确实落盘、SECRET已消失,且 hook 看到的就是内核填好meta(tokens.prompt == 50)的响应对象。
Claim 11 则验证了tool_calls的可编辑性:DropToolsHook在on_model_response里清空tool_calls后,内核不再执行任何工具(无ToolStarted/ToolResult),turn 因 pending 为空而正常完成。这与 hook.rs 中 trait 方法的注释一致:on_model_response可以「observe 或 transform——包括删除/改写tool_calls,内核会尊重(HONORS)」。
3.6 Claim 14:user_prompt_submit可以拒绝整条 prompt
user_prompt_submit_can_block_a_prompt 中RejectPromptHook对 prompt 文本返回Err,随后断言:驱动收到含 "rejected" 的Error、没有TurnStarted(turn 从未开始)、且 Snapshot 中不存在被拒的 prompt 文本(它从未进入对话)。这与 hook.rs 中user_prompt_submit的 trait 注释完全对应:「rewrite / augment the text, or returnErr(reason)to BLOCK the prompt — it never enters the conversation and no turn runs」。
四、Driver 模型:一个原语,四种前端形态
SPIKE.md 的 Driver 模型一节是整个 API 设计的主张核心:
One primitive: a long-lived session consuming
AgentCommandand emittingAgentEvent(AgentHandle). The round-trip seam is the id-correlatedAgentEvent::Request{id,kind,payload}↔AgentCommand::Respond{id,value}.
关键细节是:解析 middleware 挂起的那个oneshot只存在于RequestCtx(内核内部),绝不出现在任何事件里。正因为事件与命令中不含进程内的 channel 句柄,它们才可以整体 serde 序列化——同一套协议既能在进程内工作,也能跨 wire 工作。四种 driver 形态都坐在这一原语之上:
| Driver | 命令来源 | 事件去向 | Request 由谁回答 |
|---|---|---|---|
| one-shot / CI / CodeReview | 一条SendMessage | 聚合为Outcome | AutoRespond策略 |
| TUI | 按键 | 渲染循环 | 模态框 →Respond |
| Web | WS/HTTP →AgentCommand | AgentEvent→ SSE/WS | 用户 →Respond帧 |
| server / daemon | 每会话 RPC | 每会话 SSE | 策略或远端用户 |
其中 one-shot 路径在 engine.rs 中实现为run_to_completion(input, policy):它内部其实就是spawn()出一个 handle,然后自动循环消费事件——遇到Request就用AutoRespond(AllowAll / DenyAll)回Respond,最终把TextDelta聚合成text、把所有ToolResult收集进tool_results、把终止原因放进stop,得到结构化的 Outcome(还带error、http_status、error_code等字段,失败不会被吞掉伪装成成功)。
Claim 5 的 events_and_commands_are_wire_serializable 用最小的 serde 往返(AgentEvent::Request与AgentCommand::Respond各序列化/反序列化一次)封住了这一主张。examples/minimal_specialization.rs 则把两种形态放在一起演示:同一个AgentBuilder(挂EchoTool、RiskyWriteTool、persona、ApprovalMiddleware)先跑run_to_completion("do the demo", AutoRespond::AllowAll),再spawn()成交互式 handle,在事件循环里手动把Request答成allow——claim 4 的完整证据。
五、Hook 面:perceive 与 inject 是两套机制
SPIKE.md 对 hook 面的划分值得单独强调,因为它划定了观察与干预的能力边界:
- perceive(感知)= 只读的
AgentEvent流。观察者无法改变循环; - inject(注入)=
LifecycleHookstrait。它在循环内部运行,可以修改状态、甚至让循环继续。
trait 在 hook.rs 声明,各钩子按「是否落盘(PERMANENT/EPHEMERAL)」标注了存储契约,例如:
session_start:会话开始、turn 之前,可注入种子上下文/persona,持久化;user_prompt_submit:用户消息进循环前,改写或Err拦截,持久化;turn_start:每个用户消息触发一次(非每 round),持久化;pre_request:每次 LLM 请求前,改写外发消息;EPHEMERAL——作用于每请求的克隆,绝不污染前缀缓存;on_model_response:拿到尚未入库的&mut Message(含内核填好的 meta),可变换,持久化;offer_continuation:模型想停(无 tool calls)时被调用,返回Some(text)注入后续 user 消息并继续循环;turn_complete/on_error/session_end:回合结束、错误、会话结束的观察点。
SPIKE.md 列出的是 8 个 turn 级点(session_start、user_prompt_submit、turn_start、pre_request、on_model_response、turn_end、on_error、session_end),Claim 7 断言「每一个都被接线且会触发」。当前 lifecycle_hooks_complete_surface_all_fire 实际校验的已经是扩展后的 12 个点——新增了on_request(只读的最终外发请求观察,telemetry/前缀缓存 RCA 的落点)、on_text_delta/on_reasoning_delta(流式分片的就地变换)以及offer_continuation/turn_complete(spike 时期写作 turn 续跑/结束的机制,现与 README 中描述的 verify loop seam 对应)。这个演进方向说明 spike 的 API 形状经受住了压力:新能力是加钩子,而不是改形状。
工具级关切则被明确划到另一套组合机制ToolMiddleware(before+after),不进LifecycleHooks。Claim 12 的 tool_middleware_rewrites_blocks_and_transforms 用三个场景钉死了语义:
- (a)
before改写参数 → 改写后的参数到达执行层(结果含 "rewritten"); - (b)
before拦截 →不产生幽灵ToolStarted事件,但仍给出一条内容含 "blocked" 的ToolResult(模型不会被蒙在鼓里); - (c)
after变换结果 → 结果以[truncated]开头。
跨进程注入则复用 id 关联的Request/Respond往返:hook 向远端 driver 提问并 await 答案,机制与审批完全同构。
六、关键边界事实(Key boundary facts)
SPIKE.md 明确列出了三条边界事实,是理解「内核不做什么」的浓缩:
- 内核核心从不点名 "approval"。工具携带参数感知的
risk(&str) -> RiskLevel方法(工具自己知道哪些命令危险);审批整体住在testkit::ApprovalMiddleware(specialization 侧)之上。ApprovalMiddleware还持有会话级授权缓存——相同的危险命令一个会话只问一次。 ToolContext不携带任何语义/图/LSP 服务。当前 ToolContext 定义 只有working_dir、cancel(协作式取消 token)、progress(进度旁路)与requester(提问 seam)——「proving the kernel needs none」。- Crate 被排除在 workspace
default-members之外。工作区根 Cargo.toml 中default-members只有atomcode-cli、atomcode-daemon、atomcode-telemetry、atomcode-tuix,所以产品构建完全不受该 spike 影响。
另外两条从源码中可以确认的、与 spike 契约一脉相承的约束值得开发者留意(见 tool.rs 模块文档):
- must-not-panic 契约:
execute不允许 panic。工作区 release profile 是panic = "abort"(根 Cargo.toml 中profile.release明确设置),一个 panic 的工具会直接带走宿主进程; - 结果字节上限:
AgentBuilder::max_tool_result_bytes(默认 64 KiB)是内核对工具的唯一内建约束——过大的ToolResult.content会被截断,防止失控工具打爆上下文窗口。
七、运行与验证
SPIKE.md 给出的运行方式只有两条命令,均可以在当前仓库直接执行:
cargo test -p atomcode-kernel cargo run -p atomcode-kernel --example minimal_specialization前者跑通 tests/ 下全部测试(包括承载 14 项 claim 的 spike_claims.rs);后者运行最小程序演示,先以 one-shot 形态打印聚合结果,再以交互式形态走一遍Request → Respond的审批往返。dev-dependencies 里启用 tokio 的test-utilfeature,让涉及审批挂起的测试能用#[tokio::test(start_paused = true)]在虚拟时间上瞬时推进,测试因此不需要真实等待。
testkit模块(testkit.rs)提供了验证所需的全部零件:MockProvider(脚本化多轮流事件)、EchoTool/RiskyWriteTool/DangerousBashTool(不同风险特征的工具桩)、ApprovalMiddleware及各类 hook/middleware 示例(RedactHook、DropToolsHook、RejectPromptHook、RoundBudgetHook、ArgRewriteMiddleware等)。它们既是 spike 测试的道具,也是理解每个 seam 用法的最小可读样例。
八、Phase A1:形状已证,热路径入场
SPIKE.md 的收尾给出了明确的下一阶段路线图——把生产热点路径搬进已验证的槽位,而不是重写:
| 生产组件 | 落点 |
|---|---|
TurnRunner循环 | agent.rs |
ctx/render | persona 注入点背后的一个CtxBuilder实现 |
conversation | message.rs |
| 中立 provider 实现 | provider.rs |
约束只有一条:保留前缀缓存(prefix-cache)不变量与已有的边缘情况修复。从前文 Claim 8/9 对「历史消息逐字节不变」的断言可以看出,这些不变量在 spike 阶段就已是被测试守护的契约,而非口头约定。
小结
crates/atomcode-kernel/SPIKE.md 用一份 14 项论断的清单,把「中立内核」从一个架构愿景变成了可编译、可测试、可复现的事实:审批、预算、提示拦截、流式变换等能力全部以 middleware/hooks 的形式外挂在AgentCommand/AgentEvent这条可序列化协议之上;四种前端形态共享同一个原语;记录走Message.metasidecar、投影走pre_request尾部 reminder,从而在把执行状态反馈给 LLM 的同时保住前缀缓存。若你在设计自己的 agent 框架,这个 spike 提供的最大参考价值在于方法论——先以最小实现把 API 形状变成测试断言,再谈往里搬生产代码。
- 人工智能
- AI Agent
- 代码智能体
- 自主智能体
- MCP Clients
- CLI
【免费下载链接】atomcode
Claude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get Started
相关推荐
IronClaw 静态内核进程日志设计证明:以中立进程日志统一 Agent Turn 与扩展生命周期
IronClaw 静态内核进程日志设计证明:以中立进程日志统一 Agent Turn 与扩展生命周期 本篇技术指南以 IronClaw 仓库中的内部设计文档《S
人工智能AI 应用交互助手AI AgentFreeRTOS 内核 TaskIncrementTick 内存安全证明(CBMC 形式化验证)实战解析
FreeRTOS 内核 TaskIncrementTick 内存安全证明(CBMC 形式化验证)实战解析 导读 本文围绕 FreeRTOS 官方 CBMC 形式
操作系统嵌入式OS嵌入式物联网GitHub_Trending/om/OM1 Unitree G1:人形机器人AI控制框架
GitHub_Trending/om/OM1 Unitree G1:人形机器人AI控制框架 你是否还在为Unitree G1人形机器人的AI控制开发感到困扰?复
区块链Web3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考