LangChainGo
LangChainGo
Govern a LangChainGo agent’s tool calls with Agent Assembly. The headline point: a wrapped tool is still a valid LangChainGo tool, so adding governance is drop-in — no adapter required.
What this example demonstrates
- Defining tools that implement LangChainGo’s
tools.Toolinterface. - Wrapping those tools with
assembly.WrapToolsso every tool call is checked against a policy before it runs. - That a governed tool still satisfies
tools.Tool— the interfaces share the same shape (Name,Description,Call). - An allowed call (
search) completing and a denied call (send-email) returningassembly.PolicyViolationError. - Driving the agent with a fake LLM, so the example runs with no API key.
The framework / library
LangChainGo (v0.1.14) — the Go port
of LangChain. The example uses its tools.Tool interface
(godoc) and its
llms/fake fake LLM so it stays offline.
How it works
The key insight is structural compatibility: LangChainGo’s tools.Tool and
assembly.Tool have the same method set, so a single tool value satisfies both.
tools.godefinessearchTool(safe) andsendEmailTool(side-effecting), both implementingtools.Tool. Compile-time assertions (var _ tools.Tool = (*searchTool)(nil)) prove it.policy.godefinespolicyClient— anassembly.GovernanceClientthat deniessend-emailand allows everything else.main.gousesfake.NewFakeLLMto produce the agent’s plan, then wraps both tools withassembly.WrapToolsusingpolicyClient. Because the wrapped tools still satisfytools.Tool, they can be handed straight to a LangChainGo agent/executor.- Each tool is called:
searchis allowed and returns a result;send-emailis denied and surfaces anassembly.PolicyViolationError.
Prerequisites & running it
Complete Preparing the runtime environment
first. This example pins Go ≥ 1.26, Go SDK v0.0.1-alpha.4, and
LangChainGo v0.1.14 in its go.mod. Then:
cd agent-assembly-examples/go/langchaingo
go mod download
go run .No live gateway and no LLM API key are required — a mock client applies the
policy and fake.NewFakeLLM stands in for the model.
Code walkthrough
The tools implement LangChainGo’s interface and prove it at compile time:
type searchTool struct{}
func (s *searchTool) Name() string { return "search" }
func (s *searchTool) Description() string { return "Looks up a topic and returns a short summary." }
func (s *searchTool) Call(_ context.Context, query string) (string, error) {
return "(summary for " + query + ")", nil
}
// Compile-time proof that both tools satisfy langchaingo's tools.Tool.
var (
_ tools.Tool = (*searchTool)(nil)
_ tools.Tool = (*sendEmailTool)(nil)
)main runs the fake LLM to produce a plan, then wraps the tools — the wrapped
values stay valid LangChainGo tools:
// A fake LLM keeps the example offline. In production this is openai.New(), etc.
model := fake.NewFakeLLM([]string{
"I should search for the topic, then email the result.",
})
plan, err := llms.GenerateFromSinglePrompt(ctx, model, "How do I summarize a topic and notify the user?")
if err != nil {
log.Fatalf("[llm] generation failed: %v", err)
}
fmt.Printf("[llm] plan: %s\n\n", plan)
// Wrapped tools still satisfy tools.Tool, so a LangChainGo agent can use them.
governed := assembly.WrapTools(
[]assembly.Tool{&searchTool{}, &sendEmailTool{}},
&policyClient{},
)
runTool(ctx, governed[0], "agent governance") // allowed
runTool(ctx, governed[1], "user@example.com") // deniedNotes & caveats
The wrapping is the integration. Because
tools.Toolandassembly.Toolare structurally identical, there is no adapter layer — the same value is both a LangChainGo tool and a governed Agent Assembly tool.
This example doesn’t run a full LangChainGo agent executor. It drives a fake LLM to show the reasoning step, then calls the governed tools directly so the allow/deny outcome is unambiguous. To make it production-ready, swap
fake.NewFakeLLMfor a real model (e.g.openai.New()) andpolicyClientfor a gateway-backed client.
Expected behavior
[assembly] governing LangChainGo tools via an offline policy client
[policy] loaded: search=ALLOW, send-email=DENY
[llm] plan: I should search for the topic, then email the result.
[agent] calling tool: search input="agent governance"
[policy] ALLOWED tool=search
[agent] result: (summary for agent governance)
[agent] calling tool: send-email input="user@example.com"
[policy] DENIED tool=send-email reason="outbound email is blocked by policy"
[agent] blocked: assembly: policy violation: tool=send-email reason=outbound email is blocked by policygo test ./... runs the same paths offline — no gateway, no API key.
Links
- Example directory:
go/langchaingo README.md- LangChainGo ·
tools.Tool - Related: Integrate with a framework