Agent Package
Core Agent type for building distributed agent systems in Go
Agent Package
Core Agent type for lifecycle management, reasoner registration, and Agentfield integration
The agent package provides the core Agent type that manages HTTP servers, registers reasoners, integrates AI capabilities, and communicates with the Agentfield control plane.
Creating an Agent
import (
"github.com/Agent-Field/agentfield/sdk/go/agent"
"github.com/Agent-Field/agentfield/sdk/go/ai"
)
app, err := agent.New(agent.Config{
NodeID: "my-agent",
Version: "1.0.0",
TeamID: "my-team",
AgentFieldURL: "http://localhost:8080",
ListenAddress: ":8001",
PublicURL: "http://localhost:8001",
Token: os.Getenv("BRAIN_TOKEN"),
AIConfig: aiConfig, // Optional: nil disables AI
})Configuration
Prop
Type
CLIConfig Fields
Enable your agent binary to run as a CLI tool with CLIConfig:
Prop
Type
Example:
app, err := agent.New(agent.Config{
NodeID: "my-tool",
AgentFieldURL: os.Getenv("AGENTFIELD_URL"), // Optional for CLI mode
CLIConfig: &agent.CLIConfig{
AppName: "my-tool",
AppDescription: "Production-ready CLI with control plane integration",
HelpPreamble: "⚠️ Set API_KEY environment variable before running",
EnvironmentVars: []string{
"API_KEY (required) - Your API key",
"AGENTFIELD_URL (optional) - Control plane URL for server mode",
},
},
})See CLI Configuration for complete documentation.
Agent Methods
Lifecycle Methods
Initialize(ctx context.Context) error
Registers the agent with the Agentfield control plane and discovers reasoners.
if err := app.Initialize(ctx); err != nil {
log.Fatalf("Failed to initialize: %v", err)
}When to call: Call Initialize() before Run() to register with Agentfield. For serverless agents, this can be called on each invocation or once during cold start.
Run(ctx context.Context) error
Starts the HTTP server and begins serving reasoner requests. Blocks until context is cancelled.
// Blocking - runs until ctx is cancelled
if err := app.Run(ctx); err != nil {
log.Fatal(err)
}Reasoner Methods
RegisterReasoner(name string, handler HandlerFunc, opts ...ReasonerOption)
Registers a reasoner that Agentfield can execute.
app.RegisterReasoner("process_data", func(ctx context.Context, input map[string]any) (any, error) {
// Process input
data := input["data"].(string)
// Return structured result
return map[string]any{
"status": "completed",
"result": processedData,
}, nil
})Handler Requirements:
- Must accept
context.Contextas first parameter - Must accept
map[string]anyas input - Must return
(any, error) - Should return JSON-serializable types
Call(ctx context.Context, target string, input map[string]any) (map[string]any, error)
Calls another reasoner via the Agentfield control plane. Automatically creates parent-child workflow relationships.
// Calls another agent's reasoner
result, err := app.Call(ctx, "other-agent.process", map[string]any{
"data": "hello",
})
// Calls own reasoner (creates parent-child link)
result, err := app.Call(ctx, "my-agent.analyze", input)Automatic Workflow Tracking: The SDK automatically:
- Extracts current execution context from
ctx - Sets
X-Parent-Execution-IDheader - Agentfield creates parent-child relationship
- Builds workflow DAG for visualization
AI Methods
AI(ctx context.Context, prompt string, opts ...ai.Option) (*ai.Response, error)
Makes an AI/LLM call with structured output support.
response, err := app.AI(ctx, "What is 2+2?",
ai.WithSystem("You are a math tutor"),
ai.WithTemperature(0.3))
fmt.Println(response.Text()) // "4"Returns error if AIConfig was not set during agent creation.
AIStream(ctx context.Context, prompt string, opts ...ai.Option) (<-chan ai.StreamChunk, <-chan error)
Makes a streaming AI call. Returns channels for chunks and errors.
chunks, errs := app.AIStream(ctx, "Tell me a story")
for chunk := range chunks {
if len(chunk.Choices) > 0 {
fmt.Print(chunk.Choices[0].Delta.Content)
}
}
if err := <-errs; err != nil {
log.Printf("Stream error: %v", err)
}Common Patterns
Long-Running Agent
Standard deployment for persistent agents:
func main() {
ctx := context.Background()
app, err := agent.New(config)
if err != nil {
log.Fatal(err)
}
// Register reasoners
app.RegisterReasoner("task1", handler1)
app.RegisterReasoner("task2", handler2)
// Initialize with Agentfield
if err := app.Initialize(ctx); err != nil {
log.Fatal(err)
}
// Run forever (blocks)
log.Fatal(app.Run(ctx))
}Serverless Agent
Optimized for Lambda/Cloud Functions/Cloud Run with adapters:
var app *agent.Agent
func init() {
// Create agent once during cold start
app, _ = agent.New(agent.Config{
NodeID: "serverless-agent",
AgentFieldURL: os.Getenv("AGENTFIELD_URL"),
AIConfig: aiConfig,
DeploymentType: "serverless",
DisableLeaseLoop: true, // No heartbeats in serverless
})
app.RegisterReasoner("process", handler)
// Initialize once
ctx := context.Background()
_ = app.Initialize(ctx)
}
// HTTP handler (Cloud Run / Functions)
// http.ListenAndServe(":8080", app.Handler())
// Raw event handler (Lambda-style)
func LambdaHandler(ctx context.Context, event map[string]any) (map[string]any, error) {
normalize := func(e map[string]any) map[string]any {
return map[string]any{
"path": stringFrom(e, "rawPath", "path"),
"target": stringFrom(e, "target", "reasoner", "skill"),
"input": e["input"],
}
}
result, status, err := app.HandleServerlessEvent(ctx, event, normalize)
if err != nil {
return map[string]any{"statusCode": 500, "body": map[string]any{"error": err.Error()}}, nil
}
return map[string]any{"statusCode": status, "body": result}, nil
}AI-Powered Reasoner
Use AI within reasoners for intelligent processing:
type Analysis struct {
Summary string `json:"summary"`
Confidence float64 `json:"confidence"`
Tags []string `json:"tags"`
}
app.RegisterReasoner("analyze", func(ctx context.Context, input map[string]any) (any, error) {
text := input["text"].(string)
// AI call with structured output
response, err := app.AI(ctx,
fmt.Sprintf("Analyze: %s", text),
ai.WithSchema(Analysis{}))
if err != nil {
return nil, err
}
var analysis Analysis
if err := response.Into(&analysis); err != nil {
return nil, err
}
return analysis, nil
})Parent-Child Workflow
Orchestrate multiple reasoners automatically:
app.RegisterReasoner("orchestrate", func(ctx context.Context, input map[string]any) (any, error) {
// These calls automatically create parent-child relationships
result1, err := app.Call(ctx, "my-agent.step1", input)
if err != nil {
return nil, err
}
result2, err := app.Call(ctx, "my-agent.step2", result1)
if err != nil {
return nil, err
}
result3, err := app.Call(ctx, "my-agent.step3", result2)
if err != nil {
return nil, err
}
// Agentfield automatically tracks: orchestrate -> step1 -> step2 -> step3
return result3, nil
})Parallel Execution
Use goroutines for parallel reasoner calls:
app.RegisterReasoner("parallel_process", func(ctx context.Context, input map[string]any) (any, error) {
var wg sync.WaitGroup
results := make([]map[string]any, 4)
for i := 0; i < 4; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
// Each goroutine creates its own child execution
result, err := app.Call(ctx, "my-agent.worker", map[string]any{
"index": index,
"data": input,
})
if err == nil {
results[index] = result
}
}(i)
}
wg.Wait()
// Agentfield tracks all 4 parallel executions as children
return map[string]any{"results": results}, nil
})Dual-Mode Agent (CLI + Server)
Build agents that work as CLI tools or control plane agents:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
app, err := agent.New(agent.Config{
NodeID: "my-tool",
AgentFieldURL: os.Getenv("AGENTFIELD_URL"), // Optional for CLI mode
CLIConfig: &agent.CLIConfig{
AppName: "my-tool",
AppDescription: "Production tool with control plane integration",
HelpEpilog: `Examples:
# CLI mode (standalone)
$ ./my-tool --set input="data"
# Server mode (with control plane)
$ AGENTFIELD_URL=http://localhost:8080 ./my-tool serve`,
},
})
if err != nil {
log.Fatal(err)
}
app.RegisterReasoner("process", func(ctx context.Context, input map[string]any) (any, error) {
data := input["input"].(string)
return fmt.Sprintf("Processed: %s", data), nil
},
agent.WithCLI(), // Enable CLI access
agent.WithDefaultCLI(), // Make this the default command
)
// Automatically detects CLI or server mode
if err := app.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
# Build once
go build -o my-tool
# CLI mode (no control plane)
./my-tool --set input="hello"
# Server mode (with control plane)
AGENTFIELD_URL=http://localhost:8080 ./my-tool serveSee CLI Mode Overview for complete documentation.
Best Practices
Always Use Context
Pass context.Context through all calls for proper cancellation and timeout handling:
// ✅ Good - respects timeouts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
result, err := app.Call(ctx, "my-agent.task", input)Error Handling
Always handle errors from reasoner calls:
// ✅ Good - handles errors
result, err := app.Call(ctx, "my-agent.risky_task", input)
if err != nil {
// Log and return structured error
return map[string]any{
"status": "failed",
"error": err.Error(),
}, nil
}Structured Returns
Use maps or Go structs for JSON-serializable returns:
// ✅ Good - structured response
return map[string]any{
"status": "success",
"result": data,
"elapsed": elapsed.Milliseconds(),
}, nil
// ❌ Bad - not JSON-serializable
return myComplexStruct, nil // Unless it has json tags