Guides

Go quickstart

Build a RAG chatbot in Go with zero frameworks. One file, one dependency.

This is the Go equivalent of the zero-to-chatbot guide. It inserts documents into Aether, retrieves relevant context, and calls Claude via net/http — no LLM SDK needed.

What you'll need

Setup

Bash
mkdir aether-chatbot && cd aether-chatbot
go mod init aether-chatbot
go get github.com/quintessence-group/aether-sdk-go

Set your API keys:

Bash
export AETHER_API_KEY="your-aether-key"
export ANTHROPIC_API_KEY="your-anthropic-key"

The entire app

Create main.go and paste this:

Go
package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	aether "github.com/quintessence-group/aether-sdk-go"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	client := aether.New(aether.WithAPIKey(os.Getenv("AETHER_API_KEY")))

	// Step 1: Load your knowledge base
	docs := []string{
		"Our return policy allows returns within 30 days of purchase with a receipt.",
		"Shipping is free on orders over $50. Standard delivery takes 3-5 business days.",
		"We accept Visa, Mastercard, and PayPal. We do not accept cryptocurrency.",
		"Store hours are Monday-Saturday 9am-9pm and Sunday 10am-6pm.",
		"Loyalty members earn 1 point per dollar spent. 100 points = $5 off.",
	}

	fmt.Println("Loading documents...")
	for _, doc := range docs {
		if _, err := client.InsertText(ctx, doc, "doc.txt"); err != nil {
			log.Fatal(err)
		}
	}
	fmt.Printf("Loaded %d documents.\n\n", len(docs))

	// Step 2: Chat loop
	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print("Ask a question (or 'quit'): ")
		if !scanner.Scan() {
			break
		}
		question := strings.TrimSpace(scanner.Text())
		if question == "quit" || question == "exit" || question == "q" {
			break
		}

		// Find relevant documents
		results, err := client.Retrieve(ctx, question, 3)
		if err != nil {
			log.Printf("Retrieve error: %v\n", err)
			continue
		}

		var contextBuf bytes.Buffer
		for _, r := range results {
			fmt.Fprintf(&contextBuf, "%s\n\n", r.Content)
		}

		// Ask Claude, grounded in your documents
		answer, err := askClaude(ctx, contextBuf.String(), question)
		if err != nil {
			log.Printf("Claude error: %v\n", err)
			continue
		}
		fmt.Printf("\n%s\n\n", answer)
	}
}

func askClaude(ctx context.Context, docContext, question string) (string, error) {
	reqBody, _ := json.Marshal(map[string]any{
		"model":      "claude-sonnet-4-20250514",
		"max_tokens": 300,
		"system":     "Answer based only on this context. If the answer isn't in the context, say so.\n\n" + docContext,
		"messages":   []map[string]string{{"role": "user", "content": question}},
	})

	req, err := http.NewRequestWithContext(ctx, "POST",
		"https://api.anthropic.com/v1/messages", bytes.NewReader(reqBody))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-API-Key", os.Getenv("ANTHROPIC_API_KEY"))
	req.Header.Set("Anthropic-Version", "2023-06-01")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var result struct {
		Content []struct{ Text string } `json:"content"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "", err
	}
	if len(result.Content) == 0 {
		return "", fmt.Errorf("empty response from Claude")
	}
	return result.Content[0].Text, nil
}

Run it

Bash
go run main.go

Try asking

  • "What's your return policy?"
  • "Do you take crypto?"
  • "How do I earn loyalty points?"

Key Go patterns

Context propagation. Every Aether SDK call takes a context.Context as the first argument. In a real application, pass the request context from your HTTP handler (r.Context()) instead of context.Background() — this ensures cancellation and timeouts propagate correctly.

Error handling. The Aether Go SDK returns error as the second return value. Always check it:

Go
results, err := client.Retrieve(ctx, question, 3)
if err != nil {
    // err could be a network failure, 401 (bad key), 429 (rate limited), etc.
    // For production, check the HTTP status code to decide whether to retry:
    log.Printf("retrieve failed: %v", err)
}

No LLM SDK needed. Unlike Python and TypeScript, Go doesn't need an Anthropic or OpenAI SDK package. The net/http standard library handles API calls directly. This keeps your dependency tree small and avoids version conflicts.

Package status. The Go module path is github.com/quintessence-group/aether-sdk-go. The public module is available through the default Go module proxy, so the unversioned go get command above installs the latest release.

Make it yours

Use your own documents. Replace the docs slice with your content, or load files:

Go
data, _ := os.ReadFile("./handbook.pdf")
client.Insert(ctx, data, "handbook.pdf", "application/pdf")

Use OpenAI instead. Swap the askClaude function for an OpenAI call — see the OpenAI integration for the Go example.

Add an HTTP server. Wrap the chat logic in net/http handlers — the Go full-stack example shows exactly this pattern.

Next steps