Workflow Patterns
Combine the building blocks from the overview with these patterns to add structure and reliability to your agents:
- Sequential Processing - Steps executed in order
- Parallel Processing - Independent tasks run simultaneously
- Evaluation/Feedback Loops - Results checked and improved iteratively
- Orchestration - Coordinating multiple components
- Routing - Directing work based on context
Choose Your Approach
Consider these key factors:
- Flexibility vs Control - How much freedom does the LLM need vs how tightly you must constrain its actions?
- Error Tolerance - What are the consequences of mistakes in your use case?
- Cost Considerations - More complex systems typically mean more LLM calls and higher costs
- Maintenance - Simpler architectures are easier to debug and modify
Start with the simplest approach that meets your needs. Add complexity only when required by:
- Breaking down tasks into clear steps
- Adding tools for specific capabilities
- Implementing feedback loops for quality control
- Introducing multiple agents for complex workflows
Let's look at examples of these patterns in action.
Patterns with Examples
These patterns, adapted from Anthropic's guide on building effective agents, serve as building blocks you can combine to create comprehensive workflows. Each pattern addresses specific aspects of task execution. Combine them thoughtfully to build reliable solutions for complex problems.
Sequential Processing (Chains)
The simplest workflow pattern executes steps in a predefined order. Each step's output becomes input for the next step, creating a clear chain of operations. Use this pattern for tasks with well-defined sequences, like content generation pipelines or data transformation processes.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type QualityMetrics struct {
HasCallToAction bool `json:"hasCallToAction"`
EmotionalAppeal float64 `json:"emotionalAppeal"`
Clarity float64 `json:"clarity"`
}
func generateMarketingCopy(ctx context.Context, input string) (string, QualityMetrics, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Step 1: Generate marketing copy
copyResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: fmt.Sprintf("Write persuasive marketing copy for: %s. Focus on benefits and emotional appeal.", input),
})
if err != nil {
return "", QualityMetrics{}, err
}
copy := copyResult.Text
// Step 2: Perform quality check on copy
qualityResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"hasCallToAction": map[string]interface{}{
"type": "boolean",
"description": "Whether the copy has a clear call to action",
},
"emotionalAppeal": map[string]interface{}{
"type": "number",
"description": "Emotional appeal rating from 1-10",
"minimum": 1,
"maximum": 10,
},
"clarity": map[string]interface{}{
"type": "number",
"description": "Clarity rating from 1-10",
"minimum": 1,
"maximum": 10,
},
},
"required": []string{"hasCallToAction", "emotionalAppeal", "clarity"},
}),
Prompt: fmt.Sprintf(`Evaluate this marketing copy for:
1. Presence of call to action (true/false)
2. Emotional appeal (1-10)
3. Clarity (1-10)
Copy to evaluate: %s`, copy),
})
if err != nil {
return "", QualityMetrics{}, err
}
var metrics QualityMetrics
objBytes, err := json.Marshal(qualityResult.Object)
if err != nil {
return "", QualityMetrics{}, err
}
if err := json.Unmarshal(objBytes, &metrics); err != nil {
return "", QualityMetrics{}, err
}
// Step 3: If quality check fails, regenerate with more specific instructions
if !metrics.HasCallToAction || metrics.EmotionalAppeal < 7 || metrics.Clarity < 7 {
improvements := ""
if !metrics.HasCallToAction {
improvements += "- A clear call to action\n"
}
if metrics.EmotionalAppeal < 7 {
improvements += "- Stronger emotional appeal\n"
}
if metrics.Clarity < 7 {
improvements += "- Improved clarity and directness\n"
}
improvedResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: fmt.Sprintf(`Rewrite this marketing copy with:
%s
Original copy: %s`, improvements, copy),
})
if err != nil {
return "", QualityMetrics{}, err
}
return improvedResult.Text, metrics, nil
}
return copy, metrics, nil
}
func main() {
ctx := context.Background()
copy, metrics, err := generateMarketingCopy(ctx, "Revolutionary new AI-powered productivity app")
if err != nil {
log.Fatal(err)
}
fmt.Println("Final Copy:", copy)
fmt.Printf("Quality Metrics: CTA=%v, Emotional=%v, Clarity=%v\n",
metrics.HasCallToAction, metrics.EmotionalAppeal, metrics.Clarity)
}
Routing
This pattern lets the model decide which path to take through a workflow based on context and intermediate results. The model acts as an intelligent router, directing the flow of execution between different branches of your workflow. Use this when handling varied inputs that require different processing approaches. In the example below, the first LLM call's results determine the second call's model size and system prompt.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type Classification struct {
Reasoning string `json:"reasoning"`
Type string `json:"type"` // "general", "refund", or "technical"
Complexity string `json:"complexity"` // "simple" or "complex"
}
func handleCustomerQuery(ctx context.Context, query string) (string, Classification, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Step 1: Classify the query type
classificationResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"reasoning": map[string]interface{}{
"type": "string",
"description": "Brief reasoning for classification",
},
"type": map[string]interface{}{
"type": "string",
"enum": []string{"general", "refund", "technical"},
"description": "The type of customer query",
},
"complexity": map[string]interface{}{
"type": "string",
"enum": []string{"simple", "complex"},
"description": "The complexity level of the query",
},
},
"required": []string{"reasoning", "type", "complexity"},
}),
Prompt: fmt.Sprintf(`Classify this customer query:
%s
Determine:
1. Query type (general, refund, or technical)
2. Complexity (simple or complex)
3. Brief reasoning for classification`, query),
})
if err != nil {
return "", Classification{}, err
}
var classification Classification
classificationBytes, err := json.Marshal(classificationResult.Object)
if err != nil {
return "", Classification{}, err
}
if err := json.Unmarshal(classificationBytes, &classification); err != nil {
return "", Classification{}, err
}
// Step 2: Route based on classification
// Set model based on complexity
var responseModel string
if classification.Complexity == "simple" {
responseModel = "gpt-4o-mini"
} else {
responseModel = "o4-mini"
}
// Set system prompt based on query type
systemPrompts := map[string]string{
"general": "You are an expert customer service agent handling general inquiries.",
"refund": "You are a customer service agent specializing in refund requests. Follow company policy and collect necessary information.",
"technical": "You are a technical support specialist with deep product knowledge. Focus on clear step-by-step troubleshooting.",
}
routedModel, _ := provider.LanguageModel(responseModel)
responseResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: routedModel,
System: systemPrompts[classification.Type],
Prompt: query,
})
if err != nil {
return "", Classification{}, err
}
return responseResult.Text, classification, nil
}
func main() {
ctx := context.Background()
query := "I need help resetting my password and it's not working"
response, classification, err := handleCustomerQuery(ctx, query)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Classification: %s (%s)\n", classification.Type, classification.Complexity)
fmt.Printf("Reasoning: %s\n", classification.Reasoning)
fmt.Printf("\nResponse:\n%s\n", response)
}
Parallel Processing
Break down tasks into independent subtasks that execute simultaneously. This pattern uses parallel execution to improve efficiency while maintaining the benefits of structured workflows. For example, analyze multiple documents or process different aspects of a single input concurrently (like code review).
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"sync"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type SecurityReview struct {
Vulnerabilities []string `json:"vulnerabilities"`
RiskLevel string `json:"riskLevel"` // "low", "medium", or "high"
Suggestions []string `json:"suggestions"`
}
type PerformanceReview struct {
Issues []string `json:"issues"`
Impact string `json:"impact"` // "low", "medium", or "high"
Optimizations []string `json:"optimizations"`
}
type MaintainabilityReview struct {
Concerns []string `json:"concerns"`
QualityScore float64 `json:"qualityScore"` // 1-10
Recommendations []string `json:"recommendations"`
}
func parallelCodeReview(ctx context.Context, code string) (string, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Run parallel reviews using goroutines
var wg sync.WaitGroup
var securityReview SecurityReview
var performanceReview PerformanceReview
var maintainabilityReview MaintainabilityReview
errChan := make(chan error, 3)
// Security review
wg.Add(1)
go func() {
defer wg.Done()
result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: "You are an expert in code security. Focus on identifying security vulnerabilities, injection risks, and authentication issues.",
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"vulnerabilities": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
"riskLevel": map[string]interface{}{
"type": "string",
"enum": []string{"low", "medium", "high"},
},
"suggestions": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
},
"required": []string{"vulnerabilities", "riskLevel", "suggestions"},
}),
Prompt: fmt.Sprintf("Review this code:\n%s", code),
})
if err != nil {
errChan <- err
return
}
securityBytes, err := json.Marshal(result.Object)
if err != nil {
errChan <- err
return
}
if err := json.Unmarshal(securityBytes, &securityReview); err != nil {
errChan <- err
}
}()
// Performance review
wg.Add(1)
go func() {
defer wg.Done()
result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: "You are an expert in code performance. Focus on identifying performance bottlenecks, memory leaks, and optimization opportunities.",
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"issues": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
"impact": map[string]interface{}{
"type": "string",
"enum": []string{"low", "medium", "high"},
},
"optimizations": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
},
"required": []string{"issues", "impact", "optimizations"},
}),
Prompt: fmt.Sprintf("Review this code:\n%s", code),
})
if err != nil {
errChan <- err
return
}
performanceBytes, err := json.Marshal(result.Object)
if err != nil {
errChan <- err
return
}
if err := json.Unmarshal(performanceBytes, &performanceReview); err != nil {
errChan <- err
}
}()
// Maintainability review
wg.Add(1)
go func() {
defer wg.Done()
result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: "You are an expert in code quality. Focus on code structure, readability, and adherence to best practices.",
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"concerns": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
"qualityScore": map[string]interface{}{
"type": "number",
"minimum": 1,
"maximum": 10,
},
"recommendations": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
},
"required": []string{"concerns", "qualityScore", "recommendations"},
}),
Prompt: fmt.Sprintf("Review this code:\n%s", code),
})
if err != nil {
errChan <- err
return
}
maintainabilityBytes, err := json.Marshal(result.Object)
if err != nil {
errChan <- err
return
}
if err := json.Unmarshal(maintainabilityBytes, &maintainabilityReview); err != nil {
errChan <- err
}
}()
// Wait for all reviews to complete
wg.Wait()
close(errChan)
// Check for errors
for err := range errChan {
if err != nil {
return "", err
}
}
// Aggregate results
reviews := fmt.Sprintf(`Security Review:
- Risk Level: %s
- Vulnerabilities: %v
- Suggestions: %v
Performance Review:
- Impact: %s
- Issues: %v
- Optimizations: %v
Maintainability Review:
- Quality Score: %.1f/10
- Concerns: %v
- Recommendations: %v`,
securityReview.RiskLevel, securityReview.Vulnerabilities, securityReview.Suggestions,
performanceReview.Impact, performanceReview.Issues, performanceReview.Optimizations,
maintainabilityReview.QualityScore, maintainabilityReview.Concerns, maintainabilityReview.Recommendations)
// Generate summary
summaryResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are a technical lead summarizing multiple code reviews.",
Prompt: fmt.Sprintf("Synthesize these code review results into a concise summary with key actions:\n%s", reviews),
})
if err != nil {
return "", err
}
return summaryResult.Text, nil
}
func main() {
ctx := context.Background()
code := `
func processPayment(amount float64, cardNumber string) error {
query := fmt.Sprintf("SELECT * FROM payments WHERE card='%s'", cardNumber)
db.Exec(query)
return nil
}
`
summary, err := parallelCodeReview(ctx, code)
if err != nil {
log.Fatal(err)
}
fmt.Println("=== Code Review Summary ===")
fmt.Println(summary)
}
Orchestrator-Worker
A primary model (orchestrator) coordinates the execution of specialized workers. Each worker optimizes for a specific subtask, while the orchestrator maintains overall context and ensures coherent results. This pattern excels at complex tasks requiring different types of expertise or processing.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"sync"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type FileChange struct {
Purpose string `json:"purpose"`
FilePath string `json:"filePath"`
ChangeType string `json:"changeType"` // "create", "modify", or "delete"
}
type ImplementationPlan struct {
Files []FileChange `json:"files"`
EstimatedComplexity string `json:"estimatedComplexity"` // "low", "medium", or "high"
}
type CodeImplementation struct {
Explanation string `json:"explanation"`
Code string `json:"code"`
}
type FileImplementation struct {
File FileChange
Implementation CodeImplementation
}
func implementFeature(ctx context.Context, featureRequest string) (ImplementationPlan, []FileImplementation, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Orchestrator: Plan the implementation
planResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: "You are a senior software architect planning feature implementations.",
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"files": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"purpose": map[string]interface{}{
"type": "string",
"description": "The purpose of this file change",
},
"filePath": map[string]interface{}{
"type": "string",
"description": "Path to the file",
},
"changeType": map[string]interface{}{
"type": "string",
"enum": []string{"create", "modify", "delete"},
},
},
"required": []string{"purpose", "filePath", "changeType"},
},
},
"estimatedComplexity": map[string]interface{}{
"type": "string",
"enum": []string{"low", "medium", "high"},
},
},
"required": []string{"files", "estimatedComplexity"},
}),
Prompt: fmt.Sprintf("Analyze this feature request and create an implementation plan:\n%s", featureRequest),
})
if err != nil {
return ImplementationPlan{}, nil, err
}
var plan ImplementationPlan
planBytes, err := json.Marshal(planResult.Object)
if err != nil {
return ImplementationPlan{}, nil, err
}
if err := json.Unmarshal(planBytes, &plan); err != nil {
return ImplementationPlan{}, nil, err
}
fmt.Printf("Plan created: %d files to change (Complexity: %s)\n", len(plan.Files), plan.EstimatedComplexity)
// Workers: Execute the planned changes in parallel
var wg sync.WaitGroup
fileChanges := make([]FileImplementation, len(plan.Files))
errChan := make(chan error, len(plan.Files))
for i, file := range plan.Files {
wg.Add(1)
go func(idx int, f FileChange) {
defer wg.Done()
// Each worker is specialized for the type of change
systemPrompts := map[string]string{
"create": "You are an expert at implementing new files following best practices and project patterns.",
"modify": "You are an expert at modifying existing code while maintaining consistency and avoiding regressions.",
"delete": "You are an expert at safely removing code while ensuring no breaking changes.",
}
changeResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: systemPrompts[f.ChangeType],
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"explanation": map[string]interface{}{
"type": "string",
"description": "Explanation of the changes",
},
"code": map[string]interface{}{
"type": "string",
"description": "The implementation code",
},
},
"required": []string{"explanation", "code"},
}),
Prompt: fmt.Sprintf(`Implement the changes for %s to support:
%s
Consider the overall feature context:
%s`, f.FilePath, f.Purpose, featureRequest),
})
if err != nil {
errChan <- err
return
}
var impl CodeImplementation
implBytes, err := json.Marshal(changeResult.Object)
if err != nil {
errChan <- err
return
}
if err := json.Unmarshal(implBytes, &impl); err != nil {
errChan <- err
return
}
fileChanges[idx] = FileImplementation{
File: f,
Implementation: impl,
}
fmt.Printf("✓ Completed %s: %s\n", f.ChangeType, f.FilePath)
}(i, file)
}
wg.Wait()
close(errChan)
// Check for errors
for err := range errChan {
if err != nil {
return ImplementationPlan{}, nil, err
}
}
return plan, fileChanges, nil
}
func main() {
ctx := context.Background()
featureRequest := "Add user authentication with JWT tokens, including login, logout, and token refresh endpoints"
plan, changes, err := implementFeature(ctx, featureRequest)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n=== Implementation Plan ===\n")
fmt.Printf("Complexity: %s\n", plan.EstimatedComplexity)
fmt.Printf("Files to change: %d\n\n", len(changes))
for _, change := range changes {
fmt.Printf("=== %s: %s ===\n", change.File.ChangeType, change.File.FilePath)
fmt.Printf("Purpose: %s\n", change.File.Purpose)
fmt.Printf("Explanation: %s\n", change.Implementation.Explanation)
fmt.Printf("Code:\n%s\n\n", change.Implementation.Code)
}
}
Evaluator-Optimizer
Add quality control to workflows with dedicated evaluation steps that assess intermediate results. Based on the evaluation, the workflow proceeds, retries with adjusted parameters, or takes corrective action. This creates robust workflows capable of self-improvement and error recovery.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type TranslationEvaluation struct {
QualityScore float64 `json:"qualityScore"` // 1-10
PreservesTone bool `json:"preservesTone"`
PreservesNuance bool `json:"preservesNuance"`
CulturallyAccurate bool `json:"culturallyAccurate"`
SpecificIssues []string `json:"specificIssues"`
ImprovementSuggestions []string `json:"improvementSuggestions"`
}
func translateWithFeedback(ctx context.Context, text, targetLanguage string) (string, int, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
const maxIterations = 3
iterations := 0
// Initial translation
translationResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are an expert literary translator.",
Prompt: fmt.Sprintf("Translate this text to %s, preserving tone and cultural nuances:\n%s", targetLanguage, text),
})
if err != nil {
return "", 0, err
}
currentTranslation := translationResult.Text
// Evaluation-optimization loop
for iterations < maxIterations {
fmt.Printf("Iteration %d: Evaluating translation...\n", iterations+1)
// Evaluate current translation
evalResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
System: "You are an expert in evaluating literary translations.",
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"qualityScore": map[string]interface{}{
"type": "number",
"minimum": 1,
"maximum": 10,
"description": "Overall quality score",
},
"preservesTone": map[string]interface{}{
"type": "boolean",
"description": "Whether the translation preserves the original tone",
},
"preservesNuance": map[string]interface{}{
"type": "boolean",
"description": "Whether the translation preserves subtle nuances",
},
"culturallyAccurate": map[string]interface{}{
"type": "boolean",
"description": "Whether the translation is culturally accurate",
},
"specificIssues": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
"description": "Specific issues found in the translation",
},
"improvementSuggestions": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
"description": "Suggestions for improving the translation",
},
},
"required": []string{"qualityScore", "preservesTone", "preservesNuance", "culturallyAccurate", "specificIssues", "improvementSuggestions"},
}),
Prompt: fmt.Sprintf(`Evaluate this translation:
Original: %s
Translation: %s
Consider:
1. Overall quality
2. Preservation of tone
3. Preservation of nuance
4. Cultural accuracy`, text, currentTranslation),
})
if err != nil {
return "", 0, err
}
var evaluation TranslationEvaluation
evalBytes, err := json.Marshal(evalResult.Object)
if err != nil {
return "", 0, err
}
if err := json.Unmarshal(evalBytes, &evaluation); err != nil {
return "", 0, err
}
fmt.Printf("Quality Score: %.1f/10\n", evaluation.QualityScore)
// Check if quality meets threshold
if evaluation.QualityScore >= 8 &&
evaluation.PreservesTone &&
evaluation.PreservesNuance &&
evaluation.CulturallyAccurate {
fmt.Println("Translation meets quality standards!")
break
}
// Generate feedback for improvement
feedback := ""
for _, issue := range evaluation.SpecificIssues {
feedback += fmt.Sprintf("- Issue: %s\n", issue)
}
for _, suggestion := range evaluation.ImprovementSuggestions {
feedback += fmt.Sprintf("- Suggestion: %s\n", suggestion)
}
fmt.Printf("Issues found, improving translation...\n%s\n", feedback)
// Generate improved translation based on feedback
improvedResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are an expert literary translator.",
Prompt: fmt.Sprintf(`Improve this translation based on the following feedback:
%s
Original: %s
Current Translation: %s`, feedback, text, currentTranslation),
})
if err != nil {
return "", 0, err
}
currentTranslation = improvedResult.Text
iterations++
}
return currentTranslation, iterations, nil
}
func main() {
ctx := context.Background()
text := "The ancient temple stood silently, a testament to forgotten wisdom and the passage of time."
targetLanguage := "Japanese"
translation, iterations, err := translateWithFeedback(ctx, text, targetLanguage)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n=== Final Translation ===\n")
fmt.Printf("Original: %s\n", text)
fmt.Printf("Translation: %s\n", translation)
fmt.Printf("Iterations required: %d\n", iterations)
}
Combining Patterns
Real-world applications often combine multiple patterns. Here's an example that uses routing, parallel processing, and sequential steps:
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"sync"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
"github.com/digitallysavvy/go-ai/pkg/schema"
)
type TaskType struct {
Type string `json:"type"` // "research", "code", or "content"
Complexity string `json:"complexity"` // "simple" or "complex"
}
func intelligentTaskProcessor(ctx context.Context, task string) (string, error) {
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
})
model, _ := provider.LanguageModel("gpt-4")
// Step 1: Route - Classify the task
classificationResult, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{
Model: model,
Schema: schema.NewSimpleJSONSchema(map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"type": map[string]interface{}{
"type": "string",
"enum": []string{"research", "code", "content"},
},
"complexity": map[string]interface{}{
"type": "string",
"enum": []string{"simple", "complex"},
},
},
"required": []string{"type", "complexity"},
}),
Prompt: fmt.Sprintf("Classify this task:\n%s", task),
})
if err != nil {
return "", err
}
var taskType TaskType
taskTypeBytes, err := json.Marshal(classificationResult.Object)
if err != nil {
return "", err
}
if err := json.Unmarshal(taskTypeBytes, &taskType); err != nil {
return "", err
}
fmt.Printf("Task classified as: %s (%s)\n", taskType.Type, taskType.Complexity)
// Step 2: Parallel - Process based on type
var result string
switch taskType.Type {
case "research":
// Parallel research from multiple angles
var wg sync.WaitGroup
results := make([]string, 3)
errChan := make(chan error, 3)
angles := []string{"technical", "business", "user"}
for i, angle := range angles {
wg.Add(1)
go func(idx int, perspective string) {
defer wg.Done()
r, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: fmt.Sprintf("You are a %s expert.", perspective),
Prompt: fmt.Sprintf("Research this from a %s perspective:\n%s", perspective, task),
})
if err != nil {
errChan <- err
return
}
results[idx] = r.Text
}(i, angle)
}
wg.Wait()
close(errChan)
for err := range errChan {
if err != nil {
return "", err
}
}
// Synthesize
synthResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: fmt.Sprintf("Synthesize these research findings:\n%s\n%s\n%s", results[0], results[1], results[2]),
})
if err != nil {
return "", err
}
result = synthResult.Text
case "code":
// Sequential code generation and review
codeResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are an expert programmer.",
Prompt: fmt.Sprintf("Implement this:\n%s", task),
})
if err != nil {
return "", err
}
reviewResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are a code reviewer.",
Prompt: fmt.Sprintf("Review this code and suggest improvements:\n%s", codeResult.Text),
})
if err != nil {
return "", err
}
result = fmt.Sprintf("Code:\n%s\n\nReview:\n%s", codeResult.Text, reviewResult.Text)
case "content":
// Sequential content creation with quality check
contentResult, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
System: "You are a professional content writer.",
Prompt: task,
})
if err != nil {
return "", err
}
result = contentResult.Text
}
return result, nil
}
func main() {
ctx := context.Background()
tasks := []string{
"Research the impact of quantum computing on cryptography",
"Write a function to validate email addresses in Go",
"Create engaging social media content for a new productivity app",
}
for _, task := range tasks {
fmt.Printf("\n=== Processing Task ===\n%s\n\n", task)
result, err := intelligentTaskProcessor(ctx, task)
if err != nil {
log.Printf("Error processing task: %v", err)
continue
}
fmt.Printf("Result:\n%s\n", result)
}
}
Best Practices
1. Start Simple
Begin with the simplest pattern that solves your problem:
// Good - Simple sequential for straightforward tasks
func processDocument(ctx context.Context, doc string) (string, error) {
summary, err := generateSummary(ctx, doc)
if err != nil {
return "", err
}
return enhanceSummary(ctx, summary)
}
2. Use Parallel Processing for Independent Tasks
Run independent operations concurrently:
// Good - Parallel for independent analyses
var wg sync.WaitGroup
results := make([]string, 3)
for i, task := range independentTasks {
wg.Add(1)
go func(idx int, t string) {
defer wg.Done()
results[idx], _ = processTask(ctx, t)
}(i, task)
}
wg.Wait()
3. Add Error Handling
Always handle errors properly in workflows:
// Good - Proper error handling with context
errChan := make(chan error, len(tasks))
for _, task := range tasks {
go func(t string) {
if err := process(ctx, t); err != nil {
errChan <- err
}
}(task)
}
for err := range errChan {
if err != nil {
return fmt.Errorf("workflow failed: %w", err)
}
}
4. Use Context for Cancellation
Respect context cancellation in all steps:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// All operations respect this context
result, err := multiStepWorkflow(ctx, input)
5. Monitor and Debug
Add logging for complex workflows:
func processWithLogging(ctx context.Context, input string) (string, error) {
log.Printf("Starting workflow for input: %s", input)
result, err := step1(ctx, input)
if err != nil {
log.Printf("Step 1 failed: %v", err)
return "", err
}
log.Printf("Step 1 completed")
// Continue with more steps...
return result, nil
}
Next Steps
- Learn about loop control for advanced agent execution control
- See configuring call options for fine-tuning agent behavior
- Explore error handling for robust workflows