MINARA

Deep Research Pipeline

The isolated 6-step pipeline that produces structured market reports

Deep research is a specialized pipeline that runs alongside (but separate from) the main agent loop. Where the agent loop is conversational and permission-gated, the deep research pipeline is a six-step state machine that produces a single structured report. Implementation lives in apps/agent/src/deep-research/pipeline.ts.

Why a separate pipeline instead of letting the agent loop iterate? Multi-source research needs parallel data gathering (fan-out then merge), LLM-as-judge quality checks, and retry logic that doesn't fit the conversational plan → call → observe cadence. A dedicated pipeline also keeps research output deterministic + citable, which a conversational loop can't guarantee.

See this in use: Features → Deep Research covers the user-facing side with example report output.

When to use it

Use the pipeline when the goal is a written artifact rather than a chat answer:

  • Long-form market analysis on a specific asset or theme.
  • Multi-source synthesis that needs to cite each claim.
  • Scheduled research runs where you want an artifact you can reference later by id.

For quick conversational questions ("what's the price of BTC", "should I long here"), use the agent loop. The deep research pipeline has higher latency, a fixed tool budget, and no memory of the ongoing conversation.

The six steps

deep-research diagram

Each step is a single LLM call except collectData, which runs a sub-agent loop against the tool registry for every goal.

Isolation from the main agent loop

The pipeline is intentionally isolated:

  • It does not see the chat's conversation history.
  • It does not consume the skill system's activation state.
  • It builds its own narrow tool subset for data collection rather than using the currently active skills.
  • It does not trigger learnings (review-engine never runs on pipeline turns).

This isolation buys two things: determinism (every research run starts from the same state) and cost control (a chat mid-long session can't bleed its history into research cost).

Modes

interface ResearchRequest {
  chatId: string;
  userMessage: string;
  mode?: "light" | "heavy";
  language?: string;
}
  • light (default). 2 parallel collection goals, smaller max iterations, single-pass report. Typical latency: 30–60 seconds.
  • heavy. 4 parallel collection goals, larger iteration budget, more thorough synthesis. Typical latency: 2–5 minutes.

Pick light for scheduled/cron research and heavy when the user explicitly asks for a thorough report.

Scenarios

Reports are organized around scenarios: pre-defined angles like "bullish case," "bearish case," "base case," "regulatory risk," "macro backdrop." The metaPlan step picks 1–4 from the catalog in apps/agent/src/deep-research/scenarios.ts based on the topic.

Scenario selection is load-bearing for report quality: a BTC research report without a "macro" scenario often misses the most important context, and one without "bearish case" produces confirmation bias. The catalog is kept deliberately small so the LLM's scenario picker is reliable.

Output artifact

The result is stored in ArtifactStore as a report artifact:

interface ResearchResult {
  report_id: string;
  status: "completed" | "error" | "waiting_for_input";
  title: string | null;
  research_topic: string | null;
  scenarios: string[];
  summary: string | null;
  key_findings: string[];
  report_markdown: string | null;
  error?: string;
  tokens: { input: number; output: number };
}

The main agent can quote a finished report back to the user via the report://{id} URI scheme. This is how you compose research with conversation: the user asks for research, gets a report_id, then asks follow-up questions in the chat that reference the report.

Report bodies are markdown. They include scenario headers, inline citations (via [text](url)), and a "Key Findings" bullet list the summarize step produces.

Running it

From the REPL

The main agent activates the deep-research domain skill and calls the deep_research_run tool:

> write me a report on ETH staking yields this quarter
assistant: [activates deep-research skill]
assistant: [calls deep_research_run with mode=heavy]
assistant: ✓ report_id: r_abc123 — here's the summary…

From the CLI subcommand

Bypass the REPL entirely:

minara research "BTC ETF flows this quarter"
minara research "macro risks for Solana" --mode heavy

See Subcommands → minara research for the full flag list.

From an HTTP request

curl -X POST http://localhost:8080/research \
  -H 'content-type: application/json' \
  -d '{"topic": "BTC ETF flows", "mode": "light"}'

See API → /research for the request and response schemas.

Cost and observability

Each step records {provider, model, tokens} in the audit log under source: "deep_research". The tokens field on ResearchResult is a best-effort aggregate for quick cost accounting. For precise cost attribution, query the audit log:

SELECT model, SUM(input_tokens), SUM(output_tokens), COUNT(*)
  FROM audit
 WHERE source = 'deep_research'
   AND trace_id = ?
 GROUP BY model;

The deep research pipeline is the biggest single LLM cost in most Minara deployments. If your bill is unexpectedly high, this is usually the first query to run.

Extending the pipeline

The pipeline is deliberately short (~700 lines in pipeline.ts). Common extensions:

  • Add a scenario: append to the catalog in scenarios.ts. The meta-plan prompt reads the catalog at runtime, so no prompt changes are needed.
  • Change collection concurrency: set collectConcurrency in PipelineConfig. Default 4, matching the v1 ceiling.
  • Change max iterations per goal: set collectMaxIterations. Default 8. This is the LLM-turn budget for each data-collection sub-agent.
  • Add a post-processing step: append another method to the pipeline class and call it between summarize and the return. Do not inline logic into an existing step; the step boundaries are what make the pipeline debuggable.

Do not wire the pipeline into the main agent loop's history. The isolation is a feature; breaking it will let chat cost leak into research cost.

What NOT to put in research

  • Real-time price-dependent decisions. The pipeline takes minutes; prices move. Use the agent loop for anything that depends on current execution conditions.
  • Fund-moving tool calls. Data collection uses a narrow tool subset that excludes every fund-moving tool. The pipeline refuses to run if its tool subset accidentally includes one.
  • Private user context. The pipeline does not read the user's wallet balances or history. It speaks to public market data. If a report should account for the user's specific position, surface the position in the user message at bootstrap time; the pipeline will thread it into subsequent steps.

On this page