ModelBox Open Source

/

AgentKit

Example Tutorials

Step-by-step tutorials for building agents with AgentKit

|

Learn AgentKit through detailed tutorials that walk you through building different types of agents. Each tutorial includes complete code examples, explanations, and best practices.

Tutorial 1: Simple Agent

Build your first agent with basic tools and understand core concepts.

Overview

Create an agent that can:

  • Greet users by name
  • Perform basic calculations
  • Handle multiple tools intelligently

Step 1: Setup and Dependencies

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/model-box/agent-kit/agent"
    "github.com/model-box/agent-kit/common"
    "github.com/model-box/agent-kit/model"
    "github.com/model-box/agent-kit/session"
    "github.com/model-box/agent-kit/tool"
)

Step 2: Define Tool Input Structures

// GreetingInput defines the input for the greeting tool
type GreetingInput struct {
    Name string `json:"name" jsonschema:"description=The name of the person to greet"`
}

// CalculatorInput defines the input for the calculator tool
type CalculatorInput struct {
    Operation string  `json:"operation" jsonschema:"description=The operation to perform (add, subtract, multiply, divide)"`
    A         float64 `json:"a" jsonschema:"description=The first number"`
    B         float64 `json:"b" jsonschema:"description=The second number"`
}

Step 3: Implement Tool Handlers

// greetingHandler implements the greeting functionality
func greetingHandler(ctx common.RunContext, params GreetingInput) (string, error) {
    return fmt.Sprintf("Hello, %s! Nice to meet you!", params.Name), nil
}

// calculateHandler implements basic math operations
func calculateHandler(ctx common.RunContext, params CalculatorInput) (string, error) {
    var result float64
    
    switch params.Operation {
    case "add":
        result = params.A + params.B
    case "subtract":
        result = params.A - params.B
    case "multiply":
        result = params.A * params.B
    case "divide":
        if params.B == 0 {
            return "", fmt.Errorf("division by zero")
        }
        result = params.A / params.B
    default:
        return "", fmt.Errorf("unsupported operation: %s", params.Operation)
    }
    
    return fmt.Sprintf("%.2f", result), nil
}

Step 4: Create the Agent

func main() {
    // Get API key from environment
    apiKey := os.Getenv("OPENAI_API_KEY")
    if apiKey == "" {
        log.Fatal("OPENAI_API_KEY environment variable is required")
    }

    // Create model with configuration
    model := model.Model("gpt-4o").
        SetAPIKey(apiKey).
        SetTemperature(0.1).    // Low temperature for consistent responses
        SetMaxTokens(1000)      // Reasonable token limit

    // Create tools
    greetingTool := tool.NewFunctionTool(
        "greeting",
        "Greet a person by name",
        greetingHandler,
    )

    calculatorTool := tool.NewFunctionTool(
        "calculator", 
        "Perform basic math operations (add, subtract, multiply, divide)",
        calculateHandler,
    )

    // Create agent with tools
    agent := agent.New().
        SetModel(model).
        SetSystemPrompt("You are a helpful assistant with access to greeting and calculator tools. Use tools when appropriate to help users.").
        AddTool(greetingTool).
        AddTool(calculatorTool)

    // Test the agent
    testCases := []string{
        "Hello, can you greet me? My name is Alice",
        "What is 15 + 27?",
        "Can you calculate 144 divided by 12?",
        "Please greet Bob and then calculate 5 * 8",
    }

    for i, testCase := range testCases {
        fmt.Printf("\n--- Test Case %d ---\n", i+1)
        fmt.Printf("Input: %s\n", testCase)
        fmt.Printf("Output: ")

        // Run with session
        session := session.New(agent)
        result, err := session.Run(context.Background(), []agent.ChatMessage{
            agent.NewUserMessage(testCase),
        }, nil)
        
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }

        fmt.Println(result.FinalOutput)
    }
}

