# How Wigolo Decomposes Questions and Synthesizes Cited Reports: A Technical Deep Dive

> Explore how Wigolo decomposes questions and synthesizes reports. Discover its research pipeline for structured, citation-rich outputs using parallel searches and LLM sampling.

- Repository: [Towhid Khan/wigolo](https://github.com/KnockOutEZ/wigolo)
- Tags: deep-dive
- Published: 2026-07-19

---

**Wigolo’s research pipeline transforms free-form user questions into structured, citation-rich reports by decomposing queries into sub-questions, executing parallel searches with time budgets, and synthesizing markdown output through LLM sampling or deterministic fallback methods.**

The open-source project [KnockOutEZ/wigolo](https://github.com/KnockOutEZ/wigolo) implements a sophisticated research agent that breaks down complex inquiries into manageable search operations. This article examines the exact mechanisms the tool uses to parse questions, validate sources, and generate properly cited reports based on the actual TypeScript implementation.

## The Research Pipeline Architecture

At the highest level, the system orchestrates three distinct phases: **question decomposition**, **parallel source retrieval**, and **report synthesis**. The entry point in [`src/tools/research.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/tools/research.ts) exposes the `handleResearch` function, which validates incoming requests and delegates to `runResearchPipeline` in [`src/research/pipeline.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/pipeline.ts). This pipeline returns a `ResearchOutput` object containing the final markdown report, structured citations, and metadata about sub-queries and timing.

The architecture prioritizes resilience. If the primary LLM sampling method fails at any stage, the system cascades through template-based generation, local model inference, or deterministic fallback reports to ensure the user receives a substantive answer.

## Stage 1: Decomposing the Question

The decomposition logic resides in [`src/research/decompose.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/decompose.ts). The `decomposeQuestion` function selects a target sub-query count based on the requested **depth** parameter (`quick`, `standard`, or `comprehensive`), then attempts multiple strategies to generate search queries.

### LLM Sampling Approach

When a `SamplingCapableServer` is available, `decomposeWithSampling` sends a structured prompt requesting exactly *N* concise search queries in JSON format. This method leverages the host LLM’s reasoning capabilities to generate contextually relevant sub-questions that cover different aspects of the original inquiry.

### Template-Based Fallback

If sampling fails or is unavailable, the system invokes `decomposeWithTemplate`. This method uses `detectQueryType` to classify the question as **comparison**, **how-to**, **concept**, or **general**, then selects a predefined template strategy. Templates construct queries around identified entities or tasks, ensuring domain-appropriate search terms.

### Fallback Generator

When templates produce insufficient queries, `decomposeWithFallback` creates additional sub-queries through linguistic analysis. It extracts noun phrases, splits clauses, and generates keyword variants to populate the query list. The final step deduplicates results, optionally prepends the original verbatim question, and returns the list with a flag indicating whether sampling was used.

## Stage 2: Parallel Search and Source Validation

The `exploreInParallel` function in [`src/research/pipeline.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/pipeline.ts) executes each sub-query against a reduced set of search engines simultaneously. This phase operates under strict time constraints defined by `SEARCH_PER_QUERY_BUDGET_MS` and `SEARCH_TOTAL_BUDGET_MS`.

### Time-Budgeted Search Execution

Results from parallel searches undergo merging and deduplication via `deduplicateResults`, followed by filtering based on domain reputation, URL structure, and relevance scores. The `rerankResults` function reorders candidates by quality. An `OVER_FETCH_BUFFER_FACTOR` ensures the pipeline retrieves extra sources to compensate for potential losses during subsequent content validation.

### Content Fetching and Gating

The `fetchSources` function processes candidate URLs through `router.fetch`, which retrieves pages with optional JavaScript rendering. The `getExtractProvider` utility extracts markdown content, titles, and snippets, truncating text to `PER_SOURCE_CHAR_CAP`. Validated content is cached via `cacheContent` and optionally embedded for downstream use.

The **content gate** in [`src/research/source-validation.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/source-validation.ts) (invoked via `gateContent`) filters out shell pages—URLs that return empty or paywalled content. Sources failing validation are tracked as `rejected_sources` and excluded from the synthesis phase.

## Stage 3: Synthesizing the Cited Report

Report generation occurs in [`src/research/synthesize.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/synthesize.ts). The `synthesizeReport` function branches based on the availability of sampling capabilities.

### Sampling-Based Synthesis

When sampling is available, `synthesizeWithSampling` constructs a prompt containing numbered source blocks (formatted as `[1]`, `[2]`, etc.) and instructs the LLM to produce a markdown report with inline citation markers. The response is trimmed to respect `limits.reportChars`, ensuring token budget compliance.

### Deterministic Fallback Reports

If sampling fails, `buildFallbackReport` generates a structured markdown document by iterating over fetched sources. Each entry includes a header, source title, URL, and a trimmed excerpt of the markdown content, respecting both per-source and total character caps.

### Local Model Fallback

When host-sampling is disabled but a local LLM tier or keystore is configured, `synthesizeLocal` attempts deterministic synthesis using the local model. Successful local synthesis replaces the report content and regenerates citations to match the new text.

## Research Brief Construction

If neither host-sampling nor local synthesis produces output, the pipeline falls back to [`src/research/brief.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/brief.ts). The `buildResearchBrief` function aggregates key facts, comparison entities, and any available local-LLM text into a structured document. `renderBriefReport` then converts this brief into a readable markdown format, ensuring users receive an organized summary even when full synthesis is unavailable.

## Programmatic Usage Examples

You can invoke the research tool directly in your TypeScript applications:

```typescript
// Example: invoke the research tool programmatically
import { handleResearch } from './src/tools/research.js';
import { mySearchEngines } from './src/search/engines.js';
import { router } from './src/fetch/router.js';

// Input shape matches the API contract
const input = {
  question: "How does the Rust async runtime compare to Node.js event loop?",
  depth: "standard",
  max_sources: 12,
};

const result = await handleResearch(input, mySearchEngines, router);
if (result.ok) {
  console.log(result.data.report);        // Full markdown report
  console.log(result.data.citations);    // Structured citations
} else {
  console.error(`Research failed: ${result.error_reason}`);
}

```

For testing or custom workflows, run the pipeline directly:

```typescript
// Example: directly run the pipeline (useful for testing)
import { runResearchPipeline } from './src/research/pipeline.js';
import { engines } from './src/search/engines.js';
import { router } from './src/fetch/router.js';

const researchOut = await runResearchPipeline(
  { question: "Explain the benefits of GraphQL over REST", depth: "quick" },
  engines,
  router,
);
console.log(researchOut.report);

```

## Summary

- **Question decomposition** in [`src/research/decompose.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/decompose.ts) uses a cascading strategy: LLM sampling first, then template-based generation by query type (comparison, how-to, concept, general), followed by linguistic fallback extraction.
- **Parallel search execution** respects strict time budgets (`SEARCH_PER_QUERY_BUDGET_MS`, `SEARCH_TOTAL_BUDGET_MS`) and uses an over-fetch buffer to ensure sufficient candidates survive content gating.
- **Source validation** removes shell pages and paywalled content through `gateContent` in [`src/research/source-validation.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/source-validation.ts) before synthesis begins.
- **Report synthesis** prioritizes LLM sampling with citation markers `[1]`, `[2]`, but falls back to deterministic templates or local model inference if sampling fails.
- **Structured output** always returns a `ResearchOutput` containing the markdown report, citation array with URLs and snippets, source objects, and metadata about rejected sources and sub-queries.

## Frequently Asked Questions

### How does Wigolo handle complex comparison questions?

Wigolo detects comparison queries through `detectQueryType` in [`src/research/decompose.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/decompose.ts) and selects a comparison-specific template. This template generates sub-queries targeting each entity in the comparison separately, then synthesizes results into a unified report with citations from both sides.

### What happens when the LLM sampling service is unavailable?

The system cascades through three fallback layers: first, template-based decomposition using query-type heuristics; second, local model synthesis if configured; third, deterministic fallback reports that structure source excerpts into markdown. This ensures users receive a substantive answer even without sampling capabilities.

### How does the research tool prevent citation of low-quality sources?

Sources undergo multi-stage validation. The `deduplicateResults` and `rerankResults` functions in [`src/research/pipeline.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/pipeline.ts) filter by domain and relevance score. The `gateContent` function in [`src/research/source-validation.ts`](https://github.com/KnockOutEZ/wigolo/blob/main/src/research/source-validation.ts) removes shell pages and empty content. Only validated sources with actual markdown content proceed to the synthesis phase.

### Can I customize the depth of research performed?

Yes. The `depth` parameter accepts `quick`, `standard`, or `comprehensive`, which determines the target number of sub-queries generated by `decomposeQuestion`. Deeper settings increase parallel search breadth and source limits, producing more thorough reports at the cost of higher latency and token consumption.