139 lines
8.7 KiB
Markdown
139 lines
8.7 KiB
Markdown
# ResearchX
|
||
|
||
**TL;DR** — ResearchX turns one question into a cited research report: pick a method, approve the plan, and the engine visits real web sources, with every claim anchored to a quoted passage from a page it actually read. The engine milestone (v0.2.5) is shipped and owner-approved; hosting goes live next (v0.3, in progress).
|
||
|
||
- **What** — FastAPI backend (planning, search, extraction, verification, export gates) plus a Next.js frontend; the full test suite runs with zero API keys.
|
||
- **Why** — Readers can check every citation back to its source passage; runs are resumable from durable state.
|
||
- **Action** — no action — FYI. Developers: see the Quickstart below.
|
||
- **Details** — `frontend/README.md` (web app); `.ciagent/` (pipeline state and roadmap).
|
||
|
||
A deep-research studio where you bring your own model, storage, and memory; choose a research methodology before a single query fires; and every claim in the final report is anchored to a visited, verified, quotable source.
|
||
|
||
**Status: MVP shipped (v0.2.5, owner-approved 2026-09-27)** — the full research engine (FastAPI + asyncio state machine + adapter layer + attack-proven trust loop) behind the v0.1 frontend. Live mode covers the core research loop (G-002/G-009): the engine runs locally per the quickstart; library/settings/suggestions/refine stay mock-backed until v0.3+.
|
||
|
||
## Quickstart
|
||
|
||
```bash
|
||
# frontend (prototype, mock-backed — works today)
|
||
cd frontend
|
||
corepack pnpm install
|
||
corepack pnpm dev # http://localhost:3000
|
||
|
||
# backend (MVP — uv manages Python 3.12 and the venv)
|
||
cd backend
|
||
uv sync # installs deps + editable researchx package
|
||
uv run uvicorn researchx.api.app:app --reload --port 8000
|
||
uv run pytest # full suite, zero API keys
|
||
```
|
||
|
||
Three clicks from cold start to initiated research.
|
||
|
||
## Monorepo command map
|
||
|
||
| Layer | Install | Dev | Gates |
|
||
|---|---|---|---|
|
||
| backend (uv, Python 3.12) | `uv sync` | `uv run uvicorn researchx.api.app:app --reload --port 8000` | `uv run ruff check src tests` · `uv run mypy src` · `uv run pytest` |
|
||
| frontend (pnpm 12.6) | `corepack pnpm install` | `corepack pnpm dev` | `corepack pnpm typecheck` · `corepack pnpm lint` · `corepack pnpm test` · `corepack pnpm build` |
|
||
|
||
## Engine architecture (v0.2 P2)
|
||
|
||
The research engine is a hand-rolled asyncio state machine:
|
||
|
||
```
|
||
PLANNING → AWAITING_APPROVAL → SEARCHING → EXTRACTING → REFLECTING ─┐
|
||
(halt until ↑ depth loop (dials.depth) │
|
||
approved) └────────────────────────────┘
|
||
→ SYNTHESIZING → VERIFYING → DONE (+ CANCELLED / FAILED terminals)
|
||
```
|
||
|
||
- Durable state: `runs/<id>/state.json` rewritten atomically after every transition; resume re-enters at the recorded cursor with the persisted plan
|
||
- Event bus: typed events (`run_started`, `query_fired`, `source_visited`, `passage_extracted`, `state_changed`, `run_done`, `run_error`) — trace per run
|
||
- Visited-only constraint: the writer's context is the evidence store and nothing else
|
||
- Trust loop: SSRF-guarded liveness (live/stale/dead), quote anchoring (normalize + fuzzy ≥ 0.85, weak matches flagged), citation ledger JSON, exportable verification checklist, single-source vs corroborated confidence labels
|
||
|
||
Run artifacts per run: `state.json`, `report.md`, `ledger.json`, `checklist.md`.
|
||
|
||
Try it on fakes (no keys): `uv run python -m researchx.engine.demo`
|
||
|
||
## Adversarial guarantees (v0.2 P4)
|
||
|
||
Every trust-loop claim is attacked by the adversarial suite (`backend/tests/test_adversarial.py`) — these are tests that try to fool the verifier, and their passing proves the defenses:
|
||
|
||
| Attack | Defense (proven) |
|
||
|---|---|
|
||
| Writer hallucinates a citation to a passage that was never stored | Unknown passage IDs count as **unanchored claims** in the ledger; the export gate fails the report |
|
||
| Fabricated quote (matches nothing) | Anchoring verdict: no match, flagged — never silently accepted |
|
||
| Near-miss quote (0.80–0.85 similarity) | Flagged `weak_match` in the ledger — surfaced, not accepted |
|
||
| Liveness probe redirected to a private/metadata IP | Per-hop redirect re-guarding classifies it **dead** (SSRF refusal) |
|
||
| Redirect chain ending at a gone page (410) | Classified **dead** |
|
||
| Oversized page (5MB) response | 2MB read cap — classifies without unbounded memory |
|
||
| Pause during the depth loop | Durable state persists; resume completes the run over the HTTP API |
|
||
| Cancel mid-loop | Terminal CANCELLED state persisted, never swallowed |
|
||
|
||
REQ-23 (≥99% citation liveness) is a per-run **export gate** enforced by the verification checklist, plus an opt-in keyed live tier (`cd backend && uv run pytest -m live`) — the zero-key CI suite never asserts it vacuously.
|
||
|
||
The E2E happy path (≤3 clicks: ask, choose, approve) is validated against the real FastAPI + engine stack, defaults only — not the mock (`frontend/src/lib/api/e2e-real-stack.test.ts`).
|
||
|
||
## Adapter configuration (v0.2)
|
||
|
||
All keys resolve from env files only (`.env`, `.env.secrets`, `.env.*` — never the shell environment, never git):
|
||
|
||
| Variable | Scope | Used by |
|
||
|---|---|---|
|
||
| `OLLAMA_API_KEY` | model (cloud) | Ollama cloud endpoint `https://ollama.com/v1`; absent/keyless falls back to `http://localhost:11434/v1` automatically |
|
||
| `OLLAMA_BASE_URL` | model | Overrides the default cloud base URL |
|
||
| `TAVILY_API_KEY` | search | Tavily default search; absent falls back to the keyless DuckDuckGo adapter (degraded mode — surfaced in the run trace) |
|
||
|
||
The test suite needs **no** keys: fake model/search adapters (REQ-31) drive every engine test.
|
||
|
||
## Live mode (v0.2 P3)
|
||
|
||
The frontend runs mock-backed by default (works standalone, zero keys). To run the core research loop against the real backend:
|
||
|
||
```bash
|
||
# terminal 1 — backend (fake adapters by default; real Ollama/Tavily adapters
|
||
# are used by the engine in v0.2.5+ wiring — the served API currently ships
|
||
# fake-adapter runs for demos and tests; see Adapter configuration below)
|
||
cd backend && uv run uvicorn researchx.api.app:app --reload --port 8000
|
||
|
||
# terminal 2 — frontend in live mode
|
||
cd frontend && NEXT_PUBLIC_RESEARCHX_API_MODE=live corepack pnpm dev
|
||
```
|
||
|
||
Live mode covers the core loop (Ask → Choose → approve plan → live trace → report + ledger); the library, settings persistence, suggestions, refine, and graph surfaces stay mock-backed until v0.3. The API surface: `POST /api/runs`, `GET /api/methodologies`, run lifecycle (`approve/pause/cancel/resume`), `GET /api/runs/{id}/events` (SSE), `GET /api/config`, report + ledger artifacts.
|
||
|
||
## What's in the prototype
|
||
|
||
| Screen | What it does |
|
||
|---|---|
|
||
| `/ask` | One question box + one optional context box — plus two opt-in helpers below and beside them: open-question suggestions (follow-ups from your past runs, one click fills the box) and the Refine question dialogue (a few clarifying answers → a composed, sharper question). Neither ever auto-starts. |
|
||
| `/choose` | All 8 locked methodology cards — mock ETA, cost estimate, why-recommended, and the 12 methodology dials behind each card's Advanced drawer. |
|
||
| `/run/[id]` | Plan & Run: approve the research plan (sub-questions, source universe, outline), watch the live trace stream, pause/resume or cancel. |
|
||
| `/run/[id]/report` | Reading Room: the report with page-anchored citations — hover or focus any `[n]` marker for the exact quoted passage — plus the citation ledger and Markdown/JSON exports. |
|
||
| `/library` | Stored reports filed into auto-categorized folders (sortable, links into the Reading Room), the Connections panel (shared sources ⇄, related topics →), plus v2-preview project graph and task inbox. |
|
||
| `/settings` | API-key status (masked, never raw), the reports storage directory, methodology defaults with the resolved 12 dials, appearance, and about/version. |
|
||
|
||
The persistent shell (every screen): sidebar nav + adapter connection-status dots, top bar with the ⌘K navigation palette (screens + stored reports) and the avatar menu (local profile, Settings, About).
|
||
|
||
All data flows through a typed `ApiClient` interface backed by `MockApiClient` — the same components will talk to the FastAPI backend via the OpenAPI-generated client in the MVP milestone, with zero rewrites.
|
||
|
||
## Scripts
|
||
|
||
Run inside `frontend/`:
|
||
|
||
| Command | Purpose |
|
||
|---|---|
|
||
| `pnpm dev` | Dev server on :3000 |
|
||
| `pnpm build` | Production build |
|
||
| `pnpm lint` | ESLint (flat config) |
|
||
| `pnpm typecheck` | `tsc --noEmit` (strict) |
|
||
| `pnpm test` | Vitest + Testing Library (jsdom) |
|
||
|
||
## Repository layout
|
||
|
||
```
|
||
frontend/ Next.js 15 App Router, TypeScript strict, Tailwind v4, Vitest
|
||
.ciagent/ Pipeline state (roadmap, requirements, plans, checkpoints)
|
||
```
|
||
|
||
Milestone v0.2 (MVP: FastAPI backend, research engine, adapter layer, trust loop) begins only after this prototype is approved. |