# aacode-rs 使用指南
> Rust 版 AACode ReAct 编码 Agent,基于 `fastshell` 沙箱运行时
> 位置: `fastshell_local/aacode-rs/`
---
## 1. 快速开始
### 1.1 前置依赖
- **macOS / Linux**:默认使用本地 OS shell(`sh`),无需额外安装。
- **Windows**:默认使用本地 OS shell(`cmd`),无需额外安装。
- **Android / iOS**:使用 fastshell 沙箱引擎(VFS + 180+ 命令 + 内嵌 CPython),由 App 负责初始化。
- **LLM API key**:必需,支持 DeepSeek / OpenAI / Kimi / Anthropic 等(见 §2)。
### 1.2 安装与构建
**开源 / 同仓库开发**(`fastshell` 在 `../fastshell`,同 repo):
```bash
# 从 aacode-rs 目录直接构建(aacode-rs 是独立 workspace)
cd aacode-rs
cargo build --release # 桌面 CLI + 库
cargo build --lib --release # 仅库(需先装 cmake,见下方说明)
# 编译产物:
# 桌面 CLI: target/release/aacode
# 静态库: target/release/libaacode_rs.a
# 动态库: target/release/libaacode_rs.dylib / .so
```
> **关于 cmake**:`fastshell` 可选依赖 `aws-lc-sys`(rustls 的加密原语)在单独构建 `aacode-rs` 时需要 cmake。如无 cmake,可从 workspace 根构建(`cd .. && cargo build -p aacode-rs`),此时 fastshell 依赖链已缓存。
**开源单独发布**(仅 `aacode-rs` 目录,无 `fastshell` 源码):
```bash
# 将 fastshell 改为 git 依赖(编辑 aacode-rs/Cargo.toml)
# fastshell = { git = "https://github.com/xxx/fastshell", rev = "v0.3.4" }
cd aacode-rs
cargo build --release
```
**产物:**
- 桌面 CLI:`target/release/aacode`
- 静态库(Android):`target/release/libaacode_rs.a`(`cargo build --target aarch64-linux-android --release --lib`)
### 1.3 桌面 CLI 用法
```bash
# 单任务
aacode -p /path/to/project "用 Flask 写一个 hello world API"
# 指定 session 续接
aacode --session session_20260714_123456_abc123 "继续完善这个 API"
# Plan-first 模式(先规划,再执行)
aacode -p /path/to/project --plan-first "重构用户认证模块"
# 交互式会话模式
aacode -p /path/to/project --interactive
# 查看帮助
aacode --help
```
---### 1.4 与 Python 版 aacode 的共存与迁移
**设计意图**:Rust 版(aacode-rs)是 Python 版 aacode 的**替代品**,不是并存。以下说明两者冲突点与迁移方式。
#### 会冲突的点
**CLI 命令名**
两者安装后都提供一个叫 `aacode` 的可执行文件:
| Python | `pip install aacode` | `~/.local/bin/aacode` 或 venv |
| Rust | `cargo install --path .` | `~/.cargo/bin/aacode` |
同时安装两个时,`$PATH` 中靠前的生效。
**解决方法**:
```bash
# 切到 Rust 版:卸载 Python 版
pip uninstall aacode
# 或者不改 PATH,用绝对路径区分
~/.cargo/bin/aacode "任务" # Rust 版
~/.local/bin/aacode "任务" # Python 版
```
**`.aacode/` 项目数据目录**
两者都在项目目录下创建 `.aacode/`,写入:
- `.aacode/sessions/<id>.json` — 会话历史
- `.aacode/sessions/sessions_index.json` — 会话索引
- `.aacode/context/` — 大输出归档
- `.aacode/extracts/` — 工具输出归档
**格式基本兼容** —— Rust 版的 `SessionMessage` 序列化字段与 Python 版一致(role/content/tool_calls/tool_call_id/reasoning_content),跨版本读旧会话文件通常无问题。
**两个已知微小差异**:
| 时间戳格式 | ISO 8601 字符串 | epoch 秒数字符串 | 读取不受影响;`list_sessions` 排序时可能有微小偏差 |
| token 计数 | tiktoken(精确) | 字符估算(4:1) | 会话统计数字不一致;**不影响对话功能** |
> 如两个版混用同一个项目目录,会话文件不会被损坏,只是统计可能有轻微偏差。
#### 不会冲突的点
- **依赖生态**:`pip`(Python)和 `cargo`(Rust)完全独立,互不干扰
- **进程**:都是短命 CLI 程序,不驻留后台,不同时跑就不冲突
- **端口**:都没有长期监听端口
---
## 2. 环境配置
### 2.1 LLM 配置
**环境变量**(推荐,优先级最高):
```bash
# DeepSeek(OpenAI 兼容网关)
export LLM_API_KEY="sk-your-key"
export LLM_API_URL="https://api.deepseek.com/v1"
export LLM_MODEL_NAME="deepseek-chat"
# DeepSeek(Anthropic 兼容网关 — 同一 key,不同端点)
export LLM_API_KEY="sk-your-key"
export LLM_API_URL="https://api.deepseek.com/anthropic"
export LLM_MODEL_NAME="deepseek-v4-flash"
export LLM_GATEWAY="anthropic"
# OpenAI
export LLM_API_KEY="sk-your-key"
export LLM_API_URL="https://api.openai.com/v1"
export LLM_MODEL_NAME="gpt-4o"
# Kimi
export LLM_API_KEY="your-kimi-key"
export LLM_API_URL="https://api.moonshot.cn/v1"
export LLM_MODEL_NAME="kimi-k2"
```
**JSON 配置文件**(沙箱内 `aacode_config.json`,优先级低于 env):
```json
{
"model": {
"name": "deepseek-chat",
"api_key": "sk-...",
"base_url": "https://api.deepseek.com/v1",
"gateway": "openai"
},
"search": {
"searxng_url": "https://your-searxng-instance"
}
}
```
**taskJson 内联**(移动端/FFI 路径,通过 `aacode_task_start(taskJson, ...)` 传入):
```json
{
"task": "创建 hello.py 并运行",
"project_path": "/sandbox/projects/demo",
"session_id": "session_20260714_123456_abc123",
"model": {
"name": "deepseek-chat",
"api_key": "sk-...",
"base_url": "https://api.deepseek.com/v1",
"gateway": "openai"
},
"max_iterations": 30,
"plan_first": false,
"shell_backend": "auto",
"search": {
"searxng_url": "https://..."
},
"multimodal": {
"name": "kimi-k2.5",
"api_key": "...",
"base_url": "https://api.moonshot.cn/v1"
}
}
```
### 2.2 配置优先级
```
taskJson 内联 > 环境变量 > aacode_config.json > 默认值
```
---
## 3. Shell 执行后端
**这是 aacode-rs 区别于 Python 版的关键设计**。
### 3.1 两种后端
| **NativeShell** | 真实 OS shell (`sh -c` / `cmd /C`) | 桌面开发(Linux / macOS / Windows) |
| **FastshellBackend** | fastshell 沙箱引擎(VFS + 180+ 内置命令 + CPython) | 移动端(Android / iOS) |
### 3.2 自动选择
```rust
// 默认行为: 根据目标 OS 自动选
shell_backend: "auto"
// → 桌面 = native OS shell
// → 移动端 = fastshell 沙箱
```
### 3.3 手动切换
**三选一**,在任意平台上都可强制指定:
| `"auto"` | 按目标 OS 自动选(桌面=native, 移动=fastshell) |
| `"native"` | **强制**走真实 OS shell(需系统有 sh/cmd) |
| `"fastshell"` | **强制**走 fastshell 沙箱引擎 |
**设置方式**:
```bash
# 环境变量
export AACODE_SHELL_BACKEND=native # 或 fastshell / auto
# 或通过 taskJson 的 "shell_backend" 字段
```
### 3.4 桌面端用 NativeShell 的优势
| heredoc(`cat <<'EOF'`) | ✅ 原生支持 | ❌ 不支持 |
| 管道(`\|`) | ✅ 原生支持 | 部分支持(fastshell pipeline) |
| 真实路径(`/tmp/foo/bar`) | ✅ 自然路径 | VFS 沙箱内嵌套 |
| `grep` / `rg` | ✅ 系统原生 | fastshell 内置 |
| `python3` / `pip` / `pytest` | ✅ 系统 Python | fastshell 内嵌 CPython |
| `git` / `curl` / `docker` | ✅ 系统原生 | fastshell 内置 |
| 子进程 | ✅ 系统原生 | 受沙箱策略限制 |
### 3.5 在 observation 中识别后端
每条 `run_shell` 的返回值含 `"backend"` 字段:
```json
{"backend": "native", "returncode": 0, "stdout": "...", ...} // NativeShell
{"backend": "fastshell", "returncode": 0, "stdout": "...", ...} // FastshellBackend
```
模型可以看到这个字段,从而在沙箱模式时**自动适配命令风格**——例如 fastshell 里用 `python3` 而非 `python`、避开 heredoc、用 fastshell 内置命令替代系统工具。
---
## 4. C ABI / 移动端集成
### 4.1 公开的 C 函数(句柄式)
```c
typedef void (*aacode_event_fn)(const char* line, void* userdata);
void* aacode_task_start(const char* task_json, aacode_event_fn cb, void* userdata); // 非阻塞
char* aacode_task_wait(void* handle); // 阻塞到结束,返回终态 JSON
void aacode_task_cancel(void* handle); // 非阻塞,按句柄取消
void aacode_task_free(void* handle);
char* aacode_validate_api_key(const char* config_json);
char* aacode_list_sessions(const char* project_path);
char* aacode_get_session_messages(const char* project_path, const char* session_id);
void aacode_free_string(char* ptr);
```
### 4.2 调用流程(Android / iOS 一致)
```
宿主 UI (Kotlin / Swift)
→ aacode_task_start(taskJson, cb, userdata) // 非阻塞,返回句柄
→ Rust: TOKIO_RT.spawn(AgentRuntime::run_task)
→ 每个 JSONL 事件 → cb(line, userdata) // 从 tokio worker 线程发出
→ 宿主 parser → UI
→ aacode_task_wait(handle) // 阻塞取终态
→ aacode_task_free(handle)
取消:aacode_task_cancel(handle) → ReAct 循环在检查点响应
```
### 4.3 静态库构建
```bash
cargo build -p aacode-rs --release --target aarch64-linux-android --lib
# → target/aarch64-linux-android/release/libaacode_rs.a
CMakeLists.txt: 链接 libaacode_rs.a
# iOS
cargo build -p aacode-rs --release --target aarch64-apple-ios --lib
cargo build -p aacode-rs --release --target aarch64-apple-ios-sim --lib
```
---
## 5. 任务输入 / 输出 JSONL 协议
**严格对齐 Kotlin `AgentOutputParser`**(详见 `design.md` §7)。每条事件独占一行:
```json
{"type":"start","task":"写个 hello world"}
{"type":"session_created","session_id":"session_..."}
{"type":"seg_content","seg":"thinking","content":"..."}
{"type":"seg_content","seg":"thought","content":"..."}
{"type":"tool_progress","state":"building","name":"run_shell","chars":42}
{"type":"seg_content","seg":"observation","content":"..."}
{"type":"done","session_id":"session_..."}
{"type":"error","message":"..."}
```
token 行内换行在管道模式下用 `\x00` 转义。
### 5.1 向后兼容的增量事件(2026-08)
旧事件(`start`/`session_*`/`seg_content`/`tool_progress`/`delta`/`done`/`error`)
**始终照发且 wire 形状不变**,现有 Android / iOS 解析器零改动。新增:
- `{"type":"session","task":"...","session_id":"session_...","created":true|false}`
合并启动事件,紧跟 `start` + `session_created`/`session_switched` 之后发出。
无 `content` 字段,被解析器当作未知类型忽略或供新客户端消费。
- `seg_content`(observation)额外携带 `truncated`(bool)与 `total_chars`(int)字段,
旧解析器忽略未知字段,新客户端可据此渲染折叠 UI。
- `seg_append {"type":"seg_append","seg":"...","content":"..."}` 与
`seg_reset {"type":"seg_reset","seg":"...","content":"..."}` 增量 patch 事件,
仅当 `EventSink::supports_incremental()` 返回 `true` 时发出(旧宿主默认 `false`,
永远收不到,行为完全不变);新宿主 opt-in 后可原地 patch 存活段(稳定 id、
不触发全量重建)。
---
## 6. 测试
### 6.1 测试架构
```
单元测试 | cargo test | 否 | 136 | 所有模块
集成测试 | cargo test | 否 (mock) | 5 | ReAct 闭环 + Python + 会话
LLM 兼容测试 | cargo test | 否 (录制) | 23 | 多厂商 SSE + 格式容错
LLM 活体测试 | L KEY=... -- --ignored | 是 | 4 | DeepSeek 文本+工具双网关
shell 后端测试 | cargo test | 否 (本地 sh)| 10+ | 见 §6.2
```
### 6.2 Shell 后端专项测试
#### 自动选择 (Auto)
| `runtime::desktop_defaults_to_native_backend` | 桌面 `Auto` → `NativeShell` |
| `runtime::force_fastshell_backend` | 强制 `Fastshell` → `backend_kind()=="fastshell"` |
| `runtime::native_backend_uses_real_cwd` | `Native` 后端文件落在真实 CWD |
#### 原生 shell 功能 (`tools::backend::NativeShell`)
| `native_echo` | 基本 stdout + exit code |
| `native_heredoc_works` | heredoc `cat <<'EOF'` + 文件落地 |
| `native_real_cwd_and_pipes` | `grep \| wc -l` 真实管道 |
| `native_stdin_input` | stdin 支路 |
| `native_nonzero_exit` | `exit 3` → exit code 3 |
| `native_timeout_kills` | `sleep 10` + 1s 超时 → exit 124 |
| `native_python_via_backend` | `python3 -c` 真实输出 |
#### 工具层集成 (`tools::shell::ShellTool`)
| `native_echo_roundtrip` | JSON 返回含 `"backend":"native"` |
| `native_writes_to_real_cwd` | `echo > note.txt` 在真实 CWD |
| `native_heredoc_via_tool` | heredoc 通过工具层 |
| `fastshell_backend_still_works` | 沙箱后端 `"backend":"fastshell"` |
| `max_output_truncation` | `max_output` 参数截断 |
| `dangerous_reject_mode` | `rm -rf /` 被拦截 |
#### 跨语言能力
| `backend::tests::platform_default_is_native_on_desktop` | cfg 宏验证 |
| `runtime::desktop_defaults_to_native_backend` | 运行时自检 |
### 6.3 环境变量切换测试
```bash
# 测试 Auto (桌面 → native)
cargo test -p aacode-rs runtime::desktop_defaults_to_native_backend
# 测试强制 fastshell
cargo test -p aacode-rs runtime::force_fastshell_backend
# 测试强制 native
cargo test -p aacode-rs runtime::native_backend_uses_real_cwd
```
### 6.4 运行全部测试
```bash
# 全部离线测试
cargo test -p aacode-rs
# 离线 + 真实 API(需要 key)
LLM_API_KEY=sk-xxx LLM_API_URL=... LLM_MODEL_NAME=deepseek-chat \
cargo test -p aacode-rs -- --ignored --nocapture
# Shell 后端相关全部测试
cargo test -p aacode-rs -- backend shell native fastshell runtime
```
### 6.5 端到端验证清单
| 写文件 + heredoc + python3 | `aacode -p /tmp/demo "用 heredoc 创建 greet.py 并 python3 运行"` |
| 搜索 + fetch_url + git | 给模型一个实际 repo 路径 |
| 多轮会话 | `aacode --session <id>` 续接 |
| Plan-first | `aacode --plan-first` |
| 中断 + 恢复 | Ctrl+C → `aacode --session <id>` |
| 多语言任务 | 同时要求 Python + Shell 操作 |
| Anthropic 网关 | `LLM_GATEWAY=anthropic LLM_API_URL=https://api.deepseek.com/anthropic` |
| 强制 fastshell 桌面 | `AACODE_SHELL_BACKEND=fastshell` |
---
## 7. 故障排查
**"API key not configured"**
```bash
export LLM_API_KEY="sk-your-key"
```
或通过 `aacode_config.json` 或 taskJson 的 `model.api_key` 设置。
**401 / authentication failed**
- 检查 key 是否有效
- 检查 endpoint(`LLM_API_URL`)是否正确
**http timeout / network error**
- 检查网络连接
- 增加 `model.max_tokens` 或减小上下文
**"Python is not available" (fastshell 后端)**
- 桌面端: 默认 native 后端不受影响
- 移动端: 需要 fastshell 内嵌 CPython
**"Command rejected by safety guard"**
- 危险命令(`rm -rf /`)被拦截
- 关掉: `config.safety.dangerous_command_action = "log"` 或 "ask"
---
## 8. 相关文档
| `design.md` | 完整设计文档 |
| `LLM_CLIENT.md` | LLM 客户端设计与兼容性 |
| `DEPS.md` | Python → Rust 依赖逐条替代说明 |
| `../MOBILE_AGENT_GUIDE.md` | 移动端集成指南 |