96 Stars 🍴 9 Forks 👀 0 Watchers Go mit
GitHub 链接https://github.com/cexll/agentsdk-go
项目简介Build AI agents in light speed
创建时间2025-11-16
更新时间2026-02-09
📖 README English
[中文](README_zh.md) | English # agentsdk-go An Agent SDK implemented in Go that implements core Claude Code-style runtime capabilities, plus an optional middleware interception layer. ## Overview agentsdk-go is a modular agent development framework that implements core Claude Code-style runtime capabilities (Hooks, MCP, Sandbox, Skills, Subagents, Commands, Tasks) and optionally exposes a six-point middleware interception mechanism. The SDK supports deployment scenarios across CLI, CI/CD, and enterprise platforms. ### Dependencies - External dependencies: anthropic-sdk-go, fsnotify, gopkg.in/yaml.v3, google/uuid, golang.org/x/mod, golang.org/x/net ## Features ### Core Capabilities - **Multi-model Support**: Subagent-level model binding via `ModelFactory` interface - **Token Statistics**: Comprehensive token usage tracking with automatic accumulation - **Auto Compact**: Automatic context compression when token threshold reached - **Async Bash**: Background command execution with task management - **Rules Configuration**: `.claude/rules/` directory support with hot-reload - **OpenTelemetry**: Distributed tracing with span propagation - **UUID Tracking**: Request-level UUID for observability ### Concurrency Model - **Thread-Safe Runtime**: Runtime guards mutable state with internal locks. - **Per-Session Mutual Exclusion**: Concurrent `Run`/`RunStream` calls on the same `SessionID` return `ErrConcurrentExecution` (callers can queue/retry if they want serialization). - **Shutdown**: `Runtime.Close()` waits for in-flight requests to complete. - **Validation**: run `go test -race ./...` after changes. ### Examples - `examples/01-basic` - Minimal request/response - `examples/02-cli` - Interactive REPL with session history - `examples/03-http` - REST + SSE server on :8080 - `examples/04-advanced` - Full pipeline with middleware, hooks, MCP, sandbox, skills, subagents - `examples/05-custom-tools` - Selective built-in tools and custom tool registration - `examples/05-multimodel` - Multi-model configuration demo ## System Architecture ### Core Layer - `pkg/agent` - Agent execution loop coordinating model calls and tool execution - `pkg/middleware` - Six interception points for extending the request/response lifecycle - `pkg/model` - Model adapters, currently supports Anthropic Claude - `pkg/tool` - Tool registration and execution, including built-in tools and MCP tool support - `pkg/message` - Message history management with an LRU-based session cache - `pkg/api` - Unified API surface exposing SDK features ### Feature Layer - `pkg/core/hooks` - Hooks executor covering seven lifecycle events with custom extensions - `pkg/mcp` - MCP (Model Context Protocol) client bridging external tools (stdio/SSE) with automatic registration - `pkg/sandbox` - Sandbox isolation layer controlling filesystem and network access policies - `pkg/runtime/skills` - Skills management supporting scriptable loading and hot reload - `pkg/runtime/subagents` - Subagent management for multi-agent orchestration and scheduling - `pkg/runtime/commands` - Commands parser handling slash-command routing and parameter validation - `pkg/runtime/tasks` - Task tracking and dependency management In addition, the feature layer includes supporting packages such as `pkg/config` (configuration loading/hot reload), `pkg/core/events` (event bus), and `pkg/security` (command and path validation). ### Architecture Diagram ```mermaid flowchart TB subgraph Core API[pkg/api] --> Agent[pkg/agent] Agent --> Model[pkg/model] Agent --> Tool[pkg/tool] Agent --> Message[pkg/message] Middleware[pkg/middleware] -. intercepts .-> Agent end subgraph Feature Config[pkg/config] Hooks[pkg/core/hooks] Events[pkg/core/events] Runtime[pkg/runtime/*] MCP[pkg/mcp] Sandbox[pkg/sandbox] Security[pkg/security] end Config --> API Hooks --> Agent Events --> Agent Runtime --> Agent MCP --> Tool Tool --> Sandbox Tool --> Security ``` ### Middleware Interception Points The SDK exposes interception at critical stages of request handling: ``` User request ↓ before_agent ← Request validation, audit logging ↓ Agent loop ↓ before_model ← Prompt processing, context optimization ↓ Model invocation ↓ after_model ← Result filtering, content checks ↓ before_tool ← Tool parameter validation ↓ Tool execution ↓ after_tool ← Result post-processing ↓ after_agent ← Response formatting, metrics collection ↓ User response ``` ## Installation ### Requirements - Go 1.24.0 or later - Anthropic API Key (required to run examples) ### Get the SDK ```bash go get github.com/cexll/agentsdk-go ``` ## Quick Start ### Basic Example (examples/01-basic) Run the minimal starter in `examples/01-basic`: ```bash # 1. Set up environment cp .env.example .env # Edit .env: ANTHROPIC_API_KEY=sk-ant-your-key-here source .env # 2. Run the example go run ./examples/01-basic ``` ```go package main import ( "context" "log" "os" "github.com/cexll/agentsdk-go/pkg/api" "github.com/cexll/agentsdk-go/pkg/model" ) func main() { ctx := context.Background() // Create the model provider provider := model.NewAnthropicProvider( model.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")), model.WithModel("claude-sonnet-4-5"), ) // Initialize the runtime runtime, err := api.New(ctx, api.Options{ ProjectRoot: ".", ModelFactory: provider, }) if err != nil { log.Fatal(err) } defer runtime.Close() // Execute a task result, err := runtime.Run(ctx, api.Request{ Prompt: "List files in the current directory", SessionID: "demo", }) if err != nil { log.Fatal(err) } log.Printf("Output: %s", result.Output) } ``` ### Using Middleware ```go import ( "context" "log" "time" "github.com/cexll/agentsdk-go/pkg/middleware" ) // Logging middleware loggingMiddleware := middleware.Middleware{ BeforeAgent: func(ctx context.Context, req *middleware.AgentRequest) (*middleware.AgentRequest, error) { log.Printf("[REQUEST] %s", req.Input) req.Meta["start_time"] = time.Now() return req, nil }, AfterAgent: func(ctx context.Context, resp *middleware.AgentResponse) (*middleware.AgentResponse, error) { duration := time.Since(resp.Meta["start_time"].(time.Time)) log.Printf("[RESPONSE] %s (elapsed: %v)", resp.Output, duration) return resp, nil }, } // Inject middleware runtime, err := api.New(ctx, api.Options{ ProjectRoot: ".", ModelFactory: provider, Middleware: []middleware.Middleware{loggingMiddleware}, }) if err != nil { log.Fatal(err) } defer runtime.Close() ``` ### Streaming Output ```go // Use the streaming API to get real-time progress events := runtime.RunStream(ctx, api.Request{ Prompt: "Analyze the repository structure", SessionID: "analysis", }) for event := range events { switch event.Type { case "content_block_delta": fmt.Print(event.Delta.Text) case "tool_execution_start": fmt.Printf("\n[Tool Execution] %s\n", event.ToolName) case "tool_execution_stop": fmt.Printf("[Tool Result] %s\n", event.Output) } } ``` ### Concurrent Usage Runtime supports concurrent calls across different `SessionID`s. Calls sharing the same `SessionID` are mutually exclusive. ```go // Same runtime can be safely used from multiple goroutines runtime, _ := api.New(ctx, api.Options{ ProjectRoot: ".", ModelFactory: provider, }) defer runtime.Close() // Concurrent requests with different sessions execute in parallel var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(id int) { defer wg.Done() result, err := runtime.Run(ctx, api.Request{ Prompt: fmt.Sprintf("Task %d", id), SessionID: fmt.Sprintf("session-%d", id), // Different sessions run concurrently }) if err != nil { log.Printf("Task %d failed: %v", id, err) return } log.Printf("Task %d completed: %s", id, result.Output) }(i) } wg.Wait() // Requests with the same session ID must be serialized by the caller _, _ = runtime.Run(ctx, api.Request{Prompt: "First", SessionID: "same"}) _, _ = runtime.Run(ctx, api.Request{Prompt: "Second", SessionID: "same"}) ``` **Concurrency Guarantees:** - All `Runtime` methods are safe for concurrent use across sessions - Same-session concurrent requests return `ErrConcurrentExecution` - Different-session requests execute in parallel - `Runtime.Close()` gracefully waits for all in-flight requests - No manual locking required for different sessions; serialize/queue same-session calls in the caller ### Customize Tool Registration Choose which built-ins to load and append your own tools: ```go rt, err := api.New(ctx, api.Options{ ProjectRoot: ".", ModelFactory: provider, EnabledBuiltinTools: []string{"bash", "file_read"}, // nil = all, empty = none CustomTools: []tool.Tool{&EchoTool{}}, // appended when Tools is empty }) if err != nil { log.Fatal(err) } defer rt.Close() ``` - `EnabledBuiltinTools`: nil→全部内置;空切片→禁用内置;非空→只启用列出的内置(大小写不敏感,下划线命名)。 - `CustomTools`: 追加自定义工具;当 `Tools` 非空时被忽略。 - `Tools`: 旧字段,非空时完全接管工具集(保持向后兼容)。 See a runnable demo in `examples/05-custom-tools`. ## Examples The repository includes five progressive examples aligned to the new five-layer path: - `01-basic` – minimal single request/response. - `02-cli` – interactive REPL with session history and optional config load. - `03-http` – REST + SSE server on `:8080`. - `04-advanced` – full pipeline exercising middleware, hooks, MCP, sandbox, skills, and subagents. - `05-custom-tools` – selective built-ins plus custom tool registration. ## Project Structure ``` agentsdk-go/ ├── pkg/ # Core packages │ ├── agent/ # Agent core loop │ ├── middleware/ # Middleware system │ ├── model/ # Model adapters │ ├── tool/ # Tool system │ │ └── builtin/ # Built-in tools (bash, file, grep, glob) │ ├── message/ # Message history management │ ├── api/ # Unified SDK interface │ ├── config/ # Configuration loading │ ├── core/ │ │ ├── events/ # Event bus │ │ └── hooks/ # Hooks executor │ ├── sandbox/ # Sandbox isolation │ ├── mcp/ # MCP client │ ├── runtime/ │ │ ├── skills/ # Skills management │ │ ├── subagents/ # Subagents management │ │ └── commands/ # Commands parsing │ └── security/ # Security utilities ├── cmd/cli/ # CLI entrypoint ├── examples/ # Example code │ ├── 01-basic/ # Minimal single request/response │ ├── 02-cli/ # CLI REPL with session history │ ├── 03-http/ # HTTP server (REST + SSE) │ ├── 04-advanced/ # Full pipeline (middleware, hooks, MCP, sandbox, skills, subagents) │ └── 05-custom-tools/ # Custom tool registration and selective built-in tools ├── test/integration/ # Integration tests └── docs/ # Documentation ``` ## Configuration The SDK uses the `.claude/` directory for configuration, compatible with Claude Code: ``` .claude/ ├── settings.json # Project configuration ├── settings.local.json # Local overrides (gitignored) ├── rules/ # Rules definitions (markdown) ├── skills/ # Skills definitions ├── commands/ # Slash command definitions └── agents/ # Subagents definitions ``` Configuration precedence (high → low): - Runtime overrides (CLI/API-provided) - `.claude/settings.local.json` - `.claude/settings.json` - Built-in defaults (shipped with the SDK) `~/.claude` is no longer read; use project-scoped files for all configuration. ### Configuration Example ```json { "permissions": { "allow": ["Bash(ls:*)", "Bash(pwd:*)"], "deny": ["Read(.env)", "Read(secrets/**)"] }, "disallowedTools": ["web_search", "web_fetch"], "env": { "MY_VAR": "value" }, "sandbox": { "enabled": false } } ``` ### Token Statistics & Auto Compact ```go runtime, err := api.New(ctx, api.Options{ ProjectRoot: ".", ModelFactory: provider, // Token tracking callback OnTokenUsage: func(stats api.TokenStats) { log.Printf("Tokens: input=%d, output=%d, cache_read=%d", stats.InputTokens, stats.OutputTokens, stats.CacheReadTokens) }, // Auto compact settings CompactThreshold: 100000, // Trigger compact at 100k tokens CompactModel: "claude-haiku-4-5", // Use cheaper model for summarization }) ``` ### Async Bash Execution ```go // Start background task result, _ := runtime.Run(ctx, api.Request{ Prompt: "Run 'sleep 10 && echo done' in background", SessionID: "demo", }) // Later, check task output result, _ = runtime.Run(ctx, api.Request{ Prompt: "Get output of background task", SessionID: "demo", }) ``` ## HTTP API The SDK provides an HTTP server implementation with SSE streaming. ### Start the Server ```bash export ANTHROPIC_API_KEY=sk-ant-... cd examples/03-http go run . ``` The server listens on `:8080` by default and exposes these endpoints: - `GET /health` - Liveness probe - `POST /v1/run` - Synchronous execution returning the full result - `POST /v1/run/stream` - SSE streaming with real-time progress ### Streaming API Example ```bash curl -N -X POST http://localhost:8080/v1/run/stream \ -H 'Content-Type: application/json' \ -d '{ "prompt": "List the current directory", "session_id": "demo" }' ``` The response format follows the Anthropic Messages API and includes these event types: - `agent_start` / `agent_stop` - Agent execution boundaries - `iteration_start` / `iteration_stop` - Iteration boundaries - `message_start` / `message_stop` - Message boundaries - `content_block_delta` - Incremental text output - `tool_execution_start` / `tool_execution_stop` - Tool execution progress ## Testing ### Run Tests ```bash # All tests go test ./... # Core module tests go test ./pkg/agent/... ./pkg/middleware/... ./pkg/model/... # Integration tests go test ./test/integration/... # Generate coverage report go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out ``` ### Coverage Coverage numbers change over time; generate a report with `go test -coverprofile=coverage.out ./...`. ## Build ### Makefile Commands ```bash # Run tests make test # Generate coverage report make coverage # Lint code make lint # Build CLI tool make agentctl # Install into GOPATH make install # Clean build artifacts make clean ``` ## Built-in Tools The SDK ships with the following built-in tools: ### Core Tools (under `pkg/tool/builtin/`) - `bash` - Execute shell commands with working directory and timeout configuration - `file_read` - Read file contents with offset/limit support - `file_write` - Write file contents (create or overwrite) - `file_edit` - Edit files with string replacement - `grep` - Regex search with recursion and file filtering - `glob` - File pattern matching with multiple patterns ### Extended Tools - `web_fetch` - Fetch web content with prompt-based extraction - `web_search` - Web search with domain filtering - `bash_output` - Read output from background bash processes - `bash_status` - Poll status of background bash processes - `kill_task` - Terminate a running background bash process - `task_create` - Create a new task - `task_list` - List tasks - `task_get` - Get a task by ID - `task_update` - Update task status and dependencies - `ask_user_question` - Ask the user questions during execution - `skill` - Execute skills from `.claude/skills/` - `slash_command` - Execute slash commands from `.claude/commands/` - `task` - Spawn subagents for complex tasks (CLI/Platform entrypoints only) All built-in tools obey sandbox policies and are constrained by the path whitelist and command validator. Use `EnabledBuiltinTools` to selectively enable tools or `CustomTools` to register your own implementations. ## Security Mechanisms ### Three Layers of Defense 1. **Path whitelist**: Restricts filesystem access scope 2. **Symlink resolution**: Prevents path traversal attacks 3. **Command validation**: Blocks execution of dangerous commands ### Command Validator Located at `pkg/security/validator.go`, it blocks the following by default: - Destructive commands: `dd`, `mkfs`, `fdisk`, `shutdown`, `reboot` - Hazardous delete patterns: `rm -rf`, `rm -r`, `rmdir -p` - Shell metacharacters: `|`, `;`, `&`, `>`, `<`, `` ` `` (in Platform mode) ## Development Guide ### Add a Custom Tool Implement the `tool.Tool` interface: ```go type CustomTool struct{} func (t *CustomTool) Name() string { return "custom_tool" } func (t *CustomTool) Description() string { return "Tool description" } func (t *CustomTool) Schema() *tool.JSONSchema { return &tool.JSONSchema{ Type: "object", Properties: map[string]interface{}{ "param": map[string]interface{}{ "type": "string", "description": "Parameter description", }, }, Required: []string{"param"}, } } func (t *CustomTool) Execute(ctx context.Context, params map[string]any) (*tool.ToolResult, error) { // Tool implementation return &tool.ToolResult{ Name: t.Name(), Output: "Execution result", }, nil } ``` ### Add Middleware ```go customMiddleware := middleware.Middleware{ BeforeAgent: func(ctx context.Context, req *middleware.AgentRequest) (*middleware.AgentRequest, error) { // Pre-request handling return req, nil }, AfterAgent: func(ctx context.Context, resp *middleware.AgentResponse) (*middleware.AgentResponse, error) { // Post-response handling return resp, nil }, BeforeModel: func(ctx context.Context, msgs []message.Message) ([]message.Message, error) { // Before model call return msgs, nil }, AfterModel: func(ctx context.Context, output *agent.ModelOutput) (*agent.ModelOutput, error) { // After model call return output, nil }, BeforeTool: func(ctx context.Context, call *middleware.ToolCall) (*middleware.ToolCall, error) { // Before tool execution return call, nil }, AfterTool: func(ctx context.Context, result *middleware.ToolResult) (*middleware.ToolResult, error) { // After tool execution return result, nil }, } ``` ## Design Principles ### KISS (Keep It Simple, Stupid) - Single responsibility; each module has a clear role - Avoid overdesign and unnecessary abstractions ### Configuration-Driven - Manage all configuration under `.claude/` - Supports hot reload without restarting the service - Declarative configuration preferred over imperative code ### Modularity - Independent packages with loose coupling - Clear interface boundaries - Easy to test and maintain ### Extensibility - Middleware mechanism for flexible extensions - Tool system supports custom tool registration - MCP protocol integrates external tools ## Documentation - [Architecture](docs/architecture.md) - Detailed architecture analysis - [Getting Started](docs/getting-started.md) - Step-by-step tutorial - [API Reference](docs/api-reference.md) - API documentation - [Security](docs/security.md) - Security configuration guide - [Custom Tools Guide](docs/custom-tools-guide.md) - Custom tool registration and usage - [Trace System](docs/trace-system.md) - OpenTelemetry and HTTP trace setup - [Smart Defaults](docs/smart-defaults.md) - Auto-configuration by EntryPoint - [HTTP API Guide](examples/03-http/README.md) - HTTP server instructions ## Tech Stack - Go 1.24.0+ - [anthropic-sdk-go](https://github.com/anthropics/anthropic-sdk-go) - Anthropic official SDK - [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) - Official MCP SDK - [fsnotify](https://github.com/fsnotify/fsnotify) - Filesystem watchers - [yaml.v3](https://gopkg.in/yaml.v3) - YAML parser - [google/uuid](https://github.com/google/uuid) - UUID utilities - [golang.org/x/mod](https://pkg.go.dev/golang.org/x/mod) - Module utilities - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) - Extended net packages ## License See [LICENSE](LICENSE).