Get reliable structured data from any LLM. Built on jsonschema
for validation, type safety, and compile-time guarantees.
import (
"github.com/567-labs/instructor-go/pkg/instructor"
"github.com/sashabaranov/go-openai"
)
// Define what you want
type User struct {
Name string `json:"name" jsonschema:"description=The person's name"`
Age int `json:"age" jsonschema:"description=The person's age"`
}
// Extract it from natural language
client := instructor.FromOpenAI(openai.NewClient(apiKey))
var user User
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4o,
Messages: []openai.ChatCompletionMessage{
{Role: openai.ChatMessageRoleUser, Content: "John is 25 years old"},
},
},
&user,
)
fmt.Println(user) // User{Name: "John", Age: 25}
That's it. No JSON parsing, no error handling, no retries. Just define a struct and get validated data.
Getting structured data from LLMs is hard. You need to:
- Write complex JSON schemas
- Handle validation errors
- Retry failed extractions
- Parse unstructured responses
- Deal with different provider APIs
Instructor Go handles all of this with one simple interface:
Without Instructor | With Instructor |
resp, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4,
Messages: []openai.ChatCompletionMessage{
{Role: "user", Content: "..."},
},
Tools: []openai.Tool{
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "extract_user",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}`),
},
},
},
},
)
// Parse response
toolCall := resp.Choices[0].Message.ToolCalls[0]
var userData map[string]interface{}
json.Unmarshal([]byte(toolCall.Function.Arguments), &userData)
// Validate manually
if _, ok := userData["name"]; !ok {
// Handle error...
} |
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
)
var user User
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4,
Messages: []openai.ChatCompletionMessage{
{Role: "user", Content: "..."},
},
},
&user,
)
// That's it! user is validated and typed |
go get github.com/567-labs/instructor-go/pkg/instructor
Use the same code with any LLM provider:
import (
"github.com/567-labs/instructor-go/pkg/instructor"
instructor_openai "github.com/567-labs/instructor-go/pkg/instructor/providers/openai"
instructor_anthropic "github.com/567-labs/instructor-go/pkg/instructor/providers/anthropic"
instructor_google "github.com/567-labs/instructor-go/pkg/instructor/providers/google"
instructor_cohere "github.com/567-labs/instructor-go/pkg/instructor/providers/cohere"
)
// OpenAI
client := instructor.FromOpenAI(openai.NewClient(apiKey))
// Anthropic
client := instructor.FromAnthropic(anthropic.NewClient(apiKey))
// Google Gemini
client := instructor.FromGoogle(google.NewClient(apiKey))
// Cohere
client := instructor.FromCohere(cohere.NewClient(apiKey))
// All use the same API pattern!
var result YourStruct
_, err := client.CreateChatCompletion(ctx, request, &result)
Failed validations are automatically retried with descriptive error messages:
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
instructor.WithMaxRetries(3),
instructor.WithMode(instructor.ModeToolCall),
)
type User struct {
Name string `json:"name" jsonschema:"description=The person's name,minLength=1"`
Age int `json:"age" jsonschema:"description=The person's age,minimum=0,maximum=150"`
}
// Instructor automatically retries when validation fails
var user User
_, err := client.CreateChatCompletion(ctx, request, &user)
Hit "max retry attempts" errors? Enable debug logging to see exactly what's happening:
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
instructor.WithLogging("debug"), // 🔍 See all retry attempts
)
Output shows:
- Each retry attempt with full context
- Response previews on failures
- Token usage across retries
- Exact JSON/validation errors
Common formats:
instructor.WithLogging("debug") // Development - see everything
instructor.WithLogging("json") // Production - structured logs
instructor.WithLogging("json:error") // Production - only errors
Stream partial objects as they're generated:
import "github.com/567-labs/instructor-go/pkg/instructor"
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
schema, _ := instructor.GetSchema(&User{})
client := instructor.FromOpenAI(openai.NewClient(apiKey))
stream, err := client.CreateChatCompletionStream(ctx, request, schema)
defer stream.Close()
for stream.Next() {
var partial User
if err := stream.Scan(&partial); err == nil {
fmt.Printf("Partial: %+v\n", partial)
// User{Name: "", Age: 0}
// User{Name: "John", Age: 0}
// User{Name: "John", Age: 25}
}
}
Extract complex, nested data structures with full type safety:
type Address struct {
Street string `json:"street" jsonschema:"description=Street address"`
City string `json:"city" jsonschema:"description=City name"`
Country string `json:"country" jsonschema:"description=Country name"`
}
type User struct {
Name string `json:"name" jsonschema:"description=User's full name"`
Age int `json:"age" jsonschema:"description=User's age"`
Addresses []Address `json:"addresses" jsonschema:"description=List of addresses"`
}
// Instructor handles nested objects automatically
var user User
_, err := client.CreateChatCompletion(ctx, request, &user)
Build flexible AI agents that can choose between multiple tools or actions:
type SearchTool struct {
Type string `json:"type" jsonschema:"const=search"`
Query string `json:"query" jsonschema:"description=Search query"`
}
type FinishTool struct {
Type string `json:"type" jsonschema:"const=finish"`
Answer string `json:"answer" jsonschema:"description=Final answer"`
}
// Agent loop
for turn := 0; turn < maxTurns; turn++ {
action, _, err := client.CreateChatCompletionUnion(
ctx,
request,
instructor.UnionOptions{
Discriminator: "type",
Variants: []any{SearchTool{}, FinishTool{}},
},
)
switch tool := action.(type) {
case SearchTool:
result := executeSearch(tool.Query)
conversation.AddUserMessage(result)
case FinishTool:
return tool.Answer, nil
}
}
Unified conversation API that works across all providers:
import "github.com/567-labs/instructor-go/pkg/instructor/core"
// Create a conversation with a system prompt
conversation := core.NewConversation("You are a helpful assistant")
// Add messages
conversation.AddUserMessage("What's the weather in SF?")
// Vision support
conversation.AddUserMessageWithImageURLs(
"What's in this image?",
"https://example.com/image.jpg",
)
// Convert to provider-specific format
messages := instructor_openai.ConversationToMessages(conversation)
// OR
system, messages := instructor_anthropic.ConversationToMessages(conversation)
// OR
contents := instructor_google.ConversationToContents(conversation)
Automatic token counting across retries:
resp, err := client.CreateChatCompletion(ctx, request, &user)
fmt.Printf("Input tokens: %d\n", resp.Usage.PromptTokens)
fmt.Printf("Output tokens: %d\n", resp.Usage.CompletionTokens)
fmt.Printf("Total tokens: %d\n", resp.Usage.TotalTokens)
// Usage is summed across all retry attempts
Trusted by developers building production AI applications in Go:
- Type-safe by design - catch errors at compile time
- Zero reflection overhead - uses code generation where possible
- Battle-tested across multiple LLM providers
- Enterprise-ready with comprehensive error handling
Extract structured data from any text:
import (
"context"
"fmt"
"os"
"github.com/567-labs/instructor-go/pkg/instructor"
"github.com/sashabaranov/go-openai"
)
type Product struct {
Name string `json:"name" jsonschema:"description=Product name"`
Price float64 `json:"price" jsonschema:"description=Price in USD"`
InStock bool `json:"in_stock" jsonschema:"description=Availability status"`
}
func main() {
ctx := context.Background()
client := instructor.FromOpenAI(openai.NewClient(os.Getenv("OPENAI_API_KEY")))
var product Product
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4o,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "iPhone 15 Pro, $999, available now",
},
},
},
&product,
)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", product)
// Product{Name: "iPhone 15 Pro", Price: 999.0, InStock: true}
}
Instructor's simple API is available in many languages:
- Go - You are here
- Python - The original
- TypeScript - Full TypeScript support
- Ruby - Ruby implementation
- Elixir - Elixir implementation
- Rust - Rust implementation
- Documentation - Full API reference
- Examples - Copy-paste recipes for common patterns
- Discord - Get help from the community
vs Raw JSON mode: Instructor provides automatic validation, retries, streaming, and nested object support. No manual schema writing or parsing.
vs LangChain Go (or other frameworks): Instructor is focused on one thing - structured extraction. It's lighter, faster, and easier to debug with full type safety.
vs Custom solutions: Battle-tested across multiple providers and edge cases. Handles retries, validation, and provider differences automatically.
vs Python/JS versions: Native Go performance and type safety. No runtime overhead, compile-time guarantees, and seamless integration with Go codebases.
See examples/
for complete working examples:
examples/user/
- Basic extractionexamples/agent/
- Union types and agent loopsexamples/anthropic_agent/
- Multi-provider agentsexamples/streaming/
- Streaming responses- More examples in the
examples/
directory
We welcome contributions! Check out our good first issues to get started.
MIT License - see LICENSE for details.
Built by the Instructor community. Part of the Instructor ecosystem.