Skip to content

zzir/agents-go

Repository files navigation

agents-go

A Go port of openai-agents-python (tracking v0.17.4). Build agents that call tools, hand off to one another, enforce guardrails, stream events, persist sessions, pause for human approval, and emit traces — all with idiomatic Go APIs.

Documentation — mirrors the Python SDK docs structure, including a full comparison with the Python SDK.

Install

go get github.com/zzir/agents-go

Requires Go 1.26+.

Quick start

package main

import (
	"context"
	"fmt"

	agents "github.com/zzir/agents-go/agents"
	"github.com/zzir/agents-go/models/openai"
)

func main() {
	agent := &agents.Agent{
		Name:         "assistant",
		Instructions: agents.StaticInstructions("You are a helpful assistant."),
		Model:        "gpt-4o",
	}

	res, err := agents.Run(context.Background(), agent, "Hello!", agents.RunOptions{
		ModelProvider: openai.NewProvider(), // reads OPENAI_API_KEY
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(res.FinalOutputString())
}

Features

Capability API
Agents agents.Agent{...}
Run (blocking) agents.Run(ctx, agent, input, opts)
Streaming agents.RunStreamed(...)Events() iterator
Function tools agents.NewFunctionTool[Args, Result](name, desc, fn)
Structured output agents.OutputType[T]()
Multimodal tool output agents.ToolOutputText/ToolOutputImage/ToolOutputFile (tool returns native image/file input)
Handoffs agents.HandoffTo(targetAgent)
Agent as tool agent.AsTool(agents.AgentToolConfig{...})
Guardrails InputGuardrails, OutputGuardrails, tool-level guardrails
Sessions agents.Session, InMemorySession, memory.FileSession, sessions (SQLite/Postgres), openai.ConversationsSession (server-side), openai.CompactionSession (auto-summarize)
Session forking agents.ForkSession / ForkSessionAt / IndexOfItemID
Server-side state RunOptions.UsePreviousResponseID / RunOptions.ConversationID
Stored prompts Agent.Prompt = agents.StaticPrompt(...) / agents.PromptFunc(...) (OpenAI stored prompt)
Human-in-the-loop tool.NeedsApproval, RunState.Approve/Reject, agents.ResumeRun
Tracing tracing.NewTracer, tracing.NewBatchProcessor
MCP mcp.NewStdioServer / NewStreamableHTTPServer (NewSSEServer deprecated)
Web search bravesearch.New(bravesearch.Options{...}) (Brave Search API)
File editing editor.NewTools(dir) (str_replace editor, os.Root-confined)
Retry / fallback agents.NewRetryModel(...), agents.NewFallbackModel(...) (model-level); agents.NewRetryProvider(...), agents.NewFallbackProvider(...) (provider-level)
Multi-provider routing agents.NewRouterProvider(...) (per-agent backend by name)
Dynamic output schema agents.NewDynamicOutputSchema(name, schema, strict) (runtime JSON Schema)
Instruction composition agents.WrapInstructions(inner, prefix, suffix)
Composite hooks agents.CompositeRunHooks(hooks...) (fan out to multiple RunHooks)
Skills skills.Load / RenderIndex / ReadFileTool (Agent Skills SKILL.md format)

Tools

A function tool is a typed Go function. The argument struct is reflected into a JSON schema (with strict-mode normalization) shown to the model:

type weatherArgs struct {
	City string `json:"city" jsonschema:"the city"`
}

getWeather := agents.NewFunctionTool("get_weather", "Look up the weather.",
	func(ctx context.Context, tc *agents.ToolContext, args weatherArgs) (string, error) {
		return "sunny in " + args.City, nil
	})

agent := &agents.Agent{Name: "bot", Model: "gpt-4o", Tools: []agents.Tool{getWeather}}

Structured output

type Sentiment struct {
	Label string `json:"label"`
	Score int    `json:"score"`
}

agent := &agents.Agent{Name: "classifier", Model: "gpt-4o", OutputType: agents.OutputType[Sentiment]()}
res, _ := agents.Run(ctx, agent, "I love this!", opts)
s, _ := agents.FinalOutputAs[Sentiment](res)

Streaming

sr := agents.RunStreamed(ctx, agent, "tell me a story", opts)
for event, err := range sr.Events() {
	if err != nil { panic(err) }
	if e, ok := event.(*agents.RunItemStreamEvent); ok {
		if msg, ok := e.Item.(*agents.MessageOutputItem); ok {
			fmt.Println(msg.Text())
		}
	}
}
res, _ := sr.FinalResult()

Human-in-the-loop

tool.NeedsApproval = true

res, _ := agents.Run(ctx, agent, "delete everything", opts)
for len(res.Interruptions) > 0 {
	for _, item := range res.Interruptions {
		res.State.Approve(item, false) // or res.State.Reject(item, false, "no")
	}
	res, _ = agents.ResumeRun(ctx, res.State, opts)
}

The paused state serializes to JSON (res.State.MarshalJSON()) and rebuilds with agents.RunStateFromJSON(data, registry) for cross-process approval flows.

Sessions

sess, _ := memory.NewFileSession("sessions", "user-123") // sessions/user-123.jsonl
agents.Run(ctx, agent, "remember my name is Ada", agents.RunOptions{Session: sess, ModelProvider: p})

History is stored as JSONL with zero external dependencies. Implement the agents.Session interface yourself to back it with a database instead.

Tracing

exporter := tracing.NewConsoleExporter(os.Stdout)
proc := tracing.NewBatchProcessor(exporter, tracing.BatchProcessorOptions{})
defer proc.Shutdown(context.Background())

agents.Run(ctx, agent, "hi", agents.RunOptions{Tracer: tracing.NewTracer(proc), ModelProvider: p})

MCP

server, _ := mcp.NewStdioServer(ctx, "fs",
	exec.Command("npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"), mcp.Options{})
defer server.Close()

agent := &agents.Agent{Name: "a", Model: "gpt-4o", MCPServers: []agents.MCPServer{server}}

Sandbox (code execution)

Run untrusted, agent-generated code in an isolated environment and expose it as a tool. The Docker backend is a separate module so the core stays dependency-light:

import (
	"github.com/zzir/agents-go/sandbox"
	"github.com/zzir/agents-go/sandbox/docker" // go get github.com/zzir/agents-go/sandbox/docker
)

sb, _ := docker.New(docker.Options{Image: "python:3.12-slim",
	Limits: sandbox.Limits{MemoryBytes: 256 << 20, CPUs: 0.5}})
defer sb.Close()

agent := &agents.Agent{Name: "coder", Model: "gpt-4o",
	Tools: []agents.Tool{sandbox.CodeTool(sb, sandbox.CodeToolConfig{Name: "run_python"})}}

The Docker backend defaults to: no network, read-only root fs, dropped capabilities, non-root user, and CPU/memory/PID/time limits. sandbox.NewLocal() runs on the host without isolation — for trusted dev/tests only. The sandbox/ssh backend (also a separate module) runs commands on a remote host over SSH, transferring files via SFTP — useful for offloading to a disposable VM, but it provides no isolation or resource limits of its own.

Packages

Core module path: github.com/zzir/agents-go.

  • .../agents — core: agents, runner, tools, guardrails, sessions, HITL, tracing hooks.
  • .../models/openai — OpenAI Responses API model provider (built on openai-go v3).
  • .../memoryFileSession (JSONL file store, zero dependencies).
  • .../tracing — traces, spans, processors and exporters.
  • .../mcp — Model Context Protocol client.
  • .../sandboxSandbox interface + CodeTool + local backend.
  • .../sandbox/dockerseparate module with the Docker backend.
  • .../sandbox/sshseparate module with the remote SSH backend.
  • .../examples — runnable examples (hello, tools, handoffs, streaming, hitl, sandbox).

agents-server

A full-featured demo web app that wraps the SDK with a REST API, WebSocket streaming, and an embedded browser UI. Configure agents, MCP servers, sandboxes, memories, and skills — then run conversations with streaming output, tool approval, and tracing, all from the browser.

go run ./cmd/agents-server --port 8080

agents-server screenshot

Examples

export OPENAI_API_KEY=sk-...
go run ./examples/hello
go run ./examples/tools
go run ./examples/handoffs
go run ./examples/streaming
go run ./examples/hitl
go run ./examples/toolimage      # a tool returns a generated image to the model
go run ./examples/conversations  # server-side history via the Conversations API
go run ./examples/compaction      # auto-summarize long history via responses.compact
OPENAI_PROMPT_ID=pmpt_... go run ./examples/prompt  # drive an agent from a stored prompt
go run ./examples/sandbox   # writes & runs Python in a sandbox (host needs python3)

# isolated / remote backends live in their own modules:
(cd sandbox/docker && OPENAI_API_KEY=$OPENAI_API_KEY go run ./example)  # needs Docker
(cd sandbox/ssh && SSH_HOST=host SSH_USER=user SSH_KEY=~/.ssh/id_ed25519 \
	OPENAI_API_KEY=$OPENAI_API_KEY go run ./example)  # needs a reachable SSH host

Design notes

The core lives in a single agents/ package. The original plan split it further into tools/, outputs/ and models/, but in Go those would form an import cycle with the core (tool callbacks reference RunContext; the Model interface references Tool), so they are kept together in agents/. Provider, storage, tracing and MCP implementations live in subpackages that import agents. Items use the openai-go Responses types as the wire format, mirroring how the Python SDK reuses the OpenAI SDK types.

License

MIT

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors