Skip to content

title: Getting started description: Your first AI chat end to end: activate a provider, resolve a credential, send a Chat message, then take a structured Ask response straight into a Go struct. tags: [tutorial, getting-started, credentials, structured-output]


Getting started

This tutorial walks you through your first AI chat, end to end: activate a provider, resolve a credential, send a Chat message, and then ask for a structured Ask response unmarshalled straight into a Go struct. Allow about fifteen minutes, plus whatever the two API calls cost you. Both are small.

You will do everything in one self-contained Go program so you can watch each moving part. It uses Anthropic Claude; the same shape works for every provider, and you would swap only the blank import, the provider constant, and the credential env var.

Prerequisites

  • Go 1.27 or newer.
  • An Anthropic API key exported as ANTHROPIC_API_KEY.
  • A new module to experiment in:
mkdir chat-tutorial && cd chat-tutorial
go mod init example.test/chat-tutorial
go get gitlab.com/phpboyscout/go/chat-anthropic

Install the provider module, not the core, and let it bring the core with it. The two are released as a matched pair, and asking for the core separately can select a version newer than the provider was built against, which does not compile. Version compatibility has the detail; go mod tidy in the next step promotes the core to a direct requirement at the version already chosen, which is what you want.

Step 1: activate a provider

Providers live in separate modules and are switched on by a blank import: the same pattern as database drivers or image/* decoders. Importing chat-anthropic for its init() side effect registers the claude provider with the core:

import (
    "gitlab.com/phpboyscout/go/chat"
    _ "gitlab.com/phpboyscout/go/chat-anthropic" // registers ProviderClaude
)

Without this import, chat.New with ProviderClaude returns unsupported provider: claude. The SDK-free claude-local provider is the only one that needs no import, because it ships in the core.

Step 2: construct a client

chat.New takes package-owned Settings: a Config describing provider behaviour, and an optional *slog.Logger. Leave Token empty and the client falls back to the well-known ANTHROPIC_API_KEY environment variable.

client, err := chat.New(ctx, chat.Settings{
    Config: chat.Config{
        Provider:     chat.ProviderClaude,
        SystemPrompt: "You are a concise assistant.",
    },
})

If no Model is set, Claude defaults to claude-opus-5.

Step 3: send a message

Chat sends a prompt and returns the text reply. Message history is retained on the client, so follow-up calls continue the same conversation.

reply, err := client.Chat(ctx, "Name three uses for a CLI tool, one line each.")
if err != nil {
    log.Fatal(err)
}
fmt.Println(reply)

Step 4: ask for structured output

Ask unmarshals the model's answer directly into a Go value. Build a ResponseSchema from your struct with chat.GenerateSchema, set it on the Config, and the provider is forced to return JSON matching that shape.

type Summary struct {
    Title  string   `json:"title"`
    Points []string `json:"points"`
}

structured, err := chat.New(ctx, chat.Settings{
    Config: chat.Config{
        Provider:       chat.ProviderClaude,
        ResponseSchema: chat.GenerateSchema[Summary](),
        SchemaName:     "summary",
    },
})
if err != nil {
    log.Fatal(err)
}

var out Summary
if err := structured.Ask(ctx, "Summarise the Go language in a title and three points.", &out); err != nil {
    log.Fatal(err)
}
fmt.Printf("%s\n%v\n", out.Title, out.Points)

The complete program

package main

import (
    "context"
    "fmt"
    "log"

    "gitlab.com/phpboyscout/go/chat"
    _ "gitlab.com/phpboyscout/go/chat-anthropic"
)

type Summary struct {
    Title  string   `json:"title"`
    Points []string `json:"points"`
}

func main() {
    ctx := context.Background()

    client, err := chat.New(ctx, chat.Settings{
        Config: chat.Config{
            Provider:     chat.ProviderClaude,
            SystemPrompt: "You are a concise assistant.",
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    reply, err := client.Chat(ctx, "Name three uses for a CLI tool, one line each.")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply)

    structured, err := chat.New(ctx, chat.Settings{
        Config: chat.Config{
            Provider:       chat.ProviderClaude,
            ResponseSchema: chat.GenerateSchema[Summary](),
            SchemaName:     "summary",
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    var out Summary
    if err := structured.Ask(ctx, "Summarise the Go language in a title and three points.", &out); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s\n%v\n", out.Title, out.Points)
}

Tidy the module (this promotes the core to a direct requirement at the version the provider already pinned) then run it:

go mod tidy
export ANTHROPIC_API_KEY=sk-ant-...
go run .

Check it worked

The program prints a one-line-per-use answer, then a title and three bullet points parsed into the Summary struct, something like:

Automating repetitive tasks
Scripting deployments
Querying a service without a browser
A concise tour of Go
[Compiled and statically typed Built-in concurrency with goroutines Standard library covers most needs]

The wording will differ every run; the shape will not.

Fix it when it does not

What you see What it means
unsupported provider: claude the blank import from step 1 is missing
Anthropic API key is required but not provided ANTHROPIC_API_KEY is not exported in the shell you ran from (this fails at chat.New, before any request)
invalid operation: … is not an interface at build time the core and provider modules are at different minors (see version compatibility)

Next steps