Skip to content

Resolve Issue #115: Fix agentic navigation for deep nested document sections - #116

Merged
EricNGOntos merged 6 commits into
stagingfrom
feat/wuchengke/dev
May 29, 2026
Merged

EricNGOntos merged 6 commits into
stagingfrom
feat/wuchengke/dev

Conversation

@EricNGOntos

Copy link
Copy Markdown
Contributor

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 child DocTreeNode objects from outline_items + leaf_content paths. Uses prefix-matching to nest items level-by-level, correctly reparenting chunks into the subtree before recursing deeper.
  • _ensure_child_node() (new): Creates an intermediate DocTreeNode for a COLLECTed non-leaf section path so that reparent_leaf_content has 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.py

  • Added limit_depth: bool = True parameter to load_child_sections(). When set to False, 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.py

  • Added shallow as a new hydration mode: fetches only direct children (path matches parent / X but not parent / X / Y). This prevents over-fetching the entire deep subtree when the agent only needs one level of expansion.
  • Imported and_ from SQLAlchemy to compose the shallow filter correctly.

Minor cleanups

  • Removed unused render_evidence import from orchestrator.py.
  • Added missing and_ import and shallow mode to _get_allowed_types_for_mode dispatch table.
  • Fixed off-by-one in shard_merger.py and added missing candidate path in heading_candidates.py.

Testing

  • make lint — 0 issues
  • make typecheck — 0 errors, 0 warnings
  • Manually verified on a 5-level deep construction report (施工安全保证措施) that:
    • Section 五、施工安全保证措施 / 3.监控量测措施 / 3.1 is correctly nested and returned.
    • The agent's evidence text contains leaf-level detail instead of the parent summary.

…re with decoupled step-based collection and batch hydration
…erarchical outline nesting, improved path exclusion logic, and child node enforcement.
@EricNGOntos EricNGOntos added the agentic-rag-core Core agentic RAG retrieval pipeline label May 29, 2026
@EricNGOntos EricNGOntos self-assigned this May 29, 2026
…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.
@EricNGOntos
EricNGOntos merged commit f1ef036 into staging May 29, 2026
9 checks passed
@EricNGOntos
EricNGOntos deleted the feat/wuchengke/dev branch May 29, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agentic-rag-core Core agentic RAG retrieval pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant