llmagent

package
v1.17.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 31, 2025 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package llmagent provides an LLM-based agent implementation for Hector v2.

LLM agents use language models to generate responses and can invoke tools to perform actions. They support:

  • Instruction-based behavior control
  • Tool/function calling
  • Sub-agent delegation
  • Callbacks for customization

Usage

agent, err := llmagent.New(llmagent.Config{
    Name:        "assistant",
    Model:       myModel,
    Instruction: "You are a helpful assistant.",
    Tools:       []tool.Tool{searchTool, calculatorTool},
})

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConfigRequestProcessor

func ConfigRequestProcessor(ctx ProcessorContext, req *model.Request) error

ConfigRequestProcessor applies the agent's generate config to the request.

func ContentsRequestProcessor

func ContentsRequestProcessor(ctx ProcessorContext, req *model.Request) error

ContentsRequestProcessor builds the conversation history from session events. Following adk-go pattern: ALWAYS reads from session events on every LLM call. This is the source of truth for conversation history.

func InstructionRequestProcessor

func InstructionRequestProcessor(ctx ProcessorContext, req *model.Request) error

InstructionRequestProcessor resolves instruction templates and sets system instruction.

func LoggingResponseProcessor

func LoggingResponseProcessor(ctx ProcessorContext, req *model.Request, resp *model.Response) error

LoggingResponseProcessor logs response details for debugging.

func New

func New(cfg Config) (agent.Agent, error)

New creates a new LLM-based agent.

To convert an agent to a tool for agent-as-tool delegation (Pattern 2), use the agenttool package:

import "github.com/verikod/hector/pkg/tool/agenttool"

searchAgent, _ := llmagent.New(llmagent.Config{...})
rootAgent, _ := llmagent.New(llmagent.Config{
    Tools: []tool.Tool{
        agenttool.New(searchAgent, nil),  // ✅ Clean factory pattern
    },
})

func RAGContextRequestProcessor

func RAGContextRequestProcessor(ctx ProcessorContext, req *model.Request) error

RAGContextRequestProcessor injects relevant RAG context into the messages. This runs AFTER ContentsRequestProcessor to inject context based on user query.

func ThinkingResponseProcessor

func ThinkingResponseProcessor(ctx ProcessorContext, req *model.Request, resp *model.Response) error

ThinkingResponseProcessor processes thinking blocks in the response.

func ToolsRequestProcessor

func ToolsRequestProcessor(ctx ProcessorContext, req *model.Request) error

ToolsRequestProcessor collects tool definitions and adds them to the request.

func TransferToolsRequestProcessor

func TransferToolsRequestProcessor(ctx ProcessorContext, req *model.Request) error

TransferToolsRequestProcessor adds agent transfer tools based on sub-agents.

Types

type AfterModelCallback

type AfterModelCallback func(ctx agent.CallbackContext, resp *model.Response, err error) (*model.Response, error)

AfterModelCallback runs after an LLM call. Return non-nil Response to replace the LLM response.

type AfterToolCallback

type AfterToolCallback func(ctx tool.Context, t tool.Tool, args, result map[string]any, err error) (map[string]any, error)

AfterToolCallback runs after tool execution. Return non-nil result to replace the tool result.

type BeforeModelCallback

type BeforeModelCallback func(ctx agent.CallbackContext, req *model.Request) (*model.Response, error)

BeforeModelCallback runs before an LLM call. Return non-nil Response to skip the actual LLM call.

type BeforeToolCallback

type BeforeToolCallback func(ctx tool.Context, t tool.Tool, args map[string]any) (map[string]any, error)

BeforeToolCallback runs before tool execution. Return non-nil result to skip actual tool execution.

type Config

type Config struct {
	// Name must be unique within the agent tree.
	Name string

	// DisplayName is the human-readable name used for UI/Chat attribution.
	// If empty, defaults to Name.
	DisplayName string

	// Description helps LLMs decide when to delegate to this agent.
	Description string

	// Model is the LLM to use for generation.
	Model model.LLM

	// Instruction guides the agent's behavior.
	// Supports template placeholders like {variable} resolved from state.
	Instruction string

	// EnableStreaming enables token-by-token streaming from the LLM.
	// When false (default), responses are returned as complete chunks.
	EnableStreaming bool

	// InstructionProvider allows dynamic instruction generation.
	// Takes precedence over Instruction if set.
	InstructionProvider InstructionProvider

	// GlobalInstruction applies to all agents in the tree.
	// Only the root agent's GlobalInstruction is used.
	GlobalInstruction string

	// GlobalInstructionProvider allows dynamic global instruction.
	GlobalInstructionProvider InstructionProvider

	// GenerateConfig contains LLM generation settings.
	GenerateConfig *model.GenerateConfig

	// Tools available to the agent.
	Tools []tool.Tool

	// Toolsets provide dynamic tool resolution.
	Toolsets []tool.Toolset

	// SubAgents can receive delegated tasks.
	SubAgents []agent.Agent

	// BeforeAgentCallbacks run before the agent starts.
	BeforeAgentCallbacks []agent.BeforeAgentCallback

	// AfterAgentCallbacks run after the agent completes.
	AfterAgentCallbacks []agent.AfterAgentCallback

	// BeforeModelCallbacks run before each LLM call.
	BeforeModelCallbacks []BeforeModelCallback

	// AfterModelCallbacks run after each LLM call.
	AfterModelCallbacks []AfterModelCallback

	// BeforeToolCallbacks run before each tool execution.
	BeforeToolCallbacks []BeforeToolCallback

	// AfterToolCallbacks run after each tool execution.
	AfterToolCallbacks []AfterToolCallback

	// DisallowTransferToParent prevents delegation to parent agent.
	DisallowTransferToParent bool

	// DisallowTransferToPeers prevents delegation to sibling agents.
	DisallowTransferToPeers bool

	// IncludeContents controls conversation history inclusion.
	IncludeContents IncludeContents

	// OutputKey saves agent output to session state under this key.
	OutputKey string

	// InputSchema validates input when agent is used as a tool.
	InputSchema map[string]any

	// OutputSchema enforces structured output format.
	OutputSchema map[string]any

	// OutputSchemaName identifies the schema.
	OutputSchemaName string

	// OutputSchemaStrict enables strict schema validation.
	OutputSchemaStrict *bool

	// Reasoning configures the chain-of-thought reasoning loop.
	// When nil, defaults are applied (semantic termination, 100 max iterations).
	Reasoning *ReasoningConfig

	// WorkingMemory is the context window management strategy.
	// Controls how conversation history is filtered to fit within LLM limits.
	// If nil, all history is included (no filtering).
	WorkingMemory memory.WorkingMemoryStrategy

	// AutoRecall enables automatic memory recall.
	AutoRecall bool

	// AutoRecallLimit limits the number of recalled memories.
	AutoRecallLimit int

	// ContextProvider retrieves relevant context for RAG.
	// When set, the agent will query the provider with user input
	// and inject relevant context into the conversation.
	ContextProvider ContextProvider

	// RequestProcessors are custom processors added to the request pipeline.
	// These run AFTER the default processors.
	RequestProcessors []RequestProcessor

	// ResponseProcessors are custom processors added to the response pipeline.
	// These run AFTER the default processors.
	ResponseProcessors []ResponseProcessor

	// Pipeline allows complete customization of the processor pipeline.
	// If set, RequestProcessors and ResponseProcessors are ignored.
	Pipeline *Pipeline

	// MetricsRecorder records tool execution metrics.
	// If nil, metrics are not recorded (no-op).
	MetricsRecorder observability.Recorder

	// DisableSafetyProtocols disables the automatic injection of safety protocols
	// (Tool Reliability and Chain of Thought).
	DisableSafetyProtocols bool
}

Config contains the configuration for an LLM agent.

type ContentProcessor

type ContentProcessor struct {
	// contains filtered or unexported fields
}

ContentProcessor handles message history processing for LLM context. It handles tool call/result pairing, foreign agent message conversion, and auth filtering.

The processor is model-aware: different providers have different requirements for how tool calls and results should be formatted:

  • OpenAI: Tool results are separate function_call_output items
  • Anthropic/Gemini: Tool results must be paired with tool_use in same message

func NewContentProcessor

func NewContentProcessor(agentName string, provider model.Provider) *ContentProcessor

NewContentProcessor creates a new content processor for the given agent and provider.

func (*ContentProcessor) ConvertForeignAgentMessage

