A pure Go framework for building AI agents with tool calling, workflows, and multi-agent teams. Uses official SDKs from OpenAI, Anthropic, and Google.
- Multi-Provider — OpenAI, Anthropic, Google Gemini, Fireworks, Ollama (all via official SDKs)
- Tool Calling — interface-based tools with automatic JSON Schema generation
- MCP Support — connect to any MCP server and use its tools, or expose your agents as MCP servers
- Streaming — real-time token streaming with the same agent API
- Chat — stateful multi-turn conversations with automatic history management, thread cloning, and branching
- Structured Output — JSON schema-based typed responses from Go structs
- Workflows — sequential, parallel, conditional, loop, router, DAG, fan-out/fan-in, and subgraph composition
- Teams — multi-agent coordination (round-robin, orchestrator, network, supervisor, graph, LLM speaker selection) with streaming, event bus, hooks, and planning
- Planning — structured plans with CRUD operations, agent assignment, TaskOutput with artifacts
- A2A Protocol — Agent-to-Agent communication for cross-framework interoperability
- Checkpointing — durable state persistence (SQLite, PostgreSQL, Memory) with time-travel debugging and fork/replay
- Context Compaction — sliding window, LLM summarization, and hybrid strategies for long sessions
- Sandboxed Execution — Docker, local restricted, and cloud sandbox (E2B) for untrusted code
- Rate Limiting — per-session request rate limits with configurable windows
- Cost Limits — track and cap spending per session
- Stream Redaction — strip API keys, PII, and sensitive data from streaming responses and traces
- Cron Scheduling — time-aware execution with cron expressions, retries, timeouts, and timezone support
- Health Monitoring — built-in health endpoints, memory stats, readiness/liveness probes
- Memory — neuroscience-inspired tiered memory (sensory → working → short-term → long-term) with entity tracking
- RAG / Knowledge Base — document ingestion, chunking, embedding, and retrieval with multiple vector backends
- Learning — cross-session learning machine that improves agent behavior over time
- Multi-Tenant Isolation — shared infrastructure with isolated memory, state, and retrieval per tenant
- Guardrails — input/output safety (PII detection, prompt injection, moderation, token limits, LLM classifier, rate limiting, cost limits)
- Hooks — lifecycle middleware (pre/post run, pre/post tool, stream hooks)
- Plugin Architecture — aspect-oriented global interception (logging, context filters, global instructions)
- Human-in-the-Loop — approval system with audit trails, state editing, suspend/resume
- YAML Configuration — define agents, teams, and workflows in YAML files
- Server Adapters — embed in existing Go apps (net/http, gin, fiber, chi) with CORS and auth middleware
- Observability — tracing with spans (agent, LLM, tool), token tracking
- Events — pub/sub event bus for agent lifecycle
- Evaluation — scorers (contains, length, exact match, tool use), batch evaluation, benchmarking
- Provider Fallbacks — automatic failover between providers
- Background Execution — non-blocking agent runs with cancellation
- Response Caching — cache identical requests to reduce API calls
- Zero hard-coded models — pass any model string
go get github.com/buildwithgo/berrygempackage main
import (
"context"
"fmt"
"log"
"os"
"github.com/buildwithgo/berrygem/agent"
"github.com/buildwithgo/berrygem/providers/openai"
"github.com/buildwithgo/berrygem/tools/builtin"
)
func main() {
provider := openai.New(os.Getenv("OPENAI_API_KEY"), "gpt-4o-mini")
a, err := agent.New(
agent.WithProvider(provider),
agent.WithModel("gpt-4o-mini"),
agent.WithInstructions("You are a helpful assistant."),
agent.WithTools(builtin.Calculator(), builtin.CurrentTime()),
)
if err != nil {
log.Fatal(err)
}
result, err := a.Run(context.Background(), "What is 42 * 137?")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Content)
fmt.Printf("Tokens: %d\n", result.Usage.TotalTokens)
}All providers implement the same providers.Provider interface, so swapping providers only changes the constructor:
| Provider | Package | SDK | Constructor |
|---|---|---|---|
| OpenAI | providers/openai |
openai-go/v3 |
openai.New(apiKey, model) |
| Anthropic | providers/anthropic |
anthropic-sdk-go |
anthropic.New(apiKey, model) |
providers/google |
go-genai |
google.New(apiKey, model) |
|
| Fireworks | providers/fireworks |
openai-go (wrapper) |
fireworks.New(apiKey, model) |
| Ollama | providers/ollama |
OpenAI-compat + native | ollama.New(model) |
provider := openai.New(os.Getenv("OPENAI_API_KEY"), "gpt-4o-mini")provider := anthropic.New(os.Getenv("ANTHROPIC_API_KEY"), "claude-sonnet-4-20250514")provider := google.New(os.Getenv("GOOGLE_API_KEY"), "gemini-2.0-flash")provider := fireworks.New(os.Getenv("FIREWORKS_API_KEY"), "accounts/fireworks/models/deepseek-v3")// Default: localhost:11434
provider := ollama.New("qwen3:8b")
// Custom host
provider := ollama.New("llama3.1:8b", ollama.WithBaseURL("http://192.168.1.50:11434"))
// Model management
provider.ListModels(ctx)
provider.PullModel(ctx, "qwen3:8b")
provider.Version(ctx)Any provider that exposes an OpenAI-compatible API works with the OpenAI package:
// Together AI
provider := openai.New(os.Getenv("TOGETHER_API_KEY"), "meta-llama/Llama-3-70b-chat-hf",
openai.WithBaseURL("https://api.together.xyz/v1"),
)
// vLLM
provider := openai.New("", "my-model",
openai.WithBaseURL("http://localhost:8000/v1"),
)
// Azure OpenAI
provider := openai.New(os.Getenv("AZURE_API_KEY"), "gpt-4o",
openai.WithBaseURL("https://myresource.openai.azure.com/openai"),
)The Agent is the core abstraction. It handles the tool-calling loop automatically:
a, err := agent.New(
agent.WithProvider(provider), // required
agent.WithModel("gpt-4o-mini"), // model ID
agent.WithInstructions("You are..."), // system prompt
agent.WithTools(tool1, tool2), // register tools
agent.WithTemperature(0.7), // sampling temperature
agent.WithMaxTurns(10), // max tool-call loops
agent.WithDebug(true), // log each turn
agent.WithName("my-agent"), // agent name
)result, err := a.Run(ctx, "What's the weather in Paris?")
// result.Content — final text response
// result.Messages — full message history
// result.Usage — token usageresult, err := a.Stream(ctx, "Tell me a story")
defer result.Close()
for {
select {
case chunk, ok := <-result.C:
if !ok { break }
fmt.Print(chunk)
case err := <-result.Err:
log.Fatal(err)
case done := <-result.Done:
fmt.Printf("\nTokens: %d\n", done.Usage.TotalTokens)
return
}
}The Chat wrapper maintains conversation history automatically:
c := chat.New(a)
result, _ := c.Send(ctx, "My name is Alice.")
fmt.Println(result.Content) // "Nice to meet you, Alice!"
result, _ = c.Send(ctx, "What's my name?")
fmt.Println(result.Content) // "Your name is Alice."
// Get history
msgs := c.Messages()
// Clear history (keeps agent config)
c.Reset()result, err := c.SendStream(ctx, "Tell me about Go")
defer result.Close()
var fullContent string
for chunk := range result.C {
fmt.Print(chunk)
fullContent += chunk
}
// Finalize history
c.CompleteStream(fullContent, nil)Implement the tools.Tool interface or use tools.NewFunc:
import (
"context"
"encoding/json"
"github.com/buildwithgo/berrygem/providers"
"github.com/buildwithgo/berrygem/tools"
)
// Using NewFunc (quick)
weatherTool := tools.NewFunc(
"get_weather",
"Get the current weather for a city.",
map[string]providers.Property{
"city": {Type: "string", Description: "City name."},
},
[]string{"city"},
func(_ context.Context, args string) (string, error) {
var p struct { City string `json:"city"` }
json.Unmarshal([]byte(args), &p)
return fmt.Sprintf("72F and sunny in %s", p.City), nil
},
)
// Using Tool interface (full control)
type MyTool struct{}
func (t *MyTool) Name() string { return "my_tool" }
func (t *MyTool) Description() string { return "Does something." }
func (t *MyTool) Schema() providers.ToolDefinition { /* ... */ }
func (t *MyTool) Execute(ctx context.Context, args string) (string, error) {
return "result", nil
}import "github.com/buildwithgo/berrygem/tools/builtin"
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithTools(
builtin.Calculator(), // math expressions
builtin.CurrentTime(), // current date/time
),
)Connect to any Model Context Protocol server:
import "github.com/buildwithgo/berrygem/tools/mcp"
// Stdio transport
mcpTools, err := mcp.NewStdio("npx", "@anthropic/mcp-server-filesystem", "/data")
if err != nil {
log.Fatal(err)
}
defer mcpTools.Close()
// Discover tools from the MCP server
tools, err := mcpTools.Tools(ctx)
// Use them with an agent
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithTools(tools...),
)mcpTools, err := mcp.NewHTTP("http://localhost:3000/sse")
defer mcpTools.Close()
tools, _ := mcpTools.Tools(ctx)Steps run in order, each step's output becomes the next step's input:
import "github.com/buildwithgo/berrygem/workflow"
wf := workflow.New("research-pipeline",
workflow.AgentStep("researcher", researchAgent),
workflow.FuncStep("format", func(ctx context.Context, input string, state *workflow.State) (string, error) {
return "## Report\n\n" + input, nil
}),
workflow.AgentStep("reviewer", reviewAgent),
)
result, err := wf.Run(ctx, "Research quantum computing trends")
fmt.Println(result.Output)All steps run concurrently with the same input:
pw := workflow.NewParallel("fan-out",
workflow.AgentStep("analyst1", analyst1),
workflow.AgentStep("analyst2", analyst2),
workflow.AgentStep("analyst3", analyst3),
)
result, _ := pw.Run(ctx, "Analyze this data...")
// result.Output contains all outputs combinedroute := workflow.ConditionalStep("classify", func(ctx context.Context, state *workflow.State) string {
if val, ok := state.Get("category"); ok && val == "billing" {
return "billing"
}
return "technical"
}, map[string]workflow.Step{
"billing": workflow.AgentStep("billing", billingAgent),
"technical": workflow.AgentStep("tech", techAgent),
})
wf := workflow.New("classifier", route)Steps can read/write shared state:
step1 := workflow.FuncStep("extract", func(ctx context.Context, input string, state *workflow.State) (string, error) {
state.Set("topic", "AI")
return "extracted data", nil
})
step2 := workflow.FuncStep("summarize", func(ctx context.Context, input string, state *workflow.State) (string, error) {
topic, _ := state.Get("topic")
return fmt.Sprintf("Summary about %s: %s", topic, input), nil
})Agents take turns, each receives the full conversation:
import "github.com/buildwithgo/berrygem/team"
t, _ := team.New("debate-team", []*agent.Agent{proAgent, conAgent},
team.WithMode(team.ModeRoundRobin),
team.WithMaxTurns(6),
)
result, _ := t.Run(ctx, "Debate: should AI be regulated?")
for _, turn := range result.AgentLog {
fmt.Printf("[%s]: %s\n", turn.AgentName, turn.Output)
}A coordinator agent decides which agent to call next:
t, _ := team.New("support-team", []*agent.Agent{billingAgent, techAgent, salesAgent},
team.WithMode(team.ModeOrchestrator),
team.WithOrchestrator(coordinatorAgent),
team.WithMaxTurns(8),
)
result, _ := t.Run(ctx, "Customer needs help with billing")Agents can transfer conversations to each other:
t, _ := team.New("network", []*agent.Agent{router, agent1, agent2},
team.WithMode(team.ModeNetwork),
team.WithMaxTurns(10),
)
result, _ := t.Run(ctx, "Help me with my request")Input/output safety checks that run before and after agent execution:
import "github.com/buildwithgo/berrygem/guardrails"
chain := guardrails.NewChain(
guardrails.PIIDetector(), // redacts emails, phones, SSNs, credit cards
guardrails.PromptInjectionDetector(), // blocks injection patterns
guardrails.ModerationGuardrail(), // warns/blocks harmful content
guardrails.TokenLimiter(8000), // blocks oversized inputs
)
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithGuardrails(chain),
)Actions: pass, warn, block, redact, rewrite
Lifecycle middleware for agent execution:
import "github.com/buildwithgo/berrygem/hooks"
chain := hooks.NewChain(
hooks.CallbackHook(func(ctx context.Context, phase hooks.HookPhase, data *hooks.HookData) error {
log.Printf("phase=%s input=%s output=%s duration=%s",
phase, data.Input, data.Output, data.Duration)
return nil
}, hooks.PhasePreRun, hooks.PhasePostRun, hooks.PhasePreTool, hooks.PhasePostTool),
)
a, _ := agent.New(agent.WithHooks(chain))Phases: pre_run, post_run, pre_tool, post_tool, pre_stream, post_stream
Tiered memory system inspired by human memory architecture:
import "github.com/buildwithgo/berrygem/memory"
db, _ := storage.NewSQLite("memory.db")
store := memory.NewGORMStore(db)
tm := memory.NewTieredMemory(store).
WithAgent("researcher").
WithSession("session-123").
WithUser("user-456")
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithTieredMemory(tm),
)Tiers:
- Sensory (~5s) — raw input buffer
- Working (~30s, 7±2 items) — active processing
- Short-Term (minutes-hours) — recent context
- Long-Term (days-years) — consolidated knowledge
Long-Term Sub-Tiers:
- Episodic — events, experiences, conversations
- Semantic — facts, concepts, knowledge
- Procedural — skills, patterns, how-to
Consolidation Engine:
- Memories promote based on importance and access frequency
- Unused memories decay over time (exponential decay)
- Similar memories compete (interference detection)
Get typed/structured responses instead of plain text:
type WeatherResponse struct {
City string `json:"city" description:"The city name"`
Temperature float64 `json:"temperature" description:"Temperature in Celsius"`
Condition string `json:"condition" description:"Weather condition"`
}
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithResponseSchemaFromStruct(&WeatherResponse{}),
)
result, _ := a.Run(ctx, "What's the weather in Paris?")
resp := result.Structured.(*WeatherResponse)
fmt.Println(resp.City, resp.Temperature)Approval system with audit trails:
import "github.com/buildwithgo/berrygem/hitl"
db, _ := storage.NewSQLite("approvals.db")
approvalStore := hitl.NewApprovalStore(db)
// List pending approvals
approvals, _ := approvalStore.List(ctx, "", hitl.ApprovalPending)
// Approve or deny
approvalStore.Resolve(ctx, approvalID, true, "Looks good")Built-in tracing with spans for agent runs, LLM calls, and tool execution:
import "github.com/buildwithgo/berrygem/observability"
store := observability.NewInMemoryStore()
tracer := observability.NewTracer("my-agent", store)
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithTracer(tracer),
)
result, _ := a.Run(ctx, "Hello")
// View trace
trace := tracer.Trace()
fmt.Println(tracer.Summary())
// Persist trace
tracer.EndTrace(ctx)Pub/sub event bus for agent lifecycle:
import "github.com/buildwithgo/berrygem/events"
bus := events.NewBus()
recorder := events.NewRecorder()
bus.On(events.EventAgentStart, recorder.Handler())
bus.On(events.EventAgentEnd, recorder.Handler())
bus.On(events.EventToolStart, recorder.Handler())
bus.On(events.EventToolEnd, recorder.Handler())
a, _ := agent.New(
agent.WithProvider(provider),
agent.WithEventBus(bus),
)Event Types: agent.start, agent.end, agent.error, tool.start, tool.end, tool.error, stream.start, stream.chunk, stream.end, memory.encode, memory.retrieve, memory.consolidate, guardrail.block, guardrail.warn
Automated scoring for agent outputs:
import "github.com/buildwithgo/berrygem/eval"
e := eval.NewEvaluator(
eval.ContainsScorer([]string{"Paris", "France"}),
eval.LengthScorer(10, 100),
eval.ExactMatchScorer(),
)
result, _ := e.Evaluate(ctx, eval.TestCase{
Input: "What is the capital of France?",
Output: "Paris is the capital of France.",
Expected: "Paris",
})
fmt.Printf("Score: %.2f\n", result.AvgScore)
// Batch evaluation
results, _ := e.EvaluateBatch(ctx, []eval.TestCase{...})
summary := e.Summary()Automatic failover between providers:
import "github.com/buildwithgo/berrygem/provider"
fp, _ := provider.NewFallback(
[]providers.Provider{openaiProvider, anthropicProvider, googleProvider},
[]string{"gpt-4o-mini", "claude-sonnet-4", "gemini-2.0-flash"},
provider.WithMaxFailures(3),
)
a, _ := agent.New(agent.WithProvider(fp))Strategies:
- Fallback — tries providers in order, switches on failure
- RoundRobin — cycles through providers on each call
- Cached — caches identical requests to reduce API calls
Non-blocking agent runs:
bg := a.RunBackground(ctx, "Analyze this data...")
// Do other work...
select {
case result := <-bg.C:
fmt.Println(result.Content)
case err := <-bg.Err:
log.Fatal(err)
case <-bg.Done:
// Execution complete
}
// Or cancel early
bg.Cancel()berrygem/
├── agent/ Agent + execution loop + tool calling
├── chat/ Stateful conversations + thread cloning + branching
├── team/ Multi-agent coordination + LLM speaker selection
├── workflow/ Pipelines + DAG + fan-out/fan-in + subgraphs
├── tools/
│ ├── tool.go Tool interface + Registry
│ ├── schema.go NewFunc helper
│ ├── mcp/ MCP client (consume) + server (expose)
│ ├── builtin/ Calculator, CurrentTime
│ ├── unix/ 50+ Unix commands
│ └── agent/ File operations
├── providers/ LLM providers (OpenAI, Anthropic, Google, etc.)
├── checkpoint/ Durable state persistence + time-travel debugging
├── compaction/ Context window management (sliding window, LLM summary)
├── sandbox/ Sandboxed code execution (Docker, local, E2B)
├── redaction/ Stream & trace redaction (API keys, PII)
├── rag/ RAG/knowledge base with document ingestion
├── learning/ Cross-session learning machine
├── plugin/ Plugin architecture (logging, context filters)
├── a2a/ Agent-to-Agent protocol support
├── server/ HTTP adapters (net/http, gin, fiber, chi)
├── scheduler/ Cron scheduling with retries and timeouts
├── config/ YAML configuration + agent factory
├── health/ Health endpoints + metrics + probes
├── benchmark/ Performance benchmarking suite
├── memory/ Tiered memory + entity tracking
├── guardrails/ Input/output safety + rate limiting + cost limits
├── hooks/ Lifecycle middleware
├── hitl/ Human-in-the-loop + state editing
├── observability/ Tracing and observability
├── events/ Event bus
├── eval/ Evaluation + benchmarking
├── compression/ Context compression
├── session/ Session state management
├── storage/ Database layer (SQLite, PostgreSQL)
└── docs/ Documentation
berrygem/ ├── agent/ Agent + execution loop + tool calling │ ├── agent.go Agent struct, New(), functional options │ ├── options.go WithModel, WithTools, WithProvider, etc. │ └── run.go Run, Stream, RunWithMessages, StreamWithMessages, RunBackground ├── chat/ Stateful multi-turn conversations │ └── chat.go Chat wrapper with auto history management ├── providers/ │ ├── types.go Unified Message, ChatRequest, ToolDefinition, etc. │ ├── provider.go Provider interface (Chat, Stream) │ ├── openai/ Official OpenAI SDK wrapper │ ├── anthropic/ Official Anthropic SDK wrapper │ ├── google/ Official Google GenAI SDK wrapper │ ├── fireworks/ OpenAI wrapper with Fireworks base URL │ └── ollama/ OpenAI-compat chat + native model management ├── tools/ │ ├── tool.go Tool interface + Registry │ ├── schema.go NewFunc helper │ ├── mcp/ MCP tool adapter (stdio, HTTP, SSE) │ └── builtin/ Calculator, CurrentTime ├── workflow/ Sequential, parallel, conditional pipelines │ └── workflow.go Workflow, Parallel, ConditionalStep ├── team/ Multi-agent coordination │ └── team.go RoundRobin, Orchestrator, Network ├── memory/ Neuroscience-inspired tiered memory │ ├── store.go GORM-backed memory store │ ├── memory.go Memory interface + types │ └── tiered.go Tiered memory (sensory → working → short → long) ├── guardrails/ Input/output safety │ └── guardrails.go PII, injection, moderation, token limiter ├── hooks/ Lifecycle middleware │ └── hooks.go Hook interface + chain + built-in hooks ├── hitl/ Human-in-the-loop │ └── approval.go Approval store + lifecycle ├── observability/ Tracing and observability │ └── trace.go Tracer, spans, in-memory and GORM stores ├── events/ Event bus │ └── events.go Pub/sub event bus + recorder ├── eval/ Evaluation system │ └── eval.go Scorers, evaluator, batch evaluation ├── provider/ Provider wrappers │ └── fallback.go Fallback, RoundRobin, Cached providers ├── compression/ Context compression for token limits │ └── compression.go Summary, truncate, tool call filter, chain ├── scheduler/ Cron-based job scheduling │ └── scheduler.go Scheduler with @every duration syntax ├── session/ Session state management │ └── session.go GORM-backed session state store ├── storage/ Database layer │ └── storage.go GORM init helpers (SQLite, PostgreSQL) ├── docs/ Documentation │ ├── README.md Documentation index │ ├── getting-started.md Installation and quick start │ ├── providers.md Provider setup and configuration │ ├── tools.md Tools, MCP, and built-in tools │ ├── memory.md Neuroscience memory system │ ├── workflows.md Workflow patterns and orchestration │ └── advanced.md Advanced features guide └── examples/ ├── basic/ Simple OpenAI agent ├── toolcalling/ Anthropic agent with tools ├── agent-with-memory/ Full agent with memory, guardrails, tracing, events, structured output ├── multi-agent-team/ Customer support team with orchestrator ├── workflow-pipeline/ Content generation pipeline with evaluation ├── streaming-chat/ Interactive streaming chat with history ├── structured-output/ Typed output from Go structs └── background-agent/ Concurrent non-blocking agent execution
## Documentation
Full documentation is available in the [`docs/`](docs/) directory:
- [Getting Started](docs/getting-started.md) — Installation, quick start, first agent
- [Providers](docs/providers.md) — OpenAI, Anthropic, Google, Fireworks, Ollama setup
- [Tools](docs/tools.md) — Custom tools, built-in tools, MCP integration
- [Memory](docs/memory.md) — Neuroscience-inspired tiered memory system
- [Workflows](docs/workflows.md) — Sequential, parallel, conditional, loop, router patterns
## Examples
Production-ready examples in the [`examples/`](examples/) directory:
| Example | Description |
|---------|-------------|
| [`basic`](examples/basic/) | Simple OpenAI agent |
| [`toolcalling`](examples/toolcalling/) | Anthropic agent with tools |
| [`agent-with-memory`](examples/agent-with-memory/) | Full agent with memory, guardrails, tracing, events, structured output |
| [`multi-agent-team`](examples/multi-agent-team/) | Customer support team with orchestrator routing |
| [`workflow-pipeline`](examples/workflow-pipeline/) | Content generation pipeline with evaluation |
| [`streaming-chat`](examples/streaming-chat/) | Interactive streaming chat with history |
| [`structured-output`](examples/structured-output/) | Typed output from Go struct schemas |
| [`background-agent`](examples/background-agent/) | Concurrent non-blocking agent execution |
| [`planning`](examples/planning/) | Plan CRUD, TaskOutput, agent assignment |
| [`unix-tools`](examples/unix-tools/) | File manipulation with 50+ Unix commands |
| [`agent-file-tools`](examples/agent-file-tools/) | Precision file read/edit/write/search/exec |
| [`team-streaming`](examples/team-streaming/) | Real-time team output streaming |
| [`team-graph-mode`](examples/team-graph-mode/) | Graph-based agent routing |
| [`team-supervisor`](examples/team-supervisor/) | Hierarchical supervision with sub-teams |
## Design Decisions
| Decision | Rationale |
|----------|-----------|
| Interface-based tools | More flexible, supports complex tools with config |
| Official SDKs per provider | Each API has quirks (Anthropic system param, Gemini content parts) |
| No hard-coded models | Models change constantly; users pass strings |
| `context.Context` everywhere | Enables cancellation and timeouts |
| Functional options | Clean API, extensible without breaking changes |
| MCP for external tools | Industry standard protocol, avoids custom integrations |
| GORM for persistence | Unified ORM for SQLite and PostgreSQL, pure Go SQLite driver |
| Neuroscience memory model | Human-like memory tiers with consolidation and decay |
## License
MIT