Skip to content

SaiNageswarS/agent-boot

Repository files navigation

🤖 Agent-Boot

Go Version License Build Status Coverage

A high-performance, streaming-first AI agent framework for Go that enables real-time interaction with Large Language Models through Protocol Buffers serialization.

Agent-Boot is designed for building production-ready AI agents with streaming capabilities, native tool calling, and network-transparent communication. Perfect for microservices, distributed systems, and real-time AI applications.

Agent Boot Demo

Above snapshot is a demonstration of the Agent-Boot framework streaming in action. Source: https://github.com/SaiNageswarS/medicine-rag

✨ Features

  • 🚀 Streaming-First Architecture: Real-time response streaming with Protocol Buffers
  • 🔧 Native Tool Calling: Built-in support for function calling and tool execution
  • 🌐 Network Transparent: Serialize and transmit agent responses over gRPC
  • 🔌 Multi-LLM Support: Works with Ollama, Anthropic, and extensible to other providers
  • 🛠️ MCP Tool System: Model Context Protocol compatible tool building
  • 📊 Progress Tracking: Real-time execution progress and status updates
  • 🔄 Turn-Based Conversations: Support for multi-turn agent interactions
  • 🎯 Context Summarization: Intelligent content summarization for RAG workflows
  • 📝 Template System: Flexible prompt templating with Go templates
  • High Performance: Parallel tool execution and optimized streaming

🚀 Quick Start

Installation

go get github.com/SaiNageswarS/agent-boot

Basic Usage

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/SaiNageswarS/agent-boot/agentboot"
    "github.com/SaiNageswarS/agent-boot/llm"
    "github.com/SaiNageswarS/agent-boot/schema"
)

func main() {
    // Create LLM client
    llmClient := llm.NewOllamaClient("gpt-oss:20b")

    // Build an agent with tools
    calculatorTool := agentboot.NewMCPTool("calculator", "Performs mathematical calculations").
        StringParam("expression", "Mathematical expression to evaluate", true).
        WithHandler(calculatorHandler).
        Build()

    agent := agentboot.NewAgentBuilder().
        WithBigModel(llmClient).
        WithMiniModel(llmClient).
        WithSystemPrompt("You are a helpful calculator agent. Solve the math problems step by step.").
        AddTool(calculatorTool).
        WithMaxTokens(2000).
        WithMaxTurns(5).
        Build()

    // Execute agent
    ctx := context.Background()
    reporter := &agent.NoOpProgressReporter{}
    
    request := &schema.GenerateAnswerRequest{
        Question: "What is 15 * 23 + 7?",
    }

    response, err := agent.Execute(ctx, reporter, request)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Answer: %s\n", response.Answer)
    fmt.Printf("Processing Time: %dms\n", response.ProcessingTime)
}

func calculatorHandler(ctx context.Context, params api.ToolCallFunctionArguments) <-chan *schema.ToolResultChunk {
    ch := make(chan *schema.ToolResultChunk, 1)
    
    expression := params["expression"].(string)
    // Implement your calculation logic here
    result := "352" // Simplified for example
    
    chunk := agent.NewToolResultChunk().
        Title("Calculation Result").
        Sentences(fmt.Sprintf("%s = %s", expression, result)).
        MetadataKV("expression", expression).
        MetadataKV("result", result).
        Build()
    
    ch <- chunk
    close(ch)
    return ch
}

🔧 Advanced Usage

Streaming with Progress Updates

// Custom progress reporter that logs events
type LoggingReporter struct{}

func (r *LoggingReporter) Send(event *schema.AgentStreamChunk) error {
    switch chunk := event.ChunkType.(type) {
    case *schema.AgentStreamChunk_ProgressUpdateChunk:
        fmt.Printf("Progress: %s - %s\n", chunk.ProgressUpdateChunk.Stage, chunk.ProgressUpdateChunk.Message)
    case *schema.AgentStreamChunk_ToolResultChunk:
        fmt.Printf("Tool Result: %s\n", chunk.ToolResultChunk.Title)
    case *schema.AgentStreamChunk_Answer:
        fmt.Printf("Answer Chunk: %s", chunk.Answer.Content)
    case *schema.AgentStreamChunk_Complete:
        fmt.Printf("\n✅ Complete: %s\n", chunk.Complete.Answer)
    case *schema.AgentStreamChunk_Error:
        fmt.Printf("❌ Error: %s\n", chunk.Error.ErrorMessage)
    }
    return nil
}

// Use with agent
reporter := &LoggingReporter{}
response, err := agent.Execute(ctx, reporter, request)

