Skip to content

feat(doc-agent): Modular Document Anatomy Agent — bootstrap, planner, executor, persist - #108

Merged
EricNGOntos merged 11 commits into
stagingfrom
feat/wuchengke/dev
May 28, 2026
Merged

EricNGOntos merged 11 commits into
stagingfrom
feat/wuchengke/dev

Conversation

@EricNGOntos

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a comprehensive modular restructure of the Document Anatomy Agent, the pre-parse intelligence layer that profiles PDF documents before they enter the main parsing pipeline.

Key Changes

Architecture Refactoring (5 commits + 1 type-fix)

  • Modular decomposition: Broke the monolithic agent.py into four clean sub-packages:
    • bootstrap/ — page feature aggregation, classification, and initial probing
    • planner/ — LLM-driven planning with DAG-style step decomposition
    • executor/ — ReAct-loop tool execution engine
    • persist/ — anatomy map persistence layer
  • Tool registry overhaul: Replaced legacy tools (probe_sample_pages, probe_vlm_inspect, scan_all_page_features, classify_special_pages, find_h1_boundaries) with new modular tools:
    • find_toc_anchor_pages — locate TOC pages via text heuristics
    • vlm_toc_extractor — VLM-based TOC extraction with structured output
    • extract_toc_with_boundaries — merge TOC entries with boundary candidates
    • match_h1_pages — H1-to-page matching using text extraction
    • inspect_pages — generic VLM inspection with budget control
    • grep_text — text search across extracted page text
    • classify_page_kinds — rule-based page type classification
    • probe_page_features — per-page feature extraction (font clusters, image counts)
    • validate_anatomy_map / persist_anatomy_map — validation and persistence
    • verdict — final agent decision tool
  • State management: New state.py blackboard pattern with typed Blackboard dataclass
  • Budget system: Token budget ledger (budget.py) with visual/text/planning pools
  • Trace system: Full agent trace recording (trace.py) for debugging and analytics
  • Coordinator: New coordinator.py orchestrates the multi-phase agent loop

Shared Layer Changes (Additive Only)

  • openai_compatible_client_sync.py: Added chat_completion_raw_with_usage() method that returns the raw OpenAI response object alongside usage stats. The existing chat_completion_with_usage() is preserved unchanged — internally refactored to delegate to a shared _make_ali_pool_raw_call() helper.
  • New ORM models: ParseRun, ParseStep, DocumentPagePlan — dedicated tables for doc-agent trace persistence. No modifications to any existing tables.
  • Alembic migration: f8a9b0c1d2e3_add_parse_agent_tables.py creates 3 new tables only.

Cleanup

  • Removed legacy shard_splitter.py from document_parser/formats/pdf/ (zero external references)
  • Removed legacy page_map.py (replaced by new state model)
  • Added apps/worker/scripts/ to .gitignore for local debug scripts
  • Added test-doc-agent Makefile target

Production Safety

This PR does NOT touch the main parsing production pipeline:

  • Zero changes to parse_service.py, any format parser, pred_titles(), or retrieval engine
  • All shared-layer changes are purely additive (new methods, new tables)
  • The only refactored internal method (_make_ali_pool_raw_call) preserves identical behavior for all existing callers

Quality Checks

  • ruff check: ✅ All checks passed (tracked files)
  • pyright: ✅ 0 errors, 0 warnings

@EricNGOntos EricNGOntos added the document-agent Document Anatomy Agent development label May 25, 2026
@EricNGOntos EricNGOntos self-assigned this May 25, 2026
from app.services.document_agent.planner import ProfilePlanner
from app.services.document_agent.registry import REGISTRY
from app.services.document_agent.state import AgentBlackboard, DocumentAgentState
from app.services.document_agent import tools as _registered_tools # noqa: F401
finally:
try:
doc.close()
except Exception:
finally:
try:
doc.close()
except Exception:
finally:
try:
doc.close()
except Exception:
finally:
try:
doc.close()
except Exception:
logger.debug(f"parse agent trace flush failed: {exc}")
try:
self._db.rollback()
except Exception:
finally:
try:
doc.close()
except Exception:
if isinstance(item, dict) and "id" in item and "level" in item:
try:
llm_levels[int(item["id"])] = item["level"]
except (TypeError, ValueError):
Introduce Phase-0 anatomy infrastructure for large-PDF splitting:

