Resolve Issue #115: Fix agentic navigation for deep nested document sections - #116
Merged
Merged
Conversation
…re with decoupled step-based collection and batch hydration
…erarchical outline nesting, improved path exclusion logic, and child node enforcement.
…se schema fields The retrieval API response was extended with decision_trace, failure_reason, and stop_reason fields in a prior PR. The snapshot-style assertEqual on the full response dict broke as soon as those fields were added. Replace with per-field assertions so the test stays resilient to future additive changes.
… navigator, discovery) The LLM mock had no task detection for the three core agentic retrieval prompts (workflow planner, navigation collector, discovery select), causing all three to fall through to the 'default' task and return 'Mock LLM response'. - QueryPlanner fell back to single-step (OK), but... - parse_collector_response could not parse the response and defaulted to action=STOP, collect=[], yielding 0 referenced_chunks in every test. Fixes: - Add 'agentic-planner' detection (RETRIEVAL WORKFLOW PLANNER + concat_final_parts) → returns a valid single-step JSON plan. - Add 'agentic-navigate' detection (DOCUMENT NAVIGATION AGENT + SECTION TREE) → dynamically extracts the first visible path from the section tree block and returns a COLLECT+STOP response so the agent actually gathers evidence. - Add 'agentic-discovery-select' detection (DISCOVERY CANDIDATES + selections) → dynamically extracts the first candidate path and returns a selections response. Also adds json+re imports required by the new dynamic response builders.
… of hardcoded 'mock query'
The agentic planner mock was returning a hardcoded sub_query='mock query',
so the pipeline ran bottom_discovery on 'mock query' and found 0 documents.
Navigation was never reached and referenced_chunks stayed empty.
Fix: _build_planner_mock_response() now extracts the actual query from
'User query: {query}' in the prompt and passes it through as sub_query,
so discovery can match the seeded test documents correctly.
…ee format Section tree items are rendered by section_prompt_projection as: ▸ [L1] path="Root" [text=1] ~100 tokens [Leaf] Discovery hints are rendered by selection._project_discovery_hints() as: ▸ path="Findings" Both path extractor functions were using naive line-splitting which couldn't parse this format. Fixed both to use path="..." regex, which correctly extracts the canonical path value for COLLECT and selections responses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #115
Summary
This PR fixes a critical correctness bug in the agentic document navigation pipeline where deep nested sections (level 3+) were silently dropped and never surfaced to the agent, causing the retrieval to return overly generic answers based on high-level summaries only.
Changes
navigation/document.py_build_outline_subtree()(new): Recursively builds childDocTreeNodeobjects fromoutline_items+leaf_contentpaths. Uses prefix-matching to nest items level-by-level, correctly reparenting chunks into the subtree before recursing deeper._ensure_child_node()(new): Creates an intermediateDocTreeNodefor a COLLECTed non-leaf section path so thatreparent_leaf_contenthas a target node to move chunks into._build_exclude_set(): Extended to also cover parent paths of explicitly excluded paths, preventing partial-subtree collisions during BFS.navigation/section_tree.pylimit_depth: bool = Trueparameter toload_child_sections(). When set toFalse, the depth-level filter is bypassed entirely. This is needed when the navigation runner explicitly needs to load the full subtree without depth truncation.hydration/path.pyshallowas a new hydration mode: fetches only direct children (path matchesparent / Xbut notparent / X / Y). This prevents over-fetching the entire deep subtree when the agent only needs one level of expansion.and_from SQLAlchemy to compose theshallowfilter correctly.Minor cleanups
render_evidenceimport fromorchestrator.py.and_import andshallowmode to_get_allowed_types_for_modedispatch table.shard_merger.pyand added missing candidate path inheading_candidates.py.Testing
make lint— 0 issuesmake typecheck— 0 errors, 0 warnings