The analyze node
The analyze node is the brain of the agent. Every time we arrive at a node, we hand the LLM the current title, summary, and the list of children, then ask: how relevant is this node, and which child should we descend into? The answer comes back as structured JSON.
prompt = f"""You are navigating a research paper tree to answer a query.
Query: "{state['query']}"
Current node:
id : {node.id}
title : {node.title}
summary : {getattr(node, 'summary', '')[:300]}
pages : {node.page_start}-{node.page_end}
Children: {json.dumps(children_info, indent=2) if children_info else "None (leaf node)"}
Decide:
1. confidence : 0-1, how likely does this node contain the answer?
2. should_descend : true only if a specific child is more relevant than this node
3. target_child_id : the id of the best child to visit (null if should_descend is false)
4. reasoning : one sentence explaining your decision
Respond ONLY as valid JSON, no markdown fences."""The prompt hands the LLM exactly what it needs: the query, the current node summary, and the child menu. The response schema is declared inline so the model produces parseable output.
raw, elapsed = _call_llm(client, model, prompt, "navigate", call_num)
raw = _strip_fences(raw)
try:
decision = json.loads(raw)
except json.JSONDecodeError:
logger.warning("[!] JSON parse failed, using fallback decision")
decision = {
"confidence": 0.5,
"should_descend": bool(node.children),
"target_child_id": node.children[0].id if node.children else None,
"reasoning": "Fallback: could not parse LLM response",
}
conf = float(decision.get("confidence", 0.5))
descend = bool(decision.get("should_descend", False))
child_id = decision.get("target_child_id")Two safety nets. _strip_fences removes accidental markdown backticks before json.loads. If parsing still fails, we fall back to a neutral decision so the agent never crashes on a bad response.
Prompt-based JSON works with any OpenAI-compatible endpoint, which matters because the workshop defaults to OpenRouter so you can try cheap models. Structured outputs are better when you have the feature available, but a well-written prompt plus a fallback parser gets you very close at a fraction of the lock-in.
Quiz: Quiz
Loading practice…