func (p *ContentProcessor) ConvertForeignAgentMessage(msg *a2a.Message, author string) *a2a.Message

ConvertForeignAgentMessage converts messages from other agents to user context. In multi-agent setups, foreign agent messages need to be presented as user context.

func (*ContentProcessor) FilterAuthEvents

func (p *ContentProcessor) FilterAuthEvents(messages []*a2a.Message) []*a2a.Message

FilterAuthEvents removes authentication-related events from the history.

func (*ContentProcessor) Process

func (p *ContentProcessor) Process(messages []*a2a.Message) []*a2a.Message

Process handles tool call/result formatting for LLM protocols.

This method is model-aware:

  • OpenAI/Ollama: Returns messages as-is (tool results are separate function_call_output items)
  • Anthropic: Returns messages as-is (tool_use in assistant msg, tool_result in next user msg)
  • Gemini: May need special handling (currently returns as-is)

The Flow already creates the correct message structure for all providers:

  • Assistant message with tool_use blocks
  • User message with tool_result blocks

This processor primarily exists for future model-specific transformations and to filter out any malformed messages.

func (*ContentProcessor) RearrangeEventsForFunctionResponsesInHistory

func (p *ContentProcessor) RearrangeEventsForFunctionResponsesInHistory(events []*agent.Event) ([]*agent.Event, error)

RearrangeEventsForFunctionResponsesInHistory reorganizes entire event history to ensure every function call is immediately followed by its response.

This follows adk-go's rearrangeEventsForFunctionResponsesInHistory pattern.

func (*ContentProcessor) RearrangeEventsForLatestFunctionResponse

func (p *ContentProcessor) RearrangeEventsForLatestFunctionResponse(events []*agent.Event) ([]*agent.Event, error)

RearrangeEventsForLatestFunctionResponse handles async function responses. If the latest event is a function response, it searches backward for the matching call, removes all intervening events, and ensures proper call/response pairing.

This follows adk-go's rearrangeEventsForLatestFunctionResponse pattern for handling long-running/async tools where responses may arrive out of order.

func (*ContentProcessor) SanitizeMessage

func (p *ContentProcessor) SanitizeMessage(msg *a2a.Message) *a2a.Message

SanitizeMessage removes empty parts from a message, helpful for some models (e.g. Gemini).

type ContextProvider

type ContextProvider func(ctx agent.ReadonlyContext, query string) (string, error)

ContextProvider retrieves relevant context based on user input. Used for RAG context injection when IncludeContext is enabled. The returned string is injected into the conversation as additional context.

type Flow

type Flow struct {
	// contains filtered or unexported fields
}

Flow implements the adk-go aligned core reasoning loop. Key principles from adk-go:

  1. Outer loop continues until IsFinalResponse() returns true
  2. Each runOneStep() handles: preprocess → LLM call → postprocess → tool execution
  3. Events are yielded and persisted to session immediately
  4. ContentsRequestProcessor reads from session on EVERY iteration
  5. No manual message accumulation - session is the source of truth

func NewFlow

func NewFlow(a *llmAgent) *Flow

NewFlow creates a new flow for the given agent.

func (*Flow) Run

Run executes the reasoning loop following adk-go's pattern. The outer loop continues until we get a final response (no tool calls).

type FunctionCallInfo

type FunctionCallInfo struct {
	ID   string
	Name string
	Args map[string]any
}

FunctionCallInfo represents a function call extracted from an event.

type FunctionResponseInfo

type FunctionResponseInfo struct {
	ID      string
	Name    string
	Content string
}

FunctionResponseInfo represents a function response extracted from an event.

type IncludeContents

type IncludeContents string

IncludeContents controls conversation history handling.

const (
	// IncludeContentsDefault includes relevant conversation history.
	IncludeContentsDefault IncludeContents = "default"

	// IncludeContentsNone only uses the current turn.
	IncludeContentsNone IncludeContents = "none"
)

type InstructionProvider

type InstructionProvider func(ctx agent.ReadonlyContext) (string, error)

InstructionProvider generates instructions dynamically.

type Pipeline

type Pipeline struct {
	// contains filtered or unexported fields
}

Pipeline manages request and response processors.

func NewCustomPipeline

func NewCustomPipeline(reqProcessors []RequestProcessor, respProcessors []ResponseProcessor) *Pipeline

NewCustomPipeline creates a pipeline with custom processors.

func NewPipeline

func NewPipeline() *Pipeline

NewPipeline creates a new processor pipeline with default processors.

func (*Pipeline) AddRequestProcessor

func (p *Pipeline) AddRequestProcessor(processor RequestProcessor)

AddRequestProcessor appends a request processor to the pipeline.

func (*Pipeline) AddResponseProcessor

func (p *Pipeline) AddResponseProcessor(processor ResponseProcessor)

AddResponseProcessor appends a response processor to the pipeline.

func (*Pipeline) PrependRequestProcessor

func (p *Pipeline) PrependRequestProcessor(processor RequestProcessor)

PrependRequestProcessor adds a request processor at the beginning.

func (*Pipeline) PrependResponseProcessor

func (p *Pipeline) PrependResponseProcessor(processor ResponseProcessor)

PrependResponseProcessor adds a response processor at the beginning.

func (*Pipeline) ProcessRequest

func (p *Pipeline) ProcessRequest(ctx ProcessorContext, req *model.Request) error

ProcessRequest runs all request processors in order.

func (*Pipeline) ProcessResponse

func (p *Pipeline) ProcessResponse(ctx ProcessorContext, req *model.Request, resp *model.Response) error

ProcessResponse runs all response processors in order.

type ProcessorContext

type ProcessorContext interface {
	agent.InvocationContext

	// Agent returns the LLM agent being processed.
	LLMAgent() *llmAgent

	// Tools returns all available tools for this agent.
	Tools() []tool.Tool

	// ToolDefinitions returns tool definitions for the LLM.
	ToolDefinitions() ([]tool.Definition, error)
}

ProcessorContext provides context for processors. It extends agent.InvocationContext with processor-specific functionality.

type ReasoningConfig

type ReasoningConfig struct {
	// MaxIterations is a SAFETY limit (not the primary termination condition).
	// The loop terminates when semantic conditions are met (no tool calls, etc.)
	// Default: 100 (high enough to not interfere with normal operation)
	MaxIterations int

	// EnableExitTool adds the exit_loop tool for explicit termination.
	EnableExitTool bool

	// EnableEscalateTool adds the escalate tool for parent delegation.
	EnableEscalateTool bool

	// CompletionInstruction is appended to help the model know when to stop.
	CompletionInstruction string
}

ReasoningConfig configures the chain-of-thought reasoning loop. This follows adk-go patterns for semantic loop termination.

type RequestProcessor

type RequestProcessor func(ctx ProcessorContext, req *model.Request) error

RequestProcessor transforms an LLM request before it's sent to the model. Processors are called in order, each receiving the modified request from the previous. Return an error to abort the request pipeline.

func AuthRequestProcessor

func AuthRequestProcessor(tokenProvider func(ctx agent.ReadonlyContext) (string, error)) RequestProcessor

AuthRequestProcessor creates a processor that adds authentication tokens. The token is stored in Config.Metadata["Authorization"] for LLM implementations to use.

Example usage:

authProcessor := AuthRequestProcessor(func(ctx agent.ReadonlyContext) (string, error) {
    // Get token from session state, environment, or secret manager
    token, _ := ctx.ReadonlyState().Get("api_token")
    return token.(string), nil
})

func ContentFilterRequestProcessor

func ContentFilterRequestProcessor(filter func(content string) string) RequestProcessor

ContentFilterRequestProcessor creates a processor that filters/transforms content.

func DefaultRequestProcessors

func DefaultRequestProcessors() []RequestProcessor

DefaultRequestProcessors returns the standard request processor chain. Order matters - processors are executed sequentially.

type ResponseProcessor

type ResponseProcessor func(ctx ProcessorContext, req *model.Request, resp *model.Response) error

ResponseProcessor transforms an LLM response after it comes back from the model. Processors are called in order, each receiving the modified response from the previous. Return an error to abort the response pipeline.

func DefaultResponseProcessors

func DefaultResponseProcessors() []ResponseProcessor

DefaultResponseProcessors returns the standard response processor chain.

func ValidationResponseProcessor

func ValidationResponseProcessor(validator func(resp *model.Response) error) ResponseProcessor

ValidationResponseProcessor creates a processor that validates responses.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL