Deep Research Tool
A five-phase research pipeline that produces source-grounded, two-sided, newspaper-style HTML reports.
The Deep Research Tool turns a one-line topic into a polished, defensible research report. It combines three ideas that are usually kept apart:
- Deterministic source discovery — the breadth and diversity of sources is enforced by code, not left to an LLM’s whim.
- Multi-agent synthesis — parallel workers fetch, verify, and extract claims from every source before anything is written.
- Strict authenticity screening — AI-generated content and anonymous blog posts never reach the final report.
Every report ends with a future-work section, every numeric claim carries a clickable citation, and the output is a single self-contained HTML file with a newspaper aesthetic.
When to Use It
| Use it for | Don’t use it for |
|---|---|
| Topics needing genuine depth and breadth (15+ sources) | Quick lookups a single web search answers |
| Commercial asks: who are the players, what does it cost, how is it made | Time-critical questions (the pipeline is deliberately not time-bound) |
| Topics with real controversy or two defensible sides | Specialized domains owned by other tools (medical evidence, ticker evaluation) |
The Pipeline
Five phases. An orchestrator coordinates; determinism where it counts, LLM judgment where it matters.
Architecture
┌──────────────────────────────────────────────────────────────────────┐
│ ORCHESTRATOR (single agent) │
│ • Runs Phase 1 deterministically (no LLM in discovery) │
│ • Delegates Phases 2-5 as parallel/sequential subagents │
│ • Validates every phase output against quality gates │
│ • Not time-bound — retries weak phases until gates pass │
└──────────────┬───────────────────────────────────────────────────────┘
│
┌───────────▼───────────┐ ┌────────────────────────────────────┐
│ PHASE 1 (deterministic)│───▶│ PHASE 2 (parallel subagents) │
│ Source Discovery │ │ Verification & Synthesis │
│ 10 query angles │ │ each agent: fetch → verify → │
│ dedup + origin classify│ │ extract claims → JSON │
└────────────────────────┘ └────────────────┬───────────────────┘
│
┌─────────────────────────▼──────────────────┐
│ PHASE 3-4 (conditional, subagent) │
│ Steelman + counter ONLY for contested │
│ propositions; surveys get a single │
│ balanced debate section instead │
└─────────────────────────┬──────────────────┘
│
┌─────────────────────────▼──────────────────┐
│ PHASE 5 (subagent → deterministic renderer)│
│ digest JSON → grounded sections → │
│ newspaper HTML (renderer is code, not LLM) │
└────────────────────────────────────────────┘
Why Deterministic Discovery
Source breadth is a numbers game — an LLM asked to “find diverse sources” will silently converge on a handful of familiar domains. The tool instead issues a fixed set of query angles (overview, primary research, history, current state, mechanism, criticism, opposition, regulatory, economics, future) and enforces deduplication, origin-type counting, and adversarial coverage in code. Two of the ten angles are deliberately adversarial, so counter-evidence is never an afterthought.
Where LLMs Are Used
Phase 2 — reading, verifying, and extracting from actual pages (fetching the page is mandatory; snippets are not trusted).
Phases 3-4 — writing the strongest steelman and counter when the topic is a contested proposition.
Phase 5 — assembling the digest JSON from verified material.
Nowhere else. Discovery, dedup, classification, and HTML rendering are code.
Phase 1 — Source Discovery
Deterministic. No LLM. Runs as a script in seconds.
python3 deep_research_sources.py "<topic>" [--out sources.json] [--min-sources 15] [--max-queries 8]
The Ten Query Angles
| Angle | Template intent | Adversarial? |
|---|---|---|
| overview | Comprehensive guide to the topic | no |
| primary_research | Studies, research papers, findings | no |
| history | Origin and timeline | no |
| current_state | Present data and status | no |
| mechanism | How it works, explained | no |
| criticism | Problems and limitations | yes |
| opposition | “Overrated / fails / does not work” | yes |
| regulatory | Regulation, policy, risk, safety | no |
| economics | Cost, market, economics, industry | no |
| future | Outlook and forecast | no |
What the Script Enforces
- Deduplication — by domain and by content hash, so syndicated copies of the same article count once.
- Origin-type classification — academic, government, industry, trade press, advocacy, reference. The report must span at least four types.
- Adversarial floor — at least two sources must come from criticism/opposition angles.
- Trust tiers — established organizations are auto-trusted; blog platforms require named authors; unknown domains are tier-3.
- Noise exclusion — search engines, Q&A aggregators, and content farms never count toward the pool.
- Hard floor — the script exits non-zero if fewer than 15 independent sources are found, so weak discovery can never silently proceed.
Phase 2 — Verification & Synthesis
Parallel subagents, one batch per ~5 sources. The authenticity gate lives here.
Sources are split into batches and each batch goes to one subagent. All batches run in parallel in a single delegation call. Every agent performs the same mandatory sequence per source:
FOR EACH SOURCE IN BATCH:
1. FETCH the actual page (never trust the search snippet)
2. CHECK body for AI-generation markers
"as an AI language model" · "my knowledge cutoff"
"generated by AI" · "AI-assisted" · generic filler
┌── found? ──▶ verdict = reject_ai
└── clean? ▼
3. VERIFY authorship
named human byline ─────────▶ pass
tier-1 org domain (journal,
government, institution) ────▶ pass
"Admin" / "Editorial Team" /
no byline on blog ───────────▶ verdict = reject_unauthored
4. CONFIRM the page loads (404 / dead → reject_dead)
5. For VERIFIED sources: extract claims,
evidence grade (primary/secondary/opinion),
key numbers, bias flags
──▶ emit JSON for ALL sources, including rejects
Output Shape
[{
"url": "...",
"verdict": "verified | reject_ai | reject_unauthored | reject_dead",
"author": "Full Name or Institution",
"verification_note": "why trusted or rejected",
"claims": [...],
"evidence_grade": "primary | secondary | opinion",
"key_numbers": [...],
"bias_flags": [...]
}]
Phases 3-4 — Argumentation
Steelman and counter only when the topic is a contested proposition. Otherwise: one balanced debate section.
The Orchestrator’s Decision
| Topic shape | Treatment |
|---|---|
| Contested proposition “is X the future of Y”, “should Z be deployed”, “is X obsolete” | Phase 3 steelman agent (strongest pro case, charitably reading every objection) + Phase 4 counter agent (point-by-point rebuttal) |
| Survey / descriptive topic how X works, who makes X, what X costs, map of a domain | No steelman/counter. One single balanced “The Debate” section at the end of the report, presenting both sides compactly from the adversarial sources already collected |
Why
A research report on a general topic is a map, not an argument. Leading with a steelman presumes the topic is a claim to defend, which distorts surveys into debates they never were. The conditional design keeps the debate tool available exactly where it adds value — contested propositions — and out of the way everywhere else.
Both argument phases receive the full synthesis outputs. The steelman must be genuinely strong (never a strawman), and the counter must engage the steelman point-by-point rather than restating general skepticism.
Phase 5 — Report Assembly
Digest JSON → deterministic HTML renderer. Claims are grounded; visuals are included.
Claim Grounding
Every finding in every section carries refs — indices into the verified source appendix. A claim without a citation is unverified and is routed to the Issues section instead of the body. Every numeric figure must have a reference. In the rendered HTML, each finding shows superscript markers ([n]) that link directly to the corresponding source card.
Visuals Built In
- Diagrams — mermaid flowcharts, block diagrams, and architecture graphs, rendered client-side. Any process flow, BOM structure, supply chain, or causal chain gets a diagram.
- Tables — player comparisons, cost breakdowns, failure modes, segment splits. Styled HTML tables with captions.
- Images — external public-domain images with captions and graceful failure handling if a link breaks.
Renderer Output
┌─────────────────────────────────────────────────────┐ │ MASTHEAD HERMES DEEP RESEARCH · date · edition │ ├─────────────────────────────────────────────────────┤ │ TITLE + SUBTITLE │ │ STATS BAR 22 sources · 6 origin types · 2 adv. │ │ CALL-OUTS warnings, data gaps, key takeaways │ │ SUMMARY + VERDICT │ │ SECTIONS 1..N with findings, tables, diagrams, │ │ quotes, and in-text [n] citations │ │ [optional steelman / counter columns] │ │ [or single "The Debate" section] │ │ METAPHORS "explain it like I'm 12" layer │ │ ISSUES rejected sources + research caveats │ │ SOURCE APPENDIX numbered cards, ✓ verified badges, │ │ author lines, links │ │ FUTURE WORK & NEXT STEPS │ └─────────────────────────────────────────────────────┘
python3 deep_research_html.py digest.json --out report.html
Source Integrity
Real sources, real authors, no AI content, no fake bylines — enforced by two layers and guarded by gates.
Two-Layer Defense
Layer 1 — Deterministic (Phase 1)
| Signal | Action |
|---|---|
| AI-boilerplate title patterns (25+ markers) | Reject |
| Known AI-content-farm domains (12+) | Reject |
| Established organization domains (80+ tier-1: journals, government, institutions) | Auto-trust |
| Blog platforms (Medium, Substack, etc.) | Tier-2 — named author required in Phase 2 |
| Unknown domains / SEO-slop patterns | Tier-3 — full verification in Phase 2 |
| Search engines, Q&A sites, aggregators | Never counted as sources |
Layer 2 — Page-Level Verification (Phase 2)
- Fetch the actual page — search snippets rarely carry AI markers; the boilerplate lives in the body.
- Scan body text for AI-generation markers and generic filler.
- Require a real named human byline, or a tier-1 organization as author. “Admin”, “Editorial Team”, or no byline on a blog → reject.
- Dead links / 404s → reject.
Auditability as the Backstop
Quality Gates
Delivery is blocked until every gate passes. Depth and accuracy beat speed.
Phase 1 Gates (deterministic)
| Gate | Threshold |
|---|---|
| Independent sources | ≥ 15 (hard floor) |
| Origin types | ≥ 4 (academic, government, industry, trade press, advocacy…) |
| Adversarial sources | ≥ 2 (criticism / opposition angles) |
Phase 2 Gates
| Gate | Threshold |
|---|---|
| Verified sources | ≥ 15 with verdict: verified |
| Rejection ratio | rejects ≤ 40% of the pool (a polluted pool is re-discovered, never padded) |
| All batches returned | no dropped synthesis batches |
Pre-Delivery Checklist
- Every included source has a real author (named human or tier-1 org)
- No AI-generation markers in any included source’s body
- Steelman/counter present only for contested propositions; surveys get one debate section
- Report opens with substance, not rhetoric
- Every numeric claim carries a
refscitation (spot-checked) - In-text
[n]markers link to the correct appendix cards - Digest JSON validates; HTML renders; light theme; under size limits
- Future-work section present
Reference
Schema, design decisions, and the end-to-end usage walkthrough.
Digest JSON Schema
The contract between Phase 5 and the renderer.
{
"title": "Report Title",
"subtitle": "One-line framing",
"topic": "Original topic string",
"date": "2026-08-11",
"byline": "Hermes Deep Research",
"summary": "2-4 sentence digest",
"verdict": "Optional one-line takeaway",
"source_stats": {"independent": 20, "origin_types": 5, "adversarial": 3},
"callouts": [{"level": "warn|error|info|ok", "label": "Data Gap", "text": "..."}],
"issues": ["Issue 1", "Issue 2"],
"steelman": "markdown (optional, contested topics)",
"counter": "markdown (optional, contested topics)",
"metaphors": ["Optional explanatory layer"],
"sections": [
{"title": "Section Title",
"body": "Prose with \\n\\n paragraphs",
"findings": [{"text": "Finding with citation", "refs": [0, 2]}],
"quotes": ["pull quote"],
"callouts": [{"level": "warn", "label": "X", "text": "Y"}],
"tables": [{"headers": [...], "rows": [[...]], "caption": "..."}],
"diagrams": [{"code": "mermaid ...", "caption": "..."}],
"images": [{"url": "...", "caption": "...", "alt": "..."}]}
],
"sources": [
{"title": "...", "url": "...", "origin_type": "academic",
"domain": "example.org", "angle": "primary_research",
"adversarial": false, "snippet": "...",
"verdict": "verified", "author": "Jane Doe, PhD (or institution)"}
],
"future_work": ["What a follow-up should investigate"]
}
refs are zero-based indices into sources. steelman/counter are mutually optional and only appear for contested propositions. future_work is required. Rejected sources never appear in sources — they are summarized in issues.Design Principles
The decisions that shaped the tool, and why.
| Principle | Rationale |
|---|---|
| Determinism only where it counts | Discovery breadth, dedup, classification, and HTML rendering are code. LLM judgment is reserved for reading, verifying, arguing, and assembling. Predictable inputs → predictable outputs. |
| Enough sources, not all sources | The goal is enough independent, converging sources to establish consensus — not maximum volume. Independence beats count; stakes raise the floor. |
| Two-sided honesty | Steelman + counter for contested claims; one balanced debate section for surveys. Strawman arguments are excluded from the core pipeline entirely. |
| Grounded citations | Every claim traces to a specific verified source. Unverifiable claims are flagged, not printed. |
| Visible rejects | Rejected sources appear in Issues with reasons. The report’s integrity is auditable, including its failures. |
| Survey before debate | Descriptive topics open with substance. Rhetoric only where a real controversy exists, and only at the end. |
| Future work always | Every report ends with what a follow-up should investigate — the honest acknowledgment that research is iterative. |
| Not time-bound | Quality gates block delivery. Weak phases are re-run, never shipped. |
Usage
End to end: one topic in, one HTML report out.
1. Discover sources (deterministic)
python3 deep_research_sources.py "<topic>" --out sources.json
# gates checked automatically; script exits non-zero if the
# pool is below 15 sources or 4 origin types
2. Resolve URLs, then delegate synthesis
The orchestrator resolves every URL to a real, loadable address before delegation — subagents are never handed stubs. Sources are split into batches of ~5 and dispatched to parallel subagents in a single call. Each subagent verifies every page and returns the verdict+claims JSON.
3. Decide argumentation shape
Contested proposition → delegate steelman and counter agents. Survey topic → skip; the digest includes one balanced debate section instead.
4. Assemble the digest
One agent receives everything (syntheses + argumentation), grounds every claim to source indices, adds tables/diagrams/images, and writes the digest JSON.
5. Render (deterministic)
python3 deep_research_html.py digest.json --out report.html
6. Verify and deliver
Run the pre-delivery checklist (sources ≥ 15, all verified, citations link, no markdown leaks, light theme renders). Then deliver the single HTML file.