This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sico is an open-source AI agent platform with three main services that communicate via gRPC and a bidirectional "reverse RPC" pattern. The backend handles HTTP APIs and persistence, the core handles AI/LLM orchestration, and the frontend provides a React dashboard (frontend codebase is not opensourced).
cp .env.example .env # first time only
make compose-up # builds and starts all services (nginx, backend, core, mysql, redis, kafka, seaweedfs)
make compose-down # stop and remove containers
make compose-logs # tail all service logsServices available at http://localhost:8080 via nginx reverse proxy.
cd backend
go build ./cmd/sico-server # build the server binary
go test ./... # run all tests
go test ./internal/biz/sandbox/impl/... # run tests in a single package
go test -run TestFunctionName ./internal/biz/sandbox/impl/... # run a single testUses Google Wire for dependency injection. After changing wire.go, regenerate with:
cd backend/internal/di && wirecd core
uv sync # install dependencies (uses uv, not pip)
uv run pytest # run all tests
uv run pytest tests/chat/ # run tests in a subdirectory
uv run ruff check . # lint
uv run ruff format . # formatRequires Python >=3.13. Uses uv for dependency management (pyproject.toml + uv.lock).
All .proto files live in proto/. Generation scripts are run from the proto/ directory:
cd proto
bash gen.sh # run all targets below
bash gen.sh backend-grpc # Go gRPC stubs → backend/internal/transport/grpc/pb/
bash gen.sh backend-http # Go HTTP DTO structs → backend/internal/transport/http/dto/
bash gen.sh backend-reverse # Go reverse-RPC stubs → backend/internal/transport/reverse_grpc/pb/
bash gen.sh core # Python betterproto2 stubs → core/app/pb/Current proto generation covers Backend and Core outputs. It uses protoc + protoc-go-inject-tag for Go generation and betterproto2 for Core Python generation.
make kind-up # creates cluster, builds images, deploys via Helm
make kind-down # tear downFrontend (React) ──HTTP/SSE──▶ Nginx ──▶ Backend (Go/Gin)
│ ▲
gRPC │ │ reverse gRPC
▼ │
Core (Python/asyncio)
Backend → Core (gRPC): The backend calls core for LLM/chat operations via standard gRPC (core listens on :50053).
Core → Backend (reverse gRPC): Core calls back to the backend via a separate gRPC server (backend listens on :50054) to persist data (create messages, update conversations, sync sandbox status). This is the "reverse RPC" pattern — proto files named reverse_rpc.proto in each domain define these callbacks.
Chat uses SSE: The /conversation/chat endpoint streams responses to the frontend via Server-Sent Events (SSE).
Follows a layered architecture with Wire-based DI:
cmd/sico-server/— Entry point. Runs DB migrations on startup, starts Gin HTTP server + reverse gRPC server.internal/transport/— HTTP handlers (http/handler/), middleware (http/middleware/), HTTP DTOs (http/dto/), gRPC clients (grpc/), reverse gRPC server (reverse_grpc/), and the Gin router (router/).internal/biz/<domain>/— Business logic. Each domain (agent, conversation, sandbox, knowledge, skill, project, rbac, llmhubs) has aServiceinterface at the package root, aninit.gowith WireProviderSet, and animpl/subdirectory with the implementation.internal/store/<domain>/— Repository layer (GORM-based MySQL access).internal/entity/— Domain entity structs.internal/infra/— Infrastructure: MySQL connection, Redis cache, migrations, cron, email, ID generation (snowflake), object storage, core gRPC client.internal/shared/— Cross-cutting: error codes, constants, enums, types.pkg/— Reusable packages: env loading (godotenv), crypto, eventbus (Kafka), JWT, logger, SSE, json utilities.configs/migrations/— SQL migration files (golang-migrate, numberedNNNNNN_name.{up,down}.sql).
Key patterns:
- Each biz domain exposes a singleton via
Default()/SetDefault()— initialized by Wire at startup. - HTTP DTOs are generated from protobuf via
gen.sh backend-http, then annotated withprotoc-go-inject-tag. - Auth uses JWT with Casbin for RBAC policy enforcement. Sandbox client endpoints use a separate HMAC-based auth middleware (
X-Sico-*headers).
app/main.py— Async gRPC server entry point.app/biz/chat/— Chat orchestration: multi-turn conversation, tool calling, prompt construction, plan execution.app/biz/llm/— LLM service layer.app/biz/reverse_grpc/— Reverse gRPC client stubs that call back to the backend.app/tools/— Agent tool implementations (read, write, grep, web_search, run_python, sandbox_tools, etc.).app/llmhubs/— LLM provider abstraction layer with adapter pattern for multiple providers.app/schemas/— Pydantic models for chat, conversation.app/pb/— Auto-generated protobuf/betterproto2 stubs (do not edit manually).app/utils/— Shared utilities: Redis, caching, eventbus, response builders.
The sandbox subsystem manages isolated environments (Android emulators, AIO containers) for agent tool execution. Sandboxes are pooled and leased to agent instances. The sandbox/emulator/ directory contains the Android emulator sandbox implementation (Python).
Proto definitions are organized by domain in proto/: agent, chat, common, conversation, knowledge, llmhubs, project, rbac, sandbox, skill. Each domain may have rpc.proto (backend↔core gRPC), reverse_rpc.proto (core→backend callbacks), and restful.proto (HTTP DTO definitions).
Wrapping rules (apply mechanically):
- One line if the signature has ≤2 parameters AND fits within 130 columns.
- Otherwise put each parameter on its own line, with the closing
)and return type on a line by themselves. - Never pack multiple parameters onto a single wrapped line.
- Group related params of the same type using Go's shorthand:
instanceID, sandboxID string.
// One-liner — short enough.
func (s *Service) GetUser(ctx context.Context, id int64) (*User, error)
// Wrapped — 3+ params.
func (s *Service) validateUpdateUserEmail(
ctx context.Context,
req *user.UpdateUserRequest,
existingUser *rolerepo.UserModel,
) errorConfigured via .env (copied from .env.example). Key variables:
DB_*— MySQL connectionREDIS_HOST,REDIS_PORT,REDIS_PASSWORD— RedisCORE_GRPC_ADDRESS— Address where core listens (defaultcore:50053)REVERSE_GRPC_SERVE_ADDRESS— Address where backend's reverse gRPC server listens (default:50054)SANDBOX_CLIENT_SECRET_<CLIENT_ID>— Per-client HMAC secrets for sandbox auth (CLIENT_ID uppercased, hyphens → underscores)