Agent
An Agent is an autonomous AI entity that can use tools, maintain conversation context, and execute tasks independently.
Overview
flowchart LR
A[User Input] --> B[Build Request]
B --> C{Model Response}
C -->|has tool_calls| D[Execute Tools<br/>concurrently]
D --> B
C -->|final answer| E[Output]The agent loop lives in a dedicated runner: each turn builds the request from memory and instructions, invokes the model, and — when the model requests tools — executes them concurrently and feeds the results back. The loop stops on a final answer or when turn limits are reached.
import "github.com/rexleimo/HNO/pkg/hno/agent"
agent, err := agent.New(agent.Config{
Name: "My Agent",
Model: model,
Toolkits: []toolkit.Toolkit{calculator.New()},
Instructions: "You are a helpful assistant",
MaxLoops: 10,
})
output, err := agent.Run(context.Background(), "What is 2+2?")Configuration
Config Structure
type Config struct {
Name string // Agent name
Model models.Model // LLM model
Toolkits []toolkit.Toolkit // Available tools
Memory memory.Memory // Conversation memory
Instructions string // System instructions
MaxLoops int // Max tool call loops (default: 10)
UserID string // Optional tenant identifier
PreHooks []hooks.Hook // Pre-execution hooks
PostHooks []hooks.Hook // Post-execution hooks
Logger *slog.Logger // Custom logger (optional)
EnableCache bool // Enable response caching
CacheProvider cache.Provider // Custom cache provider (optional)
CacheTTL time.Duration // Cache TTL (default: 5m)
StoreToolMessages *bool // Include tool messages in RunOutput (default: true)
StoreHistoryMessages *bool // Include memory messages in RunOutput (default: true)
}Parameters
- Name (required): Human-readable agent identifier
- Model (required): LLM model instance (OpenAI, Claude, etc.)
- Toolkits (optional): List of available tools
- Memory (optional): Defaults to in-memory storage with 100 message limit
- Instructions (optional): System prompt/persona
- MaxLoops (optional): Prevent infinite tool call loops (default: 10)
- UserID (optional): Associate runs with a tenant or end-user
- PreHooks (optional): Validation hooks before execution
- PostHooks (optional): Validation hooks after execution
- Logger (optional): Custom logger for structured output
- EnableCache (optional): Deduplicate identical model calls
- CacheProvider (optional): Supply custom cache backend (defaults to in-memory LRU)
- CacheTTL (optional): Override cache expiration (default 5 minutes)
- StoreToolMessages (optional): Filter tool call transcripts from output
- StoreHistoryMessages (optional): Filter historical memory messages from output
Basic Usage
Simple Agent
package main
import (
"context"
"fmt"
"github.com/rexleimo/HNO/pkg/hno/agent"
"github.com/rexleimo/HNO/pkg/hno/models/openai"
)
func main() {
model, _ := openai.New("gpt-4o-mini", openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
ag, _ := agent.New(agent.Config{
Name: "Assistant",
Model: model,
Instructions: "You are a helpful assistant",
})
output, _ := ag.Run(context.Background(), "Hello!")
fmt.Println(output.Content)
}Agent with Tools
import (
"github.com/rexleimo/HNO/pkg/hno/tools/calculator"
"github.com/rexleimo/HNO/pkg/hno/tools/http"
)
ag, _ := agent.New(agent.Config{
Name: "Smart Assistant",
Model: model,
Toolkits: []toolkit.Toolkit{
calculator.New(),
http.New(),
},
Instructions: "You can do math and make HTTP requests",
})
output, _ := ag.Run(ctx, "Calculate 15 * 23 and fetch https://api.github.com")Advanced Features
Custom Memory
import "github.com/rexleimo/HNO/pkg/hno/memory"
// Create memory with custom limit
mem := memory.NewInMemory(50) // Keep last 50 messages
ag, _ := agent.New(agent.Config{
Memory: mem,
// ... other config
})Hooks & Guardrails
Validate inputs and outputs with hooks:
import "github.com/rexleimo/HNO/pkg/hno/guardrails"
// Built-in prompt injection guard
promptGuard := guardrails.NewPromptInjectionGuardrail()
// Custom validation hook
customHook := func(ctx context.Context, input *hooks.HookInput) error {
if len(input.Input) > 1000 {
return fmt.Errorf("input too long")
}
return nil
}
ag, _ := agent.New(agent.Config{
PreHooks: []hooks.Hook{customHook, promptGuard},
PostHooks: []hooks.Hook{outputValidator},
// ... other config
})Context and Timeouts
import "time"
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
output, err := ag.Run(ctx, "Complex task...")
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Timeout!")
}
}Response Caching (v1.2.6)
Enable deterministic responses to reuse cached model outputs:
ag, _ := agent.New(agent.Config{
Model: model,
EnableCache: true,
CacheTTL: 2 * time.Minute,
})
first, _ := ag.Run(ctx, "Summarise REST vs gRPC")
second, _ := ag.Run(ctx, "Summarise REST vs gRPC")
if cached, _ := second.Metadata["cache_hit"].(bool); cached {
// Handle cached response
}Provide a custom cache.Provider when you want Redis or shared storage; otherwise an in-memory LRU is used.
Run Output
The Run method returns *RunOutput:
type RunOutput struct {
Content string // Agent's response
Messages []types.Message // Full message history
Metadata map[string]interface{} // Additional data
}Example:
output, err := ag.Run(ctx, "Tell me a joke")
if err != nil {
log.Fatal(err)
}
fmt.Println("Response:", output.Content)
fmt.Println("Messages:", len(output.Messages))
fmt.Println("Metadata:", output.Metadata)Memory Management
Clear Memory
// Clear all conversation history
ag.ClearMemory()Access Memory
// Get current messages
messages := ag.GetMemory().GetMessages()
fmt.Println("History:", len(messages))Error Handling
output, err := ag.Run(ctx, input)
if err != nil {
switch {
case errors.Is(err, types.ErrInvalidInput):
// Handle invalid input
case errors.Is(err, types.ErrRateLimit):
// Handle rate limit
case errors.Is(err, context.DeadlineExceeded):
// Handle timeout
default:
// Handle other errors
}
}Best Practices
1. Always Use Context
ctx := context.Background()
output, err := ag.Run(ctx, input)2. Set Appropriate MaxLoops
// For simple tasks
MaxLoops: 5
// For complex reasoning
MaxLoops: 153. Provide Clear Instructions
Instructions: `You are a customer support agent.
- Be polite and professional
- Use tools to look up information
- If unsure, ask for clarification`4. Use Type-Safe Tool Configurations
calc := calculator.New()
httpClient := http.New(http.Config{
Timeout: 10 * time.Second,
})
ag, _ := agent.New(agent.Config{
Toolkits: []toolkit.Toolkit{calc, httpClient},
})Performance Considerations
Agent construction measurements are environment-dependent. See Performance for the checked-in benchmark command, machine details, allocation ranges, and what the benchmark does not measure. It uses a local MockModel; it is not an LLM latency or production-capacity measurement. Add concurrency limits based on the workload and resources of the deployment rather than a fixed claim.
// Concurrent agents
for i := 0; i < 100; i++ {
go func(id int) {
ag, _ := agent.New(config)
output, _ := ag.Run(ctx, input)
fmt.Printf("Agent %d: %s\n", id, output.Content)
}(i)
}Examples
See working examples:
API Reference
For complete API documentation, see Agent API Reference.

