agentkit

package module
v0.0.0-...-d9d9ad5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 12 Imported by: 0

README

agentkit

一个轻量的 Go Agent SDK。当前把 Claude 和 OpenAI 都收敛到统一的 Provider 抽象上,对外只保留两条核心调用路径:

  • 无状态的 Agent.Stream(...)
  • 带历史的 Session.Stream(...)

内部 provider 层使用 Provider.Generate(...),但公开给 SDK 用户的名字继续保留 Stream,因为你拿到的就是事件流,别没事瞎换名。

非目标

  • 不做多 agent runtime。
  • 不接 provider 原生 session ID。
  • 不在首版接通 MCP transport。
  • 不暴露 Claude / OpenAI 原始协议结构体给上层业务。

快速开始

go test ./...
cp .env.example .env
go run ./examples/basic
go run ./examples/tools
go run ./examples/streaming_input
go run ./examples/multi_agent

.env 默认不会进仓库,示例会自动读取项目根目录下的 .env。默认统一使用 AGENTKIT_API_KEY;如果你还没改历史配置,示例也兼容旧的 ANTHROPIC_API_KEY / OPENAI_API_KEY

AGENTKIT_BACKEND 建议明确写成 claudeopenaiopenai-chat。其中 openai 默认就是 Responses 模式,老的 openai-responses 也继续兼容。

单次模式

import (
    _ "github.com/evanli18/agentkit/provider/claude"
)

agent, _ := agentkit.New(
    agentkit.WithModel("claude-sonnet-4-5"),
)

stream := agent.Stream(context.Background(), agentkit.Query{
    Prompt: "Find and fix the bug in auth.py",
    Options: agentkit.QueryOptions{
        History:   []agentkit.Message{agentkit.UserMessage("Keep the answer short.")},
        MaxTokens: 512,
    },
})
for event, err := range stream {
    if err != nil {
        panic(err)
    }
    fmt.Println(event)
}

会话模式

session, err := agent.CreateSession(context.Background(), agentkit.QueryOptions{
    History: []agentkit.Message{agentkit.UserMessage("回复尽量简洁。")},
    Model:   "claude-sonnet-4-5",
})
if err != nil {
    panic(err)
}

for event, err := range session.Stream(context.Background(), agentkit.UserMessage("先介绍一下 agentkit。")) {
    if err != nil {
        panic(err)
    }
    fmt.Println(event)
}

forked, err := session.Fork(context.Background())
if err != nil {
    panic(err)
}

for event, err := range forked.Stream(context.Background(), agentkit.UserMessage("把刚才两句合成一句。")) {
    if err != nil {
        panic(err)
    }
    fmt.Println(event)
}

if err := session.Close(); err != nil {
    panic(err)
}

resumed, err := agent.ResumeSession(context.Background(), session.ID())
if err != nil {
    panic(err)
}
fmt.Println(resumed.ID())

Session.Stream(...) 自动带上当前 session history;成功后提交 history/usage,失败或取消则不提交。

工具调用

agent, err := agentkit.New(
    agentkit.WithWorkspace(agentkit.WorkspaceConfig{Root: "."}),
    agentkit.WithTools(tool.Read("."), tool.Grep("."), tool.Bash(".")),
)
if err != nil {
    panic(err)
}

stream := agent.Stream(context.Background(), agentkit.Query{
    Prompt: "请调用 Grep 工具搜索 README.md 里所有包含 Session.Stream 的行,再用一句话总结会话模式入口;如果需要删除临时文件,再调用 Bash。",
})
for event, err := range stream {
    if err != nil {
        panic(err)
    }
    fmt.Println(event.Type, event.ToolUse, event.ToolResult, event.Text)
}

注册工具后,Stream 会自动执行 tool loop:模型发出 tool_use,本地执行 handler,随后回填 tool_result,直到拿到最终 assistant 回复。AllowedTools 用来限制已注册工具的可见子集,MaxTurns 用来限制单次请求内部的工具往返次数。

图片输入

stream := agent.Stream(context.Background(), agentkit.Query{
    Input: &agentkit.Message{
        Role: agentkit.RoleUser,
        Content: []agentkit.ContentBlock{
            agentkit.TextBlock("描述一下这张图。"),
            agentkit.ImageURLBlock("https://example.com/cat.png"),
        },
    },
})
for event, err := range stream {
    if err != nil {
        panic(err)
    }
    fmt.Println(event)
}