Key Concepts Explained

  1. Model Configuration: Set temperature and token limits appropriate for your use case
  2. Tool Design: Define clear input structures with JSON schema descriptions
  3. Error Handling: Always handle errors gracefully in tool handlers
  4. Agent Configuration: Use descriptive system prompts that mention available tools
  5. Session Usage: Use sessions for stateful conversations

Tutorial 2: Streaming Agent with Sessions

Build an agent that provides real-time streaming responses and maintains conversation history.

Overview

This tutorial covers:

  • Real-time streaming responses
  • Session-based conversation memory
  • Advanced tool usage
  • Event handling patterns

Step 1: Advanced Tool Implementation

type CalculatorInput struct {
    Operation string   `json:"operation" jsonschema:"enum=add,enum=subtract,enum=multiply,enum=divide,enum=sqrt,enum=power"`
    A         float64  `json:"a" jsonschema:"description=First number"`
    B         *float64 `json:"b,omitempty" jsonschema:"description=Second number (not needed for sqrt)"`
}

func calculatorTool(ctx common.RunContext, input CalculatorInput) (string, error) {
    fmt.Printf("🔧 Calculator tool called with: %+v\n", input)

    switch input.Operation {
    case "add":
        if input.B == nil {
            return "", fmt.Errorf("operation 'add' requires both a and b")
        }
        result := input.A + *input.B
        return fmt.Sprintf("%.2f", result), nil
    case "sqrt":
        if input.A < 0 {
            return "", fmt.Errorf("cannot calculate square root of negative number")
        }
        result := math.Sqrt(input.A)
        return fmt.Sprintf("%.2f", result), nil
    case "power":
        if input.B == nil {
            return "", fmt.Errorf("operation 'power' requires both a and b")
        }
        result := math.Pow(input.A, *input.B)
        return fmt.Sprintf("%.2f", result), nil
    // ... other operations
    }
    
    return "", fmt.Errorf("unsupported operation: %s", input.Operation)
}

Step 2: Streaming Implementation

func runStreamingExample(agent *agent.Agent) {
    // Create session for conversation state
    sess := session.New(agent)
    
    inputs := []string{
        "Hi! I'm learning about math. Can you help me?",
        "What's the square root of 144?",
        "Now calculate 12 to the power of 2",
        "Perfect! What's 12 * 12 + 10?",
    }

    for i, input := range inputs {
        fmt.Printf("\n=== Conversation Turn %d ===\n", i+1)
        fmt.Printf("👤 User: %s\n", input)
        fmt.Printf("🤖 Assistant: ")

        // Start streaming
        stream, err := sess.RunStreamed(context.Background(), []agent.ChatMessage{
            agent.NewUserMessage(input),
        }, nil)

        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }

        // Process streaming events
        for event, err := range stream.StreamEvents() {
            if err != nil {
                fmt.Printf("\nStream error: %v\n", err)
                break
            }

            switch {
            case event.IsTextDelta():
                // Print each text chunk as it arrives
                fmt.Print(event.GetContent())
            case event.IsCompleted():
                fmt.Println("\n✅ Response completed!")
                break
            }
        }

        // Small delay between conversations
        time.Sleep(1 * time.Second)
    }
}

Step 3: Complete Streaming Agent

func main() {
    apiKey := os.Getenv("OPENAI_API_KEY")
    if apiKey == "" {
        log.Fatal("OPENAI_API_KEY environment variable is required")
    }

    fmt.Println("🚀 AgentKit Streaming Session Example")
    fmt.Println("=====================================")

    // Create model with streaming-optimized settings
    model := model.Model("gpt-4o").
        SetAPIKey(apiKey).
        SetTemperature(0.3).    // Balanced creativity
        SetMaxTokens(800)       // Reasonable for conversations

    // Create advanced calculator tool
    calcTool := tool.NewFunctionTool(
        "advanced_calculator",
        "Perform mathematical operations including basic arithmetic, square root, and power",
        calculatorTool,
    )

    // Create agent optimized for conversations
    agent := agent.New().
        SetModel(model).
        SetSystemPrompt(`You are a friendly and helpful math tutor. You have access to an advanced calculator tool for mathematical operations.

Guidelines:
- Use the calculator for any mathematical computations
- Explain your reasoning step by step
- Be encouraging and supportive
- Build on previous conversation context`).
        AddTool(calcTool)

    // Run streaming example
    runStreamingExample(agent)

    fmt.Println("\n🎓 Math tutoring session completed!")
}

Key Streaming Concepts

  1. Event Types: Handle different event types (IsTextDelta(), IsCompleted())
  2. Real-time Output: Print chunks immediately for responsive UX
  3. Session Continuity: Sessions maintain context across streaming calls
  4. Error Handling: Handle both setup errors and streaming errors
  5. Event Loop: Use range over StreamEvents() for clean iteration

Tutorial 3: Business Agent (Sales Assistant)

Build a production-ready sales agent with lead qualification and business logic.

Overview

Create a sophisticated sales agent that:

  • Qualifies leads using BANT methodology
  • Provides product information and pricing
  • Handles objections professionally
  • Maintains sales conversation flow

Step 1: Business Logic Implementation

// LeadQualificationInput for BANT analysis
type LeadQualificationInput struct {
    CompanySize    string `json:"company_size" jsonschema:"description=Number of employees or company size indicator"`
    Industry       string `json:"industry" jsonschema:"description=Industry or business vertical"`
    CurrentSolution string `json:"current_solution,omitempty" jsonschema:"description=Current tools or solutions they use"`
    Budget         string `json:"budget,omitempty" jsonschema:"description=Budget range or procurement process"`
    Timeline       string `json:"timeline,omitempty" jsonschema:"description=Implementation timeline or urgency"`
    Authority      string `json:"authority,omitempty" jsonschema:"description=Decision making role or influence"`
}

func leadQualificationTool(ctx common.RunContext, input LeadQualificationInput) (string, error) {
    // Implement BANT scoring logic
    score := 0
    feedback := []string{}

    // Budget assessment
    if strings.Contains(strings.ToLower(input.Budget), "budget") || 
       strings.Contains(strings.ToLower(input.Budget), "$") {
        score += 25
        feedback = append(feedback, "✅ Budget: Expressed budget awareness")
    } else {
        feedback = append(feedback, "⚠️ Budget: Needs budget qualification")
    }

    // Authority assessment
    if strings.Contains(strings.ToLower(input.Authority), "decision") ||
       strings.Contains(strings.ToLower(input.Authority), "manager") ||
       strings.Contains(strings.ToLower(input.Authority), "director") {
        score += 25
        feedback = append(feedback, "✅ Authority: Has decision making influence")
    } else {
        feedback = append(feedback, "⚠️ Authority: May need to involve decision makers")
    }

    // Need assessment
    if input.CurrentSolution != "" {
        score += 25
        feedback = append(feedback, "✅ Need: Has existing solution (replacement opportunity)")
    } else {
        feedback = append(feedback, "✅ Need: Greenfield opportunity")
        score += 20
    }

    // Timeline assessment
    if strings.Contains(strings.ToLower(input.Timeline), "urgent") ||
       strings.Contains(strings.ToLower(input.Timeline), "asap") ||
       strings.Contains(strings.ToLower(input.Timeline), "month") {
        score += 25
        feedback = append(feedback, "✅ Timeline: Urgent implementation need")
    } else if input.Timeline != "" {
        score += 15
        feedback = append(feedback, "⚠️ Timeline: Has timeline but not urgent")
    }

    // Generate qualification summary
    var qualification string
    switch {
    case score >= 80:
        qualification = "🔥 HOT LEAD - High priority follow-up recommended"
    case score >= 60:
        qualification = "🌟 WARM LEAD - Good opportunity, continue qualification"
    case score >= 40:
        qualification = "🔄 DEVELOPING LEAD - Needs further nurturing"
    default:
        qualification = "❄️ COLD LEAD - Early stage, focus on education"
    }

    result := fmt.Sprintf(`BANT Qualification Score: %d/100

%s

%s

Company Profile:
- Size: %s
- Industry: %s
- Current Solution: %s

Next Steps: %s`,
        score,
        qualification,
        strings.Join(feedback, "\n"),
        input.CompanySize,
        input.Industry,
        input.CurrentSolution,
        getNextStepsRecommendation(score))

    return result, nil
}

func getNextStepsRecommendation(score int) string {
    switch {
    case score >= 80:
        return "Schedule demo, prepare custom proposal, involve technical team"
    case score >= 60:
        return "Continue discovery call, identify key stakeholders, share case studies"
    case score >= 40:
        return "Provide educational content, schedule follow-up, build relationship"
    default:
        return "Share general resources, add to nurture campaign, quarterly check-in"
    }
}

Step 2: Product Information Tool

type ProductInfoInput struct {
    ProductArea string `json:"product_area" jsonschema:"description=Product area of interest (project-management, analytics, integrations, security)"`
    CompanySize string `json:"company_size" jsonschema:"description=Company size for appropriate recommendations"`
}

func productInfoTool(ctx common.RunContext, input ProductInfoInput) (string, error) {
    products := map[string]map[string]string{
        "project-management": {
            "small":      "AgentKit Starter: $29/user/month - Perfect for small teams with basic project tracking",
            "medium":     "AgentKit Professional: $49/user/month - Advanced workflows, custom fields, reporting", 
            "large":      "AgentKit Enterprise: $89/user/month - Advanced security, SSO, unlimited everything",
            "enterprise": "AgentKit Enterprise+: Custom pricing - White-label options, dedicated support",
        },
        "analytics": {
            "small":      "Basic Analytics: Included in Professional+ plans",
            "medium":     "Advanced Analytics: $19/user/month add-on - Custom dashboards, exports",
            "large":      "Analytics Suite: $39/user/month - Real-time insights, ML predictions",
            "enterprise": "Enterprise Analytics: Custom pricing - Data warehouse integration, API access",
        },
    }

    sizeCategory := categorizeSizeForPricing(input.CompanySize)
    
    if productMap, exists := products[input.ProductArea]; exists {
        if price, exists := productMap[sizeCategory]; exists {
            return fmt.Sprintf(`Product Recommendation for %s (%s company):

%s

Key Features:
%s

Would you like me to schedule a personalized demo to show these features in action?`,
                input.ProductArea,
                input.CompanySize,
                price,
                getKeyFeatures(input.ProductArea, sizeCategory)), nil
        }
    }

    return "I'd be happy to provide detailed product information. Could you specify which area you're most interested in: project management, analytics, integrations, or security?", nil
}

func categorizeSizeForPricing(companySize string) string {
    size := strings.ToLower(companySize)
    switch {
    case strings.Contains(size, "1-") || strings.Contains(size, "startup") || strings.Contains(size, "small"):
        return "small"
    case strings.Contains(size, "50") || strings.Contains(size, "medium") || strings.Contains(size, "100"):
        return "medium"
    case strings.Contains(size, "500") || strings.Contains(size, "large") || strings.Contains(size, "1000"):
        return "large"
    default:
        return "enterprise"
    }
}

Step 3: Complete Sales Agent

