deep-search-agent
A Python library for deep internet searches (deep search), similar to the deep research features of ChatGPT and Claude. Built on top of LangChain's deepagents.
📖 Documentation: https://giurlanda.github.io/deep-search-agent/
Architecture
The create_deep_search_agent factory returns a deep agent configured with the
orchestrator + specialized sub-agents + evaluation loop pattern:
| Component | Implementation |
|---|---|
| Orchestrator | Main agent (create_deep_agent): decomposes the query with write_todos, delegates, synthesizes with citations |
perspective-agent |
Explores the topic from 3-6 distinct angles (analysis axes, stakeholder viewpoints, dimensions of the problem) before decomposition, saved to /research/perspectives.md; enabled by default, toggle with enable_perspectives=False |
search-agent |
Web search via SearxNG (+ optional additional search tools), reformulates queries, saves results with their source |
fetch-agent |
Downloads and extracts content from URLs: clean HTML with trafilatura, PDFs read with pypdf, User-Agent from real browsers |
fact-check-agent |
Verifies claims against multiple independent sources (has both search and fetch) |
| Shared memory | deepagents virtual filesystem: each sub-agent writes /findings/<source-slug>.md with URL, date, and claims |
| Shared source index | /findings/_sources.md: one line per URL (saved / failed / discarded) that every agent consults and appends to, so searches and fetches are not duplicated across cycles |
| Evaluator/critic | RubricMiddleware (beta): an LLM grader evaluates the answer against a rubric and re-runs the orchestrator up to max_research_cycles cycles |
Each sub-agent runs with an isolated context: raw page content does not pollute
the orchestrator's memory; only the synthetic reports and the files in
findings/ bubble up.
Installation
uv sync # from the repository
# or, as a dependency:
uv add deep-search-agent
Requires Python ≥ 3.12. The search tool needs a reachable
SearxNG instance with the JSON format enabled
(default: http://localhost:8888).
Quickstart
from deep_search_agent import create_deep_search_agent
agent = create_deep_search_agent(
model="anthropic:claude-sonnet-4-6",
searxng_base_url="http://localhost:8888",
max_research_cycles=3,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "State of the art in quantum error correction in 2026?"}]},
config={"configurable": {"thread_id": "research-1"}},
)
print(result["messages"][-1].content)
The default evaluation rubric (DEEP_SEARCH_RUBRIC) is injected automatically:
the refinement loop works with no configuration. For an ad-hoc rubric, pass it
in the invoke state ({"rubric": "- ..."}) or to the factory
(rubric="- ...").
Factory parameters
Deep-search-specific parameters:
| Parameter | Default | Description |
|---|---|---|
model |
— (required) | Orchestrator model; inherited by sub-agents and the rubric grader |
max_research_cycles |
3 |
Maximum refinement cycles of the evaluator loop (and budget cited in the orchestrator prompt) |
max_query_variants |
3 |
Number of parallel query variants the search agent issues per sub-question (synonyms, broader/narrower terms, English variants) to widen recall |
max_search_results_per_query |
5 |
Maximum results per search query |
max_urls_to_scrape_per_cycle |
3 |
Maximum URLs to fetch per research cycle |
searxng_base_url |
http://localhost:8888 |
URL of the SearxNG instance |
searxng_engines |
None |
List of SearxNG engines to restrict the search to |
searxng_rate_limit |
None |
Minimum seconds between SearxNG requests (thread-safe, shared across concurrent searches); None disables rate limiting |
searxng_budget |
None |
Maximum SearxNG searches per research cycle; when exhausted the tool returns an ERROR: telling the model no budget is left. None means unlimited |
request_timeout |
15.0 |
HTTP timeout (s) for search and fetch |
max_content_chars_per_page |
20000 |
Truncation of extracted content per page |
enable_js_render_fallback |
False |
Re-fetch pages whose static HTML yields no content through a headless Chromium, recovering JavaScript-only pages and bot walls. Requires the js-render extra plus playwright install chromium |
js_render_timeout |
30.0 |
Seconds the headless renderer waits for a page to settle; ignored unless the fallback is enabled |
search_tools |
None |
Additional search tools for search-agent, fact-check-agent, and perspective-agent (e.g. Tavily, RAG retrieval) |
enable_perspectives |
True |
Adds perspective-agent and instructs the orchestrator to delegate to it before decomposing the query. Set False for simple queries where a single-axis decomposition is sufficient |
rubric |
DEEP_SEARCH_RUBRIC |
Custom evaluation rubric |
auto_rubric |
True |
Auto-inject the rubric into the state on every invoke |
on_evaluation |
None |
Callback invoked with each RubricEvaluation after the grader scores a cycle (e.g. to log verdicts); exceptions are logged and suppressed |
subagents_middleware |
() |
Extra middleware injected into each built-in sub-agent (perspective-agent, search-agent, fetch-agent, fact-check-agent) |
subagents |
None |
Extra sub-agents, added to the built-in ones |
backend |
StateBackend() |
Filesystem backend shared by the orchestrator and every sub-agent |
All other keyword arguments (tools, checkpointer, store, skills,
interrupt_on, ...) are passed through unchanged to create_deep_agent.
Extensions
Adding a retrieval agent (RAG) over the internal knowledge base
rag_agent = {
"name": "rag-agent",
"description": "Retrieval over the internal company knowledge base",
"system_prompt": "Search the vector store and save results to findings/.",
"tools": [my_vector_store_tool],
}
agent = create_deep_search_agent(
model="anthropic:claude-sonnet-4-6",
subagents=[rag_agent],
)
Adding search engines
from langchain_tavily import TavilySearch
agent = create_deep_search_agent(
model="anthropic:claude-sonnet-4-6",
search_tools=[TavilySearch(max_results=5)],
)
Persistent backend for findings
from deepagents.backends import FilesystemBackend
agent = create_deep_search_agent(
model="anthropic:claude-sonnet-4-6",
backend=FilesystemBackend(root_dir="./research", virtual_mode=True),
)
Testing
uv run pytest
The unit tests require neither network nor API keys: HTTP and LLM are simulated.
Publishing
Releases are published to PyPI by
the publish workflow, triggered on pushing a
v* tag. It uses PyPI Trusted Publishing (OIDC), so no API token is stored.
One-time setup on PyPI (Account → Publishing → Add a pending publisher):
| Field | Value |
|---|---|
| PyPI project name | deep-search-agent |
| Owner | giurlanda |
| Repository name | deep-search-agent |
| Workflow name | publish.yml |
| Environment name | pypi |
To cut a release: bump __version__ in
src/deep_search_agent/__init__.py, update
the CHANGELOG, then push a matching tag (e.g. git tag v0.4.1 && git push origin v0.4.1). Build locally with uv build and validate with
uvx twine check dist/*.
Package structure
src/deep_search_agent/
├── factory.py # create_deep_search_agent
├── prompts.py # orchestrator/sub-agent prompts + default rubric
├── middleware.py # DefaultRubricMiddleware (rubric auto-injection)
├── subagents.py # perspective/search/fetch/fact-check agent definitions
└── tools/
├── search.py # SearxNG tool
└── fetch.py # URL fetch tool (trafilatura + pypdf)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file deep_search_agent-0.4.1.tar.gz.
File metadata
- Download URL: deep_search_agent-0.4.1.tar.gz
- Upload date:
- Size: 51.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca686843aeae3ef33055d75887ed99344e8123b297206c522c028104d7daa5a1
|
|
| MD5 |
422221fd6ef0d7fa00321154d2199b72
|
|
| BLAKE2b-256 |
74248f4132d3bf9243c9df34690a5d50f412a3c28658fcff17e60328a59155cc
|
Provenance
The following attestation bundles were made for deep_search_agent-0.4.1.tar.gz:
Publisher:
publish.yml on giurlanda/deep-search-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
deep_search_agent-0.4.1.tar.gz -
Subject digest:
ca686843aeae3ef33055d75887ed99344e8123b297206c522c028104d7daa5a1 - Sigstore transparency entry: 2217950624
- Sigstore integration time:
-
Permalink:
giurlanda/deep-search-agent@9fcab1bce049801101b6a26828936b90aca764fb -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/giurlanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9fcab1bce049801101b6a26828936b90aca764fb -
Trigger Event:
push
-
Statement type:
File details
Details for the file deep_search_agent-0.4.1-py3-none-any.whl.
File metadata
- Download URL: deep_search_agent-0.4.1-py3-none-any.whl
- Upload date:
- Size: 39.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0df0971b905a11b45532627653abd4e8be521d697c8a6fc8180107973aef9c0e
|
|
| MD5 |
2225134ea45ae3ab1dddcd8d14e64650
|
|
| BLAKE2b-256 |
0716cb34efb8596d8f0fb71b148c1f445d00bc810ab5adcb7ca06c8d987fd5d8
|
Provenance
The following attestation bundles were made for deep_search_agent-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on giurlanda/deep-search-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
deep_search_agent-0.4.1-py3-none-any.whl -
Subject digest:
0df0971b905a11b45532627653abd4e8be521d697c8a6fc8180107973aef9c0e - Sigstore transparency entry: 2217950665
- Sigstore integration time:
-
Permalink:
giurlanda/deep-search-agent@9fcab1bce049801101b6a26828936b90aca764fb -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/giurlanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9fcab1bce049801101b6a26828936b90aca764fb -
Trigger Event:
push
-
Statement type: