-
Notifications
You must be signed in to change notification settings - Fork 0
Codebase Review
This document contains a comprehensive, strict review of the DeepDelve project. It evaluates the architecture, state management, grounding systems, and specific bug patterns found in the codebase.
DeepDelve is a 3-tier multi-agent deep research assistant that improves significantly upon its predecessor. Its primary architectural strengths are:
- Per-Attempt Quota Top-Up: Resolves the retry quota starvation bug structurally.
- Auto-Fetch Fusion: Combines search and fetch to prevent LLMs from shortcutting to snippets.
- Artifact Quarantine: Renames flawed drafts aside to prevent the model from re-conditioning on its own mistakes.
-
Deterministic URL Grounding: Centralized tracking of URLs fetched via
fetch_url_to_workspacerather than trusting the model's narration.
However, a strict code audit has revealed several latent design flaws, thread-safety violations, and broken fallbacks that undermine its production readiness.
One of the key issues tracked in the roadmap is: "salvage doesn't fire on a second quarantine."
The salvage fallback (_salvage_narrated_report) is designed to capture reports that the model narrates in chat text but fails to write via the write_workspace_file tool. However, the current implementation is flawed due to recency bias and per-turn state resets:
-
The Turn Reset in CLI (
run_cliin tui.py): At the start of each iteration in the execution loop, the text accumulator is reset:while has_requests: has_requests = False user_input_requests = [] turn_text = "" # <-- RESET HERE
-
The Turn Reset in TUI (
run_agentin tui.py): In TUI mode,state["current_msg"]is explicitly cleared toNonewhenever a function call or result is processed:elif content.type == "function_result": ... state["current_msg"] = None # <-- WIPE HERE
Let's trace how this happened in the actual session log session_ffe5dbc7-4730-4093-98e3-8d51d4dbb0ac.json:
-
Attempt 1 (First Check): The required artifact
final_report.mdwas missing. The system nudged the model, returningshould_continue = True. -
Attempt 2 (Second Check): The agent called
write_workspace_file, but the report cited ungrounded URLs (https://arxiv.org/html/2603.26718v2andhttps://www.facebook.com/...). The grounding check failed, quarantinedfinal_report.mdasfinal_report.md.rejected_attempt_2, and nudged the model again. -
Attempt 3 (Third Check): The model delegated research, got results, and then narrated the entire correct report as chat text (including a python code snippet showing how to write the file, but without actually invoking the
write_workspace_filetool). -
The System Check: Since the tool was not called,
final_report.mdwas missing.problemwas set to"missing_artifact". -
The Loop Transition: Since
attempt = 2(which is< MAX_COMPLETION_CHECK_ATTEMPTSof 3), the completion check returnedTrue, advancing the attempt counter to3and instructing the model:SYSTEM WARNING: THIS IS YOUR FINAL ATTEMPT... Pushing agent to create it. -
Attempt 4 (Final Check): The loop executed one last time. But the model, seeing the final nudge, either produced nothing, errored out, or reached its token limit. Thus,
turn_text(orstate["current_msg"]) for this final iteration was""(empty string). -
Budget Exhausted: The loop exited. The system checked if it should salvage the report. However, it called
_salvage_narrated_report(..., last_assistant_text=turn_text)whereturn_textwas"". - Conclusion: The long, highly-accurate report narrated in Attempt 3 was thrown away because the system only checked the text of the immediate last turn (Attempt 4), which was empty.
In TUI mode, salvage is structurally impossible in almost all cases. Because state["current_msg"] is cleared to None on any function call or result, if the model narrates a draft and then invokes any tool (e.g., think_tool, write_todos), turn_msg becomes None before run_completion_check runs. Thus, last_assistant_text is passed as "" and salvage is skipped.
Tiers 2 and 3 are architecturally separated in app.py into distinct sub-agents:
- Tier 2:
WebSearchervs.AcademicSearcher - Tier 3:
DocumentAnalyzervs.DataAnalyzer
However, if you inspect the tool list definitions in app.py:
- Both
WebSearcherandAcademicSearcherare configured with the exact same tools:[web_search, fetch_url_to_workspace, think_tool]. - Both
DocumentAnalyzerandDataAnalyzerare configured with:[read_workspace_file, grep_workspace_file, think_tool].
Problem: The specialization is 100% prompt-driven; there are no hard tool or network-level constraints enforcing the separation. If the Planner misroutes a task or if the specialist model suffers from prompt drift, the specialization fails with no system-level guardrails.
Recommendation: The AcademicSearcher should have distinct, literature-specific tools (e.g., an ArXiv or Semantic Scholar API tool), while WebSearcher should be barred from using academic filters. Likewise, the Analyzers should have distinct parser utilities (e.g., JSON/CSV structured extraction tools for DataAnalyzer and semantic summarization tools for DocumentAnalyzer).
In tui.py, the session events and IDs are defined as global variables:
_session_events = []
_current_call_by_source = {}
_current_text_by_source = {}
_current_session_id = str(uuid.uuid4())Problem: This design is highly stateful and completely thread-unsafe. While acceptable for a single-user CLI run, if DeepDelve is run in web mode (python src/app.py --web which serves the TUI using textual-serve), multiple users accessing the web application in parallel will share the same global session lists.
- User A's logs and prompts will leak into User B's UI stream.
- The shared lists will suffer from concurrent write corruption, causing the engine to crash with
IndexErrororKeyErrorinsidelog_stream_content.
Recommendation: All session log lists and variables must be refactored into instance attributes of the Textual App class (e.g., self._session_events) or managed via task-isolated contextvars.
The RunState class in run_state.py defines:
def add_finding(self, source_url: str, summary: str) -> None:
self.data["findings"].append({"source_url": source_url, "summary": summary, ...})Problem: This function is never called anywhere in the codebase.
- The agent framework executes and outputs content to the UI and workspace files, but the structured findings cache remains empty.
- Because
findingsis never populated, the "Workflow as Knowledge" goal (reusing extracted facts to answer or verify future similar queries) is only half-implemented.
Recommendation: The output of sub-agent runs inside delegate_tasks should be parsed, and their findings (URL + summary block) should be explicitly logged into RunState via add_finding().
The content-level grounding check extracts salient terms using regex in tui.py:
for m in re.finditer(r'\b[A-Z][a-zA-Z0-9]*(?:\s+[A-Z][a-zA-Z0-9]*){1,4}\b', text):
phrase = m.group(0)
if phrase.split()[0] not in _PROPER_NOUN_STOPWORDS:
terms.add(phrase)Problem: The stopword check is a blunt instrument. If a multi-word proper noun begins with a stopword (e.g., "The Python Programming Language", "Ayanami0730_deep_research_bench", or "A Multi-Agent Framework"), the entire phrase is discarded because the first word matches _PROPER_NOUN_STOPWORDS ("The", "A", etc.).
- This leads to false negatives in term extraction, causing the grounding check to miss legitimate semantic overlaps.
Recommendation: The code should strip leading stopwords from the matched phrase (or split and filter them) rather than discarding the entire multi-word match.
In web.py, the DDGS client is initialized lazily using a singleton pattern:
_ddgs_lock = threading.Lock()
_ddgs_client = None
def get_ddgs_client():
global _ddgs_client
with _ddgs_lock:
if _ddgs_client is None:
_ddgs_client = DDGS()Problem: While the initialization is thread-safe, the usage of _ddgs_client is not. Since delegate_tasks runs sub-agent searches concurrently using asyncio.gather, multiple threads will invoke client.text() or client.news() concurrently on the same DDGS instance.
- The
duckduckgo_searchlibrary is not officially documented as thread-safe for concurrent calls on a single client instance. - High concurrency can lead to HTTP connection pooling issues, session pollution, or PyO3 interpreter lock contention.
Recommendation: Instantiate DDGS() directly inside _do_search as a local, short-lived object instead of using a global singleton. It is extremely lightweight and safe to create per-search.
To resolve these issues without breaking compatibility or introducing regressions, we recommend the following specific code improvements:
Instead of relying on the single-turn last_assistant_text parameter, the completion check should look backwards through _session_events to find the most recent substantial text block generated by the primary Planner agent:
def _find_last_planner_text(events: list) -> str:
# Scan backward for the most recent text block from the main agent
for event in reversed(events):
if (
event.get("source") == "Agent"
and event.get("type") == "text"
and event.get("depth", 0) == 0
):
text = event.get("data", {}).get("text", "")
# Filter out system notifications that got appended to the log
text_clean = re.sub(r'System \(\d/\d\):.*', '', text).strip()
text_clean = re.sub(r'System \(final\):.*', '', text_clean).strip()
if len(text_clean) >= 200:
return text_clean
return ""In run_completion_check, the salvage condition can then be updated to:
elif problem == "missing_artifact":
salvage_text = _find_last_planner_text(_session_events)
if _salvage_narrated_report(req_artifact, salvage_text):
# Notify success...Refactor _session_events and associated tracking dicts to be part of the App instance in TUI mode, or use a thread-local/context-local variable for CLI runs to guarantee isolated state per execution thread.
Modify _extract_salient_terms to strip leading stopwords instead of discarding:
for m in re.finditer(r'\b[A-Z][a-zA-Z0-9]*(?:\s+[A-Z][a-zA-Z0-9]*){1,4}\b', text):
phrase = m.group(0)
words = phrase.split()
while words and words[0] in _PROPER_NOUN_STOPWORDS:
words.pop(0)
if words:
terms.add(" ".join(words))We ran a live CLI test with the complex, time-sensitive query:
"Who eliminated Colombia from the 2026 FIFA World Cup?"
- Result: Colombia was eliminated by Switzerland in the Round of 16.
- Match Details: The match ended in a 0-0 draw after extra time. Switzerland won 4-3 in the subsequent penalty shootout.
- Context: This occurred in June/July 2026, which is outside the training cutoff of standard local models.
-
Attempt 1 (Factual Lookup): The agent attempted to answer from memory, claiming the qualifying format was not decided. The completion check flagged this as
not_delegatedand forced research. -
Attempt 2 (Planning): The agent wrote a plan but still did not delegate. Flagged as
not_delegatedagain. -
Attempt 3 (Research): The agent finally called
delegate_tasks. A specialist performed a search, auto-fetched a Sporting News page, and stored the correct facts in the plan (Switzerland, 0-0 draw, 4-3 PK shootout). However, it forgot to write the report. Flagged asmissing_artifact. -
Attempt 4 (Synthesis & Citation): The agent wrote
final_report.mdviawrite_workspace_file. It correctly synthesized the facts (Switzerland, 0-0 score, Stage: Round of 16). -
The Grounding Check Failure: The agent included a citation to
https://www.fifa.com/competition/worldcup/rounds/round-of-16/match/8759744. Since the agent never actually fetched this URL (it only fetched the Sporting News page), the strict URL grounding check caught the fabrication and flagged it asnot_grounded. -
Budget Exhaustion: Since it was the final attempt, the run ended with the status:
"Retry budget exhausted with an unresolved issue (not_grounded). final_report.md exists but could NOT be fully verified this run."
- Fact Accuracy: 100% Correct. The agent successfully gathered the correct facts (Switzerland, Stage: Round of 16, 0-0, 4-3 PKs) from the web search and overcame its lack of training data.
-
Citation Reliability: Failed. The agent fabricated an official FIFA match link (
fifa.com/competition/worldcup...) to make its report look more authoritative, rather than citing the actual Sporting News page it fetched. - Grounding Check Success: 100% Correct. The new grounding system successfully identified the fabricated URL and refused to verify the report, demonstrating that the URL-presence hard gate works exactly as designed to block hallucinated sources.
- Budget Distribution Issue: The agent wasted 2 of its 3 attempts on memory-refusal and planning before delegating, leaving it with no attempts remaining to fix the citation fabrication once flagged.
History
Model Research
Reviews & Audits
Reference