Autonomous Deep Research Agent
██╗ ██╗██████╗ ███████╗███████╗███████╗ █████╗ ██████╗ ██████╗██╗ ██╗
██║ ██╔╝██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██║
█████╔╝ ██████╔╝█████╗ ███████╗█████╗ ███████║██████╔╝██║ ███████║
██╔═██╗ ██╔══██╗██╔══╝ ╚════██║██╔══╝ ██╔══██║██╔══██╗██║ ██╔══██║
██║ ██╗██║ ██║███████╗███████║███████╗██║ ██║██║ ██║╚██████╗██║ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
KResearch is an autonomous, provider-agnostic research agent that takes a plain-language question, conducts multi-source web research through an unrestricted reasoning loop, and produces a publication-quality report where every factual claim is backed by an inline citation. The agent is not a fixed pipeline — it freely decides what to search, what to read, when to verify, and when to stop. All tools (search, scraping, code execution) are implemented in Python and work with any LLM provider.
- Key Principles
- Quick Start
- How the Agent Works
- Tools
- The Mind Map
- Provider Abstraction
- Configuration Reference
- CLI Reference
- Web UI
- Proxy Support
- Output Format
- Architecture
- Dependencies
- Roadmap
- License
- Support
-
No hardcoded models. Model IDs are configuration defaults, fetched and validated against the provider API at runtime. Two defaults are configured — a complex-task model (
gemini-3-flash-preview) and a fast model (gemini-3.1-flash-lite-preview) — but the user can override either via CLI flags or environment variables. -
No hardcoded tools. Web search uses the
ddgslibrary (DuckDuckGo), page scraping usestrafilaturawith anhttpx/beautifulsoup4fallback, and code execution uses a subprocess sandbox. None of these are tied to any provider's built-in features. The LLM calls Python functions via standard function-calling; our code executes them and returns results. -
No forced behaviour. The agent's system prompt gives it a five-phase research methodology (Decompose, Search, Read, Verify, Synthesize), but it is not forced into any fixed order. It freely calls tools, revisits earlier phases, and decides when it has gathered enough evidence to write the report.
-
Interruptible. While the agent loop is running, a concurrent
asynciotask monitorsstdin. The user can typestopto force immediate synthesis, or type any other message (e.g., "focus more on the economic impact") which is injected into the conversation as a[USER INTERRUPT]. -
Every sentence cited. The system prompt enforces that every factual claim in the final report carries an inline citation
[N]linking to a numbered source at the bottom. The agent is instructed to omit any claim it cannot cite. -
Plain text copyable. The Rich library is used only for the live progress panel in the terminal. The report itself is clean Markdown that can be copied, piped, or saved to a file without any formatting artifacts.
- Python 3.10 or later
- A Google Gemini API key (get one at ai.google.dev)
- Or an OpenAI API key, or any OpenAI-compatible API endpoint
git clone https://github.com/KuekHaoYang/KResearch.git
cd kresearch
pip install -e .
# For OpenAI / custom provider support:
pip install -e ".[openai]"# Gemini (default provider)
export GOOGLE_API_KEY=your-key-here
# OpenAI
export OPENAI_API_KEY=sk-...
# Custom OpenAI-compatible API (e.g. DeepSeek, Together, Ollama)
export KRESEARCH_CUSTOM_API_KEY=your-key
export KRESEARCH_CUSTOM_API_BASE=https://api.deepseek.com/v1Or create a .env file in the project root (see .env.example).
# With Gemini (default)
kresearch "What are the latest breakthroughs in quantum computing?"
# With OpenAI
kresearch --provider openai --model gpt-4o "What are the latest breakthroughs in quantum computing?"
# With a custom OpenAI-compatible API
kresearch --provider custom --model deepseek-chat "What are the latest breakthroughs in quantum computing?"# List all models available from the provider API
kresearch --list-models
# Use a specific model
kresearch --model gemini-3.1-pro-preview "your query"
# Save the report to a file
kresearch -o report.md "your query"
# Remove the iteration safety limit (agent runs until it decides to stop)
kresearch --max-iterations 0 "your query"
# Run through a proxy
kresearch --proxy http://127.0.0.1:7890 "your query"User Query
│
▼
┌──────────────────────────────────────────────────────────────┐
│ AUTONOMOUS LOOP │
│ │
│ The LLM sits in a multi-turn chat with function-calling │
│ tools. It FREELY decides what to call and when. │
│ │
│ Loop: │
│ 1. LLM receives: system prompt + current mind map state │
│ 2. LLM responds with either: │
│ a. Tool calls → orchestrator executes them → │
│ all results sent back in one batch → repeat │
│ b. Final text (after draft_report()) → this IS the │
│ report → done │
│ │
│ Termination (checked in this order): │
│ 1. Agent calls draft_report() → it decided to stop │
│ 2. User sends "stop" via stdin → forced synthesis │
│ 3. Safety limit reached (default 20 iterations, │
│ configurable, set to 0 for unlimited) │
└──────────────────────────────────────────────────────────────┘
│
▼
Final Report (Markdown with inline citations)
The system prompt (src/kresearch/prompts.py) is a ~7,000-character document that gives the agent a five-phase research methodology:
| Phase | What the agent does |
|---|---|
| 1. Decompose | Break the query into 3-7 sub-questions covering What, Why, How, Who, When, debates, and implications. |
| 2. Broad Search | For each sub-question, run web_search with diverse queries — literal, rephrased, academic, counterargument, data-seeking. Never repeat a query. |
| 3. Deep Reading | Use read_webpage on the 5-10 most authoritative results. Prefer primary sources (papers, official reports) over secondary. Read full articles, not just snippets. |
| 4. Verify | Cross-reference claims across 2+ independent sources. Call log_contradiction for disagreements. Use execute_python for math/data verification. Assign confidence scores honestly (0.9+ requires 3+ reliable sources agreeing). |
| 5. Synthesize | Call update_findings for each topic, then draft_report(). Write the complete report as the next response. |
The prompt also contains strict citation rules (every factual claim must carry [N]), a required report structure (Executive Summary, thematic sections, Contradictions & Debates, Limitations, Sources), and quality standards (depth, specificity, balance, honesty).
When the LLM returns multiple tool calls in a single response, the orchestrator executes all of them concurrently, then sends all results back in a single message. This is the correct behaviour for the Gemini API (and is handled by ChatSession.send_function_responses()). Providers that do not support batch responses fall back to sending results one at a time.
While the agent loop runs, a separate asyncio task polls stdin using select() (non-blocking, 300ms intervals):
stop/quit/done— The agent is told to calldraft_report()and write the best report it can with current findings.- Any other text — Injected into the conversation as
[USER INTERRUPT]: <message>. The agent sees it and adjusts its research direction.
All tools are provider-agnostic Python functions, registered via ToolRegistry and exposed to the LLM through standard function-calling declarations. The agent discovers tools automatically from the registry — adding a new tool requires zero changes to the agent loop.
| Tool | Implementation | Purpose |
|---|---|---|
web_search(query, max_results) |
ddgs library (DuckDuckGo Search) |
Free, no-API-key web search. Returns {title, url, snippet} for each result. Max 20 results per call. |
read_webpage(url) |
trafilatura primary, httpx + beautifulsoup4 fallback |
Fetches a URL and extracts the main article content as clean Markdown. Strips navigation, scripts, ads. Truncates at 15,000 chars to protect context. |
execute_python(code, timeout) |
subprocess.run() in a restricted environment |
Runs Python code in an isolated subprocess with a 30-second timeout and a restricted PATH. Used for math verification, data analysis, and fact-checking. |
update_findings(topic, content, sources, confidence) |
Writes to the MindMap data structure |
Records a verified finding under a topic node with source URLs and a confidence score (0.0-1.0). The agent calls this frequently to build its working memory. |
log_contradiction(topic, claim_a, claim_b, source_a, source_b) |
Writes to the MindMap data structure |
Records a conflict between two sources. The agent is instructed to never ignore contradictions — they must appear in the final report. |
spawn_subagent(query, context) |
Creates a new ChatSession with a focused system prompt |
Launches an independent sub-agent that runs its own mini research loop (max 5 iterations, no further sub-spawning). Returns structured findings. Used for clearly independent sub-questions. |
draft_report() |
Sets state.draft_requested = True, returns mind map JSON |
Signals the agent is ready to write the final report. Returns the complete mind map data so the agent can reference it while writing. The agent's next text response becomes the report. |
Gemini has built-in google_search and code_execution. OpenAI has code_interpreter. But each provider's built-in tools are different, and some providers have none. By implementing all tools in Python, KResearch's tool set is identical regardless of which LLM backend is used. Switching from Gemini to OpenAI to Anthropic requires zero changes to the tool layer.
The mind map (src/kresearch/models/mind_map.py) is the agent's persistent epistemic state — a hierarchical tree of everything it knows, structured by topic. Even if the LLM's conversation history gets long, the mind map retains all findings in a compact format that is included in the system prompt.
MindMap
├── root: MindMapNode (topic = the original query)
│ ├── child: MindMapNode (topic = "Quantum Hardware")
│ │ ├── content: "IBM unveiled Nighthawk, a 120-qubit processor..."
│ │ ├── sources: [{url, title}, {url, title}]
│ │ ├── confidence: 0.95
│ │ └── contradictions: []
│ ├── child: MindMapNode (topic = "Error Correction")
│ │ ├── content: "Google's Willow chip operates below threshold..."
│ │ ├── sources: [{url, title}, {url, title}, {url, title}]
│ │ ├── confidence: 0.98
│ │ └── contradictions: [Contradiction(...)]
│ └── ...
└── query: "What are the latest breakthroughs in quantum computing?"
add_finding(topic, content, sources, confidence)— Finds or creates a topic node and appends the finding. Content is accumulated (not replaced), sources are extended, confidence takes the max.log_contradiction(topic, claim_a, claim_b, source_a, source_b)— Attaches aContradictionrecord to the topic node.get_summary()— Returns a compact text tree used in the system prompt so the agent knows its current state.get_gaps()— Returns topics with confidence below 0.3 (areas needing more research).get_contradictions()— Returns all unresolved contradictions across the entire tree.source_count()— Total number of sources across all nodes.
KResearch is designed to work with any LLM that supports function calling. The provider layer (src/kresearch/providers/) defines two abstract base classes:
Every provider must implement:
| Method | Description |
|---|---|
generate(messages, system_instruction, tools, thinking_level) |
One-shot generation with optional tool declarations. |
generate_stream(messages, ...) |
Streaming generation (yields text chunks). |
create_chat(system_instruction, tools, thinking_level) |
Creates a multi-turn ChatSession — the primary interface used by the orchestrator. |
list_models() |
Fetches available models from the provider API. Returns list[ModelInfo]. |
The multi-turn chat interface used by the agent loop:
| Method | Description |
|---|---|
send(message) |
Send a user message, returns GenerateResponse with text and/or function calls. |
send_function_response(name, response) |
Send a single tool result back to the model. |
send_function_responses(responses) |
Send multiple tool results in one batch (providers should override for correctness). |
get_history() |
Return the conversation history. |
All types are defined in src/kresearch/providers/types.py:
Message— role + contentFunctionCall— name + args dictGenerateResponse— text, function_calls, thinking, usage, rawModelInfo— id, name, context_windowToolDeclaration— name, description, parameters (JSON Schema)
| Provider | Status | Default model | Fast model |
|---|---|---|---|
| Gemini | Fully implemented | gemini-3-flash-preview |
gemini-3.1-flash-lite-preview |
| OpenAI | Fully implemented | gpt-4o |
gpt-4o-mini |
| Custom API | Fully implemented | (user-specified) | (user-specified) |
| Anthropic | Placeholder (not yet implemented) | claude-sonnet-4-6 |
claude-haiku-4-5 |
| xAI | Placeholder (not yet implemented) | grok-3 |
grok-3-fast |
| Perplexity | Placeholder (not yet implemented) | sonar-pro |
sonar |
Adding a new provider requires implementing ProviderInterface and ChatSession, registering it in providers/__init__.py, and optionally adding the SDK as an optional dependency in pyproject.toml. Zero changes to the orchestrator, tools, or agent logic.
Configuration is handled by KResearchConfig (Pydantic Settings), loaded from environment variables, a .env file, and CLI overrides (CLI takes priority).
| Variable | CLI Flag | Default | Description |
|---|---|---|---|
GOOGLE_API_KEY |
— | (required for gemini) | Gemini API key. |
OPENAI_API_KEY |
— | (required for openai) | OpenAI API key. |
KRESEARCH_CUSTOM_API_KEY |
— | (required for custom) | API key for custom OpenAI-compatible endpoint. |
KRESEARCH_CUSTOM_API_BASE |
— | (required for custom) | Base URL for custom endpoint (e.g. https://api.deepseek.com/v1). |
KRESEARCH_PROVIDER |
--provider |
gemini |
LLM provider name. |
KRESEARCH_MODEL |
--model |
gemini-3-flash-preview |
Primary model for the agent loop. |
KRESEARCH_FAST_MODEL |
--fast-model |
gemini-3.1-flash-lite-preview |
Fast model for sub-agents. |
KRESEARCH_PROXY |
--proxy |
— | Global HTTP proxy for all outbound requests. |
KRESEARCH_GEMINI_PROXY |
— | — | Proxy override for the Gemini provider only. |
KRESEARCH_OPENAI_PROXY |
— | — | Proxy override for the OpenAI provider only. |
KRESEARCH_CUSTOM_PROXY |
— | — | Proxy override for the custom provider only. |
KRESEARCH_MAX_ITERATIONS |
--max-iterations |
20 |
Safety limit on agent loop iterations. Set to 0 for unlimited. |
KRESEARCH_MAX_CONCURRENT_SUBAGENTS |
— | 3 |
Maximum number of concurrent sub-agents. |
KRESEARCH_THINKING_LEVEL |
— | high |
Thinking level passed to providers that support it. |
KRESEARCH_VERBOSE |
--verbose |
false |
Enable verbose logging. |
KRESEARCH_OUTPUT_DIR |
— | . |
Default directory for saved reports. |
CLI flags > environment variables > .env file > defaults.
When multiple proxies are configured, KResearch uses the most specific one. For example, if both KRESEARCH_PROXY and KRESEARCH_GEMINI_PROXY are set, the Gemini provider uses KRESEARCH_GEMINI_PROXY while web scraping tools use KRESEARCH_PROXY.
kresearch [OPTIONS] [QUERY]
Arguments:
QUERY The research question (omit for usage info).
Options:
--model TEXT Override the primary model.
--fast-model TEXT Override the fast/sub-agent model.
--provider TEXT LLM provider (gemini, openai, anthropic, xai, perplexity).
--proxy TEXT HTTP proxy URL (e.g., http://127.0.0.1:7890).
--list-models Fetch and display available models from the API.
--verbose Enable verbose logging.
-o, --output PATH Save the final report to a file.
--max-iterations INTEGER Agent loop safety limit (0 = unlimited).
--web Launch the Web UI instead of CLI mode.
--host TEXT Web UI bind address (default: 127.0.0.1).
--port INTEGER Web UI port (default: 8000).
--help Show usage and exit.
# Basic research
kresearch "Impact of AI on healthcare diagnostics"
# Use a larger model for more complex reasoning
kresearch --model gemini-3.1-pro-preview "Compare monetary policy approaches of the Fed vs ECB"
# Use OpenAI
kresearch --provider openai --model gpt-4o "Impact of AI on healthcare diagnostics"
# Use a custom OpenAI-compatible endpoint (e.g. DeepSeek)
kresearch --provider custom --model deepseek-chat "Impact of AI on healthcare diagnostics"
# Save output and remove iteration limit
kresearch --max-iterations 0 -o deep_dive.md "History and future of nuclear fusion energy"
# List models to see what's available
kresearch --list-models
kresearch --provider openai --list-modelsKResearch includes an optional browser-based interface that streams research events in real time over WebSocket.
pip install -e ".[web]"# Via CLI flag
kresearch --web
# Or directly
kresearch-web
# Custom host/port
kresearch --web --host 0.0.0.0 --port 3000Then open http://127.0.0.1:8000 in your browser.
- Real-time streaming — tool calls, thinking, stats, and the final report stream over WebSocket as the agent works.
- Settings panel — override provider, model, max iterations, and other config fields from the browser.
- User interrupts — type a message or click Stop to redirect or halt the agent mid-research.
- Past reports — completed reports are stored in SQLite (
~/.kresearch/reports.db) and can be browsed, viewed, or deleted. - Concurrent sessions — each browser tab gets its own independent Orchestrator instance.
The Web UI uses the same UIProtocol interface as the CLI's ConsoleUI. A WebUI class implements this protocol by serializing events as JSON and sending them over WebSocket, rather than printing to the terminal. The Orchestrator itself is unchanged.
Browser <──WebSocket──> Starlette ──> Orchestrator (existing)
├── WebUI (replaces ConsoleUI)
├── Provider (existing)
├── ToolRegistry (existing)
└── ResearchState (existing)
| Variable | Default | Description |
|---|---|---|
KRESEARCH_WEB_HOST |
127.0.0.1 |
Web server bind address |
KRESEARCH_WEB_PORT |
8000 |
Web server port |
KRESEARCH_WEB_DB_PATH |
~/.kresearch/reports.db |
SQLite database path for past reports |
KResearch supports HTTP and SOCKS proxies for all outbound traffic. Proxies can be set globally or per-provider.
# Global proxy (applies to all API calls AND web scraping)
export KRESEARCH_PROXY=http://127.0.0.1:7890
# Provider-specific proxy (overrides global for that provider only)
export KRESEARCH_GEMINI_PROXY=socks5://127.0.0.1:1080
export KRESEARCH_OPENAI_PROXY=socks5://127.0.0.1:1080
export KRESEARCH_CUSTOM_PROXY=http://127.0.0.1:7890
# CLI override
kresearch --proxy http://proxy.example.com:8080 "your query"The proxy is passed to:
google.genai.Client(http_options={"proxy": ...})for Gemini API calls.openai.AsyncOpenAI(http_client=DefaultAsyncHttpxClient(proxy=...))for OpenAI and custom API calls.httpx.AsyncClient(proxy=...)forread_webpagefallback scraping.- Future provider SDK clients.
The final report is plain Markdown with this structure:
# Research Report: {Descriptive Title}
## Executive Summary
4-6 dense sentences: what was investigated, key findings with data,
conclusions, and caveats.
## {Thematic Section 1}
3-5 substantive paragraphs with inline citations [N] for every
factual claim. Specific data, statistics, expert opinions.
## {Thematic Section 2}
...
## Contradictions & Debates
Where sources disagree, both sides presented with citations.
Assessment of which has stronger evidence and why.
## Limitations
Gaps in available sources, unverifiable claims, areas needing
further investigation.
## Sources
[1] Source Title - https://source-url.com/path
[2] Source Title - https://source-url.com/path
...The report is printed to stdout as plain text and can optionally be saved to a file with -o.
src/kresearch/
├── __init__.py # Package version
├── __main__.py # python -m kresearch entry point
├── cli.py # Click CLI (argument parsing, provider init, orchestrator launch)
├── config.py # KResearchConfig (Pydantic Settings from env/.env/CLI)
├── prompts.py # SYSTEM_TEMPLATE — the 7K-char agent system prompt
├── orchestrator.py # Orchestrator class — the autonomous agent loop
│
├── models/
│ ├── mind_map.py # MindMap, MindMapNode, Source, Contradiction
│ ├── task_graph.py # TaskGraph, TaskNode, TaskStatus (sub-task tracking)
│ └── state.py # ResearchState, ActionLog, TokenUsage (session state)
│
├── providers/
│ ├── __init__.py # get_provider() factory + PROVIDER_REGISTRY
│ ├── base.py # ProviderInterface (ABC), ChatSession (ABC)
│ ├── types.py # Message, GenerateResponse, FunctionCall, ModelInfo, ToolDeclaration
│ ├── gemini/
│ │ ├── provider.py # GeminiProvider — google-genai SDK integration
│ │ └── chat.py # GeminiChatSession — multi-turn chat with batch function responses
│ ├── openai/
│ │ ├── provider.py # OpenaiProvider — openai SDK integration
│ │ └── chat.py # OpenaiChatSession — multi-turn chat with tool_call_id tracking
│ ├── custom/
│ │ └── provider.py # CustomProvider — subclass of OpenaiProvider with custom base_url
│ ├── anthropic/__init__.py
│ ├── xai/__init__.py
│ └── perplexity/__init__.py
│
├── tools/
│ ├── registry.py # ToolRegistry + create_default_registry()
│ ├── web_search.py # DuckDuckGo search via ddgs
│ ├── web_reader.py # trafilatura extraction + httpx/bs4 fallback
│ ├── code_executor.py # Sandboxed subprocess Python execution
│ ├── research_tools.py # update_findings, log_contradiction, draft_report
│ └── subagent_tool.py # spawn_subagent (mini research loop)
│
└── output/
├── console.py # ConsoleUI — Rich banner, live panel, model table
├── protocol.py # UIProtocol — async display contract (ConsoleUI & WebUI)
└── markdown.py # ensure_citations, format_source_list, save_report
├── web/ # Optional Web UI (pip install -e ".[web]")
│ ├── __init__.py # Exports create_app
│ ├── app.py # Starlette ASGI app factory + kresearch-web entry point
│ ├── models.py # Pydantic models: WSEvent, ConfigResponse, ReportSummary
│ ├── webui.py # WebUI — UIProtocol over WebSocket
│ ├── db.py # Async SQLite storage for past reports
│ ├── session.py # ResearchSession + SessionManager
│ ├── routes.py # REST endpoints (config, reports, health)
│ ├── ws.py # WebSocket endpoint for real-time streaming
│ └── static/
│ └── index.html # Single-page frontend (vanilla JS, dark theme)
CLI (cli.py)
│ Parses args, builds KResearchConfig, creates provider via get_provider()
▼
Orchestrator (orchestrator.py)
│ Builds system prompt from SYSTEM_TEMPLATE + current ResearchState
│ Creates a ChatSession via provider.create_chat()
│ Enters the agent loop
▼
Agent Loop
│ Sends query to LLM → gets response
│ If response has function_calls:
│ Execute ALL calls via ToolRegistry.execute()
│ Send ALL results back via chat.send_function_responses()
│ Process the next response (may recurse if more calls)
│ If response has text + draft_requested:
│ Return text as the final report
│ If over budget:
│ Force synthesis via _finalize()
▼
Tools (tools/*.py)
│ Each tool is an async function: (args, **ctx) -> dict
│ ctx contains: state (ResearchState), provider, config
│ Tools modify state.mind_map via add_finding(), log_contradiction()
▼
ConsoleUI (output/console.py)
│ Rich Live panel updated after each tool call
│ Shows: tool actions log, mind map tree, iteration/source/token stats
▼
Final Report → stdout and optionally → file
| Package | Purpose |
|---|---|
google-genai |
Gemini SDK — first provider implementation |
pydantic |
Data models (MindMap, ResearchState, provider types) |
pydantic-settings |
Configuration from environment, .env, CLI |
rich |
Terminal UI — banner, live progress panel, model table |
click |
CLI argument parsing |
ddgs |
DuckDuckGo web search — free, no API key, no rate limits |
trafilatura |
Article/webpage content extraction |
httpx |
Async HTTP client (fallback scraping) |
beautifulsoup4 |
HTML parsing (fallback scraping) |
lxml |
Fast HTML/XML parser (used by trafilatura and bs4) |
pytest, pytest-asyncio, ruff, mypy
openai (for OpenAI and custom providers), anthropic (for future Anthropic provider), starlette + uvicorn + aiosqlite (for the Web UI)
- OpenAI provider — GPT-4o / GPT-4o-mini via the OpenAI SDK with function calling
- Custom API provider — Any OpenAI-compatible endpoint (DeepSeek, Together, Ollama, etc.)
- Anthropic provider — Claude Sonnet / Haiku via the Anthropic SDK with tool use
- xAI provider — Grok-3 / Grok-3-fast
- Perplexity provider — Sonar Pro / Sonar for search-native research
- Ollama / local models — Run research with locally hosted models via the Ollama API
- Provider auto-fallback — If one provider fails (rate limit, downtime), automatically retry with a configured fallback
- PDF reader tool — Extract and analyze content from PDF URLs (research papers, reports)
- Image/chart analysis — Use multimodal models to interpret charts, graphs, and infographics found during research
- Academic search — Dedicated tool for Semantic Scholar / arXiv / Google Scholar APIs
- Brave Search API — Alternative search backend with an API key for higher-quality results
- Memory across sessions — Persist mind maps to disk so research can be resumed later
- Concurrent sub-agents — Run multiple sub-agents in parallel with
asyncio.gather()instead of sequentially
- Telegram bot — Run KResearch as a Telegram bot: send a query, receive the report as a message. Support for inline progress updates via message editing
- Discord bot — Same as Telegram, with thread-based research sessions
- Web UI — Starlette + WebSocket frontend: submit queries, watch research happen in real time, browse past reports
- Export formats — PDF export, HTML export, DOCX export via pandoc
- Structured JSON output — Machine-readable report format alongside Markdown for programmatic consumption
- Multi-turn follow-up — After the report is generated, allow the user to ask follow-up questions that trigger additional research with the existing mind map as context
- Source quality scoring — Automatic domain reputation scoring (academic > news > blog) to weight findings
- Fact-checking mode — Given a specific claim, verify it against multiple sources and return a confidence assessment
- Comparative research — "Compare X vs Y" mode that structures the report as a side-by-side analysis
- Timeline mode — For historical queries, build a chronological timeline with cited events
- Plugin system — Allow third-party tools to be loaded from entry points or a plugins directory
- Webhook notifications — POST the completed report to a webhook URL when research finishes
- API server mode — Run KResearch as a REST API (
kresearch serve) for integration into other applications - Docker image — Pre-built container for easy deployment
MIT — see LICENSE.
If KResearch saves you time, consider buying me a coffee: