# 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.