# Building a Research Document Generator with ADK-Go

This guide walks through building a multi-agent system that researches a topic and produces a comprehensive, well-structured document.

---

## Overview

The research document generator uses a **sequential pipeline** of specialized agents:

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Planner    │───▶│ Researcher  │───▶│   Writer    │───▶│   Editor    │
│             │    │             │    │             │    │             │
│ Creates     │    │ Gathers     │    │ Synthesizes │    │ Polishes    │
│ outline     │    │ sources     │    │ into prose  │    │ final doc   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
```

---

## Project Setup

```bash
mkdir research-agent && cd research-agent
go mod init research-agent
go get google.golang.org/adk
```

---

## Complete Implementation

```go
package main

import (
	"context"
	"log"
	"os"

	"google.golang.org/adk/agent"
	"google.golang.org/adk/agent/llmagent"
	"google.golang.org/adk/agent/sequentialagent"
	"google.golang.org/adk/cmd/launcher/adk"
	"google.golang.org/adk/cmd/launcher/full"
	"google.golang.org/adk/model/gemini"
	"google.golang.org/adk/server/restapi/services"
	"google.golang.org/adk/tool"
	"google.golang.org/adk/tool/functiontool"
	"google.golang.org/adk/tool/geminitool"
	"google.golang.org/genai"
)

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

	// Initialize model
	model, err := gemini.NewModel(ctx, "gemini-3-flash-preview", &genai.ClientConfig{
		APIKey: os.Getenv("GOOGLE_API_KEY"),
	})
	if err != nil {
		log.Fatalf("Failed to create model: %v", err)
	}

	// Build the research pipeline
	pipeline, err := buildResearchPipeline(model)
	if err != nil {
		log.Fatalf("Failed to build pipeline: %v", err)
	}

	// Launch
	l := full.NewLauncher()
	cfg := &adk.Config{AgentLoader: services.NewSingleAgentLoader(pipeline)}
	if err := l.Execute(ctx, cfg, os.Args[1:]); err != nil {
		log.Fatalf("Execution failed: %v", err)
	}
}

func buildResearchPipeline(model *gemini.Model) (agent.Agent, error) {
	// Stage 1: Research Planner
	planner, err := llmagent.New(llmagent.Config{
		Name:        "research_planner",
		Model:       model,
		Description: "Creates a structured research plan and outline",
		Instruction: `You are a research planner. Given a topic, create a comprehensive research plan.

Your output must include:
1. **Research Questions**: 5-7 key questions to investigate
2. **Outline**: Hierarchical document structure with sections and subsections
3. **Search Queries**: Specific search queries to find relevant information
4. **Source Types**: What kinds of sources to prioritize (academic, news, official, etc.)

Format your plan clearly with headers and bullet points.
Be thorough but focused on the most important aspects of the topic.`,
		OutputKey: "research_plan",
	})
	if err != nil {
		return nil, err
	}

	// Stage 2: Researcher (with web search)
	researcher, err := llmagent.New(llmagent.Config{
		Name:        "researcher",
		Model:       model,
		Description: "Gathers information from multiple sources",
		Instruction: `You are a thorough researcher. Using the research plan below, gather comprehensive information.

RESEARCH PLAN:
{research_plan}

Your task:
1. Execute the search queries from the plan
2. Gather facts, statistics, quotes, and evidence
3. Note the source of each piece of information
4. Identify conflicting information or debates
5. Find recent developments and updates

For each section in the outline, compile relevant findings.
Organize your research notes by section.
Include direct quotes where impactful (with attribution).
Flag any gaps where information couldn't be found.`,
		Tools:     []tool.Tool{geminitool.GoogleSearch{}},
		OutputKey: "research_notes",
	})
	if err != nil {
		return nil, err
	}

	// Stage 3: Writer
	writer, err := llmagent.New(llmagent.Config{
		Name:        "writer",
		Model:       model,
		Description: "Synthesizes research into a cohesive document",
		Instruction: `You are an expert writer. Transform the research into a polished document.

RESEARCH PLAN:
{research_plan}

RESEARCH NOTES:
{research_notes}

Writing guidelines:
1. Follow the outline structure from the research plan
2. Synthesize information into flowing prose (no bullet points in body)
3. Use topic sentences and smooth transitions
4. Integrate evidence naturally with proper attribution
5. Maintain objectivity; present multiple perspectives where relevant
6. Include an executive summary at the beginning
7. Add a references section at the end

Target length: Comprehensive but concise (aim for depth over breadth)
Tone: Professional, authoritative, accessible
Format: Use markdown with headers (## for main sections, ### for subsections)`,
		OutputKey: "draft_document",
	})
	if err != nil {
		return nil, err
	}

	// Stage 4: Editor
	editor, err := llmagent.New(llmagent.Config{
		Name:        "editor",
		Model:       model,
		Description: "Reviews and polishes the final document",
		Instruction: `You are a meticulous editor. Review and improve the document.

DRAFT DOCUMENT:
{draft_document}

Editorial checklist:
1. **Clarity**: Simplify complex sentences; remove jargon or define it
2. **Flow**: Ensure logical progression; improve transitions
3. **Accuracy**: Flag any claims that seem unsupported
4. **Completeness**: Note any gaps in coverage
5. **Consistency**: Standardize formatting, terminology, tone
6. **Grammar**: Fix errors in spelling, punctuation, syntax
7. **Engagement**: Strengthen the opening; ensure compelling conclusion

Output the complete, polished document.
Add an "Editor's Note" section at the end with:
- Summary of major changes made
- Any remaining concerns or suggestions for future updates
- Confidence assessment (how well-supported is this document?)`,
		OutputKey: "final_document",
	})
	if err != nil {
		return nil, err
	}

	// Assemble pipeline
	pipeline, err := sequentialagent.New(sequentialagent.Config{
		AgentConfig: agent.Config{
			Name:        "research_document_generator",
			Description: "Researches a topic and produces a comprehensive document",
			SubAgents:   []agent.Agent{planner, researcher, writer, editor},
		},
	})
	if err != nil {
		return nil, err
	}

	return pipeline, nil
}
```

---

## Enhanced Version with Custom Tools

Add tools for saving drafts and managing sources:

```go
// Source tracking tool
type AddSourceInput struct {
	Title  string `json:"title" jsonschema:"Source title"`
	URL    string `json:"url" jsonschema:"Source URL"`
	Type   string `json:"type" jsonschema:"Type: academic, news, official, blog"`
	Quote  string `json:"quote,omitempty" jsonschema:"Key quote from source"`
	Date   string `json:"date,omitempty" jsonschema:"Publication date"`
}

type AddSourceOutput struct {
	SourceID int    `json:"source_id"`
	Message  string `json:"message"`
}

var sourceCounter int
var sources []AddSourceInput

func addSource(ctx tool.Context, input AddSourceInput) AddSourceOutput {
	sourceCounter++
	sources = append(sources, input)

	// Store in session state for other agents
	state := ctx.Session().State()
	state.Set("sources", sources)

	return AddSourceOutput{
		SourceID: sourceCounter,
		Message:  fmt.Sprintf("Added source #%d: %s", sourceCounter, input.Title),
	}
}

// Document section tool
type SaveSectionInput struct {
	Section string `json:"section" jsonschema:"Section name (e.g., 'Introduction')"`
	Content string `json:"content" jsonschema:"Section content in markdown"`
}

type SaveSectionOutput struct {
	Message     string `json:"message"`
	WordCount   int    `json:"word_count"`
	TotalSections int  `json:"total_sections"`
}

var documentSections = make(map[string]string)

func saveSection(ctx tool.Context, input SaveSectionInput) SaveSectionOutput {
	documentSections[input.Section] = input.Content
	wordCount := len(strings.Fields(input.Content))

	// Persist to state
	ctx.Session().State().Set("document_sections", documentSections)

	return SaveSectionOutput{
		Message:       fmt.Sprintf("Saved section: %s", input.Section),
		WordCount:     wordCount,
		TotalSections: len(documentSections),
	}
}

// Create tools
func createResearchTools() ([]tool.Tool, error) {
	sourceTool, err := functiontool.New(
		functiontool.Config{
			Name:        "add_source",
			Description: "Track a source used in research. Call this for every source you reference.",
		},
		addSource,
	)
	if err != nil {
		return nil, err
	}

	sectionTool, err := functiontool.New(
		functiontool.Config{
			Name:        "save_section",
			Description: "Save a completed section of the document",
		},
		saveSection,
	)
	if err != nil {
		return nil, err
	}

	return []tool.Tool{
		geminitool.GoogleSearch{},
		sourceTool,
		sectionTool,
	}, nil
}
```

---

## Adding Parallel Research

For faster research, use parallel agents to investigate different aspects simultaneously:

```go
import "google.golang.org/adk/agent/parallelagent"

func buildParallelResearcher(model *gemini.Model) (agent.Agent, error) {
	// Background/history researcher
	historyResearcher, _ := llmagent.New(llmagent.Config{
		Name:        "history_researcher",
		Model:       model,
		Instruction: `Research the historical background and evolution of: {topic}
Focus on: origins, key milestones, how it developed over time.`,
		Tools:     []tool.Tool{geminitool.GoogleSearch{}},
		OutputKey: "history_research",
	})

	// Current state researcher
	currentResearcher, _ := llmagent.New(llmagent.Config{
		Name:        "current_researcher",
		Model:       model,
		Instruction: `Research the current state and recent developments of: {topic}
Focus on: latest news, current statistics, recent changes.`,
		Tools:     []tool.Tool{geminitool.GoogleSearch{}},
		OutputKey: "current_research",
	})

	// Expert opinions researcher
	expertResearcher, _ := llmagent.New(llmagent.Config{
		Name:        "expert_researcher",
		Model:       model,
		Instruction: `Research expert opinions and analysis on: {topic}
Focus on: thought leaders, academic perspectives, industry experts.`,
		Tools:     []tool.Tool{geminitool.GoogleSearch{}},
		OutputKey: "expert_research",
	})

	// Future outlook researcher
	futureResearcher, _ := llmagent.New(llmagent.Config{
		Name:        "future_researcher",
		Model:       model,
		Instruction: `Research predictions and future outlook for: {topic}
Focus on: trends, forecasts, potential developments.`,
		Tools:     []tool.Tool{geminitool.GoogleSearch{}},
		OutputKey: "future_research",
	})

	// Run all in parallel
	parallel, err := parallelagent.New(parallelagent.Config{
		AgentConfig: agent.Config{
			Name: "parallel_research_team",
			SubAgents: []agent.Agent{
				historyResearcher,
				currentResearcher,
				expertResearcher,
				futureResearcher,
			},
		},
	})

	return parallel, err
}
```

Then modify the main pipeline to use the parallel researcher:

```go
// In buildResearchPipeline, replace single researcher with:
parallelResearcher, err := buildParallelResearcher(model)
if err != nil {
    return nil, err
}

// Update writer instruction to use all research outputs:
writer, err := llmagent.New(llmagent.Config{
    Instruction: `Synthesize all research into a cohesive document.

OUTLINE:
{research_plan}

HISTORICAL BACKGROUND:
{history_research}

CURRENT STATE:
{current_research}

EXPERT ANALYSIS:
{expert_research}

FUTURE OUTLOOK:
{future_research}

Create a comprehensive document that weaves all perspectives together.`,
    // ...
})
```

---

## Running the Agent

```bash
# Console mode - interactive
go run main.go

# Example prompts:
# > Write a research document on the impact of AI on healthcare
# > Create a comprehensive report on renewable energy trends in 2024
# > Research and document the history and future of quantum computing

# Web UI mode - for debugging and visualization
go run main.go web api webui
# Open http://localhost:8080
```

---

## Output Example

For the prompt "Research the impact of remote work on urban planning":

```markdown
# The Impact of Remote Work on Urban Planning

## Executive Summary

The rise of remote work, accelerated by the COVID-19 pandemic, is fundamentally
reshaping urban planning paradigms. This document examines...

## 1. Historical Context

### 1.1 Pre-Pandemic Work Patterns

Before 2020, remote work was limited to approximately 5% of the workforce...

### 1.2 The Pandemic Catalyst

The COVID-19 pandemic forced an unprecedented experiment in remote work...

## 2. Current Urban Impacts

### 2.1 Commercial Real Estate Transformation

Office vacancy rates in major cities have reached historic highs...

### 2.2 Residential Migration Patterns

Data from the U.S. Census Bureau shows significant population shifts...

## 3. Planning Responses

### 3.1 Zoning Adaptations

Cities are revising zoning codes to allow mixed-use developments...

## 4. Future Outlook

...

## References

1. Smith, J. (2024). "Remote Work and Urban Form." Journal of Planning...
2. ...

---

**Editor's Note**: This document synthesizes 23 sources spanning academic
research, government data, and industry reports. Confidence: High for
current trends; Medium for long-term predictions.
```

---

## Best Practices

1. **Be specific with prompts**: "Research quantum computing" is too broad; "Research the current state of quantum error correction and its implications for practical quantum computers" is better.

2. **Monitor intermediate outputs**: Use the web UI to inspect `research_plan`, `research_notes`, etc. to debug issues.

3. **Adjust pipeline stages**: Some topics may need more research depth; others may need multiple editing passes.

4. **Handle long documents**: For very comprehensive documents, consider chunking the writing phase by section.

5. **Add human review points**: Insert a "reviewer" agent or callback that flags sections needing human verification.
