11 KiB
11 KiB
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_runnerSubscribeProgress: 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 服务实现
开发命令
基础构建和运行
# 构建所有 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 # 启动开发容器
开发环境命令
# 启动开发模式容器
make dev-build # 首次:构建 Docker 镜像
make dev-up # 启动容器
make dev-restart # 代码修改后重启容器
# 查看容器日志
make dev-logs
# 停止开发容器
make dev-down
测试和质量检查
# 运行所有测试
cargo test
# 运行特定 crate 测试
cargo test -p rcoder
cargo test -p docker_manager
# 代码质量
cargo fmt # 格式化代码
cargo clippy # 代码检查
cargo tree # 查看依赖树
Docker 开发命令
# 构建 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} |
配置系统
多层级配置优先级 (从高到低):
- 命令行参数 -
--port,--projects-dir,--enable-proxy - 环境变量 -
RCODER_PORT,DOCKER_SOCKET_PATH,RCODER_DOCKER_IMAGE_* - 配置文件 -
config.yml(自动生成) - 默认配置 - 代码中的默认值
环境配置
核心环境变量
# 服务配置
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: 停止 AgentGET /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 格式:
struct HttpResult<T> {
success: bool,
data: Option<T>,
code: String, // 业务错误码
message: String, // 错误描述
tid: Option<String>, // 追踪ID
}
特殊注意事项
禁止事项
- 禁止使用模拟响应逻辑 - 所有 AI 调用必须真实执行
- 禁止编写 unsafe 代码 - 项目要求内存安全
- AgentSideConnection 必须在 LocalSet 中使用 - 由于未实现 Send trait
- ** 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 响应格式
- 实现完整的错误追踪和日志记录
调试和开发
日志配置
# 启用详细日志
RUST_LOG=debug cargo run --bin rcoder
# 特定模块日志
RUST_LOG=rcoder=debug,tower_http=debug cargo run
# 在容器中启用调试
RUST_LOG=debug make dev-up
容器调试
# 查看容器状态
docker ps | grep rcoder
# 查看容器日志
make dev-logs
# 进入容器调试
docker exec -it <container_id> /bin/bash
网络调试
# 检查容器网络
docker network ls
docker network inspect rcoder_agent-network
# 测试容器间连通性
docker exec <container1> ping <container2_ip>
开发工作流程
-
首次开发环境设置:
make dev-build # 构建 Docker 镜像 make dev-up # 启动开发容器 -
日常开发:
# 修改代码后 make dev-restart # 重新编译并重启容器 -
测试新功能:
# 直接运行 cargo run --bin rcoder -- --port 8080 # 或使用容器 make dev-up curl -X POST http://localhost:8087/chat -d '{"prompt":"hello"}' -
调试问题:
# 查看详细日志 make dev-logs RUST_LOG=debug make dev-restart
关键代码模式
ACP 协议集成模式
// 正确的 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 高效使用模式
// 使用 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 容器创建模式
// 容器配置模式
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),
// ... 其他配置
};