What Beluga can do.
A complete agent stack organized around three concerns. Build is where agents come from. Know is what agents remember and retrieve. Ship is how agents behave in production.
Agents that reason, act, and recover.
The runtime runs a Plan → Act → Observe → Replan loop on every turn. Eight planning strategies share one Planner interface — swap them with a config change. LLM calls route across 22 providers with a unified ChatModel. Tools are any Go function wrapped with a schema. Handoffs between agents are auto-generated tools.
import (
"github.com/lookatitude/beluga-ai/v2/agent"
"github.com/lookatitude/beluga-ai/v2/config"
"github.com/lookatitude/beluga-ai/v2/llm"
_ "github.com/lookatitude/beluga-ai/v2/llm/providers/anthropic"
"github.com/lookatitude/beluga-ai/v2/tool"
)
model, err := llm.New("anthropic", config.ProviderConfig{Model: "claude-sonnet-4-6"})
if err != nil {
return err
}
search := tool.NewFuncTool("search", "Search the knowledge base",
func(ctx context.Context, in SearchInput) (*tool.Result, error) {
return tool.TextResult(lookup(in.Query)), nil
})
researcher := agent.New("researcher",
agent.WithLLM(model),
agent.WithPersona(agent.Persona{Role: "senior researcher", Traits: []string{"cites sources"}}),
agent.WithPlannerName("react"),
agent.WithTools([]tool.Tool{search}),
)
for ev, err := range researcher.Stream(ctx, "summarise streaming-first patterns in Beluga") {
if err != nil {
return err
}
if ev.Type == agent.EventText {
fmt.Print(ev.Text)
}
}
Memory that persists. Retrieval that finds the right thing.
Three-tier memory — working, recall, archival — with graph-store support. RAG uses hybrid retrieval: BM25 and dense vectors, fused with Reciprocal Rank Fusion. Strategies include CRAG, Adaptive RAG, HyDE, multi-query, and RAPTOR. Thirteen vector-store backends, nine embedding providers, eight memory stores.
import (
"github.com/lookatitude/beluga-ai/v2/config"
"github.com/lookatitude/beluga-ai/v2/rag/embedding"
_ "github.com/lookatitude/beluga-ai/v2/rag/embedding/providers/openai"
"github.com/lookatitude/beluga-ai/v2/rag/retriever"
"github.com/lookatitude/beluga-ai/v2/rag/vectorstore"
_ "github.com/lookatitude/beluga-ai/v2/rag/vectorstore/providers/pgvector"
)
emb, err := embedding.New("openai", config.ProviderConfig{Model: "text-embedding-3-small"})
if err != nil {
return err
}
store, err := vectorstore.New("pgvector", config.ProviderConfig{
BaseURL: "postgres://beluga@db:5432/kb",
Options: map[string]any{"dimension": 1536.0},
})
if err != nil {
return err
}
// Hybrid retrieval: dense vectors + BM25 (any retriever.BM25Searcher),
// fused with Reciprocal Rank Fusion.
hybrid := retriever.NewHybridRetriever(store, emb, bm25Index, retriever.WithHybridRRFK(60))
docs, err := hybrid.Retrieve(ctx, "how does crash-durable workflow replay work?", retriever.WithTopK(10))
Production defaults, not production afterthoughts.
The guard pipeline runs three stages — Input, Output, Tool — around every LLM interaction. Rate limits, fallback, hooks, and tracing are middleware on the same interface as your LLM call; retry and circuit breakers wrap the call site. OTel GenAI spans emit from 17 packages at every boundary. Durable workflows replay from an event log. Cost tracking attributes every token to a team.
// Resilience + observability + safety compose on the same interface.
// The first middleware listed is the outermost wrapper.
safeModel := llm.ApplyMiddleware(base,
llm.WithHooks(guardHooks), // guard.Pipeline checks in BeforeGenerate
llm.WithTracing(), // gen_ai.* OTel spans at every boundary
llm.WithProviderLimits(llm.ProviderLimits{RPM: 60, MaxConcurrent: 10}),
llm.WithFallback(backup), // switch models when the primary fails
)
// Retries wrap the call site and only retry errors core.IsRetryable accepts.
resp, err := resilience.Retry(ctx, resilience.DefaultRetryPolicy(),
func(ctx context.Context) (*schema.AIMessage, error) {
return safeModel.Generate(ctx, msgs)
})
Frame-based voice, built in.
STT → LLM → TTS as a typed pipeline. Six STT providers, seven TTS providers, three speech-to-speech providers. A WebSocket transport today; the LiveKit, Daily, and Pipecat transport adapters are registered but not yet functional. Silero and WebRTC VAD. No other Go agent framework includes this.
import (
"github.com/lookatitude/beluga-ai/v2/voice"
"github.com/lookatitude/beluga-ai/v2/voice/stt"
_ "github.com/lookatitude/beluga-ai/v2/voice/stt/providers/deepgram"
"github.com/lookatitude/beluga-ai/v2/voice/transport"
"github.com/lookatitude/beluga-ai/v2/voice/tts"
_ "github.com/lookatitude/beluga-ai/v2/voice/tts/providers/cartesia"
_ "github.com/lookatitude/beluga-ai/v2/voice/vad/providers/silero"
)
recognizer, err := stt.New("deepgram", stt.Config{
Model: "nova-3",
Extra: map[string]any{"api_key": os.Getenv("DEEPGRAM_API_KEY")},
})
if err != nil {
return err
}
synth, err := tts.New("cartesia", tts.Config{
Extra: map[string]any{"api_key": os.Getenv("CARTESIA_API_KEY")},
})
if err != nil {
return err
}
vad, err := voice.NewVAD("silero", map[string]any{"threshold": 0.5})
if err != nil {
return err
}
conn, err := transport.NewWebSocketTransport(ctx, "wss://voice.example.com/session")
if err != nil {
return err
}
// Frame-based: VAD gates audio into STT, text flows to your agent,
// and TTS streams audio back over the same transport.
pipe := voice.NewPipeline(
voice.WithTransport(conn),
voice.WithVAD(vad),
voice.WithSTT(stt.AsFrameProcessor(recognizer)),
voice.WithLLM(agentProcessor), // your voice.FrameProcessor around an agent
voice.WithTTS(tts.AsFrameProcessor(synth, 24000)),
)
err = pipe.Run(ctx)
Agents meet the network.
Expose the same Agent over MCP for tools, A2A for inter-agent discovery, REST/SSE for streaming clients, gRPC for low-latency internal calls, or WebSocket for bidirectional voice. Pick one or ship them all — the Runner is the deployment boundary, not the agent.
import (
"github.com/lookatitude/beluga-ai/v2/protocol/a2a"
"github.com/lookatitude/beluga-ai/v2/protocol/rest"
)
api := rest.NewServer()
if err := api.RegisterAgent("/agents/researcher", researcher); err != nil {
return err
}
peer := a2a.NewServer(researcher, a2a.AgentCard{
Name: "researcher",
Endpoint: "https://agents.example.com",
})
errc := make(chan error, 2)
go func() { errc <- api.Serve(ctx, ":3000") }() // REST + SSE streaming
go func() { errc <- peer.Serve(ctx, ":8081") }() // A2A, /.well-known/agent.json
return <-errc
Everything you just read is open source.
MIT licensed. 110 providers. Zero paid tiers.