Files
correx/examples/workflows/research.toml
T
kami 7a0d4d0ee2 feat(research): wire tools + research workflow graph (research-workflow §2/§3)
Makes the research feature runnable end-to-end, off by default.

- config: [tools.research] (enabled, searxng_url, max_results, max_fetch_bytes).
- registration: web_search/web_fetch are built into BOTH the default and per-workspace
  tool registries when research.enabled, sharing one HTTP client threaded from Main
  (none built on the static path). Egress stays harness-enforced: web_fetch is T2
  (operator-approved) and the existing NetworkHostRule still applies.
- workflow: examples/workflows/research.toml — decompose → gather → report, with the
  three artifact schemas and prompts. Fan-out (search per sub-question, fetch per source)
  runs as repeated tool calls inside the gather stage (Correx has no parallel agents);
  per-source synthesis into the dossier is the compression step, so the report stage
  consumes summaries, never raw pages. ResearchWorkflowTest validates the graph contract.

To run: set [tools.research].enabled, register the 3 [[artifacts]], copy research.toml +
prompts + schemas into the workflows dir, start SearXNG. Launch like any workflow (the
T2 fetch approval surfaces as an approval card; the report opens in the artifact viewer).

Follow-ups (noted, not blocking): batch fetch-approval at the source-list level (§3),
a dedicated SourceFetched/LowQualityExtraction event (quality + content hash are already
in tool-result metadata), dynamic per-session egress allowlist, and the web approval client (§6).
2026-06-13 23:18:19 +04:00

80 lines
2.9 KiB
TOML

# Deep research workflow: decompose -> gather (search + fetch) -> report
#
# A native Correx workflow (research-workflow-spec): one graph, two network tools, no new
# orchestration machinery. The fan-out the spec describes (search per sub-question, fetch per
# source) happens *inside* the gather stage as repeated tool calls — Correx runs stages linearly
# (no parallel agents), so the model drives the fan-out itself.
#
# Per-source synthesis is the compression mechanism, not a nicety: the gather stage summarizes
# each fetched page into the source_dossier and the raw page text is discarded. The report stage
# consumes only those summaries, so research survives the token budget.
#
# Requires (in ~/.config/correx/config.toml):
# [tools.research]
# enabled = true
# searxng_url = "http://localhost:8888" # your self-hosted SearXNG (format=json enabled)
# [[artifacts]]
# id = "research_plan"; schema_path = "schemas/research_plan.json"; llm_emitted = true
# [[artifacts]]
# id = "source_dossier"; schema_path = "schemas/source_dossier.json"; llm_emitted = true
# [[artifacts]]
# id = "research_report"; schema_path = "schemas/research_report.json"; llm_emitted = true
#
# web_fetch is T2: the operator approves fetches before they leave the machine. web_search is T1
# (it only ever touches the configured SearXNG instance).
id = "research"
start = "decompose"
description = "Deep research: decompose a question, search + fetch sources, synthesize a cited report."
# 1. Question -> sub-questions + search queries. Reuses the planning discipline; read-only.
[[stages]]
id = "decompose"
prompt = "prompts/research_decompose.md"
produces = [{ name = "research_plan", kind = "research_plan" }]
token_budget = 8192
max_retries = 2
# 2. Run the searches, fetch promising sources, and summarize each one (per-source synthesis).
# web_search (T1) hits only SearXNG; web_fetch (T2) is operator-approved per source.
[[stages]]
id = "gather"
prompt = "prompts/research_gather.md"
needs = ["research_plan"]
produces = [{ name = "source_dossier", kind = "source_dossier" }]
allowed_tools = ["web_search", "web_fetch"]
token_budget = 32768
max_retries = 2
# 3. Cross-source synthesis into a cited report. Consumes the dossier summaries only — never raw pages.
[[stages]]
id = "report"
prompt = "prompts/research_report.md"
needs = ["source_dossier"]
produces = [{ name = "research_report", kind = "research_report" }]
token_budget = 16384
max_retries = 2
# --- forward edges ---
[[transitions]]
id = "decompose-to-gather"
from = "decompose"
to = "gather"
condition_type = "artifact_validated"
condition_artifact_id = "research_plan"
[[transitions]]
id = "gather-to-report"
from = "gather"
to = "report"
condition_type = "artifact_validated"
condition_artifact_id = "source_dossier"
[[transitions]]
id = "report-done"
from = "report"
to = "done"
condition_type = "artifact_validated"
condition_artifact_id = "research_report"