Building Complex Tools

// Web search tool with summarization
searchTool := agent.NewMCPTool("web_search", "Searches the web for information").
    StringParam("query", "Search query", true).
    StringParam("max_results", "Maximum number of results", false).
    Summarize(true). // Enable automatic summarization
    WithHandler(func(ctx context.Context, params api.ToolCallFunctionArguments) <-chan *schema.ToolResultChunk {
        ch := make(chan *schema.ToolResultChunk, 10)
        
        go func() {
            defer close(ch)
            
            query := params["query"].(string)
            // Perform web search (implement your search logic)
            results := performWebSearch(query)
            
            for _, result := range results {
                chunk := agent.NewToolResultChunk().
                    Title(result.Title).
                    Sentences(result.Summary).
                    Attribution(result.URL).
                    MetadataKV("url", result.URL).
                    MetadataKV("score", fmt.Sprintf("%.2f", result.Score)).
                    Build()
                
                ch <- chunk
            }
        }()
        
        return ch
    }).
    Build()

// Database query tool
dbTool := agent.NewMCPTool("query_database", "Queries the database for information").
    StringParam("sql", "SQL query to execute", true).
    StringSliceParam("parameters", "Query parameters", false).
    WithHandler(databaseHandler).
    Build()

// Multi-tool agent
agent := agent.NewAgentBuilder().
    WithBigModel(llmClient).
    WithMiniModel(summarizationModel).
    AddTool(searchTool).
    AddTool(dbTool).
    AddTool(calculatorTool).
    WithMaxTokens(4000).
    WithMaxTurns(10).
    Build()

gRPC Streaming Service

package main

import (
    "context"
    "net"

    "github.com/SaiNageswarS/agent-boot/agentboot"
    "github.com/SaiNageswarS/agent-boot/schema"
    "google.golang.org/grpc"
)

type AgentService struct {
    schema.UnimplementedAgentServer
    agent *agentboot.Agent
}

func (s *AgentService) Execute(
    req *schema.GenerateAnswerRequest,
    stream schema.Agent_ExecuteServer,
) error {
    ctx := stream.Context()
    reporter := &agentboot.GrpcProgressReporter{Stream: stream}
    
    _, err := s.agent.Execute(ctx, reporter, req)
    return err
}

func main() {
    // Setup agent (same as above)
    agentInstance := setupAgent()
    
    // Create gRPC server
    server := grpc.NewServer()
    schema.RegisterAgentServer(server, &AgentService{agent: agentInstance})
    
    // Listen and serve
    lis, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    
    fmt.Println("🚀 Agent server starting on :8080")
    if err := server.Serve(lis); err != nil {
        panic(err)
    }
}

Multi-Provider LLM Configuration

// Configure different models for different purposes
bigModel := llm.NewAnthropicClient("claude-3-5-sonnet-20241022")    // For complex reasoning
miniModel := llm.NewOllamaClient("llama3.2:3b")                     // For summarization

agent := agentboot.NewAgentBuilder().
    WithBigModel(bigModel).      // Used for main inference
    WithMiniModel(miniModel).    // Used for summarization
    AddTool(complexAnalysisTool).
    WithMaxTokens(8000).
    Build()

🏗️ Architecture

Agent-Boot follows a modular, streaming-first architecture:

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Client App    │───▶│   Agent Service  │───▶│   LLM Provider  │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                       │                       │
         │              ┌─────────────────┐              │
         └──────────────▶│  Tool Handlers  │◀─────────────┘
                        └─────────────────┘
                               │
                      ┌─────────────────┐
                      │  Progress       │
                      │  Reporter       │
                      └─────────────────┘

Key Components

  • Agent Core: Orchestrates LLM interactions and tool execution
  • LLM Clients: Abstractions for different LLM providers
  • MCP Tools: Model Context Protocol compatible tool system
  • Progress Reporter: Real-time progress and result streaming
  • Schema: Protocol Buffer definitions for serializable communication

📚 Package Overview

/agent

Core agent functionality including execution logic, tool management, and progress reporting.

/llm

LLM client implementations with support for:

  • Ollama: Local and self-hosted models
  • Anthropic: Claude models via API
  • Extensible: Easy to add new providers

/schema

Protocol Buffer generated code for:

  • Request/response types
  • Streaming chunk definitions
  • gRPC service definitions

/prompts

Template system for:

  • Tool selection prompts
  • Context summarization
  • Custom prompt templates

🔌 Tool Development

Creating Custom Tools

