Skip to content

Current Planner Graph

This reference documents the current planner subgraph as implemented in src/venturescope/planner/agent.py and driven by run_planner_step() in src/venturescope/planner/__init__.py.

Mermaid graph

flowchart TD
    planner_start([START]) --> plan[plan]

    plan -->|decision.action = search| search[search]
    plan -->|decision.action = ask_user| ask_user[ask_user / interrupt]
    plan -->|decision.action = reflect| reflect[reflect]
    plan -->|decision.action = calculate| calculate[calculate]
    plan -->|decision.action = finish| finish[finish]

    search -->|last_observation present| observe[observe]
    search -->|no hits or backend failure| plan

    observe --> plan
    calculate --> plan
    ask_user --> observe_user[observe_user]
    observe_user --> plan
    reflect --> plan
    finish --> planner_end([END])

Node roles

  • plan: increments iterations, handles early stop conditions, asks region/currency questions first, manages decomposition/calculation routing, and produces a structured PlannerDecision.
  • search: runs the configured search backend, refines queries, and either stores last_observation or records a failed search attempt.
  • observe: parses search results into FieldEvidence, validates the evidence, and merges accepted values into the schema.
  • calculate: runs the deterministic calculator for calculator-backed profiles such as subscription_recurring.
  • ask_user: raises a LangGraph interrupt() with field, optional component, question, reasoning, and suggested_answers.
  • observe_user: parses the resumed user answer, supports multi-component extraction, validates it, and merges accepted values into the schema.
  • reflect: appends a short reflection note to planner state.
  • finish: returns status="done" when the planner can stop cleanly, otherwise status="aborted" when iteration/attempt limits were hit with unresolved work.

Routing details

plan -> ask_user

plan_node() routes to ask_user when:

  • core.region is still missing and region retries are not exhausted
  • core.currency is still missing and currency retries are not exhausted
  • acquisition logic selects a user question for a missing field or component
  • the planner LLM chooses ask_user
  • a search cap forces fallback from search to ask_user

plan_node() routes to search when:

  • acquisition logic selects a search task
  • the planner LLM chooses search
  • a web-preferred field was incorrectly targeted with ask_user before any search attempt, so the decision is redirected to search

plan -> calculate

plan_node() rewrites finish to calculate for calculator-backed profiles when raw inputs are complete enough but the deterministic calculator has not yet succeeded and the calculation-attempt cap has not been reached.

plan -> reflect

plan_node() routes to reflect when:

  • the planner LLM explicitly chooses reflect
  • a component-derived field needs decomposition-aware follow-up instead of a direct aggregate question
  • a blocked calculator requires another reasoning pass before the planner can finish

plan -> finish

plan_node() routes to finish when:

  • the planner was already aborted
  • max_iters has been exceeded
  • calculation attempts are exhausted for a calculator-backed profile
  • all actionable raw inputs are collected and there are no open acquisition tasks
  • ask-user retries for the same field hit the per-field cap
  • the planner LLM returns invalid structured output and the planner falls back to finish

search -> observe | plan

route_after_search() sends the flow to:

  • observe if last_observation is populated
  • plan if the search produced no hits or the backend failed

Outer-turn entry and resume

run_planner_step() drives the subgraph in two modes:

  • bootstrap: graph.invoke(initial_state(...), config=config)
  • resume after an interrupt: graph.invoke(Command(resume=user_message), config=config)

The planner checkpoint thread is namespaced as {conversation_id}:planner via planner_thread_id().

Source of truth

  • graph construction: src/venturescope/planner/agent.py:2082
  • planner node implementations: src/venturescope/planner/agent.py
  • outer planner step wrapper: src/venturescope/planner/__init__.py:94