- agent/loop.go: Record assistant message with ToolCalls before tool results, and set ToolCallID on tool-result messages so follow-up requests have complete context (prevents models from losing track of already-attempted tools). - llm/providers/llamacpp/client.go: Buffer fragmented tool call deltas during streaming, assemble them into complete calls when finish_reason arrives. Add ToolCalls, ToolCallID, Name fields to request building. - llm/providers/openai/client.go: Send ToolCalls, ToolCallID, Name when building chat requests so messages are wire-format correct. - llm/types.go: Add ToolCalls field to Message struct for serialization back into conversation history. - agent/integration_test.go: Move integration test skip from TestMain to a per-test skipUnlessIntegration() so it doesn't hide other package tests. - sandbox & tools: Add edge-case tests (relative traversal, array paths, non-path strings, zero-value guards, sentinel errors). |
||
|---|---|---|
| .. | ||
| integration_test.go | ||
| loop.go | ||
| loop_test.go | ||
| README.es.md | ||
| README.md | ||
pkg/agent
The main loop that runs an LLM agent with guardrails.
Responsibility
Coordinate the iterative cycle between the LLM and tool execution:
while iteration < MaxIterations:
response = llm.Generate(messages, tools)
if no tool calls: return response
for tool_call in response.ToolCalls:
if needs_approval: ask_user()
result = execute(tool_call)
append tool result to messages
Public API
type Loop interface {
Run(ctx context.Context, input string, history ...llm.Message) (Response, error)
RunStream(ctx context.Context, input string, history ...llm.Message) iter.Seq2[Chunk, error]
}
type Config struct {
LLM llm.LLMClient
Persona persona.Persona
Tools tools.Registry
Sandbox Sandbox
MaxIters int
Approver Approver // nil = auto-approve all
OnIteration func(Iteration) // observability hook
}
type Response struct {
Content string
ToolCalls []tools.Call
Iterations int
Duration time.Duration
TokenUsage llm.TokenUsage
}
Guarantees
- Termination: Always terminates (max iterations, error, or final response)
- Idempotency: Re-running with the same input produces the same output (given the same LLM)
- Observability: Each iteration emits an OpenTelemetry span
- Approval: Destructive tools (
Askpermission) require confirmation
See also
- pkg/tools — Tool execution
- pkg/llm — LLMClient interface
- pkg/persona — Persona assembly