// Define your tool
weatherTool := agentboot.NewMCPTool("get_weather", "Gets current weather information").
    StringParam("location", "City or location name", true).
    StringParam("units", "Temperature units (celsius/fahrenheit)", false).
    Summarize(false).
    WithHandler(func(ctx context.Context, params api.ToolCallFunctionArguments) <-chan *schema.ToolResultChunk {
        ch := make(chan *schema.ToolResultChunk, 1)
        
        go func() {
            defer close(ch)
            
            location := params["location"].(string)
            units := "celsius" // default
            if u, ok := params["units"]; ok {
                units = u.(string)
            }
            
            // Call weather API
            weather, err := getWeather(location, units)
            if err != nil {
                chunk := agent.NewToolResultChunk().
                    Error(fmt.Sprintf("Failed to get weather: %v", err)).
                    Build()
                ch <- chunk
                return
            }
            
            chunk := agentboot.NewToolResultChunk().
                Title(fmt.Sprintf("Weather in %s", location)).
                Sentences(
                    fmt.Sprintf("Temperature: %d°%s", weather.Temperature, strings.ToUpper(units[:1])),
                    fmt.Sprintf("Condition: %s", weather.Condition),
                    fmt.Sprintf("Humidity: %d%%", weather.Humidity),
                ).
                Attribution("Weather API").
                MetadataKV("temperature", fmt.Sprintf("%d", weather.Temperature)).
                MetadataKV("condition", weather.Condition).
                Build()
            
            ch <- chunk
        }()
        
        return ch
    }).
    Build()

Tool Result Utilities

// Mathematical calculations
result := agentboot.NewMathToolResult("2 + 2", "4", []string{
    "Step 1: Add 2 + 2",
    "Step 2: Result is 4",
})

// Generic results
result := agentboot.NewToolResultChunk().
    Title("Analysis Complete").
    Sentences("The analysis has been completed successfully.").
    Attribution("Analysis Engine").
    MetadataKV("status", "success").
    MetadataKV("duration", "1.2s").
    Build()

🧪 Testing

Agent-Boot comes with comprehensive tests (70.1% coverage):

# Run all tests
go test ./...

# Run tests with coverage
go test ./... -cover

# Run benchmarks
go test ./... -bench=.

# Run specific package tests
go test ./agent -v

Testing Your Tools

func TestWeatherTool(t *testing.T) {
    tool := NewWeatherTool()
    
    ctx := context.Background()
    params := api.ToolCallFunctionArguments{
        "location": "New York",
        "units":    "celsius",
    }
    
    resultChan := tool.Handler(ctx, params)
    
    var results []*schema.ToolResultChunk
    for chunk := range resultChan {
        results = append(results, chunk)
    }
    
    assert.Len(t, results, 1)
    assert.Contains(t, results[0].Title, "Weather in New York")
}

🚀 Performance

Agent-Boot is optimized for high-performance scenarios:

  • Parallel Tool Execution: Multiple tools can run concurrently
  • Streaming Responses: Real-time result delivery
  • Memory Efficient: Minimal allocations in hot paths
  • Connection Pooling: Efficient HTTP client management

Benchmarks

BenchmarkAgentExecution-12      1000    1.2ms/op    245B/op    3 allocs/op
BenchmarkToolExecution-12       5000    0.3ms/op     98B/op    2 allocs/op
BenchmarkStreamingChunk-12     50000    0.05ms/op    24B/op    1 allocs/op

🔧 Configuration

Environment Variables

# Ollama Configuration
export OLLAMA_HOST="http://localhost:11434"

# Anthropic Configuration
export ANTHROPIC_API_KEY="your-api-key"

# Optional: Logging level
export LOG_LEVEL="info"

Agent Configuration

agent := agentboot.NewAgentBuilder().
    WithBigModel(primaryModel).
    WithMiniModel(summarizationModel).
    WithMaxTokens(4000).          // Maximum tokens per request
    WithMaxTurns(10).             // Maximum conversation turns
    AddTool(tool1).
    AddTool(tool2).
    Build()

📖 Examples

Check out the /examples directory for more comprehensive examples:

  • Simple Calculator Agent: Basic tool usage
  • RAG Agent: Retrieval-Augmented Generation with summarization
  • Multi-Tool Research Assistant: Complex agent with multiple tools
  • gRPC Streaming Service: Network-enabled agent service
  • Custom LLM Provider: Adding new LLM providers

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for your changes
  5. Ensure tests pass (go test ./...)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

📄 License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

🙏 Acknowledgments

📞 Support


Built with ❤️ by Sai Nageswar Satchidanand

⭐ Star this repo if you find it helpful!

About

Streaming-first AI agent framework for Go

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published