func main() {
    apiKey := os.Getenv("OPENAI_API_KEY")
    if apiKey == "" {
        log.Fatal("OPENAI_API_KEY environment variable is required")
    }

    fmt.Println("💼 AgentKit Sales Agent Example")
    fmt.Println("===============================")

    // Create model optimized for sales conversations
    model := model.Model("gpt-4o").
        SetAPIKey(apiKey).
        SetTemperature(0.7).    // Higher temperature for personable conversations
        SetMaxTokens(1200)      // Longer responses for detailed explanations

    // Create sales tools
    qualificationTool := tool.NewFunctionTool(
        "bant_qualification",
        "Qualify leads using BANT methodology (Budget, Authority, Need, Timeline)",
        leadQualificationTool,
    )

    productTool := tool.NewFunctionTool(
        "product_information",
        "Provide product information and pricing based on company needs",
        productInfoTool,
    )

    // Create sales agent
    agent := agent.New().
        SetModel(model).
        SetSystemPrompt(`You are a professional and friendly sales representative for AgentKit, a comprehensive business automation platform.

Your Role:
1. Engage prospects in meaningful conversations about their business needs
2. Qualify leads using BANT methodology (Budget, Authority, Need, Timeline)
3. Provide relevant product information and pricing
4. Handle objections professionally and empathetically
5. Guide prospects through the sales process
6. Build trust and rapport with potential customers

Approach:
- Be consultative, not pushy
- Ask discovery questions to understand their specific challenges
- Listen actively and provide relevant solutions
- Use tools to provide accurate qualification and product information
- Always focus on solving their problems, not just selling products

Tools Available:
- BANT Qualification: Use when you have enough information about budget, authority, need, and timeline
- Product Information: Use when prospects ask about specific features, pricing, or recommendations`).
        AddTool(qualificationTool).
        AddTool(productTool)

    // Sales scenarios for testing
    scenarios := []string{
        "Hi, I'm interested in your software solution for my small business. We have about 25 employees and are looking for a project management tool.",
        "I saw your ad online and want to know more about pricing for enterprise customers. We're a 500-person company in the healthcare industry.",
        "We're currently using a competitor's product but aren't happy with it. Can you tell me how your solution compares?",
        "I'm evaluating different vendors for our Q2 budget. What's your best price for a 100-user license?",
    }

    // Run sales conversations
    for i, scenario := range scenarios {
        fmt.Printf("\n=== Sales Scenario %d ===\n", i+1)
        fmt.Printf("👤 Prospect: %s\n", scenario)
        fmt.Printf("💼 Sales Agent: ")

        session := session.New(agent)
        
        // Use streaming for more natural sales conversations
        stream, err := session.RunStreamed(context.Background(), []agent.ChatMessage{
            agent.NewUserMessage(scenario),
        }, nil)

        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }

        for event, err := range stream.StreamEvents() {
            if err != nil {
                break
            }
            
            if event.IsTextDelta() {
                fmt.Print(event.GetContent())
            }
        }
        
        fmt.Println("\n")
    }

    fmt.Println("🎯 Sales scenarios completed!")
}

Business Agent Best Practices

  1. Domain-Specific Prompts: Tailor system prompts to specific business roles
  2. Business Logic Tools: Implement real business processes (BANT, pricing, etc.)
  3. Professional Tone: Higher temperature for natural, personable conversations
  4. Comprehensive Responses: Longer token limits for detailed business discussions
  5. Industry Knowledge: Build in relevant industry context and terminology

Key Takeaways

Development Patterns

  1. Start Simple: Begin with basic tools and gradually add complexity
  2. Use Sessions: Always use sessions for stateful conversations
  3. Handle Errors: Implement comprehensive error handling
  4. Stream Responses: Use streaming for better user experience
  5. Test Thoroughly: Test with various scenarios and edge cases

Production Considerations

  1. API Key Management: Never hardcode keys, always use environment variables
  2. Rate Limiting: Implement appropriate limits for production usage
  3. Monitoring: Add logging and metrics for production systems
  4. Security: Validate inputs and sanitize outputs
  5. Scalability: Consider session management for concurrent users

Tool Design

  1. Clear Schemas: Use descriptive JSON schema annotations
  2. Validation: Validate inputs in tool handlers
  3. Error Messages: Provide helpful error messages
  4. Business Logic: Implement real business processes, not just demos
  5. Testing: Test tools independently before integration

These tutorials provide a solid foundation for building sophisticated agents with AgentKit. Use them as starting points and adapt the patterns to your specific use cases.

Edit on GitHub