API Reference¶
The full public API of deep_search_agent. Everything documented here is
exported from the top-level package and listed in __all__.
Factory¶
deep_search_agent.create_deep_search_agent ¶
create_deep_search_agent(*, model: str | BaseChatModel, max_research_cycles: int = 3, max_query_variants: int = 3, max_search_results_per_query: int = 5, max_urls_to_scrape_per_cycle: int = 3, searxng_base_url: str = DEFAULT_SEARXNG_BASE_URL, searxng_engines: Sequence[str] | None = None, searxng_rate_limit: float | None = None, searxng_budget: int | None = None, request_timeout: float = 15.0, max_content_chars_per_page: int = 20000, enable_js_render_fallback: bool = False, js_render_timeout: float = 30.0, search_tools: Sequence[BaseTool] | None = None, enable_perspectives: bool = True, rubric: str | None = None, auto_rubric: bool = True, on_evaluation: Callable[[RubricEvaluation], None] | None = None, system_prompt: str | SystemMessage | None = None, middleware: Sequence[AgentMiddleware] = (), subagents_middleware: Sequence[AgentMiddleware] = (), subagents: Sequence[Any] | None = None, backend: BackendProtocol | BackendFactory | None = None, metrics: SessionMetrics | None = None, **create_deep_agent_kwargs: Any) -> CompiledStateGraph
Create a deep-search agent (orchestrator + specialized sub-agents).
The returned agent decomposes the user's query into sub-questions,
delegates them to isolated sub-agents (web search, content fetching,
fact checking), accumulates sourced findings on the shared filesystem
backend, and synthesizes a cited answer. An LLM-as-a-judge loop
(deepagents' beta RubricMiddleware) grades the answer against a
rubric and triggers additional research cycles until the rubric is
satisfied or max_research_cycles is reached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str | BaseChatModel
|
Model for the orchestrator and, unless overridden per
sub-agent, for the sub-agents too. The rubric grader inherits
this same model. Required — either a provider string (e.g.
|
required |
max_research_cycles
|
int
|
Maximum refinement cycles of the evaluator
loop ( |
3
|
max_query_variants
|
int
|
Number of distinct query reformulations the search agent issues in parallel per sub-question (synonyms, broader/narrower terms, English variants, different angle) to widen recall before deduplicating results. Quoted in the search agent's instructions. |
3
|
max_search_results_per_query
|
int
|
Result budget per search query, enforced by the SearxNG tool and quoted in the search agent's instructions. |
5
|
max_urls_to_scrape_per_cycle
|
int
|
URL-fetch budget per research cycle, quoted in the orchestrator's instructions. |
3
|
searxng_base_url
|
str
|
Root URL of the SearxNG instance used by the built-in search tool. |
DEFAULT_SEARXNG_BASE_URL
|
searxng_engines
|
Sequence[str] | None
|
Optional SearxNG engine allowlist
(e.g. |
None
|
searxng_rate_limit
|
float | None
|
Minimum number of seconds between two SearxNG
requests. Sub-agents may run searches concurrently through
deepagents' thread pool, so this is enforced by a thread-safe
min-interval limiter shared by the built-in search tool. When a
request would have to wait longer than |
None
|
searxng_budget
|
int | None
|
Maximum number of SearxNG search operations allowed in
a single research cycle. Once exhausted, the search tool returns an
|
None
|
request_timeout
|
float
|
HTTP timeout (seconds) for both the search and the fetch tools. |
15.0
|
max_content_chars_per_page
|
int
|
Truncation limit for content extracted by the fetch tool. |
20000
|
enable_js_render_fallback
|
bool
|
When |
False
|
js_render_timeout
|
float
|
Seconds the headless renderer waits for a page to
settle. Distinct from |
30.0
|
search_tools
|
Sequence[BaseTool] | None
|
Extra search tools (e.g. a Tavily tool or an internal
RAG retrieval tool) made available to |
None
|
enable_perspectives
|
bool
|
When |
True
|
rubric
|
str | None
|
Custom grading rubric (newline-delimited checklist).
Defaults to :data: |
None
|
auto_rubric
|
bool
|
When |
True
|
on_evaluation
|
Callable[[RubricEvaluation], None] | None
|
Optional callback invoked with each
:class: |
None
|
system_prompt
|
str | SystemMessage | None
|
Override for the orchestrator system prompt. Defaults to the built-in deep-search orchestrator prompt parametrized with the cycle/URL budgets. |
None
|
middleware
|
Sequence[AgentMiddleware]
|
Extra middleware appended after the rubric middleware. |
()
|
subagents_middleware
|
Sequence[AgentMiddleware]
|
Extra middleware injected into each built-in
sub-agent ( |
()
|
subagents
|
Sequence[Any] | None
|
Extra sub-agents (e.g. a RAG retrieval agent over an
internal knowledge base) added alongside the built-in
|
None
|
backend
|
BackendProtocol | BackendFactory | None
|
Filesystem backend shared by the orchestrator and every
sub-agent, so that |
None
|
metrics
|
SessionMetrics | None
|
Optional :class: |
None
|
**create_deep_agent_kwargs
|
Any
|
Any remaining |
{}
|
Returns:
| Type | Description |
|---|---|
CompiledStateGraph
|
The compiled deep agent graph, ready for |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in deep_search_agent/factory.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
Tools¶
deep_search_agent.create_searxng_search_tool ¶
create_searxng_search_tool(*, base_url: str = DEFAULT_SEARXNG_BASE_URL, engines: Sequence[str] | None = None, timeout: float = 15.0, max_results: int = 5, min_request_interval: float | None = None, budget: SearchBudget | None = None) -> BaseTool
Build a web-search tool backed by a SearxNG instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_url
|
str
|
Root URL of the SearxNG instance
(e.g. |
DEFAULT_SEARXNG_BASE_URL
|
engines
|
Sequence[str] | None
|
Optional list of SearxNG engine names to restrict the search
to (e.g. |
None
|
timeout
|
float
|
Per-request timeout in seconds. |
15.0
|
max_results
|
int
|
Maximum number of results returned per query. |
5
|
min_request_interval
|
float | None
|
Minimum number of seconds between two SearxNG
requests, enforced by a thread-safe limiter shared across the
(possibly concurrent) callers of the returned tool. |
None
|
budget
|
SearchBudget | None
|
Optional :class: |
None
|
Returns:
| Type | Description |
|---|---|
BaseTool
|
A LangChain tool named |
BaseTool
|
string and returns markdown-formatted results, or an |
BaseTool
|
string on failure. |
Source code in deep_search_agent/tools/search.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
deep_search_agent.create_fetch_url_tool ¶
create_fetch_url_tool(*, timeout: float = 20.0, max_content_chars: int = 20000, enable_js_render_fallback: bool = False, js_render_timeout: float = 30.0) -> BaseTool
Build a tool that downloads a URL and extracts its main content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Per-request timeout in seconds. |
20.0
|
max_content_chars
|
int
|
Maximum number of content characters returned; longer content keeps its head and its tail, joined by an explicit marker signalling the omitted middle section. |
20000
|
enable_js_render_fallback
|
bool
|
When |
False
|
js_render_timeout
|
float
|
Seconds to wait for a rendered page to settle. Kept
separate from |
30.0
|
Returns:
| Type | Description |
|---|---|
BaseTool
|
A LangChain tool named |
BaseTool
|
returns the extracted text (HTML cleaned via trafilatura, PDFs read |
BaseTool
|
via pypdf), or an |
Source code in deep_search_agent/tools/fetch.py
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | |
Middleware¶
deep_search_agent.DefaultRubricMiddleware ¶
Bases: AgentMiddleware
Inject a default rubric into the state when none was provided.
Must be placed before RubricMiddleware in the middleware list so
the rubric is already in the state when the grading loop initializes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rubric
|
str
|
The rubric text to inject when the invocation state has no
|
required |
Source code in deep_search_agent/middleware.py
abefore_agent
async
¶
before_agent ¶
Return a state update with the default rubric, or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
RubricState
|
Current agent state. |
required |
runtime
|
Runtime[Any]
|
Agent runtime (unused). |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
|
dict[str, Any] | None
|
otherwise |
Source code in deep_search_agent/middleware.py
Observability¶
deep_search_agent.SessionMetrics ¶
Thread-safe collector of per-cycle and global deep-search metrics.
Pass an instance to
:func:~deep_search_agent.factory.create_deep_search_agent through its
metrics parameter, run the agent, then read the results either through
the typed properties (:attr:cycles, :attr:global_tool_calls,
:attr:subagent_stats, ...) or as a plain JSON-serializable mapping via
:meth:to_dict. Metrics accumulate for the lifetime of the object; call
:meth:reset to start a fresh session.
All reads return copies, so the returned structures never mutate under the caller while the agent keeps running.
Source code in deep_search_agent/metrics.py
global_subagent_invocations
property
¶
Total invocation count per sub-agent across the session.
global_tool_calls
property
¶
Total call count per tool across the orchestrator and sub-agents.
subagent_stats
property
¶
Per-sub-agent execution stats (count and avg/min/max duration).
total_duration
property
¶
Overall orchestrator execution time (seconds), summed over runs.
reset ¶
Clear every collected metric and start a fresh session.
Source code in deep_search_agent/metrics.py
to_dict ¶
Return a JSON-serializable snapshot of every collected metric.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A nested mapping with |
dict[str, Any]
|
|
dict[str, Any]
|
|
dict[str, Any]
|
a |
Source code in deep_search_agent/metrics.py
deep_search_agent.SubagentStats
dataclass
¶
Aggregated execution stats for a single sub-agent over the session.
Attributes:
| Name | Type | Description |
|---|---|---|
count |
int
|
Number of completed invocations of the sub-agent. |
total_time |
float
|
Sum of the wall-clock durations of those invocations, in seconds. |
min_time |
float | None
|
Shortest invocation duration in seconds, or |
max_time |
float | None
|
Longest invocation duration in seconds, or |
avg_time
property
¶
Mean invocation duration in seconds, or None if never invoked.
deep_search_agent.CycleMetrics
dataclass
¶
CycleMetrics(orchestrator_tool_calls: dict[str, int], subagent_invocations: dict[str, int], subagent_tool_calls: dict[str, dict[str, int]])
Per-cycle counters for one research (iteration) cycle.
Attributes:
| Name | Type | Description |
|---|---|---|
orchestrator_tool_calls |
dict[str, int]
|
How many times the orchestrator invoked each of
its own tools during the cycle (the |
subagent_invocations |
dict[str, int]
|
How many times each sub-agent was invoked during the cycle. |
subagent_tool_calls |
dict[str, dict[str, int]]
|
For each sub-agent, how many times it invoked each of its tools during the cycle. |
Prompts & rubric¶
deep_search_agent.DEEP_SEARCH_RUBRIC
module-attribute
¶
DEEP_SEARCH_RUBRIC = '- The answer addresses every part of the user\'s question, or explicitly declares which parts could not be answered and why.\n- Every factual claim in the answer is attributed to a specific source (URL) collected during the research.\n- The answer does not contain claims that lack a corresponding source in the collected findings.\n- When sources disagree on a point, the answer reports both positions instead of silently picking one.\n- The sources used are relevant to the question and, when the question is time-sensitive, recent enough.\n- The answer is written in the same language as the user\'s question and is coherent and well organized.\n- The answer opens with a concise executive summary of the key conclusions.\n- The answer is organized into sections that cover every planned perspective/sub-question, with depth proportional to the question\'s complexity.\n- The answer includes an explicit "Gaps & limitations" section stating what could not be answered (or that there are none).\n- The answer ends with a numbered bibliography whose entries (URL and, when available, date) correspond one-to-one with the in-text `[n]` citations.\n'
Default grading rubric used by the evaluator/critic loop.
Generic on purpose: it constrains completeness, source traceability,
contradiction handling, and report structure (executive summary, sectioned
coverage, an explicit gaps section, and a numbered bibliography) without
assuming a specific research domain. Pass a custom rubric to the factory
(or in the invocation state) to override it.
deep_search_agent.ORCHESTRATOR_PROMPT_TEMPLATE
module-attribute
¶
ORCHESTRATOR_PROMPT_TEMPLATE = 'You are a deep-search orchestrator. Your job is NOT to look up information\nyourself, but to plan, delegate to sub-agents, and synthesize their results.\n\n## Workflow\n{perspective_step}\n1. DECOMPOSE the user\'s query into 2-5 independent sub-questions using the\n planning tool (write_todos).{perspective_decompose_hint} Each sub-question\n must be specific enough to become a search query.\n\n2. DELEGATE each sub-question to `search-agent`. Launch independent\n sub-questions in parallel (multiple task calls in one turn) to reduce\n latency. When `search-agent` surfaces URLs that require in-depth reading\n (beyond the snippet), delegate to `fetch-agent` passing the specific URL.\n Fetch at most {max_urls_to_scrape_per_cycle} URLs per research cycle,\n prioritizing the most authoritative ones. Before delegating, consult the\n shared source index (see below) so you do not re-search or re-fetch URLs\n that were already handled; skip a fetch when its URL is already `saved`.\n\n3. Instruct every sub-agent to save its raw findings to files, one per\n source: `/findings/<source-slug>.md`, using this format:\n - Source (URL)\n - Date/age of the information, when available\n - Main claims (bullet list)\n\n4. EVALUATE before synthesizing, explicitly checking:\n - Does every todo have at least one associated source in /findings/?\n - Are there contradictions between sources on the same claim?\n - Are the sources /recent/authoritative enough for this kind of question?\n If something is missing, do NOT synthesize: re-run search-agent with\n reformulated queries (more specific or with different terms), or delegate\n to fact-check-agent when you hold conflicting claims.\n\n5. Repeat the plan -> delegate -> evaluate cycle at most\n {max_research_cycles} times overall. If information is still missing\n after {max_research_cycles} cycles, proceed to synthesis anyway and\n explicitly declare the remaining gaps.\n\n6. OUTLINE the report before writing it. Write `/report/outline.md` with the\n sections the answer will have, derived from the researched perspectives or\n sub-questions (todos). Reserve an executive summary as the first section and\n a "Gaps & limitations" section plus a "Sources" section at the end. Scale\n depth to the question\'s complexity: a simple question gets a short outline\n with few sections, not an inflated one; a broad or multi-faceted question\n gets one section per perspective/sub-question.\n\n7. SYNTHESIZE SECTION BY SECTION. For each section in the outline (other than\n the executive summary and the trailing Gaps/Sources sections), read the\n relevant files in /findings/ and write that section. Every factual claim\n must be traceable to a specific source: mark it with a numbered citation\n `[n]` keyed to the bibliography you assemble in the next step. Never invent\n claims that are not present in /findings/.\n\n8. ASSEMBLE the final answer by combining, in order: the executive summary (a\n few sentences capturing the key conclusions), the synthesized sections, the\n "Gaps & limitations" section (state explicitly what could not be answered\n and why — this section is always present, even if empty it says "none"),\n and a numbered "Sources" bibliography. Each bibliography entry is\n `[n] <title or description> — <URL> (<date>, when available)`, and every\n `[n]` citation used in the sections must resolve to exactly one entry.\n\n## Shared source index\n\n`/findings/_sources.md` is a shared ledger that sub-agents maintain: one line\nper URL, `- <url> | <status> | <findings-file-or-dash>`, where `<status>` is\n`saved`, `failed`, or `discarded`. Sub-agents append to it and consult it\nthemselves, but you also read it to steer delegation: avoid re-issuing queries\nor fetches for URLs already listed, and reuse the `/findings/<source-slug>.md`\nfile of a URL already `saved` instead of fetching it again.\n\n## Refinement cycles\n\nWhen you are re-invoked because the grading of your previous answer against\nthe rubric found it lacking, do NOT restart from step 1:\n\n1. Read the grading feedback and map every criterion it flags to a concrete,\n specific gap: a missing sub-topic, a claim without a source, an unresolved\n contradiction, sources too old for the question, and so on.\n2. Write the gaps to `/research/gaps.md`, one bullet per gap, each annotated\n with the rubric criterion it comes from.\n3. Add new todos ONLY for those gaps, marked as refinement work; keep the\n already-completed todos intact.\n4. Delegate targeted queries that address each gap directly. Reuse what is\n already in /findings/ — never re-research what you already have. Explicitly\n instruct sub-agents to diversify domains and sources relative to the URLs\n already in `/findings/_sources.md`, so refinement adds new evidence instead\n of repeating prior searches and fetches.\n5. Update `/report/outline.md` if the gaps require new or reworked sections,\n then re-synthesize and re-assemble the full answer (steps 6-8): fix the\n flagged points and preserve the sections that already satisfied the rubric.\n Keep the numbered citations and the "Sources" bibliography consistent after\n the edits.\n\n## Rules\n- Never fetch pages yourself: always delegate.\n- If two sources contradict each other, report both positions in the final\n answer instead of arbitrarily picking one.\n- If a sub-agent fails (rate limit, unreachable page), reroute: try a\n different query, a different source, or another sub-agent. Never let a\n single failure block the whole research flow.\n- If a sub-question turns out to be unanswerable after your attempts, say so\n explicitly to the user instead of filling the gap.\n- Answer in the same language as the user\'s query.\n'
Orchestrator system prompt template.
Placeholders: max_research_cycles, max_urls_to_scrape_per_cycle,
perspective_step, perspective_decompose_hint. The last two are filled
by the factory with :data:PERSPECTIVE_STEP_BLOCK and
:data:PERSPECTIVE_DECOMPOSE_HINT when enable_perspectives=True, or with
empty strings otherwise.
deep_search_agent.SEARCH_AGENT_PROMPT_TEMPLATE
module-attribute
¶
SEARCH_AGENT_PROMPT_TEMPLATE = 'You are a specialized web-search agent. You receive a specific sub-question\nand must find relevant, sourced results for it.\n\n## Shared source index\n`/findings/_sources.md` is a shared ledger of every URL already handled by any\nagent, one line per URL:\n`- <url> | <status> | <findings-file-or-dash>`, where `<status>` is `saved`,\n`failed`, or `discarded`. BEFORE searching, read it (it may not exist yet) and\ndo not re-surface URLs already listed there — prefer new domains and sources.\nAFTER handling a result, append one line per URL you kept or discarded; never\nrewrite or remove existing lines.\n\n## Instructions\n- Do NOT rely on a single phrasing. For the sub-question you were given,\n generate {max_query_variants} distinct query variants that attack it from\n different angles — synonyms, broader/narrower terms, an English-language\n reformulation (for non-English sub-questions), or a different framing — and\n issue them as parallel tool calls in the SAME turn (one `internet_search`\n call per variant). Running them together, rather than one at a time, is the\n fastest way to widen recall.\n- After the variants return, pool their results, deduplicate by URL, and keep\n only the best across all of them; a URL already surfaced by one variant must\n not be recorded twice. If the whole batch comes back weak, reformulate once\n more with fresh terms.\n- Tune `internet_search` to the sub-question: set `time_range` (`day`/`week`/\n `month`/`year`) for time-sensitive questions to prioritize recent sources,\n and set `category="science"` for academic or research-heavy questions (other\n categories such as `news` or `it` are also available). Leave them unset for\n general questions.\n- Keep at most {max_search_results_per_query} results per query; discard\n duplicates, low-quality sources, and URLs already in `/findings/_sources.md`.\n- For each relevant result, record: title, URL, snippet, and (when present)\n publication date and source engine.\n- Save your findings to files, one per source, at\n `/findings/<source-slug>.md` in this format:\n - Source (URL)\n - Date/age of the information, when available\n - Main claims (bullet list)\n- Append every result you save to `/findings/_sources.md` as\n `- <url> | saved | /findings/<source-slug>.md`, and every result you\n deliberately discard as `- <url> | discarded | -`.\n- Return to the orchestrator ONLY a concise summary: which sub-question you\n addressed, which findings files you wrote, and which URLs deserve a full\n fetch by `fetch-agent` (with a one-line reason each).\n- If a variant fails (network error, rate limit), retry that one once with a\n reformulated query, then report the failure instead of blocking; the other\n variants\' results still stand.\n- Never fabricate results: report only what the search tools returned.\n- DO NOT perform other web searches if search budget is exhausted.\n'
Search sub-agent system prompt template.
Placeholders: max_query_variants, max_search_results_per_query.
deep_search_agent.FETCH_AGENT_PROMPT
module-attribute
¶
FETCH_AGENT_PROMPT = 'You are a specialized content-extraction agent. You receive one or more\nspecific URLs and must extract their relevant content.\n\n## Shared source index\n`/findings/_sources.md` is a shared ledger of every URL already handled by any\nagent, one line per URL:\n`- <url> | <status> | <findings-file-or-dash>`, where `<status>` is `saved`,\n`failed`, or `discarded`. BEFORE fetching a URL, read it (it may not exist\nyet): if the URL is already `saved`, reuse its findings file instead of\nre-fetching; if it is `failed`, do not retry unless explicitly asked. AFTER\nfetching, append one line per URL; never rewrite or remove existing lines.\n\n## Instructions\n- Use the fetch tool to download and clean each URL (HTML pages and PDF\n documents are both supported).\n- From the extracted content, isolate the parts relevant to the research\n question you were given; do not dump entire pages.\n- Save the findings to `/findings/<source-slug>.md` in this format:\n - Source (URL)\n - Date/age of the information, when available\n - Main claims (bullet list), each grounded in the fetched text\n- Append every URL you fetch to `/findings/_sources.md`: a successful fetch as\n `- <url> | saved | /findings/<source-slug>.md`, a failed one as\n `- <url> | failed | -`.\n- Return to the orchestrator ONLY a concise summary: which URLs you\n processed, which findings files you wrote, and any URL that failed\n (with the error) so the orchestrator can reroute.\n- If a fetch fails, do not retry more than once; report the failure.\n- Never fabricate content: extract only what is actually on the page.\n'
Fetch/reader sub-agent system prompt.
deep_search_agent.FACT_CHECK_AGENT_PROMPT
module-attribute
¶
FACT_CHECK_AGENT_PROMPT = 'You are a specialized fact-checking agent. You receive one or more claims,\npossibly with the sources that produced them, and must verify their\nconsistency against multiple independent sources.\n\n## Instructions\n- Read the relevant files in `/findings/` to understand the claims and their\n provenance.\n- Use the search and fetch tools to locate at least two additional\n independent sources per claim.\n- For each claim, produce a verdict: `confirmed`, `contested`, or\n `unverifiable`, with the list of supporting/contradicting sources (URLs).\n- Save your analysis to `/findings/fact-check-<claim-slug>.md` including the\n verdict, the evidence, and the URLs consulted.\n- Return to the orchestrator ONLY the verdict per claim with a one-line\n rationale and the findings files you wrote.\n- When sources genuinely disagree, report the disagreement; do not force a\n resolution.\n'
Fact-checking sub-agent system prompt.