Skip to content

ADK (Go)

The Go SDK has no framework adapters — deliberately. Framework glue in an SDK forces that framework into the build of every consumer who does not use it, and it rots on the framework’s release schedule rather than ours. The glue belongs in your agent.

It is about thirty lines.

Same rule as LangChain: a model that can skip the safety check will skip it under instruction pressure — including pressure injected through text it is reading. Put the verdict in your loop, not in the toolset.

Retrieval as a tool is fine. The verdict is not.

cfg := vectadyne.Config{App: "release-train"}.FromEnv().WithDefaults()
client, err := vectadyne.New(cfg)
if err != nil {
return err
}
func memoryTool(c *vectadyne.Client) *adk.Tool {
return adk.NewTool("recall_governed_memory",
"Search governed engineering memory for what is known about this area.",
func(ctx context.Context, args struct {
Query string `json:"query"`
}) (any, error) {
hits, err := c.RecallText(ctx, args.Query)
if err != nil {
return nil, err
}
return hits, nil
})
}

Note the error is returned rather than swallowed. An agent that silently gets no memory looks the same as one whose corpus is empty, and you will debug the wrong thing.

assembled, err := client.Assemble(ctx, wire.AssembleRequest{
Action: wire.Action{
ActionType: actionType,
Summary: summary,
ChangedFiles: files,
ScopeKey: scope,
},
Purpose: purpose,
})
if err != nil {
return fmt.Errorf("governed assemble: %w", err) // NOT a verdict — see Handle errors
}
g := vectadyne.InterpretContext(assembled)
if g.Hold {
return escalate(g.Reason, g.Blockers)
}
for _, a := range g.Advisories {
instructions = append(instructions, "Governed memory notes: "+a.Message)
}

Go’s Hold is true for stop — the inverse of Python’s proceed.

An orchestrator holding one client means every member authenticates as the orchestrator. Per-member scoping evaporates and the audit trail records one actor doing everything.

Give each member its own client, or narrow per call with an actor block. And carry identity in session state so it survives a handoff — this is the one that gets lost silently, because everything still works.

ADK will pass prior conversation content to the next agent unless you say otherwise. That hands member B everything member A retrieved — including memory B’s own credential would not have been allowed to see, which quietly defeats the clearance model.

Restrict what travels to what the next member actually needs. Assert it in a test: run a two-hop handoff and check the second member did not receive the first’s retrieved memory.

The SDK has no OpenTelemetry dependency, by design — it would land in the build of every consumer who does not use OTel. Propagation is a seam: supply an http.Client whose transport injects traceparent, which is exactly what otelhttp.NewTransport does.

cfg.HTTPClient = &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}

If you emit spans, a governance refusal is a span EVENT, never a span ERROR. Span status Error means the call failed, not that the answer was restrictive. Marking warn as an error teaches every dashboard that governance is a failure mode — the exact inversion of advisory-first, arriving through telemetry instead of through the API, and it makes the platform look broken precisely when it is working.