{
  "schema": "leumas.docs.page/1",
  "id": "how-to:deep-research",
  "slug": "how-to/deep-research",
  "kind": "pages",
  "bucket": "how-to",
  "title": "Deep Research (@leumas/research)",
  "name": "Deep Research",
  "eyebrow": null,
  "chip": "@leumas/research",
  "summary": "The ecosystem's web-research pipeline: an iterative query → web-search → summarize → reflect loop (ported from the legacy ollama-research tool; LangGraph dropped) that ANY surface can call.",
  "keywords": [
    "deep-research",
    "web-research",
    "web-search",
    "langgraph",
    "iterative",
    "leumas deep research",
    "how to deep research",
    "reflect"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# Deep Research (@leumas/research)\n\nThe ecosystem's web-research pipeline: an iterative **query → web-search → summarize → reflect**\nloop (ported from the legacy `ollama-research` tool; LangGraph dropped) that ANY surface can call.\n\n## How agents call it\n\n| Surface | How |\n|---|---|\n| Chatbot / Leviathan | attach the `deep_research` functioncall (stored doc, executor `action:deep-research`) |\n| Automation rule | action `deep-research` (e.g. cron trigger → daily research) |\n| MCP client | `functioncalls` server → `deep_research` tool; raw search via the `websearch` server |\n| HTTP | `POST /api/research/run` (JSON) or `POST /api/research/run/stream` (SSE) |\n| Studio | Intelligence → Research Lab |\n| Code | `runDeepResearch({ topic, loops, searchApi, provider, onEvent, signal, providerRegistry, adapters, connector })` from `@leumas/research` |\n\nArgs: `topic` (required) · `loops` 1–10 (default 3) · `searchApi` auto|duckduckgo|wikipedia|tavily|perplexity ·\n`fetchFullPage` (scrape top results via the `scraping` adapter) · `provider`/`model` (any @leumas/providers brain).\n\n## Layering (do NOT duplicate)\n\n- **Search** = the `websearch` domain adapter (`shared/engines/adapters/domain/websearch`): one\n  `search` verb, fallback chain requested → duckduckgo → wikipedia; Tavily/Perplexity need\n  `TAVILY_API_KEY`/`PERPLEXITY_API_KEY` env on the API.\n- **Scrape** = the existing `scraping` adapter (SSRF-guarded). websearch does no page fetching.\n- **LLM** = the injected provider registry — never a raw provider fetch.\n- **Persistence** = `research_runs` dynamic collection (auto-written when a connector is injected);\n  history in Studio → Intelligence → Run History.\n\n## SSE vocabulary\n\n`start · loop · query · sources · page · summary · reflection · final · done · error`\n(+ `: ping` heartbeat every 15s; client disconnect aborts the run; read POST-SSE with the shared\n`admin/_shared/sse.js` `streamPostSse` — EventSource is GET-only).\n\n## Gating\n\n`requireAuth` + `requireMembership('intelligence')` (admins bypass) + PassNode guard\n`feature:research.run` — author a `passnode_rules` doc with that resource id to meter/charge runs;\nno rule = free pass. External search keys cost real money: meter before white-labeling.\n",
  "source": {
    "path": "shared/services/knowledge/build-knowledge/deep-research.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 2448,
    "hash": "3599e2001748653bb57f6ef1a9d2e160a772f217"
  },
  "urls": {
    "html": "/p/how-to/deep-research",
    "json": "/docs/how-to/deep-research.json",
    "md": "/docs/how-to/deep-research.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  }
}
