PRODUCT

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.

BUILD

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.

ReAct planner with two tools, streamed to the caller. Swap WithPlannerName("react") for "lats", "tot", or "moa" without touching the rest.
examples/research-agent.go
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)
    }
}
The eight reasoning strategies
KNOW

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.

BM25 + dense vector retrieval fused with RRF — the recommended default for most knowledge bases.
examples/hybrid-retrieval.go
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))
SHIP

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.

Four middlewares around base plus retry at the call site is the difference between a demo and a system you page on.
main.go · wrapping a model for production
// 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)
    })
VOICE

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.

examples/voice-agent.go
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)
PROTOCOLS

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.

examples/serve.go
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.