Skip to content

Quick Start

Quick Start

This walkthrough takes you from zero to a governed tool call in three steps: install the SDK, initialise the runtime, and wrap your tools so every call is checked against the AI Agent Assembly gateway. The whole thing is a single main you can copy, paste, and run.

Agent registration is not reachable from a plain go get today — following this quick-start will not make your agent appear in the dashboard. The steps below wrap and govern tool calls, but the register handshake runs only under the opt-in native cgo binding (-tags aa_ffi_go, CGO_ENABLED=1), and that native library (libaa_ffi_go) is not published anywhere yet: building with -tags aa_ffi_go fails with ld: library 'aa_ffi_go' not found outside a full monorepo checkout. The default pure-Go build has no native transport, so it does not register even when WithSidecarAddress is set (see that option’s godoc). Publishing the native library — or dropping the cgo requirement — is a separate product decision; track status in AAASM-4547 and AAASM-4469.

Prerequisites

  • Go ≥ 1.26 (the floor declared in go.mod).

  • For local development: nothing else — Init auto-discovers a gateway on http://localhost:7391, and starts one for you if none is running (the aasm CLI must be on your PATH).

    Local-mode transports — :7391 REST + :50051 gRPC. Init shells out to the following command to auto-start the gateway:

    aasm start --mode local --foreground

    The :7391 auto-discovery above only resolves the REST gateway URL. Agent registration is a separate concern that talks to the gateway’s gRPC endpoint (default 127.0.0.1:50051) — Init does not auto-derive this address the way the Python and Node SDKs do. Reaching it requires an explicit WithSidecarAddress (or WithSidecarBinary) option; without one, Init returns ErrSidecarUnavailable. And per the warning at the top of this page, the registration handshake itself only runs under the opt-in native cgo binding today.

    To confirm both surfaces are actually up rather than guessing from Init’s behavior, check them directly:

    curl http://localhost:7391/healthz   # REST — real JSON: mode, storage, version, uptime_secs
    nc -z localhost 50051 && echo "gRPC port open"   # gRPC has no health endpoint yet; this only confirms the port accepts connections
  • For production: a gateway URL and, if your gateway requires auth, an API key. Both can come from options, environment variables, or a config file — see Configuration.

  • (Optional) a C compiler, only if you opt into the native FFI transport with -tags aa_ffi_go. The default transport is pure-Go and needs none.

Step 1 — Install

go get github.com/ai-agent-assembly/go-sdk

Step 2 — Initialise the runtime

Init returns an *assembly.Assembly — your runtime handle. Always Close it when you’re done so the connection (and any managed sidecar) is released.

package main

import (
    "context"
    "log"

    "github.com/ai-agent-assembly/go-sdk/assembly"
)

func main() {
    // Stamp this agent's identity onto the context. The SDK forwards it to
    // the gateway on every check and record.
    ctx := assembly.WithAgentID(context.Background(), "my-agent")

    a, err := assembly.Init(ctx,
        assembly.WithGatewayURL("https://gateway.example.com"),
        assembly.WithAPIKey("..."), // optional — omit for local, unauthenticated dev
    )
    if err != nil {
        log.Fatalf("init assembly runtime: %v", err)
    }
    defer func() {
        if err := a.Close(); err != nil {
            log.Printf("close assembly runtime: %v", err)
        }
    }()

    log.Println("assembly runtime ready")
}

For local development you can drop both options entirely — assembly.Init(ctx) resolves the gateway from the environment, then ~/.aasm/config.yaml, then the local default. See Configuration for the full resolution order.

Step 3 — Wrap your tools

Your tools just need to satisfy the SDK’s small Tool interface:

type Tool interface {
    Name() string
    Description() string
    Call(ctx context.Context, input string) (string, error)
}

WrapTools takes your []Tool and a governance client, and returns a new []Tool where every Call is governed:

governed := assembly.WrapTools(myTools, nil)

The second argument is the GovernanceClient that talks to the gateway. Under the default fail-closed enforce posture, passing nil denies every wrapped call (ErrGovernanceUnavailable) rather than running it unchecked — pass assembly.WithFailClosed(false) for a true passthrough wrapper (the tools run, no Check/RecordResult calls) while you wire in a real client, ready to enforce policy (see Handle allow/deny decisions and errors).

Hand governed to your agent in place of the originals. From here on, each call against a governed tool is checked against the gateway policy before execution and recorded after.

Govern your first agent

Pick your framework — each tab shows the governance slice copied verbatim from a runnable example in the examples repo (a CI drift check keeps them in lockstep). Go’s per-framework surface is thin today, so the tabs are LangChainGo (the framework path) and Plain (the framework-agnostic path). Two more validated Go examples already exist — Tool Policy and CLI Runtime (sidecar) — but those are patterns (an allow/deny policy demo and sidecar wiring), not “first agent” frameworks, so they’re intentionally left out of this quick-start; see metadata/quickstart/README.md for the tab-selection rationale. A new tab appears automatically once a new Go framework example lands.

Governance slice from the runnable go/langchaingo/main.go example.

// Wrap the LangChainGo tools with Agent Assembly governance. The wrapped
// values still satisfy langchaingo's tools.Tool, so they can be handed
// straight to a LangChainGo agent/executor.
governed := assembly.WrapTools(
	[]assembly.Tool{&searchTool{}, &sendEmailTool{}},
	&policyClient{},
)

Putting it together

package main

import (
    "context"
    "log"

    "github.com/ai-agent-assembly/go-sdk/assembly"
)

// echoTool is a minimal Tool implementation.
type echoTool struct{}

func (echoTool) Name() string       { return "echo" }
func (echoTool) Description() string { return "returns its input unchanged" }
func (echoTool) Call(_ context.Context, input string) (string, error) {
    return input, nil
}

func main() {
    ctx := assembly.WithAgentID(context.Background(), "my-agent")

    a, err := assembly.Init(ctx,
        assembly.WithGatewayURL("https://gateway.example.com"),
        assembly.WithAPIKey("..."),
    )
    if err != nil {
        log.Fatalf("init: %v", err)
    }
    defer a.Close()

    tools := []assembly.Tool{echoTool{}}
    governed := assembly.WrapTools(tools, nil)

    out, err := governed[0].Call(ctx, "hello, governance")
    if err != nil {
        log.Fatalf("tool call: %v", err)
    }
    log.Println("result:", out) // result: hello, governance
}

What to expect

  • Init succeeds once a gateway is reachable (resolved or auto-started). If no gateway can be found and no aasm binary is on PATH, you’ll get a typed *assembly.ConfigurationError — see Troubleshooting.
  • Tool calls run and return the inner tool’s result. With a real governance client wired in, a deny decision surfaces as a *assembly.PolicyViolationError and the inner tool never runs.

Where to next

  • Core Concepts — what’s actually happening inside the SDK.
  • Examples — wire the SDK into the framework you actually use.
  • Guides — wrap a real agent, integrate a framework, handle decisions.
  • Configuration — every Init option, defaults, and enforcement modes.
  • Troubleshooting — what to do when Init or a check fails.
Last updated on • Bryant