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 Requirement | LangChainGo's Position |
|---|---|
| Policy enforcement before chains | Not provided—chains execute based on configuration |
| PII detection in prompts/responses | Not addressed—data flows through without filtering |
| SQL injection prevention | Not provided—must implement at chain level |
| Per-chain or per-user cost attribution | Not tracked—requires external monitoring |
| Audit trails | Not built in—must implement logging externally |
| Access control for chains | Not addressed—any chain can be invoked |
| Token budget enforcement | Not 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
| Pattern | Language | Link |
|---|---|---|
| Governed Chain Execution | Go | langchaingo/chains |
| RAG with Vector Store | Go | langchaingo/rag |
| HTTP API Service | Go | langchaingo/http |