# ToolLoopAgent

`ToolLoopAgent` is an agent that loops through tool calls until the task
completes. It lives in `github.com/digitallysavvy/go-ai/pkg/agent` (not
`pkg/ai`) and implements the [`agent.Agent`](https://goaisdk.com/docs/reference/ai/agent.md) interface.

## Signature

```go
func NewToolLoopAgent(config AgentConfig) *ToolLoopAgent
```

## Parameters

### AgentConfig (selected fields)

`AgentConfig` has many fields; these are the ones most commonly used.

| Field | Type | Description |
|-------|------|-------------|
| Model | provider.LanguageModel | Language model for the agent |
| System | string | System prompt for the agent |
| Tools | []types.Tool | Tools available to the agent |
| ActiveTools | []string | Restricts the available tools by name before model calls |
| StopWhen | []ai.StopCondition | Conditions that terminate the tool-calling loop, e.g. `[]ai.StopCondition{ai.IsStepCount(20)}` (default) |
| MaxSteps | int | **Deprecated** — use `StopWhen` with `ai.IsStepCount` instead |
| Temperature | *float64 | Sampling temperature |
| MaxTokens | *int | Maximum tokens per generation |
| ToolChoice | types.ToolChoice | Controls how the model may call tools |
| ProviderOptions | map[string]interface{} | Provider-specific options |
| Skills | *SkillRegistry | Reusable agent behaviors, registered with `AddSkill` |
| Subagents | *SubagentRegistry | Specialized agents that can be delegated to |
| PrepareCall | func(ctx, PrepareCallConfig) PrepareCallConfig | Called before each generation step to adjust settings dynamically |
| OnStepEnd | func(types.StepResult) | Called after each step completes |
| OnFinish | func(*AgentResult) | Called when agent execution completes |
| ToolApprovalRequired | bool | Whether tools require approval before execution |

See `go doc ./pkg/agent AgentConfig` for the complete field list, including
telemetry, retry, timeout, and callback-event options.

## Methods

```go
func (a *ToolLoopAgent) Execute(ctx context.Context, prompt string) (*AgentResult, error)
func (a *ToolLoopAgent) ExecuteWithMessages(ctx context.Context, messages []types.Message) (*AgentResult, error)
func (a *ToolLoopAgent) Generate(ctx context.Context, opts AgentGenerateOptions) (*ai.GenerateTextResult, error)
func (a *ToolLoopAgent) GenerateAgent(ctx context.Context, opts AgentGenerateOptions) (*AgentResult, error)
func (a *ToolLoopAgent) Stream(ctx context.Context, opts AgentStreamOptions) (*ai.StreamTextResult, error)
func (a *ToolLoopAgent) AddTool(tool types.Tool)
func (a *ToolLoopAgent) RemoveTool(toolName string)
func (a *ToolLoopAgent) Tools() []types.Tool
func (a *ToolLoopAgent) AddSkill(skill *Skill) error
func (a *ToolLoopAgent) AddSubagent(name string, subagent Agent) error
func (a *ToolLoopAgent) DelegateToSubagent(ctx context.Context, name string, prompt string) (*AgentResult, error)
func (a *ToolLoopAgent) SetMaxSteps(maxSteps int)
func (a *ToolLoopAgent) SetStopConditions(conditions []ai.StopCondition)
func (a *ToolLoopAgent) SetSystem(system string)
func (a *ToolLoopAgent) ID() string
func (a *ToolLoopAgent) Version() string
```

## Return Value

### AgentResult

| Field | Type | Description |
|-------|------|-------------|
| Text | string | Agent's final text output |
| Output | interface{} | Parsed/structured output when an output strategy is configured |
| Steps | []types.StepResult | All steps in the loop |
| ToolResults | []types.ToolResult | All tool results from execution |
| FinishReason | types.FinishReason | Final finish reason |
| StopReason | string | Why the loop stopped |
| Usage | types.Usage | Total token usage |
| Warnings | []types.Warning | Warnings from any step |

## Examples

### Basic Tool Loop Agent

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/digitallysavvy/go-ai/pkg/agent"
    "github.com/digitallysavvy/go-ai/pkg/provider/types"
    "github.com/digitallysavvy/go-ai/pkg/providers/openai"
)

func main() {
    p := openai.New(openai.Config{
        APIKey: "your-api-key",
    })
    model, _ := p.LanguageModel("gpt-4")

    searchTool := types.Tool{Name: "search"}
    priceCompareTool := types.Tool{Name: "price_compare"}

    a := agent.NewToolLoopAgent(agent.AgentConfig{
        Model: model,
        Tools: []types.Tool{searchTool, priceCompareTool},
    })

    result, err := a.Execute(context.Background(), "Find and compare prices of three popular smartphones")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Answer: %s\n", result.Text)
    fmt.Printf("Tool calls: %d\n", len(result.ToolResults))
}
```

### With a Custom Stop Condition

```go
a := agent.NewToolLoopAgent(agent.AgentConfig{
    Model: model,
    Tools: tools,
    StopWhen: []ai.StopCondition{
        ai.HasToolCall("final_answer"),
        ai.IsStepCount(5),
    },
})

result, err := a.Execute(ctx, "Research topic and gather 3 key facts")
```

### With Step and Tool Callbacks

```go
a := agent.NewToolLoopAgent(agent.AgentConfig{
    Model: model,
    Tools: tools,
    OnStepEnd: func(step types.StepResult) {
        fmt.Printf("Step finished, %d tool calls\n", len(step.ToolCalls))
    },
    OnToolResult: func(result types.ToolResult) {
        fmt.Printf("Tool result: %+v\n", result.Result)
        if result.Error != nil {
            fmt.Printf("Tool error: %v\n", result.Error)
        }
    },
})

result, err := a.Execute(ctx, "Complex multi-step research task")
```

## See Also

- [Agent](https://goaisdk.com/docs/reference/ai/agent.md) - The `agent.Agent` interface
- [Tool](https://goaisdk.com/docs/reference/ai/tool.md) - Tool definition
- [Agents Guide](https://goaisdk.com/docs/agents/overview.md)
