Files
qiming/qiming-rcoder/CLAUDE.md
2026-06-01 13:54:52 +08:00

360 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
RCoder 是一个基于 ACP (Agent Client Protocol) 的 AI 驱动开发平台,使用 Rust 构建。该项目采用微服务架构,集成了 Docker 容器化部署、高性能反向代理和多种 AI 代理支持。
## 核心架构
### 工作空间结构
- **Workspace**: 使用 Cargo workspace 管理多个 crate
- **主要 crate**: `rcoder` (主应用), `agent_runner` (代理运行时), `shared_types` (共享类型), `docker_manager` (容器管理), `pingora-proxy` (反向代理)
### 容器化架构设计
项目采用动态容器化架构,每个项目对应一个独立的 Docker 容器:
- **RCoder 主服务**: HTTP API 服务 + 容器管理 + gRPC 客户端
- **Agent Runner 容器**: 每个项目独立的 AI 代理运行环境 + gRPC 服务器
- **Pingora 代理**: 高性能反向代理服务,支持端口路由
### 通信架构gRPC
RCoder 和 Agent Runner 之间使用 **gRPC** 进行内部通信,提供类型安全和高性能:
```
外部客户端 (HTTP/SSE)
RCoder (HTTP API Server)
↓ gRPC (Chat, CancelSession, SubscribeProgress)
Agent Runner (gRPC Server in Docker)
↓ Server Streaming (实时进度事件)
RCoder (转换为 SSE)
外部客户端 (SSE)
```
**核心 RPC 方法**
- `Chat`: 发送聊天请求到 agent_runner
- `SubscribeProgress`: Server Streaming实时推送进度事件
- `CancelSession`: 取消正在执行的会话
- `GetStatus`: 查询 Agent 状态
**Proto 定义位置**: `crates/shared_types/proto/agent.proto`
### 核心组件
- **DockerManager**: 全局容器管理器,负责容器生命周期
- **ContainerManager**: 项目级别的容器创建和管理
- **ProxyAgentManager**: ACP 代理管理器,处理代理生命周期
- **AppState**: 应用状态管理,使用 DashMap 进行并发访问
- **GrpcChannelPool**: gRPC 连接池,基于 DashMap 实现高效连接复用
- **AgentServiceImpl**: agent_runner 的 gRPC 服务实现
## 开发命令
### 基础构建和运行
```bash
# 构建所有 crates
cargo build --release
# 运行主服务 (默认端口 8087)
cargo run --bin rcoder
# 运行特定端口
cargo run --bin rcoder -- --port 8080 --enable-proxy
# 使用 Makefile (推荐)
make build # 本地编译
make install # 安装到 ~/.cargo/bin
make dev-build # Docker 镜像构建
make dev-up # 启动开发容器
```
### 开发环境命令
```bash
# 启动开发模式容器
make dev-build # 首次:构建 Docker 镜像
make dev-up # 启动容器
make dev-restart # 代码修改后重启容器
# 查看容器日志
make dev-logs
# 停止开发容器
make dev-down
```
### 测试和质量检查
```bash
# 运行所有测试
cargo test
# 运行特定 crate 测试
cargo test -p rcoder
cargo test -p docker_manager
# 代码质量
cargo fmt # 格式化代码
cargo clippy # 代码检查
cargo tree # 查看依赖树
```
### Docker 开发命令
```bash
# 构建 Docker 镜像
make docker-build
# 完整开发流程 (推荐)
make dev-build && make dev-up
# 更新镜像标签
make update-image-tag
```
## 重要技术细节
### gRPC 通信架构
- 使用 **Tonic 0.14.2** 实现 gRPC 服务端和客户端
- Proto 文件使用 **Protobuf oneof** 实现类型安全的事件系统,完全消除 JSON 序列化
- **GrpcChannelPool** 基于 DashMap 提供高效的连接复用
- **Server Streaming** 用于实时推送进度事件(替代轮询)
- **HTTP 回退机制**gRPC 失败时自动回退到 HTTP兼容性保障
- gRPC 默认端口:`50051`(定义在 `shared_types::GRPC_DEFAULT_PORT`
**关键文件**
- `crates/shared_types/proto/agent.proto` - Proto 定义
- `crates/rcoder/src/grpc/channel_pool.rs` - 连接池
- `crates/rcoder/src/grpc/chat_client.rs` - gRPC 客户端
- `crates/agent_runner/src/grpc/agent_service_impl.rs` - gRPC 服务实现
### ACP 协议集成
- 使用 `agent-client-protocol = "0.6"``agent_client_protocol = "0.4"` 实现多版本兼容
- AgentSideConnection 和 ClientSideConnection **未实现 Send trait**
- **必须**在 LocalSet 和 spawn_local 中使用这些连接
- 参考示例目录: `/Volumes/soddy/git_workspace/rcoder/tmp/agent-client-protocol/rust/examples`
### 并发模型和状态管理
- 使用 **DashMap** 替代 `Arc<RwLock<HashMap>>` 以获得更好的性能
- 使用写时复制 (CoW) 模式进行状态更新
- 主应用使用 `#[tokio::main(flavor = "current_thread")]`
- ACP 操作必须在 `LocalSet` 中执行以支持 `spawn_local`
### Docker 容器动态创建
- **多级隔离架构**: 支持三种隔离级别
- `project` (默认): 每个项目对应一个独立的 Docker 容器
- `tenant`: 租户级隔离,同一租户共享容器
- `space`: 空间级隔离,同一空间共享容器
- **容器复用**: 通过 `pod_id` 字段实现跨用户容器复用
- **自动架构检测**: 根据 OS 和 ARCH 自动选择合适的镜像
- **内部网络通信**: 容器间通过 Docker 内部网络直接通信,无需端口映射
- **路径自动解析**: 自动检测容器内路径到宿主机路径的映射
### 容器工作空间路径
| 隔离类型 | RCoder 路径 | Computer 路径 |
|---------|------------|--------------|
| project | `/app/project_workspace/{project_id}` | `/app/computer-project-workspace/{user_id}/{project_id}` |
| tenant/space | `/app/project_workspace/{tenant_id}/{space_id}/{project_id}` | `/app/computer-project-workspace/{tenant_id}/{space_id}/{project_id}` |
### 配置系统
多层级配置优先级 (从高到低):
1. **命令行参数** - `--port`, `--projects-dir`, `--enable-proxy`
2. **环境变量** - `RCODER_PORT`, `DOCKER_SOCKET_PATH`, `RCODER_DOCKER_IMAGE_*`
3. **配置文件** - `config.yml` (自动生成)
4. **默认配置** - 代码中的默认值
## 环境配置
### 核心环境变量
```bash
# 服务配置
RCODER_PORT=8087 # 服务端口
RUST_LOG=debug # 日志级别
# Docker 配置
DOCKER_SOCKET_PATH=/var/run/docker.sock # Docker socket 路径
RCODER_DOCKER_IMAGE=custom/image # 自定义镜像
RCODER_DOCKER_IMAGE_ARM64=arm64/image # ARM64 专用镜像
RCODER_DOCKER_IMAGE_AMD64=amd64/image # AMD64 专用镜像
# 代理配置
ANTHROPIC_API_KEY=sk-xxx # Claude API 密钥
COMPOSE_PROJECT_NAME=rcoder # Docker Compose 项目名
```
### 开发环境要求
- Rust 1.75+ (2024 Edition)
- Docker 和 Docker Compose
- Claude Code CLI (可选)
## API 接口
### 核心端点
- `POST /chat`: 发送聊天消息到 AI 代理 (支持 `pod_id`, `tenant_id`, `space_id`, `isolation_type` 多租户参数)
- `POST /computer/chat`: Computer Agent 聊天接口 (支持多租户参数)
- `GET /agent/progress/{session_id}`: SSE 进度流,接收实时通知
- `POST /agent/session/cancel`: 取消正在执行的任务
- `POST /agent/stop`: 停止 Agent
- `GET /agent/status/{project_id}`: 查询 Agent 状态
- `POST /pod/ensure`: 确保容器存在,支持多租户参数
- `POST /pod/restart`: 重启容器,支持多租户参数
- `GET /health`: 健康检查
### Pingora 反向代理
- `GET /proxy/{port}/{path}`: 端口路由到指定后端服务
- `GET /proxy/status`: 查看代理服务状态
- `GET /proxy/stats`: 查看代理统计信息
### 响应格式
所有 API 响应都使用统一的 HttpResult 格式:
```rust
struct HttpResult<T> {
success: bool,
data: Option<T>,
code: String, // 业务错误码
message: String, // 错误描述
tid: Option<String>, // 追踪ID
}
```
## 特殊注意事项
### 禁止事项
1. **禁止使用模拟响应逻辑** - 所有 AI 调用必须真实执行
2. **禁止编写 unsafe 代码** - 项目要求内存安全
3. **AgentSideConnection 必须在 LocalSet 中使用** - 由于未实现 Send trait
4. ** Always Response in 中文** - 所有响应必须使用中文
### Docker 容器管理
- **容器名称格式**: `rcoder-agent-{project_id}``rcoder-agent-{pod_id}` (当 pod_id 提供时)
- **镜像选择策略**: 通用镜像 > 架构特定镜像 > 默认回退镜像
- **网络模式**: 优先使用内部网络,支持 host 网络模式
- **安全配置**: 自动移除 NET_RAW 和 NET_ADMIN 权限
### 性能优化
- 使用 DashMap 进行并发访问,避免 RwLock 竞争
- 实现写时复制 (CoW) 模式,减少不必要的内存分配
- 使用 MPMC 架构处理多个 AI 请求
- 通过内部网络进行容器间通信,避免宿主机端口映射
### 错误处理
- 使用 anyhow 进行错误传播
- 使用 HttpResult 统一 API 响应格式
- 实现完整的错误追踪和日志记录
## 调试和开发
### 日志配置
```bash
# 启用详细日志
RUST_LOG=debug cargo run --bin rcoder
# 特定模块日志
RUST_LOG=rcoder=debug,tower_http=debug cargo run
# 在容器中启用调试
RUST_LOG=debug make dev-up
```
### 容器调试
```bash
# 查看容器状态
docker ps | grep rcoder
# 查看容器日志
make dev-logs
# 进入容器调试
docker exec -it <container_id> /bin/bash
```
### 网络调试
```bash
# 检查容器网络
docker network ls
docker network inspect rcoder_agent-network
# 测试容器间连通性
docker exec <container1> ping <container2_ip>
```
## 开发工作流程
1. **首次开发环境设置**:
```bash
make dev-build # 构建 Docker 镜像
make dev-up # 启动开发容器
```
2. **日常开发**:
```bash
# 修改代码后
make dev-restart # 重新编译并重启容器
```
3. **测试新功能**:
```bash
# 直接运行
cargo run --bin rcoder -- --port 8080
# 或使用容器
make dev-up
curl -X POST http://localhost:8087/chat -d '{"prompt":"hello"}'
```
4. **调试问题**:
```bash
# 查看详细日志
make dev-logs
RUST_LOG=debug make dev-restart
```
## 关键代码模式
### ACP 协议集成模式
```rust
// 正确的 LocalSet 使用模式
let local_set = LocalSet::new();
local_set.run_until(async move {
let (client_conn, handle_io) = ClientSideConnection::new(
client, outgoing, incoming, |fut| {
tokio::task::spawn_local(fut);
}
);
tokio::task::spawn_local(handle_io);
// ... 处理逻辑
}).await;
```
### DashMap 高效使用模式
```rust
// 使用 entry API 避免多次锁获取
let entry = state.project_and_agent_map.entry(project_id.clone());
match entry {
dashmap::mapref::entry::Entry::Occupied(mut occupied) => {
// 只在需要更新时进行写时复制
if needs_update {
let mut mutable_info = (**occupied.get()).clone();
mutable_info.update_field(value);
occupied.insert(Arc::new(mutable_info));
}
}
dashmap::mapref::entry::Entry::Vacant(vacant) => {
// 创建新条目
let new_info = ProjectAndContainerInfo::new(project_id);
vacant.insert(Arc::new(new_info));
}
}
```
### Docker 容器创建模式
```rust
// 容器配置模式
let container_config = DockerContainerConfig {
project_id: project_id.clone(),
image: get_docker_image_from_config(image, arm64_image, amd64_image, default_image),
host_path: resolve_container_path_to_host(&project_path).await?,
container_path: project_path.clone(),
port_bindings: HashMap::new(), // 内部网络,无需端口映射
network_name: Some(network_name),
// ... 其他配置
};
```