Files
agentos/README.md
T
emmettlu f863f83960 chore: Add Chinese README and refactor NPU backend to use Unix domain sockets
- Introduced a new Chinese version of the README (README_CN.md) to provide localized documentation for AgentOS.
- Refactored the NPU bridge to utilize Unix domain sockets instead of HTTP loopback, enhancing security and performance.
- Updated the NPU backend to include a server socket configuration, ensuring proper communication over Unix sockets.
- Modified the Ntex client to support both network and Unix socket transports, improving flexibility in backend communication.
- Adjusted validation logic to enforce the use of Unix sockets for local model endpoints, rejecting loopback HTTP addresses.
- Enhanced error messages and documentation throughout the codebase to clarify the new socket-based architecture.
2026-08-02 15:48:09 +08:00

352 lines
15 KiB
Markdown

# AgentOS
[简体中文](README_CN.md)
AgentOS is a low-level, Linux-only operating-system agent written in Rust. It
combines a bounded tool-calling loop with native Linux identities, read-only
system inspection, durable audit state, Btrfs-backed filesystem history, and
multiple inference backends—including an in-progress pure-Rust path for a
CALCULET NPU.
> **Development status:** the remote OpenAI-compatible mock path and explicit
> subprocess/fake test adapters are implemented. Locally deployed model services
> use Unix domain sockets, never localhost or TCP. The Rust Candle/CALRT host
> pipeline can parse and validate the captured Qwen3 deployment, but CALRT
> hardware job submission is not implemented yet. `npu_candle` therefore fails
> closed and is not production-ready.
## Design
AgentOS is not a container sandbox or a kernel fork. Its isolation boundary is
the Linux process identity:
- every active `AgentId` is stably bound to an owner UID and a non-root agent
UID/GID;
- the runtime verifies the process effective UID/GID before every run;
- UID 0 or GID 0 agents are rejected;
- `no_new_privs` is enabled before model/tool execution;
- the model sees an explicit tool allowlist, never a raw root shell;
- system mutation is intentionally unavailable until a typed, authenticated
broker is implemented.
The current execution path is:
```text
agentos CLI
-> runtime + Linux principal
-> budgeted agent loop
-> ChatBackend (remote OpenAI-compatible / UDS NPU / in-process Candle)
-> strict ToolRegistry (read-only Linux tools)
-> SQLite audit log
filesystem state -> WorldlineStore -> Btrfs snapshots or copy fallback
NPU host path -> Candle/Qwen3 -> CALRT -> PCIe driver ABI
local model IPC -> Unix domain socket -> OpenAI-compatible HTTP payload
```
## What works today
- Provider-neutral `agentos.chat.v1` messages, tools, responses, usage, and
backend interface.
- Remote OpenAI-compatible non-streaming Chat Completions and Responses APIs
over `ntex`, with bounded responses, timeouts, retries, and verified TLS when
HTTPS is selected.
- OpenAI-compatible HTTP payloads over an AF_UNIX transport for the local legacy
llama-server, without a localhost/TCP hop.
- A JSON stdin/stdout subprocess adapter retained for explicit tests and custom
bridges; it is not the transport for a persistent local model service.
- Bounded agent iterations, tool calls, elapsed time, total tokens, parallel
read-only calls, duplicate call-ID checks, and no-progress/cycle detection.
- Strict JSON tool schemas, allowlists, argument/output limits, deadlines, and
hashed audit records.
- Read-only Linux, package, systemd, journal, and CALCULET NPU inspection.
- SQLite WAL state, FTS5 memory primitives, immutable principal bindings, and
append-only agent/tool audit events.
- Btrfs subvolume/snapshot worldlines with branch, diff, commit, log,
non-destructive rollback, stale-branch protection, and checkout recovery.
- Rust representations of CALCULET driver 0.9.0 / ABI 1.0.0, BAR and DMA access,
Calbin 0.7.6 parsing, memory allocation, tensors, and command encoding.
- A Candle-based Qwen3 host runner with chat templates, tool-call parsing,
sampling, prefill/decode contracts, BF16/F32 logits, and vendor tiled-logit
decoding.
## Workspace
`agentos-cli` is the only binary crate; it produces two binaries. Every other
workspace member is a library.
| Crate | Type | Responsibility |
| --- | --- | --- |
| `agentos-cli` | binary | `agentos` CLI and `agentos-npu-bridge` |
| `agentos-runtime` | library | configuration and composition root |
| `agentos-agent` | library | bounded agent loop and audit emission |
| `agentos-protocol` | library | provider-neutral chat/tool protocol |
| `agentos-inference` | library | ntex, remote, subprocess, and test backends |
| `agentos-tools` | library | tool policy, schema validation, approvals, Linux tools |
| `agentos-core` | library | identities, IDs, events, and shared state types |
| `agentos-kernel` | library | rustix-backed Linux and Btrfs boundary |
| `agentos-memory` | library | SQLite memory, audit, and principal state |
| `agentos-worldline` | library | filesystem branch/commit/rollback history |
| `agentos-npu` | library | NPU discovery, legacy bridge, Candle assembly |
| `agentos-candle` | library | Qwen3 host inference and CALRT tensor adapter |
| `calculet-pcie-abi` | library | captured 64-bit Linux ioctl ABI |
| `calculet-pcie` | library | safe PCIe device operations |
| `calculet-calrt` | library | Calbin/runtime/command/tensor implementation |
## Requirements
- 64-bit Linux. AgentOS itself is Linux-only, and the captured CALCULET ABI has
only been verified on 64-bit Linux.
- A current Rust toolchain with Rust 2024 edition support.
- A C toolchain, `pkg-config`, and OpenSSL development files. SQLite is built
from the bundled source.
- Btrfs is recommended for kernel-enforced read-only commit snapshots, but is
not required for development.
On Debian/Ubuntu, the native build dependencies are typically:
```bash
sudo apt install build-essential pkg-config libssl-dev
```
Build both binaries and all libraries:
```bash
cargo build --workspace
```
## Quick start without NPU hardware
First inspect the host and backend configuration:
```bash
cargo run -p agentos-cli --bin agentos -- \
--state-dir target/agentos-state --json doctor
```
Run a deterministic smoke test of the model/tool plumbing. This built-in fake
requests the real read-only `system_inspect` tool, but it is not the remote mock
used to substitute for unavailable NPU hardware:
```bash
cargo run -p agentos-cli --bin agentos -- \
--state-dir target/agentos-state --fake-model --json \
agent once "Inspect this Linux system"
```
Do not run the agent as root. By default, its principal uses the current real
UID as owner and the current effective UID/GID as the agent identity.
## Remote OpenAI-compatible mock inference
Hardware-independent mock testing uses a remote OpenAI-compatible model.
AgentOS supports either `/chat/completions` or `/responses` under a configured
API base URL. Both HTTP and HTTPS remote URLs are accepted; HTTPS should be used
whenever credentials or untrusted networks are involved. Localhost and loopback
model URLs are rejected because local model services must use UDS.
```bash
export AGENTOS_MODEL_BACKEND=openai_compatible
export AGENTOS_OPENAI_COMPATIBLE_API_KEY='...'
export AGENTOS_OPENAI_COMPATIBLE_BASE_URL='https://api.openai.com/v1'
export AGENTOS_OPENAI_COMPATIBLE_MODEL='your-model'
export AGENTOS_OPENAI_COMPATIBLE_API='responses' # or chat_completions
cargo run -p agentos-cli --bin agentos -- \
--state-dir target/agentos-state \
agent once "Summarize the current system state"
```
`OPENAI_API_KEY` is accepted as a fallback. A non-empty credential is currently
required by the remote adapter. Streaming is not implemented.
## Local model transport
A persistent model on the same Linux host must expose a filesystem Unix domain
socket. Do not bind it to localhost, a loopback address, or another TCP address.
The socket path and its owner/group/mode are part of the OS security boundary.
The legacy llama-server already supports this mode when `--host` ends in
`.sock`. AgentOS carries the familiar OpenAI-compatible HTTP request format over
AF_UNIX, so this is HTTP message framing over UDS—not HTTP over localhost.
The pure-Rust Candle backend runs in-process and therefore needs no IPC socket.
## Read-only tools
| Tool | Availability | Operation |
| --- | --- | --- |
| `system_inspect` | always | kernel, uptime, load, memory, root filesystem, OS release |
| `npu_inspect` | always | CALCULET device nodes, module/sysfs/proc monitor, CALRT library |
| `package_query` | always | exact package lookup via dpkg, rpm, or pacman |
| `service_inspect` | configured | bounded `systemctl show` for an allowlisted service |
| `journal_read` | configured | bounded journal read for an allowlisted service |
Enable service tools with an exact comma-separated allowlist:
```bash
export AGENTOS_SERVICE_INSPECT_ALLOWLIST='agentos.service,agentos-npud.service'
```
There are currently no mutation tools. The approval ledger in
`agentos-tools` is a contract primitive, not a complete privilege broker.
## Filesystem worldlines
Worldlines provide Git-like filesystem history without patching the kernel.
On Btrfs, current/branch trees are writable snapshots and commit trees are
read-only snapshots. Other filesystems use a copy fallback; that fallback does
not provide kernel-enforced immutability.
```bash
AGENTOS=(cargo run -q -p agentos-cli --bin agentos -- \
--state-dir target/agentos-state --json worldline)
"${AGENTOS[@]}" init
"${AGENTOS[@]}" branch
"${AGENTOS[@]}" status
"${AGENTOS[@]}" log --limit 20
```
`branch` returns a worldline ID and path. Modify that branch tree, then use:
```bash
"${AGENTOS[@]}" diff <worldline-id>
"${AGENTOS[@]}" commit <worldline-id> --message "describe the change"
"${AGENTOS[@]}" rollback <commit-id> --message "revert to known state"
"${AGENTOS[@]}" discard <worldline-id>
```
Rollback creates a new commit and retains history. Worldline operations are
currently explicit CLI operations; agent runs do not automatically create or
commit a branch.
## NPU paths
### Legacy llama-server bridge
The legacy `npu` backend executes `agentos-npu-bridge`, which connects only to a
filesystem Unix domain socket and sends `/v1/chat/completions` over that socket.
It additionally requires a ready CALCULET device, driver sysfs state, and vendor
CALRT shared library.
```bash
export AGENTOS_MODEL_BACKEND=npu
export AGENTOS_NPU_BRIDGE_COMMAND='agentos-npu-bridge'
export AGENTOS_NPU_SERVER_SOCKET='/run/agentos/npu.sock'
export AGENTOS_NPU_SERVER_MODEL='agentos-npu'
# Optional when CALRT is not in a probed system location:
export AGENTOS_CALRT_LIBRARY='/opt/calculet/lib/libcalrt-linux-x86_64.so'
```
Start the captured llama-server with the same socket path, for example
`--host /run/agentos/npu.sock`, and restrict access using the socket directory
and socket ownership/mode. No TCP listener is required.
### Pure-Rust Candle/CALRT path
The `npu_candle` path replaces the modified llama.cpp host code with Rust:
```bash
export AGENTOS_MODEL_BACKEND=npu_candle
export AGENTOS_NPU_CALBIN='/path/to/calbin-directory'
export AGENTOS_NPU_TOKENIZER='/path/to/tokenizer.json'
export AGENTOS_NPU_DEVICE_INDEX=0
```
`doctor` can parse these files and report the model plan without opening the
device. For the captured Qwen3 deployment, the host validates a 40,960-token
context, 151,936 logits, a 151,669-token vocabulary, and masks 267 padded logit
IDs.
This backend is deliberately not ready: `calculet-calrt` can deploy parameters
and transfer tensors, but `ConfiguredRuntime::submit()` still lacks CCU
relocation, job launch/completion, and device KV-cache reset. No real NPU result
has been validated on the current development machine.
## Configuration
| Variable | Default | Meaning |
| --- | --- | --- |
| `AGENTOS_STATE_DIR` | `$XDG_DATA_HOME/agentos` or `~/.local/share/agentos` | SQLite and worldline root |
| `AGENTOS_MODEL_BACKEND` | `auto` | `auto`, `openai_compatible`, `npu`, `npu_candle`, `subprocess`, `fake` |
| `AGENTOS_AGENT_ID` | `default-agent` | stable logical agent ID |
| `AGENTOS_OWNER_UID` | real UID | owning Linux UID |
| `AGENTOS_AGENT_UID` | effective UID | required execution UID |
| `AGENTOS_AGENT_GID` | effective GID | required execution GID |
| `AGENTOS_OPENAI_COMPATIBLE_API_KEY` | `OPENAI_API_KEY` | remote bearer credential |
| `AGENTOS_OPENAI_COMPATIBLE_BASE_URL` | `https://api.openai.com/v1` | remote compatible API root; no localhost/loopback |
| `AGENTOS_OPENAI_COMPATIBLE_MODEL` | empty | provider model name |
| `AGENTOS_OPENAI_COMPATIBLE_API` | `chat_completions` | `chat_completions` or `responses` |
| `AGENTOS_OPENAI_COMPATIBLE_MAX_RETRIES` | `2` | bounded HTTP retries |
| `AGENTOS_MODEL_COMMAND` | empty | explicit subprocess test/bridge command |
| `AGENTOS_MODEL_TIMEOUT_S` | `240` | model/bridge timeout |
| `AGENTOS_NPU_BRIDGE_COMMAND` | empty | legacy NPU bridge command |
| `AGENTOS_NPU_SERVER_SOCKET` | `/run/agentos/npu.sock` | legacy llama-server Unix socket |
| `AGENTOS_NPU_SERVER_MODEL` | `agentos-npu` | legacy llama-server model name |
| `AGENTOS_NPU_SERVER_TIMEOUT_S` | `240` | legacy llama-server UDS HTTP timeout |
| `AGENTOS_NPU_CALBIN` | empty | pure-Rust Calbin directory |
| `AGENTOS_NPU_TOKENIZER` | empty | pure-Rust tokenizer JSON |
| `AGENTOS_NPU_DEVICE_INDEX` | `0` | `/dev/calculetN` index |
| `AGENTOS_CALRT_LIBRARY` | probed | vendor CALRT shared library override |
| `AGENTOS_SERVICE_INSPECT_ALLOWLIST` | empty | enabled systemd service names |
| `AGENTOS_TOOL_OUTPUT_LIMIT_BYTES` | `65536` | per-tool serialized output limit |
| `AGENTOS_TOOL_ARGUMENT_LIMIT_BYTES` | `65536` | per-call argument limit |
| `AGENTOS_AGENT_MAX_ITERATIONS` | `8` | model/tool loop turns |
| `AGENTOS_AGENT_MAX_TOOL_CALLS` | `16` | total tool calls per run |
| `AGENTOS_AGENT_MAX_ELAPSED_S` | `120` | total run deadline |
| `AGENTOS_AGENT_MAX_OUTPUT_TOKENS` | `2048` | model output budget per turn |
| `AGENTOS_AGENT_MAX_TOTAL_TOKENS` | `65536` | cumulative token budget |
| `AGENTOS_AGENT_MAX_SAFE_WORKERS` | `4` | parallel safe-tool workers |
In `auto` mode, AgentOS selects the UDS-backed legacy NPU and then the remote
OpenAI-compatible backend. Subprocess and fake are explicit test adapters. The
incomplete Candle backend is not selected automatically.
## State layout
```text
AGENTOS_STATE_DIR/
agentos.sqlite3 # principals, audit events, memory, FTS index
worldline/
current/ # active filesystem tree
branches/ # writable candidate trees
commits/ # commit trees
metadata/ # commit and branch records
refs/ # HEAD references
replaced/ # recoverable previous checkouts
```
## Development
```bash
cargo fmt --all --check
cargo test --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
```
Some NPU parser tests use the local, gitignored
`npu_features/snapshot_20260801` capture. Those tests validate host-side parsing
only. They are not hardware tests, and a clean clone may not contain the
fixture.
Project-owned low-level Linux calls use `rustix`; AgentOS does not directly add
`libc` syscall wrappers. This is not a claim that the final `std`/OpenSSL binary
has no libc ABI dependency.
## Known gaps
- No long-running AgentOS control daemon or privilege broker yet; the local
llama-server model transport already uses UDS.
- No mutation tools or complete privilege-approval path.
- No streaming remote inference.
- No completed pure-Rust NPU job submission or physical-board validation.
- The copy worldline backend lacks Btrfs's kernel-enforced read-only commits.
- Memory/recall primitives exist as a library but are not exposed as agent tools
or CLI commands yet.
## License notes
AgentOS crates declare Apache-2.0. The captured CALCULET PCIe ABI and device
wrapper crates declare GPL-2.0-only, while `calculet-calrt` retains its vendor
license file. Check the individual crate manifests before redistribution.