# AgentOS 工程约定 本文件面向在本仓库内工作的代码代理。修改前先读本文件、根 `Cargo.toml`,再读目标 crate 的源码。不要根据旧 Python 版本、Octos 或 `npu_features/` 中的厂商样例臆测当前行为;`crates/` 才是实现事实来源。 ## 项目目标 AgentOS 是运行在裸 Linux 上的 low-level OS agent,而不是 Linux 发行版、 容器沙箱或内核 fork。核心原则是尽量复用 Linux 原生能力:进程 UID/GID、 文件权限、Unix 凭据、pidfd、`no_new_privs`、Btrfs subvolume/snapshot 和设备 ioctl。 当前安全模型是稳定的 `AgentId -> (owner UID, agent UID, agent GID)` 绑定: - agent 进程不能以 UID 0 或 GID 0 运行; - 每次执行前必须核对进程的 effective UID/GID; - 一个状态数据库中,agent UID 只能绑定给一个 active `AgentId`; - 不额外构造 namespace、容器或通用 sandbox; - `no_new_privs` 是补充保护,不能替代 UID/GID 和工具策略; - 模型不能获得任意 shell,更不能直接获得 root shell。 不要把 `owner_uid` 与 `agent_uid` 混为一谈。前者表示拥有者,后者才是内核 实际执行主体。新增 daemon 或 broker 时,Unix socket 对端必须用内核提供的 peer credentials 鉴权,不能信任请求体里的 UID。 ## 当前成熟度边界 已经可用: - `agentos` one-shot CLI、doctor 和确定性 fake backend; - OpenAI-compatible Chat Completions / Responses 非流式后端; - `agentos.chat.v1` 内部协议、严格工具 schema、预算和循环检测; - 只读 Linux 工具、SQLite 审计/记忆、Btrfs worldline 与 copy fallback; - CALCULET PCIe ABI、Rust ioctl/DMA 包装、Calbin 解析、张量/命令结构; - Candle/Qwen3 host pipeline、tool-call 模板和 CALRT 张量适配; - 旧 llama-server UDS bridge;HTTP 消息格式直接承载在 AF_UNIX 上,不经过 TCP。 尚未完成: - 纯 Rust CALRT 的 CCU relocation、job launch/completion 和设备 KV reset; - 可在真实 NPU 上完成推理的 `npu_candle` backend; - typed mutation broker 和任何变更型系统工具; - 长驻 AgentOS control daemon、control UDS RPC、服务安装和完整硬件端到端测试。 `ConfiguredRuntime::submit()` 当前必须 fail closed 并返回 `HardwareExecutionUnavailable`。在没有真实板卡证据前,不得把 `npu_candle` 标记为 ready,不得让 `auto` 选择它,也不得用 mock 测试宣称 硬件推理已验证。 ## Workspace 与模块所有权 除 `agentos-cli` 外,`crates/` 下均为 library crate。 | Crate | 职责 | | --- | --- | | `agentos-cli` | `agentos` 与 `agentos-npu-bridge` 两个 binary;只做参数解析和装配入口 | | `agentos-runtime` | composition root、环境配置、identity/backend/tool 装配、doctor | | `agentos-agent` | 有预算的 agent loop、工具调度、循环检测、审计事件 | | `agentos-protocol` | provider-neutral `agentos.chat.v1` 类型与 `ChatBackend` trait | | `agentos-inference` | ntex HTTP、OpenAI-compatible、subprocess、fake/scripted backend | | `agentos-tools` | fail-closed registry、schema 校验、审批契约和 Linux 只读工具 | | `agentos-core` | ID、principal、execution identity、审计和 worldline 公共类型 | | `agentos-kernel` | Linux/rustix 边界、凭据、pidfd、文件原子写、Btrfs ioctl | | `agentos-memory` | SQLite WAL、FTS5、principal 绑定、memory 和 audit event | | `agentos-worldline` | 类 Git 的 filesystem history、branch/commit/diff/rollback/recovery | | `agentos-npu` | NPU probe、旧 bridge、Rust Candle NPU backend 装配 | | `agentos-candle` | Qwen3 模板/tokenizer/sampling 与 Calbin prefill/decode host runner | | `calculet-pcie-abi` | 驱动 0.9.0 / ABI 1.0.0 的 64-bit Linux ioctl 布局 | | `calculet-pcie` | 安全的设备、BAR、DMA、MSI、reset 和 board/process API | | `calculet-calrt` | Calbin、allocator、tensor、command stream、DeviceIo 和 runtime 骨架 | 保持依赖方向从高层向低层: ```text agentos-cli -> agentos-runtime -> agentos-agent/tools/inference/npu/worldline/memory agentos-npu -> agentos-candle -> calculet-calrt -> calculet-pcie -> calculet-pcie-abi agentos-kernel/core/protocol 是底层公共边界 ``` 不要让底层 crate 反向依赖 CLI 或 runtime。跨 provider 的消息类型放在 `agentos-protocol`;Linux syscall/ABI 放在 `agentos-kernel` 或对应的 `calculet-*` crate;composition 逻辑只放在 `agentos-runtime`。 ## 不可破坏的设计约束 ### Linux 与系统调用 - 项目只支持 Linux;当前 CALCULET ABI 只验证过 64-bit Linux。 - 项目自有的低层 syscall/ioctl 优先走 `rustix`,不要直接新增 `libc` 调用。 - 这不表示最终二进制完全不链接 libc;Rust `std`、OpenSSL 等依赖仍可使用 系统 ABI。 - `unsafe` 仅允许出现在无法避免的 ABI 边界,必须就结构布局、指针生命周期 和 opcode 写英文 `SAFETY` 注释,并在调用前完成长度、对齐和范围校验。 - 不要为了 worldline patch 内核。优先使用现有 Btrfs ioctl;非 Btrfs 环境保留 copy fallback。 ### 工具与权限 - registry 必须显式 allowlist;未知工具一律拒绝。 - 所有 tool schema 默认 strict:object、列出全部 required、 `additionalProperties: false`。 - 参数和输出必须有字节上限,外部命令必须有 deadline、`kill_on_drop`,并移除 API key 环境变量。 - 只有标记为 `ConcurrencyClass::Safe` 的只读工具可以并行;变更工具必须 exclusive。 - 现有 `ApprovalLedger` 只是进程内、单次、精确参数绑定的契约。实现 mutation 前还必须有 typed broker、内核凭据校验、持久审计和失败恢复,不能把审批 ID 当作任意命令授权。 - 不得增加通用 `shell`、`exec`、任意路径写入或任意 systemd unit 工具。 ### 推理后端 - 内部统一使用 `agentos.chat.v1`,provider 差异留在 backend adapter。 - 缺少 NPU 硬件时的 mock 是远程 OpenAI-compatible 模型,不是内置 `FakeBackend`。远程传输可以使用 HTTP 或 HTTPS;使用 HTTPS 时必须验证 peer。 - 本机常驻模型服务必须使用 filesystem Unix domain socket,禁止监听或连接 localhost、loopback 或其他 TCP 地址。纯 Rust Candle in-process 路径不需要 IPC。 - 本机 llama-server 可以保留 OpenAI-compatible HTTP 消息格式,但必须通过 ntex 自定义 connector 承载在 AF_UNIX 上,用户配置只能暴露 socket path,不能 接受本机 URL。 - OpenAI-compatible HTTP 和 UDS HTTP client 使用 `ntex`,不要引入 `reqwest`。 - 禁止自动 redirect;响应大小、超时和 retry 必须有界。 - 支持 Chat Completions 与 Responses 两种方言,但不要假设所有兼容服务支持 完全相同的字段。新增兼容逻辑必须有请求构造和响应解析测试。 - subprocess backend 只通过 stdin/stdout 传 JSON,stderr 仅用于有界错误信息; 不要把 secret 传给子进程。它只作为显式测试/bridge adapter,不能代表本机 常驻模型传输,也不能进入 `auto`。 - `auto` 当前顺序是 UDS legacy NPU、远程 OpenAI-compatible。Fake 和 subprocess 必须显式选择,Rust Candle NPU 在硬件提交完成前不能进入 auto。 ### NPU - `npu_features/` 是忽略提交的厂商源码/部署抓取,仅作逆向参考,不能成为 发布包运行时依赖。 - 原始版本边界是 driver package 0.9.0、driver ABI 1.0.0、CALRT 0.7.6。 - ioctl struct 使用 `repr(C, packed)`,任何改动都必须同步 size/opcode 测试。 - DMA 单次上限 8 MiB,已知 H2C/C2H 各 8 个 channel;不要绕过现有验证。 - Calbin 参数部署会真实写设备。没有用户明确要求和真实硬件测试计划时,不要 默认开启 `deploy_parameters`。 - captured Qwen3 fixture 的 logits 是 151936,tokenizer vocab 是 151669;额外 267 个 padded logits 必须在采样前屏蔽。 - host-side `MockDevice` 测试只能验证解析、地址、buffer 和命令编码,不能证明 job submission、同步、KV cache 或输出数值正确。远程模型 mock 同样不能证明 NPU 硬件正确。 - legacy llama-server 的默认 socket 是 `/run/agentos/npu.sock`;doctor 必须检查 它确实是可写的 Unix socket,不能仅检查路径存在。 ### Worldline 与持久化 - Btrfs commit tree 使用 readonly snapshot;branch/current 使用 writable snapshot。 - copy fallback 用于开发和无 Btrfs 环境,但当前不会提供内核强制的只读 commit tree。不要在文档里把它描述成与 Btrfs 等价的不可变性。 - commit 是内容/元数据哈希标识;rollback 必须创建新 commit,不能改写历史。 - checkout 要保留 transaction journal、目录 fsync 和可恢复的 replaced tree。 - 分支提交必须检查 base HEAD,禁止 stale branch 覆盖新 HEAD。 - SQLite principal 绑定不可静默重绑;schema 变更要考虑已有数据库升级。 ## Rust 与依赖规则 - 使用 workspace 的 Rust edition,不添加 MSRV 或 `rust-toolchain.toml`。 - 第三方依赖集中写在根 `Cargo.toml` 的 `[workspace.dependencies]`。 - 版本只写主版本号,例如 `serde = "1"`,不要固定 `1.2.3`。 - 内部 crate 统一用 `*.workspace = true`。 - 读取依赖源码时,从 `$CARGO_HOME/registry/src` 找实际锁定版本,不靠记忆猜 API。 - 优先复用已有 crate 和抽象,不复制协议类型、HTTP client、schema validator、 runtime probe 或设备 ABI。 - 代码注释默认英文;用户可见文档分别维护英文与中文。 - 不要加入 Python、`reqwest` 或直接 `libc` 依赖。 - 保持开发/测试 profile 的快速编译取向,除非有基准数据,不要随意调高 dev 优化或减少 codegen units。 ## 修改流程 1. 用 `rg` 定位实现和调用方,先确认改动属于哪个 crate。 2. 先写清安全边界和失败模式;系统层能力默认 fail closed。 3. 修改公共协议时,同步所有 backend、tool adapter、CLI 和双语 README。 4. 新增环境变量时,同步 `RuntimeConfig::from_env`、doctor 和配置表。 5. 新增 NPU ABI 时,对照 `npu_features/cal-pcie-0.9.0` 或 CALRT 源码,并补布局、 opcode、边界和 mock 测试。 6. 不要改写或删除用户的 `npu_features/` 抓取、模型文件和未提交工作。 完成前至少执行: ```bash cargo fmt --all --check cargo test --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings ``` 涉及 CLI 时还要实际运行相关命令;涉及 OpenAI-compatible 时至少覆盖两种 API 方言的 serialization/parsing;涉及硬件而本机无板卡时,明确报告未执行的验证。 部分 NPU 测试依赖本地、被 `.gitignore` 忽略的 `npu_features/snapshot_20260801`。fixture 不存在时,应将硬件抓取测试与普通 workspace 测试分层,而不是把模型数据提交进仓库或伪造通过结果。 ## 完成标准 一次改动只有在以下条件都满足时才算完成: - crate 边界和依赖方向没有被破坏; - 非法输入、权限不足、后端缺失和硬件缺失均 fail closed; - 无 secret 出现在日志、tool output、子进程或 doctor 报告中; - 单元/集成测试覆盖正常路径与关键拒绝路径; - fmt、workspace test、严格 clippy 通过; - README 与实际成熟度一致,未把 host-side `MockDevice` 或远程模型 mock 写成 NPU 硬件验证。