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.
Above snapshot is a demonstration of the Agent-Boot framework streaming in action. Source: https://github.com/SaiNageswarS/medicine-rag
- 🚀 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
go get github.com/SaiNageswarS/agent-bootpackage 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
}// 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)// 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()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)
}
}// 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()Agent-Boot follows a modular, streaming-first architecture:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Client App │───▶│ Agent Service │───▶│ LLM Provider │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ ┌─────────────────┐ │
└──────────────▶│ Tool Handlers │◀─────────────┘
└─────────────────┘
│
┌─────────────────┐
│ Progress │
│ Reporter │
└─────────────────┘
- 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
Core agent functionality including execution logic, tool management, and progress reporting.
LLM client implementations with support for:
- Ollama: Local and self-hosted models
- Anthropic: Claude models via API
- Extensible: Easy to add new providers
Protocol Buffer generated code for:
- Request/response types
- Streaming chunk definitions
- gRPC service definitions
Template system for:
- Tool selection prompts
- Context summarization
- Custom prompt templates
// 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()// 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()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 -vfunc 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")
}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
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
# 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 := agentboot.NewAgentBuilder().
WithBigModel(primaryModel).
WithMiniModel(summarizationModel).
WithMaxTokens(4000). // Maximum tokens per request
WithMaxTurns(10). // Maximum conversation turns
AddTool(tool1).
AddTool(tool2).
Build()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
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for your changes
- Ensure tests pass (
go test ./...) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Ollama for local LLM support
- Anthropic for Claude API
- Protocol Buffers for serialization
- gRPC for network communication
- 📧 Email: sai@example.com
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Built with ❤️ by Sai Nageswar Satchidanand
⭐ Star this repo if you find it helpful!