如果你拿的是本地二进制图片,就传 base64 + media_type

stream := session.Stream(context.Background(), agentkit.UserMessageWithBlocks(
    agentkit.TextBlock("继续分析这张图。"),
    agentkit.ImageDataBlock("image/png", imageBytes),
))
for event, err := range stream {
    if err != nil {
        panic(err)
    }
    fmt.Println(event)
}

包结构

  • 根包 agentkit:SDK 主入口,统一暴露 AgentSessionProvider 抽象、消息/事件模型和共享运行时能力。
  • provider/claude:Anthropic 官方 Go SDK 适配层。
  • provider/openai:OpenAI Responses API 适配层。
  • provider/openaichat:OpenAI Chat Completions API 适配层。
  • tool:本地工具定义、typed handler 适配器和原始 handler 入口。
  • examples/*:可运行示例,覆盖基础调用、工具调用、session 和多 agent 编排。

设计取向

  • New(...opts) 优先通过 WithProvider(...) 显式注入 provider;不传时按 AGENTKIT_BACKEND 自动加载默认 provider。
  • WithWorkspace(...) 只加载工作空间根目录下的 CLAUDE.mdAGENTS.md,再和 WithInstructions(...) 合并;单次调用每次重新解析,托管 session 在创建时固化快照。
  • Provider.Generate(...) 是内部 provider 抽象;对外保留 Agent.Stream(...)Session.Stream(...),统一返回 iter.Seq2[Event, error]
  • Session.Stream(ctx, message) 自动使用当前 session history;CreateSessionResumeSessionDefaultSessionSession.ForkSession.Close 是当前会话管理的最小核心口。
  • Query.Prompt 是纯文本便捷口,Query.Input 是结构化输入口;两者互斥。
  • QueryOptions.Model 可覆盖默认模型;session 会把自己的 Model 配置持久化并在 ResumeSession / Fork 时继承。
  • WithTools(...) 注册本地工具;AllowedTools 过滤已注册工具;MaxTurns 限制单次请求内部的工具往返次数。
  • ImageSource 统一支持 URLbase64 + media_type,不把本地文件路径塞进核心类型。
  • token 和 cache 只认 provider 返回的 usage 真值;Anthropic 的 cache create/read、OpenAI 的 cached tokens 都会归一化到 Event.UsageSession.Info()

开发

make test
make vet
make fmt-check
make example

Documentation

Overview

Package agentkit provides a compact Go SDK for building agent runtimes on top of Claude and OpenAI style streaming model APIs.

The root package intentionally keeps the public surface flat: agent/session orchestration, message and event models, provider abstractions, and shared runtime helpers all live here. Provider adapters and local tools stay in dedicated subpackages.

Index

Constants

View Source
const SessionStateVersion = 2

Variables

View Source
var (
	ErrTurnInProgress   = errors.New("agentkit: session turn already in progress")
	ErrSessionNotFound  = errors.New("agentkit: session not found")
	ErrSessionClosed    = errors.New("agentkit: session is closed")
	ErrMaxTurnsExceeded = errors.New("agentkit: max turns exceeded")
)

Functions

func AgentTool

func AgentTool(agent *Agent, description string) toolpkg.Definition

func RegisterProvider

func RegisterProvider(name string, loader ProviderLoader)

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

func New

func New(opts ...Option) (*Agent, error)

func (*Agent) CreateSession

func (a *Agent) CreateSession(ctx context.Context, options QueryOptions) (*Session, error)

func (*Agent) DefaultSession

func (a *Agent) DefaultSession() *Session

func (*Agent) ResumeSession

func (a *Agent) ResumeSession(ctx context.Context, sessionID string) (*Session, error)

func (*Agent) Stream

func (a *Agent) Stream(ctx context.Context, query Query) EventStream

type AtomicSessionStore

type AtomicSessionStore interface {
	AtomicUpdate(context.Context, string, string, func(*SessionRecord) error) (SessionRecord, error)
}

type ContentBlock

type ContentBlock struct {
	Type       ContentBlockType `json:"type"`
	Text       string           `json:"text,omitempty"`
	Image      *ImageSource     `json:"image,omitempty"`
	Thinking   string           `json:"thinking,omitempty"`
	Signature  string           `json:"signature,omitempty"`
	Data       string           `json:"data,omitempty"`
	ToolUse    *ToolUse         `json:"tool_use,omitempty"`
	ToolResult *ToolResult      `json:"tool_result,omitempty"`
}

func ImageDataBlock

func ImageDataBlock(mediaType string, data []byte) ContentBlock

func ImageURLBlock

func ImageURLBlock(url string) ContentBlock

func TextBlock

func TextBlock(text string) ContentBlock

func ToolResultBlock

func ToolResultBlock(toolUseID, name, content string, isError bool) ContentBlock

func ToolUseBlock

func ToolUseBlock(id, name string, input []byte) ContentBlock

type ContentBlockType

type ContentBlockType string
const (
	ContentBlockTypeText             ContentBlockType = "text"
	ContentBlockTypeImage            ContentBlockType = "image"
	ContentBlockTypeThinking         ContentBlockType = "thinking"
	ContentBlockTypeRedactedThinking ContentBlockType = "redacted_thinking"
	ContentBlockTypeToolUse          ContentBlockType = "tool_use"
	ContentBlockTypeToolResult       ContentBlockType = "tool_result"
	ContentBlockTypeServerToolUse    ContentBlockType = "server_tool_use"
)

type ContextTruncator

type ContextTruncator interface {
	Truncate(context.Context, []Message) ([]Message, error)
}

type Event

type Event struct {
	Type       EventType   `json:"type"`
	TurnID     uint64      `json:"turn_id,omitempty"`
	Text       string      `json:"text,omitempty"`
	ToolUse    *ToolUse    `json:"tool_use,omitempty"`
	ToolResult *ToolResult `json:"tool_result,omitempty"`
	Message    *Message    `json:"message,omitempty"`
	Usage      *Usage      `json:"usage,omitempty"`
	StopReason string      `json:"stop_reason,omitempty"`
}

type EventStream

type EventStream = iter.Seq2[Event, error]

type EventType

type EventType string
const (
	EventTypeMessageStart EventType = "message_start"
	EventTypeTextDelta    EventType = "text_delta"
	EventTypeToolUse      EventType = "tool_use"
	EventTypeToolResult   EventType = "tool_result"
	EventTypeMessageStop  EventType = "message_stop"
)

type ImageSource

type ImageSource struct {
	URL       string `json:"url,omitempty"`
	MediaType string `json:"media_type,omitempty"`
	Data      []byte `json:"data,omitempty"`
}

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

func NewMemoryStore

func NewMemoryStore() *MemoryStore

func (*MemoryStore) AtomicUpdate

func (s *MemoryStore) AtomicUpdate(_ context.Context, agentID, sessionID string, update func(*SessionRecord) error) (SessionRecord, error)

func (*MemoryStore) Create

func (s *MemoryStore) Create(_ context.Context, record SessionRecord) error

func (*MemoryStore) Delete

func (s *MemoryStore) Delete(_ context.Context, agentID, sessionID string) error

func (*MemoryStore) Get

func (s *MemoryStore) Get(_ context.Context, agentID, sessionID string) (SessionRecord, error)

func (*MemoryStore) Put

func (s *MemoryStore) Put(_ context.Context, record SessionRecord) error

type Message

type Message struct {
	Role    Role           `json:"role"`
	Content []ContentBlock `json:"content"`
}

func AssistantMessage

func AssistantMessage(text string) Message

func AssistantMessageWithBlocks

func AssistantMessageWithBlocks(blocks ...ContentBlock) Message

func UserMessage

func UserMessage(text string) Message

func UserMessageWithBlocks

func UserMessageWithBlocks(blocks ...ContentBlock) Message

func UserMessageWithImage

func UserMessageWithImage(text, imageURL string) Message

func UserMessageWithImageData

func UserMessageWithImageData(text, mediaType string, data []byte) Message

type Option

type Option func(*Agent) error

func WithID

func WithID(id string) Option

func WithInstructions

func WithInstructions(instructions string) Option

func WithModel

func WithModel(model string) Option

func WithName

func WithName(name string) Option

func WithProvider

func WithProvider(provider Provider) Option

func WithSessionManager

func WithSessionManager(manager *SessionManager) Option

func WithTelemetry

func WithTelemetry(telemetry Telemetry) Option

func WithTools

func WithTools(definitions ...toolpkg.Definition) Option

func WithWorkspace

func WithWorkspace(workspace WorkspaceConfig) Option

type Provider

type Provider interface {
	Generate(context.Context, string, Request) EventStream
}

Provider generates model events for a normalized Request.

type ProviderCallTrace

type ProviderCallTrace struct {
	Model      string
	Request    Request
	Duration   time.Duration
	StopReason string
	Usage      *Usage
	Err        error
}

type ProviderLoader

type ProviderLoader func() (Provider, error)

ProviderLoader constructs a Provider on demand, typically from environment configuration or an SDK client factory.

type Query

type Query struct {
	Prompt  string       `json:"prompt,omitempty"`
	Input   *Message     `json:"input,omitempty"`
	Options QueryOptions `json:"options,omitempty"`
}

func (Query) Messages

func (q Query) Messages() ([]Message, error)

type QueryOptions

type QueryOptions struct {
	History          []Message        `json:"history,omitempty"`
	AllowedTools     []string         `json:"allowed_tools,omitempty"`
	Model            string           `json:"model,omitempty"`
	MaxTurns         int              `json:"max_turns,omitempty"`
	MaxTokens        int              `json:"max_tokens,omitempty"`
	StopSequences    []string         `json:"stop_sequences,omitempty"`
	Temperature      *float64         `json:"temperature,omitempty"`
	ToolChoice       *ToolChoice      `json:"tool_choice,omitempty"`
	BudgetTokens     int              `json:"budget_tokens,omitempty"`
	ContextTruncator ContextTruncator `json:"-"`
}

type Request

type Request struct {
	Query        Query            `json:"query"`
	Instructions string           `json:"instructions,omitempty"`
	Tools        []ToolDefinition `json:"tools,omitempty"`
}

Request is the normalized provider request produced by the agent runtime. Provider implementations should only depend on this shape rather than provider-specific SDK structs leaking into application code.

type Role

type Role string
const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

type Session

type Session struct {
	// contains filtered or unexported fields
}

func (*Session) Close

func (s *Session) Close() error

func (*Session) Fork

func (s *Session) Fork(ctx context.Context) (*Session, error)

func (*Session) History

func (s *Session) History() []Message

func (*Session) ID

func (s *Session) ID() string

func (*Session) Info

func (s *Session) Info() SessionInfo

func (*Session) State

func (s *Session) State() SessionState

func (*Session) Stream

func (s *Session) Stream(ctx context.Context, input Message) EventStream

func (*Session) Turns

func (s *Session) Turns() []TurnRecord

type SessionInfo

type SessionInfo struct {
	SessionID       string              `json:"session_id"`
	AgentID         string              `json:"agent_id"`
	ParentSessionID string              `json:"parent_session_id,omitempty"`
	Model           string              `json:"model,omitempty"`
	CreatedAt       time.Time           `json:"created_at"`
	UpdatedAt       time.Time           `json:"updated_at"`
	DirectUsage     SessionUsageSummary `json:"direct_usage,omitempty"`
	TotalUsage      SessionUsageSummary `json:"total_usage,omitempty"`
}

type SessionManager

type SessionManager struct {
	// contains filtered or unexported fields
}

func NewSessionManager

func NewSessionManager(store SessionStore) *SessionManager

type SessionRecord

type SessionRecord struct {
	SessionID           string              `json:"session_id"`
	AgentID             string              `json:"agent_id"`
	ParentSessionID     string              `json:"parent_session_id,omitempty"`
	CreatedAt           time.Time           `json:"created_at"`
	UpdatedAt           time.Time           `json:"updated_at"`
	State               SessionState        `json:"state"`
	InstructionSnapshot string              `json:"instruction_snapshot,omitempty"`
	BaseUsage           SessionUsageSummary `json:"base_usage,omitempty"`
	Turns               []TurnRecord        `json:"turns,omitempty"`
}

type SessionState

type SessionState struct {
	Version       int         `json:"version"`
	History       []Message   `json:"history,omitempty"`
	AllowedTools  []string    `json:"allowed_tools,omitempty"`
	Model         string      `json:"model,omitempty"`
	MaxTurns      int         `json:"max_turns,omitempty"`
	MaxTokens     int         `json:"max_tokens,omitempty"`
	StopSequences []string    `json:"stop_sequences,omitempty"`
	Temperature   *float64    `json:"temperature,omitempty"`
	ToolChoice    *ToolChoice `json:"tool_choice,omitempty"`
	BudgetTokens  int         `json:"budget_tokens,omitempty"`
}

type SessionUsageSummary

type SessionUsageSummary struct {
	TurnCount                int    `json:"turn_count"`
	CacheReadTurnCount       int    `json:"cache_read_turn_count"`
	CacheCreationTurnCount   int    `json:"cache_creation_turn_count"`
	InputTokens              *int64 `json:"input_tokens,omitempty"`
	OutputTokens             *int64 `json:"output_tokens,omitempty"`
	TotalTokens              *int64 `json:"total_tokens,omitempty"`
	CacheReadInputTokens     *int64 `json:"cache_read_input_tokens,omitempty"`
	CacheCreationInputTokens *int64 `json:"cache_creation_input_tokens,omitempty"`
}

type SlidingWindowTruncator

type SlidingWindowTruncator struct {
	MaxMessages int
}

func (SlidingWindowTruncator) Truncate

func (t SlidingWindowTruncator) Truncate(_ context.Context, history []Message) ([]Message, error)

type Telemetry

type Telemetry interface {
	OnProviderCall(context.Context, ProviderCallTrace)
	OnToolExecution(context.Context, ToolExecutionTrace)
	OnTurnComplete(context.Context, TurnTrace)
}

type ToolChoice

type ToolChoice struct {
	Mode ToolChoiceMode `json:"mode,omitempty"`
	Name string         `json:"name,omitempty"`
}

type ToolChoiceMode

type ToolChoiceMode string
const (
	ToolChoiceModeAuto     ToolChoiceMode = "auto"
	ToolChoiceModeRequired ToolChoiceMode = "required"
	ToolChoiceModeNone     ToolChoiceMode = "none"
)

type ToolDefinition

type ToolDefinition struct {
	Name        string         `json:"name"`
	Description string         `json:"description,omitempty"`
	InputSchema map[string]any `json:"input_schema,omitempty"`
	ReadOnly    bool           `json:"read_only,omitempty"`
}

type ToolExecutionTrace

type ToolExecutionTrace struct {
	SessionID string
	TurnID    uint64
	ToolUse   ToolUse
	Duration  time.Duration
	IsError   bool
	Err       error
}

type ToolResult

type ToolResult struct {
	ToolUseID string `json:"tool_use_id"`
	Name      string `json:"name,omitempty"`
	Content   string `json:"content,omitempty"`
	IsError   bool   `json:"is_error,omitempty"`
}

type ToolUse

type ToolUse struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Input []byte `json:"input,omitempty"`
}

type TurnRecord

type TurnRecord struct {
	Input       Message   `json:"input"`
	Output      Message   `json:"output"`
	Usage       *Usage    `json:"usage,omitempty"`
	StopReason  string    `json:"stop_reason,omitempty"`
	CommittedAt time.Time `json:"committed_at"`
}

type TurnTrace

type TurnTrace struct {
	SessionID  string
	TurnID     uint64
	Duration   time.Duration
	StopReason string
	Usage      *Usage
	Err        error
}

type Usage

type Usage struct {
	InputTokens              *int64 `json:"input_tokens,omitempty"`
	OutputTokens             *int64 `json:"output_tokens,omitempty"`
	TotalTokens              *int64 `json:"total_tokens,omitempty"`
	CacheReadInputTokens     *int64 `json:"cache_read_input_tokens,omitempty"`
	CacheCreationInputTokens *int64 `json:"cache_creation_input_tokens,omitempty"`
}

func AccumulateUsage

func AccumulateUsage(current, next *Usage) *Usage

func MergeUsage

func MergeUsage(parts ...*Usage) *Usage

func NormalizeUsage

func NormalizeUsage(usage *Usage) *Usage

func (*Usage) HasCacheCreation

func (u *Usage) HasCacheCreation() bool

func (*Usage) HasCacheRead

func (u *Usage) HasCacheRead() bool

func (*Usage) IsZero

func (u *Usage) IsZero() bool

type WorkspaceConfig

type WorkspaceConfig struct {
	Root string `json:"root,omitempty"`
}

WorkspaceConfig controls workspace-scoped instruction loading.

Directories

Path Synopsis
examples
basic command
multi_agent command
streaming_input command
tools command
provider
claude
Package claude provides the Anthropic Claude adapter for agentkit.
Package claude provides the Anthropic Claude adapter for agentkit.
openai
Package openai provides the OpenAI Responses API adapter for agentkit.
Package openai provides the OpenAI Responses API adapter for agentkit.
openaichat
Package openaichat provides the OpenAI Chat Completions adapter for agentkit.
Package openaichat provides the OpenAI Chat Completions adapter for agentkit.
Package tool defines tool handlers and the built-in local tools used by agentkit runtimes.
Package tool defines tool handlers and the built-in local tools used by agentkit runtimes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL