Deep-dive research analyst powered by Armature — showcases fan-out, continuation, triggers, and structured output
Project description
Armature Research
Automated research analyst powered by agentic AI. Given a topic, Armature Research searches the web, reads and extracts sources, iteratively deepens coverage, and produces a structured Markdown + HTML research briefing.
Example Project: Armature Research is a reference implementation demonstrating Armature, a YAML-configured agentic workflow harness. Use this repo as a template for building your own Armature-based applications.
# Install from PyPI (pulls in the Armature harness automatically)
pip install armature-research
# Clone for the workflow specs (the wheel ships the tools, not the YAMLs)
git clone https://github.com/bryansparks/armature-research
cd armature-research
# Run a research task
armature run workflows/research-analyst.yaml --input "topic=AI regulation in the EU"
What's New
This release rewrites Armature Research as a production-grade Armature workflow with an iterative, multi-agent research pipeline:
- Iterative deep research — runs 1–3 research rounds, carrying forward gaps, themes, and fetched URLs so each round targets what the previous round missed.
- Subagent delegation — the core search/extract/synthesize cycle is isolated in
workflows/research-round.yamland invoked in a loop from the parent workflow. - Multi-source search — Tavily web search, Hacker News, Polymarket, and GitHub (free public APIs, no keys needed) plus optional Reddit discussions and YouTube transcripts. Each source degrades gracefully if unreachable.
- Recency filtering — optionally constrain a run to a recent window (
recency=30d,3d,2mo,1y) so queries, hard API filters, and report framing focus on the recent window. Every source honors it: Tavilydays(web + YouTube), Reddittime_filter, and date cutoffs for Hacker News / Polymarket / GitHub. - Engagement-weighted ranking — every result carries a native engagement signal (HN points, GitHub stars, Polymarket volume, Reddit score, YouTube views) surfaced as badges in the report and used to weight synthesis. A non-LLM
collect_source_manifeststage joins selected URLs with raw search results to attachengagement_labelso badges render by design, not by accident, and the manifest accumulates across research rounds. - Production reliability — checkpoint/resume, cross-run source deduplication, continuation for incremental updates, cron/webhook triggers, and strict safety rules.
- Category-aware reports — automatically formats output as product reviews, comparisons, how-to guides, fact-checks, or landscape briefings.
- Self-contained HTML reports — dark/light theme, table of contents, collapsible sources, and print/export toolbar.
What Armature Research Does
A single command runs the full pipeline:
- Decomposes the topic into 5–8 specific sub-questions
- Plans targeted search queries for each sub-question
- Searches the web (Tavily), Hacker News, Polymarket, GitHub, Reddit, and YouTube in parallel
- Selects the most valuable URLs from search results
- Fetches full content from each selected URL in parallel
- Extracts structured findings from each source
- Synthesizes findings into a coherent research summary
- Evaluates coverage completeness — if gaps remain, loops back for another iteration
- Writes a comprehensive research briefing in Markdown
- Generates a self-contained HTML report with dark/light theme, TOC, and collapsible sources
Typical run time: 2–5 minutes per iteration (1–3 iterations depending on coverage).
How the Iterative Loop Works
The deep_research_round stage in workflows/research-analyst.yaml delegates to workflows/research-round.yaml as a subagent and runs it in a loop:
- id: deep_research_round
depends_on: [decompose_query]
subagent_spec: workflows/research-round.yaml
loop:
max_iterations: 3
until: "{{ decide_round.continue_research == false }}"
carry_forward:
- decide_round.gaps
- decide_round.key_themes
- decide_round.coverage_score
- decide_round.urls_fetched
- decide_round.queries_used
- decide_round.source_count
- collect_source_manifest.sources_manifest
Each research round performs a full plan → search → select → fetch → extract → synthesize → decide cycle. The decide_round stage returns a continue_research boolean plus a list of remaining gaps. If coverage is insufficient and the iteration cap hasn't been reached, Armature carries the selected keys forward and runs another round.
Why this matters:
- Gap-filling queries — round 2+ generates queries from the gaps identified in round 1, not rephrasings of the original topic.
- URL deduplication —
urls_fetchedis carried forward, so later rounds don't waste LLM calls re-reading the same pages. - Progressive synthesis — the evolving report is merged with new findings each round instead of being rewritten from scratch.
For a deep-dive into the design (IterResearch pattern, strategic workspace reconstruction, prompt-injection guards, low-quality filtering), see RESEARCH-MECHANICS.md.
Built on Armature
Armature Research is a production implementation of Armature, a YAML-configured agentic workflow harness. The entire research pipeline is declared in workflow specs and executed as a directed acyclic graph (DAG) of LLM agents, tool calls, and subagent delegation.
Armature Features Used
| Feature | Benefit to Armature Research |
|---|---|
Iterative loop with carry_forward |
Deepens coverage across 1–3 iterations, passing only the compressed state between rounds |
| Subagent delegation | Research round runs as an isolated subagent with its own 15-stage pipeline |
| Fan-out / Fan-in | Parallel per-query search, per-URL fetch, per-source extraction |
| Model tier routing | Cost-optimized routing (small for planning, large for extraction/synthesis) |
| Cross-run memory | Remembers which URLs were already fetched across runs |
| Checkpoint/resume | Interrupted runs recover gracefully — completed iterations are not re-run |
| Continuation | Carries prior research themes forward for incremental "what's new" updates |
| Cron & webhook triggers | Scheduled weekly refresh or on-demand webhook-triggered research |
| Strict safety mode | Fail-closed tool governance with explicit allow rules |
| Post-run self-analysis | Automatic quality review suggests improvements to the workflow |
| Category-aware formatting | Report structure adapts to product, comparison, howto, factcheck, or landscape |
Workflow Specs
| Workflow | Purpose | Stages | Iterations |
|---|---|---|---|
workflows/research-analyst.yaml |
Deep research briefing | 6 (parent) | Up to 3 (subagent loop) |
workflows/research-round.yaml |
Single research iteration (subagent) | 15 | 1 per loop iteration |
workflows/competitive-intel.yaml |
Competitive intelligence monitor | — | — |
The parent workflow delegates to the subagent in a loop. Each iteration performs a full search→extract→synthesize→evaluate cycle. The loop continues until coverage is adequate or max_iterations (3) is reached.
Installation
armature-research is published on PyPI. It pulls in the Armature agentic harness (armature-agents) automatically as a dependency — no separate Armature install needed.
pip install armature-research
The workflow specs that define the pipeline (workflows/research-analyst.yaml, workflows/research-round.yaml) ship in this repo, not in the wheel, so clone it to run them:
git clone https://github.com/bryansparks/armature-research
cd armature-research
armature run workflows/research-analyst.yaml --input "topic=AI regulation in the EU"
From source / development: instead of the PyPI install, run
pip install -e ".[dev]"from your clone. This installs theresearchtools in editable mode and is the recommended path if you plan to modify the workflows or tools.
Optional social sources
To enable Reddit and YouTube research, install the social extras:
pip install -e ".[social]"
This installs praw (Reddit) and youtube-transcript-api (YouTube transcripts). If these are missing, the workflow continues with web-only results.
Hacker News, Polymarket, and GitHub search are built in and use free public APIs — no extra packages or keys required. An optional GITHUB_TOKEN env var raises GitHub rate limits (unauthenticated requests are limited to ~10/min); without it, GitHub search degrades gracefully and the run continues.
API Keys
Copy .env.example to .env and add your keys:
# Required: web search and content extraction
TAVILY_API_KEY=tvly-...
# Required: LLM access (default provider is OpenRouter)
OPENROUTER_API_KEY=sk-or-...
# Optional: Reddit discussion search
# REDDIT_CLIENT_ID=...
# REDDIT_CLIENT_SECRET=...
Get a Tavily key at app.tavily.com. Get an OpenRouter key at openrouter.ai.
Using the Armature Workflow
Armature Research is run with the armature CLI, not a custom Python entry point. The workflow spec in workflows/research-analyst.yaml declares the entire agentic pipeline.
Basic Research
armature run workflows/research-analyst.yaml \
--input "topic=SLM fine-tuning advances using LoRA and distillation"
With Focus Constraint
armature run workflows/research-analyst.yaml \
--input "topic=AI regulation" \
--input "focus=how does the EU AI Act affect open-source LLM providers?"
Recent Results (Recency Window)
Constrain the run to a recent window. Queries are phrased "in the last N days," each source applies its native recency filter (Tavily days for web and YouTube, PRAW time_filter for Reddit, date cutoffs for Hacker News / Polymarket / GitHub), and the report frames findings as recent. Unset or invalid = open-ended (default behavior).
armature run workflows/research-analyst.yaml \
--input "topic=GLM-5.2 reception" \
--input "recency=30d"
Supported formats: Nd (days), Nmo (months, ×30), Ny (years, ×365), bare N (days). Example values: 3d, 30d, 2mo, 90d, 1y.
Force Fresh Run (Clear Checkpoint)
armature run workflows/research-analyst.yaml \
--input "topic=AI regulation" --force
Incremental Research (Continuation)
Armature Research remembers prior runs. On subsequent executions with the same topic, it carries forward prior themes and identified gaps:
# First run: initial research
armature run workflows/research-analyst.yaml --input "topic=AI regulation"
# Second run (one week later): focuses on "what's new"
armature run workflows/research-analyst.yaml --input "topic=AI regulation"
Inputs Reference
| Input | Required | Description |
|---|---|---|
topic |
✅ | The research question or subject area |
focus |
— | Optional angle or constraint, e.g. focus=regulatory implications |
recency |
— | Recent-results window, e.g. recency=30d (Nd, Nmo, Ny, bare N). Unset = open-ended |
documents |
— | Comma-separated local file paths to include as sources |
max_sources |
— | Cap on URLs fetched per round (default: 12) |
Cron & Webhook Triggers
The workflow spec includes built-in triggers:
triggers:
- type: cron
schedule: "0 6 * * 1" # 6am UTC every Monday — weekly topic refresh
- type: webhook
path: /webhook/research # POST with {"topic": "...", "focus": "..."}
Configure these in workflows/research-analyst.yaml or via your Armature deployment settings.
Output
HTML Report
Written to ./research-output/<topic>_<run_id>.html. Features:
- Dark/light theme with aurora gradient hero section
- Table of contents sidebar
- Collapsible source list with credibility ratings
- Engagement badges on community sources (★ GitHub stars, ▲ HN points · comments, $ Polymarket volume · odds, Reddit score · comments)
- Category-specific formatting (product reviews, comparisons, how-to guides, etc.)
- Print/export toolbar
- Inline citations linking back to sources
Report Categories
The decompose_query stage classifies the research topic into one of five categories, each with a tailored report format:
| Category | Structure |
|---|---|
| product | Executive Summary → Quick Comparison → Detailed Reviews → Verdict |
| comparison | At a Glance → By Option → Head-to-Head → Best For |
| howto | Prerequisites → Quick Guide → Step-by-Step → Troubleshooting |
| factcheck | The Claim → Evidence For → Evidence Against → Verdict |
| landscape | Executive Summary → Key Findings → By Sub-Question → Contradictions |
Markdown Briefing
The validated Markdown report is available in the run output and is used as the source for the HTML render. It includes a source-quality assessment and an appendix of all sources consulted.
Project Structure
armature-research/
├── research/
│ └── tools/
│ ├── web.py # web_search, fetch_url, read_document, generate_html_report
│ ├── social.py # search_reddit, search_youtube_videos, fetch_youtube_transcript
│ ├── communities.py # search_hackernews, search_polymarket, search_github (+ engagement metrics)
│ ├── engagement.py # per-source engagement metric normalization
│ ├── recency.py # recency window parser + parse_recency tool
│ ├── manifest.py # build_source_manifest — joins selected URLs with engagement labels
│ └── reporting.py # Visual HTML report generator
├── workflows/
│ ├── research-analyst.yaml # Parent workflow (6 stages + iterative loop)
│ ├── research-round.yaml # Subagent workflow (15 stages per iteration)
│ └── competitive-intel.yaml # Competitive intelligence monitor
├── tests/
│ ├── tools/ # Handler unit tests (web, social, communities, engagement, recency, manifest, reporting)
│ └── workflows/ # Structural + carry-forward contract tests
├── RESEARCH-MECHANICS.md # Deep-dive into the iterative research design
├── .env.example # API key template
├── pyproject.toml
└── README.md
Configuration
Model Swapping
Edit workflows/research-analyst.yaml to swap LLM providers or models:
model_tiers:
small:
provider: openrouter
model: qwen/qwen3.6-27b # planning, source selection
medium:
provider: openrouter
model: moonshotai/kimi-k2.6 # orchestration, evaluation
large:
provider: openrouter
model: z-ai/glm-5.2 # extraction, synthesis, writing
Iterative Research Loop
The deep_research_round stage runs the subagent in a loop:
- id: deep_research_round
subagent_spec: workflows/research-round.yaml
loop:
max_iterations: 3
until: "{{ decide_round.continue_research == false }}"
carry_forward:
- decide_round.gaps
- decide_round.key_themes
- decide_round.coverage_score
- decide_round.urls_fetched
- decide_round.queries_used
- decide_round.source_count
- collect_source_manifest.sources_manifest
Each iteration receives carry-forward data from the previous one, enabling:
- Gap-filling queries on iteration 2+ (focused on identified coverage gaps)
- URL deduplication (avoids re-fetching already-read sources)
- Progressive synthesis (builds on prior themes rather than rewriting)
Tuning the Loop
| Setting | Effect |
|---|---|
max_iterations |
Hard cap on research rounds |
carry_forward |
Which state survives between rounds (keep this minimal) |
until |
Jinja2 expression that decides when to stop |
For best results, do not carry forward raw search results or full fetched articles — only compressed state like gaps, urls_fetched, queries_used, and sources_manifest. Carried dot-paths (e.g. collect_source_manifest.sources_manifest) are reachable in downstream stages as {{ _iteration.carry_forward.<stage>.<key> }}.
Extending Armature Research
The workflows are standard Armature specs and can be customized:
Add a New Search Source
Create a tool module in research/tools/, then add it to both workflow specs:
tools:
- module: research.tools.web
- module: research.tools.social
- module: research.tools.academic # your new tool
Add a New Stage
- id: custom_analysis
depends_on: [extract_findings]
role:
name: Custom Analyst
type: researcher
model_tier: large
description: "Your custom analysis task..."
output_mode: guided_json
Adjust Safety Rules
The default safety_mode: strict blocks any tool not explicitly allowed. Add allow rules for custom tools:
safety_rules:
- tool: my_tool
condition: {field: query, op: truthy, value: ""}
action: allow
Contributing
Contributions welcome! See CONTRIBUTING.md for guidelines.
License
MIT. See LICENSE for details.
Armature Research is built on Armature, combining iterative multi-agent research with production-grade workflow orchestration.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file armature_research-0.1.1.tar.gz.
File metadata
- Download URL: armature_research-0.1.1.tar.gz
- Upload date:
- Size: 140.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4901ddf037f4ad0aa5fda056a4d48f6bcb590c3113f3b980b695838bf5d80000
|
|
| MD5 |
1f9fd18ce5482cc7a2c66f4291e2071d
|
|
| BLAKE2b-256 |
31ac78a6a65598818c701dfc6d564ad304242305d9dc560ff89a3d636f091c68
|
File details
Details for the file armature_research-0.1.1-py3-none-any.whl.
File metadata
- Download URL: armature_research-0.1.1-py3-none-any.whl
- Upload date:
- Size: 40.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee501c3fa75e3944137c37f25a3b87255c70632c105c061dffdbf9d0eee6df8a
|
|
| MD5 |
7b5740faff2c68f529da6ea1c05a69b0
|
|
| BLAKE2b-256 |
3f26b8eb2482d9bfad195cced14a2aaac6e8fdbf833cacc36cee39b1c6e8578c
|