Prompt Engineering
Prompt engineering is the practice of designing and refining prompts to get the best results from Large Language Models (LLMs). This guide covers fundamental concepts, advanced techniques, and Go-specific patterns for working with the Go-AI SDK.
What is a Large Language Model (LLM)?
A Large Language Model is a prediction engine that takes a sequence of words as input and predicts the most likely sequence to follow. It assigns probabilities to potential next sequences and selects one, continuing until a stopping criterion is met.
These models are trained on massive text corpuses, making them better suited to some use cases than others. For example, a model trained on GitHub data understands source code patterns particularly well. However, generated sequences, while often plausible, can sometimes be random and not grounded in reality.
Quick Start: Build a Slogan Generator
Start with an Instruction
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
)
func main() {
ctx := context.Background()
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: "Create a slogan for an organic coffee shop.",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Text)
// Output: "Naturally Brewed, Ethically Sourced"
}
Include Examples (Few-Shot Learning)
prompt := `Create three slogans for a business with unique features.
Business: Bookstore with cats
Slogans: "Purr-fect Pages", "Books and Whiskers", "Novels and Nuzzles"
Business: Gym with rock climbing
Slogans: "Peak Performance", "Reach New Heights", "Climb Your Way Fit"
Business: Coffee shop with live music
Slogans:`
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
// Output: "Sip, Savor, and Swing", "Beans and Beats", "Melody in Every Cup"
Adjust Temperature
Temperature controls output randomness (0 = deterministic, 1 = creative):
// Deterministic output (same result every time)
temperature := 0.0
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Temperature: &temperature,
Prompt: "Create a slogan for a coffee shop.",
})
// Creative, varied output (different each time)
temperature := 0.8
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Temperature: &temperature,
Prompt: "Create a slogan for a coffee shop.",
})
Temperature Guide:
- 0.0-0.3: Focused, deterministic, factual (good for tool calls, data extraction)
- 0.4-0.7: Balanced creativity and consistency (good for general tasks)
- 0.8-1.0: Creative, varied, exploratory (good for brainstorming, creative writing)
Advanced Prompting Techniques
System Prompts
Define the model's behavior and personality:
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: `You are a professional marketing copywriter with 10 years of experience.
You specialize in creating memorable, punchy slogans.
Always think about:
- Brand voice and personality
- Target audience
- Emotional impact
- Memorability`,
Prompt: "Create three slogans for an eco-friendly coffee shop.",
})
Chain-of-Thought Prompting
Encourage step-by-step reasoning:
prompt := `Create a slogan for a sustainable fashion brand. Think through this step by step:
1. First, identify the key values of sustainable fashion
2. Consider the target audience and their motivations
3. Think about what makes this brand unique
4. Combine these elements into a memorable slogan
5. Present your final slogan
Let's begin:`
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
Iterative Refinement
Use multiple calls to refine output:
// Step 1: Generate initial slogans
result1, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: "Create 5 slogans for a tech startup making AI productivity tools.",
})
// Step 2: Refine the best ones
refinePrompt := fmt.Sprintf(`Here are some slogans:
%s
Pick the best 2 and refine them to be:
- More punchy and memorable
- Focused on user benefits
- Under 6 words each`, result1.Text)
result2, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: refinePrompt,
})
Prompts for Tools
When using tools, getting good results becomes more challenging as tool complexity increases.
Tips for Effective Tool Prompts
- Use Strong Models: Use models proficient at tool calling (GPT-4, GPT-5, Claude Sonnet)
- Limit Tool Count: Keep tools to 5 or less for best results
- Simplify Schemas: Avoid deeply nested or overly complex schemas
- Use Semantic Names: Choose descriptive names for tools and parameters
- Add Descriptions: Use field tags to provide context
- Show Examples: Include example tool calls in your prompt
- Explain Tool Outputs: Document what each tool returns
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/provider/types"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
)
func main() {
ctx := context.Background()
// Define tool with clear descriptions
weatherTool := types.Tool{
Name: "get_weather",
Description: "Get current weather for a location. Returns temperature in Celsius, conditions (e.g. sunny, rainy), and humidity percentage.",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"location": map[string]string{
"type": "string",
"description": "City name (e.g., 'New York' or 'London, UK')",
},
"units": map[string]interface{}{
"type": "string",
"enum": []string{"celsius", "fahrenheit"},
"description": "Temperature units (defaults to celsius)",
},
},
"required": []string{"location"},
},
Execute: func(ctx context.Context, args map[string]interface{}, _ types.ToolExecutionOptions) (interface{}, error) {
// Implementation
return map[string]interface{}{
"temperature": 22,
"conditions": "sunny",
"humidity": 65,
}, nil
},
}
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Use temperature 0 for deterministic tool calls
temperature := 0.0
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Temperature: &temperature,
Tools: []types.Tool{weatherTool},
StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
Prompt: "What's the weather in Brooklyn, New York?",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Text)
}
Tool Prompt with Examples
prompt := `You have access to weather and flight tools. Here are examples of how to use them:
Example 1:
User: "What's the weather in Paris?"
Action: Call get_weather({"location": "Paris, France"})
Example 2:
User: "Find flights from NYC to LAX"
Action: Call search_flights({"from": "JFK", "to": "LAX"})
Now answer: What's the weather like in Tokyo and are there flights from San Francisco?`
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Tools: tools,
StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
Prompt: prompt,
})
Go-Specific Schema Patterns
Using Struct Tags
When working with structured outputs in Go, leverage struct tags:
type MarketingCampaign struct {
Slogan string `json:"slogan" description:"Catchy slogan under 10 words"`
Tagline string `json:"tagline" description:"Supporting tagline explaining the value"`
Target string `json:"target" description:"Target audience (e.g., 'young professionals')"`
Channels []string `json:"channels" description:"Marketing channels (e.g., 'social', 'email')"`
Budget int `json:"budget" description:"Estimated monthly budget in USD"`
Tone string `json:"tone" description:"Brand tone (e.g., 'professional', 'playful')"`
}
// Build a JSON schema from the struct's json tags
schema := ai.SchemaFor[MarketingCampaign]()
result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
Schema: schema,
Prompt: "Create a marketing campaign for an eco-friendly water bottle company.",
})
JSON Schema Considerations
When defining schemas for tool parameters or structured output:
// ✅ Good: Simple, clear schema
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"name": map[string]string{
"type": "string",
"description": "Person's full name",
},
"age": map[string]string{
"type": "number",
"description": "Age in years",
},
"email": map[string]string{
"type": "string",
"description": "Email address",
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
},
},
"required": []string{"name", "age"},
}
// ❌ Avoid: Overly complex nested schemas
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"person": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"name": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"first": map[string]string{"type": "string"},
"middle": map[string]string{"type": "string"},
"last": map[string]string{"type": "string"},
// ... too deep
},
},
},
},
},
}
Optional vs Required Parameters
Be explicit about required fields:
// Clear required fields
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"command": map[string]string{
"type": "string",
"description": "Shell command to execute",
},
"workdir": map[string]string{
"type": "string",
"description": "Working directory (optional, defaults to current dir)",
},
"timeout": map[string]string{
"type": "number",
"description": "Timeout in seconds (optional, defaults to 30)",
},
},
"required": []string{"command"}, // Only command is required
}
Structured Output Prompting
Guide the model to return structured information:
prompt := `Create marketing slogans for a vegan restaurant. Return your response in this exact format:
PRIMARY SLOGAN: [main slogan]
TAGLINE: [supporting tagline]
SOCIAL MEDIA: [shorter version for social media]
EMOTIONAL APPEAL: [one-word emotion]
Example:
PRIMARY SLOGAN: "Plant Power, Every Hour"
TAGLINE: "Deliciously sustainable dining"
SOCIAL MEDIA: "🌱 Power Up Plant-Based"
EMOTIONAL APPEAL: Energetic`
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
For guaranteed structured output, use GenerateObject:
type SloganSet struct {
Primary string `json:"primary" description:"Main slogan under 8 words"`
Tagline string `json:"tagline" description:"Supporting tagline"`
Social string `json:"social" description:"Social media version with emoji"`
Emotion string `json:"emotion" description:"Single word describing emotion"`
}
schema := ai.SchemaFor[SloganSet]()
result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
Schema: schema,
Prompt: "Create marketing slogans for a vegan restaurant.",
})
// result.Object is guaranteed to match SloganSet structure
slogans := result.Object.(SloganSet)
fmt.Printf("Primary: %s\n", slogans.Primary)
Debugging Techniques
Inspect HTTP Requests
Enable debug mode to see full API requests:
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
DebugMode: true, // Logs full HTTP requests and responses
})
Check Token Usage
Monitor token consumption:
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Input tokens: %d\n", result.Usage.GetInputTokens())
fmt.Printf("Output tokens: %d\n", result.Usage.GetOutputTokens())
fmt.Printf("Total tokens: %d\n", result.Usage.GetTotalTokens())
// Check for unexpectedly high usage
if result.Usage.GetTotalTokens() > 10000 {
log.Printf("WARNING: High token usage detected")
}
Inspect Tool Calls
Debug tool execution:
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Tools: tools,
Prompt: "What's the weather in New York?",
OnStepFinish: func(step ai.Step) {
// Called after each step
fmt.Printf("Step %d:\n", step.StepIndex)
for _, call := range step.ToolCalls {
fmt.Printf(" Tool: %s\n", call.Name)
fmt.Printf(" Args: %v\n", call.Arguments)
if call.Result != nil {
fmt.Printf(" Result: %v\n", call.Result)
}
}
},
})
Log Provider Warnings
Some providers return warnings about your prompts:
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
if err != nil {
log.Fatal(err)
}
// Check for warnings
if len(result.Warnings) > 0 {
for _, warning := range result.Warnings {
log.Printf("Warning: %s", warning)
}
}
Test with Different Models
Compare results across models:
models := []string{"gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"}
for _, modelID := range models {
model, _ := provider.LanguageModel(modelID)
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
if err != nil {
log.Printf("Model %s failed: %v", modelID, err)
continue
}
fmt.Printf("\n%s:\n%s\n", modelID, result.Text)
}
Best Practices
1. Be Specific
// ❌ Bad: Vague
"Write about coffee"
// ✅ Good: Specific
"Write a 3-sentence product description for organic Ethiopian coffee beans
emphasizing fruity notes and sustainability"
2. Use Examples (Few-Shot Learning)
// Show the pattern you want
prompt := `Convert casual text to professional:
Casual: "Hey, wanna meet up?"
Professional: "Would you be available for a meeting?"
Casual: "Got your email, thx!"
Professional: "Thank you for your email."
Casual: "Can't make it tomorrow"
Professional:`
3. Set Constraints
prompt := `Create a slogan for a coffee shop.
Requirements:
- Maximum 5 words
- Include the word "brew"
- Sound energetic
- Avoid cliches`
4. Use System Prompts for Consistent Behavior
system := `You are a professional copywriter. For every slogan:
- Keep it under 6 words
- Make it memorable
- Avoid cliches
- Explain your rationale`
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: system,
Prompt: "Create a slogan for an artisan bakery",
})
5. Test Different Temperatures
prompts := []float64{0.0, 0.5, 1.0}
for _, temp := range prompts {
temperature := temp
result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Temperature: &temperature,
Prompt: prompt,
})
fmt.Printf("Temperature %.1f: %s\n", temp, result.Text)
}
6. Use Temperature 0 for Tool Calls
// ✅ Good: Deterministic tool calls
temperature := 0.0
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Temperature: &temperature,
Tools: tools,
Prompt: "Get the weather in Tokyo",
})
// ❌ Avoid: High temperature with tools
temperature := 0.8 // Can cause inconsistent tool usage
7. Handle Context Limits
// Check if prompt fits in context
maxTokens := 128000 // GPT-4 context limit
promptTokens := len(prompt) / 4 // Rough estimate (1 token ≈ 4 chars)
if promptTokens > maxTokens-2000 {
log.Printf("Warning: Prompt may exceed context limit")
// Truncate or summarize prompt
}
Common Pitfalls
1. Ambiguous Instructions
// ❌ Bad
"Make it better"
// ✅ Good
"Rewrite this slogan to be more energetic and under 5 words"
2. Conflicting Instructions
// ❌ Bad: Contradictory
"Be creative and unique, but follow these 10 exact examples"
// ✅ Good: Clear expectations
"Use these examples as style inspiration, but create original slogans"
3. Assuming Context
// ❌ Bad: No context
"What should I do?"
// ✅ Good: Full context
"I'm creating a marketing campaign for a vegan restaurant targeting
young professionals. What social media platforms should I focus on?"
4. Ignoring Token Limits
// ❌ Bad: No MaxTokens limit
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: "Write a comprehensive guide to Go programming",
// Could generate 10,000+ tokens
})
// ✅ Good: Set reasonable limits
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
MaxTokens: 500, // Limit output length
Prompt: "Write a brief introduction to Go programming",
})
5. Not Handling Errors
// ✅ Good: Handle provider-specific errors
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: prompt,
})
if err != nil {
// Check for specific error types
if strings.Contains(err.Error(), "rate_limit") {
log.Println("Rate limit hit, retrying after delay...")
time.Sleep(60 * time.Second)
// Retry logic
} else if strings.Contains(err.Error(), "context_length") {
log.Println("Prompt too long, truncating...")
// Truncate and retry
} else {
log.Fatal(err)
}
}
Provider-Specific Considerations
OpenAI
// GPT-4 is better at complex tasks
model, _ := provider.LanguageModel("gpt-4")
// GPT-3.5-turbo is faster and cheaper
model, _ := provider.LanguageModel("gpt-3.5-turbo")
// o1 models use extended thinking
model, _ := provider.LanguageModel("o1")
// Note: o1 models have different parameters (no temperature, system prompts, etc.)
Anthropic
// Claude excels at long-form content and analysis
model, _ := provider.LanguageModel("claude-sonnet-4-5")
// Use system prompts effectively
system := `You are Claude, an AI assistant created by Anthropic.`
Google
// Gemini models support multimodal input
model, _ := provider.LanguageModel("gemini-2.0-flash")
// Long context windows (up to 1M tokens)
model, _ := provider.LanguageModel("gemini-1.5-pro")
Resources
- Anthropic Prompt Engineering Guide
- OpenAI Prompt Engineering Guide
- Prompt Engineering Guide
- Google Gemini Prompting Guide