Skip to main content

LangChainGo + AxonFlow Integration

How to use this doc:

  • Understanding the problem? Read sections 1–4
  • Ready to implement? Jump to Integration Pattern

What LangChainGo Does Well

LangChainGo is the Go implementation of the popular LangChain framework. Its strengths are substantial for Go developers:

Native Go Implementation: Compile-time type checking, high performance, and low memory footprint. No Python runtime dependencies.

Provider Support: Works with OpenAI, Anthropic, Ollama, and other LLM providers. Swap providers without rewriting code.

Vector Store Integrations: Pinecone, Weaviate, Chroma, and more. Build RAG systems with your preferred store.

Chains and Agents: Modular abstractions for building LLM applications. Compose chains and build tool-using agents.

Concurrent Execution: Native Go concurrency patterns. Process multiple requests efficiently.

Memory Abstractions: Conversation memory, summary memory, and more. Maintain context across interactions.


What LangChainGo Doesn't Try to Solve

LangChainGo focuses on LLM orchestration. These concerns are explicitly out of scope:

Production RequirementLangChainGo's Position
Policy enforcement before chainsNot provided—chains execute based on configuration
PII detection in prompts/responsesNot addressed—data flows through without filtering
SQL injection preventionNot provided—must implement at chain level
Per-chain or per-user cost attributionNot tracked—requires external monitoring
Audit trailsNot built in—must implement logging externally
Access control for chainsNot addressed—any chain can be invoked
Token budget enforcementNot provided—chains can consume unlimited tokens

This isn't a criticism—it's a design choice. LangChainGo handles orchestration. Governance is a separate concern.


Where Teams Hit Production Friction

Based on real enterprise deployments, here are the blockers that appear after the prototype works:

1. The Chain Explosion

A conversational retrieval chain runs in a loop. Each iteration refines the query. By the time the user gives up, 150 API calls have been made.

LangChainGo executed the chain as designed. Nothing was watching how many iterations occurred.

2. The "What Happened?" Question

A chain produced an unexpected result. Debugging requires:

  • What was the prompt at each stage?
  • What context was retrieved?
  • What intermediate outputs occurred?
  • Who was the requesting user?

LangChainGo executed the chain. Without custom logging, the execution trace is gone.

3. The Production Security Review

Security review: BLOCKED
- No audit trail for chain execution
- PII can flow through chains without filtering
- No policy enforcement at chain level
- Cost controls missing
- Access control for agents not implemented

The LLM application worked perfectly. It can't ship.

4. The Cost Attribution Problem

Three teams use the same LangChainGo service. The bill arrives. Who used what? LangChainGo has no built-in cost attribution.

5. The Goroutine Leak

A chain spawns goroutines for parallel processing. An error occurs. The goroutines keep running, making API calls. No circuit breaker stops them.


How AxonFlow Plugs In

AxonFlow doesn't replace LangChainGo. It sits underneath it—providing the governance layer that LangChainGo intentionally doesn't include:

┌─────────────────┐
│ Your App │
└────────┬────────┘

v
┌─────────────────┐
│ LangChainGo │ <-- Chains, Agents, Memory
└────────┬────────┘

v
┌─────────────────────────────────┐
│ AxonFlow │
│ ┌───────────┐ ┌────────────┐ │
│ │ Policy │ │ Audit │ │
│ │ Enforce │ │ Trail │ │
│ └───────────┘ └────────────┘ │
│ ┌───────────┐ ┌────────────┐ │
│ │ PII │ │ Cost │ │
│ │ Detection│ │ Control │ │
│ └───────────┘ └────────────┘ │
└────────────────┬────────────────┘

v
┌─────────────────┐
│ LLM Provider │
└─────────────────┘

What this gives you:

  • Every chain execution logged with input/output and user context
  • PII detected and blocked before reaching the LLM
  • SQL injection attempts blocked in agent tools
  • Cost tracked per chain, per user, per request
  • Full audit trail for the complete execution history

What stays the same:

  • Your LangChainGo code doesn't change
  • Chain definitions work as before
  • No new abstractions to learn

Integration Pattern

Wrap LangChainGo LLM calls with AxonFlow governance:

package main

import (
"context"
"fmt"
"time"

"github.com/getaxonflow/axonflow-sdk-go"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/openai"
)

type GovernedLLM struct {
llm llms.Model
axonflow *axonflow.AxonFlowClient
provider string
model string
}

func NewGovernedLLM(
axonflowURL, clientSecret, openaiKey string,
) (*GovernedLLM, error) {
llm, err := openai.New(openai.WithToken(openaiKey), openai.WithModel("gpt-4"))
if err != nil {
return nil, fmt.Errorf("failed to create LLM: %w", err)
}

client := axonflow.NewClient(axonflow.Config{
Endpoint: axonflowURL,
ClientID: "langchaingo-app",
ClientSecret: clientSecret,
})

return &GovernedLLM{
llm: llm,
axonflow: client,
provider: "openai",
model: "gpt-4",
}, nil
}

type GovernedCallResult struct {
Response string
ContextID string
Blocked bool
Reason string
}

func (g *GovernedLLM) Call(
ctx context.Context,
userToken, prompt string,
callContext map[string]interface{},
) (*GovernedCallResult, error) {
startTime := time.Now()

callContext["framework"] = "langchaingo"
result, err := g.axonflow.ExecuteQuery(userToken, prompt, "chat", callContext)
if err != nil {
return nil, fmt.Errorf("pre-check failed: %w", err)
}

if result.Blocked {
return &GovernedCallResult{
ContextID: result.ContextID,
Blocked: true,
Reason: result.BlockReason,
}, nil
}

response, err := llms.GenerateFromSinglePrompt(ctx, g.llm, prompt)
latencyMs := int(time.Since(startTime).Milliseconds())

if err != nil {
g.axonflow.AuditLLMCall(axonflow.AuditRequest{
ContextID: result.ContextID,
ResponseSummary: fmt.Sprintf("Error: %v", err),
Provider: g.provider,
Model: g.model,
LatencyMs: latencyMs,
Metadata: map[string]interface{}{"error": true},
})
return nil, err
}

g.axonflow.AuditLLMCall(axonflow.AuditRequest{
ContextID: result.ContextID,
ResponseSummary: truncate(response, 200),
Provider: g.provider,
Model: g.model,
TokenUsage: axonflow.TokenUsage{PromptTokens: 100, CompletionTokens: 50, TotalTokens: 150},
LatencyMs: latencyMs,
})

return &GovernedCallResult{
Response: response,
ContextID: result.ContextID,
}, nil
}

func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen]
}

More Examples

PatternLanguageLink
Governed Chain ExecutionGolangchaingo/chains
RAG with Vector StoreGolangchaingo/rag
HTTP API ServiceGolangchaingo/http