- page_map.py: data contracts — PageMap, PageFeature, Shard, CutPoint,
  H1BoundaryResult, H1Match
- agent.py: DocumentAnatomyAgent — LLM tool-calling loop (scan →
  find_h1 → propose_cuts → finalize) with deterministic fallback
- tools/scan_all_page_features.py: full-page PyMuPDF structural feature
  extraction (text density, image coverage, table count, orientation)
  in an isolated child process
- tools/find_h1_boundaries.py: TOC-page grep + body grep to locate
  level-1 heading physical pages; falls back to preview grep when no
  TOC is detected
- shard_splitter.py: split_pdf_by_shards() and merge_shard_dataframes()
  with page_nums offset correction

Integration into parse_pdfs() is the next step.
Delete unused Phase 1 modules that were never integrated into the
production parser pipeline:

- manifest.py: ShardManifest / ShardSignal / GlobalSignals / SpecialPage
- tools/propose_shard_plan.py: LLM + fallback shard planning
- tools/probe_sample_pages.py: stratified page sampling
- tools/probe_vlm_inspect.py: VLM page screenshot inspection

Adapt surviving modules:
- classify_special_pages.py: inline SpecialKind Literal type (was
  imported from deleted manifest.py)
- __init__.py / tools/__init__.py: remove all Phase 1 exports
- page_map.py / scan_all_page_features.py: clean stale docstring refs

The Phase 0 DocumentAnatomyAgent (agent.py, page_map.py, scan_all_page_features,
find_h1_boundaries, shard_splitter) is the sole path forward.

No production impact: neither Phase 0 nor Phase 1 was ever called from
parse_pdfs() or any other production entry point.
This commit removes the following unused files and classes related to the DocumentAnatomyAgent:

- agent.py: Deleted the DocumentAnatomyAgent class, which was not integrated into the production pipeline.
- page_map.py: Removed PageMap, PageFeature, and related classes that were part of the legacy structure.
- tools: Deleted all tools related to page feature extraction and heading boundary detection, including scan_all_page_features.py and find_h1_boundaries.py.

Updated the __init__.py files to reflect these changes and cleaned up imports accordingly. This refactor simplifies the codebase and focuses on the current implementation of the DocumentAnatomyAgent.
…ine transitions

This commit modifies the DocumentAgent's state management and tool integration by replacing the legacy H1 boundary and TOC page detection tools with a new boundary candidate system. Key changes include:

- Updated the state transitions to utilize `collect.boundary_candidates` instead of the removed `find.h1_boundaries` and `find.toc_pages`.
- Introduced a new `BoundaryCandidate` data structure in the manifest to encapsulate boundary candidate details.
- Adjusted the `ProfileCoordinator` and validation logic to accommodate the new boundary candidates.
- Enhanced the `ParseRunRecorder` to include detailed trace information for boundary candidates.
- Removed obsolete tools related to H1 and TOC detection, simplifying the toolset.

These changes aim to improve the clarity and efficiency of the DocumentAgent's processing pipeline.
…rd planning resilience with validation fallback logic.
…nd implement PDF shard splitting and merging logic.
…n up stale pipeline code

- Remove hardcoded num_pos/num_neg params; derive zero-filled arrays dynamically
- Remove dead 'Sure' return branch from get_max_lvl, add -> int type annotation
- Restore include_punc=False default in remove_by_conditions to defer punc
  checking to judge_negs second pass (reduces false heading filtering)
- Remove dead 'collapse' task code and rename functions for clarity
- Simplify redundant regex character class in NEG rule 3
- Normalize -2 -> 1 in est_hierarchies_naive for valid LLM-failure fallback

Closes #112
@EricNGOntos
EricNGOntos force-pushed the feat/wuchengke/dev branch from 2c7f6f1 to f34958a Compare May 28, 2026 04:31
@EricNGOntos
EricNGOntos merged commit 0ea2fba into staging May 28, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

document-agent Document Anatomy Agent development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants