Reusable Go library for building LLM agents
- Run() reported "max iterations reached" even when a valid final answer arrived exactly on the last allowed iteration, throwing the response away; a completed flag now distinguishes success from budget exhaustion. - Tool schemas are marshaled once per Run/RunStream instead of once per loop iteration — they never change between iterations. - RunStream's content gate (which hides raw deltas during a tool-call round) also swallowed that round's token usage, so callers only ever saw the final round's count and context tracking lagged exactly when the context grew fastest. Usage is now forwarded in its own chunk. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .agents/skills/add-feature | ||
| docs | ||
| examples | ||
| pkg | ||
| .gitignore | ||
| AGENTS.md | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| README.es.md | ||
| README.md | ||
rony-llm-agent
🔑 Reusable core library for building LLM agents in Go.
This library is the heart of several Victor Vargas projects:
harness— AI agent harness for software development (TUI)chat-bot— HTTP chatbot for portfolios and websites
It provides all the generic logic of an LLM agent:
| Component | Location | Responsibility |
|---|---|---|
| Agent loop | pkg/agent/ |
Iterative loop with guardrails |
| LLM clients | pkg/llm/ |
Multi-provider abstraction (OpenAI, Anthropic, Ollama) |
| RAG / memory | pkg/rag/ |
Short and long-term memory with semantic search |
| Persona system | pkg/persona/ |
Configurable system prompts + AGENTS.md discovery |
| Tool registry | pkg/tools/ |
JSON Schema + execution sandbox |
| Config loading | pkg/config/ |
YAML loading with hierarchical precedence |
🎯 Philosophy
- Reusable, not opinionated. Does not force a UI type, deployment, or use case.
- Pure Hexagonal. Ports & adapters — every external dependency is behind an interface.
- Streaming-first. Uses
iter.Seq2from Go 1.23+ for natural streaming without callbacks. - Secure by default. Filesystem path sandbox with
os.Root(Go 1.24+). - Zero magic. No reflection, no codegen, no DSLs. Idiomatic and explicit Go.
- YAML configurable. Everything that affects behavior is declarative.
📦 Installation
go get github.com/VictorVargas/rony-llm-agent
🚀 Basic usage
package main
import (
"context"
"fmt"
"github.com/VictorVargas/rony-llm-agent/pkg/agent"
"github.com/VictorVargas/rony-llm-agent/pkg/llm"
"github.com/VictorVargas/rony-llm-agent/pkg/persona"
)
func main() {
// 1. Create LLM client
llmClient, _ := llm.NewAnthropicClient(llm.AnthropicConfig{
APIKey: os.Getenv("ANTHROPIC_API_KEY"),
Model: "claude-sonnet-4.5",
})
// 2. Load persona
p := persona.Load("./persona.yaml")
// 3. Create agent loop
loop := agent.New(agent.Config{
LLM: llmClient,
Persona: p,
MaxIters: 50,
Sandbox: agent.NewSandbox("./workspace"),
})
// 4. Run
resp, err := loop.Run(context.Background(), "Refactor auth.go")
if err != nil { panic(err) }
fmt.Println(resp.Content)
}
🔌 Included adapters
LLM Providers (pkg/llm/providers/)
| Provider | Import | Models |
|---|---|---|
| OpenAI | providers/openai |
gpt-4o, gpt-4o-mini, gpt-4-turbo |
| Anthropic | providers/anthropic |
claude-sonnet-4.5, claude-haiku-4 |
| Ollama | providers/ollama |
llama3.1, qwen2.5, mistral, etc. |
| llama.cpp | providers/llamacpp |
Custom GGUF models |
Vector DBs (pkg/rag/backends/)
| Backend | Status |
|---|---|
| ChromaDB embedded | ✅ Stable |
| Qdrant embedded | 🚧 In development |
| SQLite + sqlite-vec | 📋 Planned |
Embeddings (pkg/rag/embeddings/)
- Ollama embeddings (nomic-embed-text, bge-m3, etc.)
- Local sentence-transformers via ONNX
🧪 Testing
go test ./...
go test -race ./...
go test -bench=. ./pkg/agent/
Includes MockLLMClient for deterministic tests without burning API calls.
📐 Versions
- Go minimum: 1.26 (uses
os.Root,iter.Seq,unique.Handle, container-aware GOMAXPROCS) - Versioning policy: Strict semver. API breaking changes only on MAJOR.
📄 License
MIT — see LICENSE.
🔗 Projects that use this library
VictorVargas/rony-harness— TUI agent for software devVictorVargas/rony-chat-bot— HTTP chatbot