Loop Engineering for RAG Question Parsing: The Small Loop That Runs Before Retrieval
A user asks the pipeline: “what is the premium?” on a fifty-page insurance policy. The parser looks at the doc profile it just got from the parsing brick:
doc_type: insurance_policyn_pages: 47toc_dfnames General Information, Coverages, Exclusions, Endorsements. No section called Premium.
Naive top-k embeds “premium” and scans the whole document. It returns exclusion clauses that mention the word, endorsement boilerplate, and a few incidental references. The actual premium schedule sits under General Information on page 3, but nothing in the query said so.
A loop-engineered pipeline does something different. The parser sees “premium” has no match in the TOC, holds the pipeline, and sends one plain question back to the user:
“I don’t see a ‘Premium’ section in this policy. Where should I look?”
They might type “General Info” (short), “generale information” (typo), or “try under coverages” (rephrased). None of that matters. The parser re-runs on the enriched question, the LLM fits whatever came back into section_hint or another typed field, and the pipeline continues.
That single turn removes the ambiguity and scopes retrieval.
The fuller mechanism (an explicit list of candidate values, a proposed default, an audit trail so the answer can be cached and applied silently next time) is what Article 6bis develops. This article stays on the smallest possible version: a question in, an answer out, one LLM pass.
Enterprise Document Intelligence builds enterprise RAG on four bricks (document parsing, question parsing, retrieval, generation). This article re-reads brick 2 through the loop-engineering lens, the emerging discipline that names the loops built around the LLM as the place where production quality is won. Articles 6A (thesis), 6B (extraction), and 6C (dispatch) code the brick. Article 6bis codes the clarification loop’s mechanics. This one names why that mechanic is the loop-engineering pattern applied at the smallest possible scope.

📓 The three cases in Section 3 all run in the shipped notebook: reproduce the clarification loop yourself against the arXiv corpus and a broker contract, watch how the LLM’s “what is missing?” question shifts with the doc profile. Repo → doc-intel/notebooks-vol1.

1. From prompt to context to loop engineering
The frame has moved twice in eighteen months.
Prompt engineering (2023). The user does the work. Learn to write the right prompt, add few-shot examples, use “think step by step”. The LLM is a stateless oracle. Quality is a wording problem.
Context engineering (mid-2025). The engineer does the work. Tobi Lütke and Andrej Karpathy rename the practice: “the delicate art of filling the context window with just the right information for the next step.” The prompt is one slot among many. See Article 6quater (context engineering for question parsing) for that framing applied to this brick.
Loop engineering (2026). The engineer designs the loops around the LLM call. LangChain puts it plainly: “the potential in agents is in the loops you build around them.” MindStudio frames it as “designing AI systems that operate in iterative cycles, repeating until a goal is met”, the discipline that “closes the feedback gap.”
The three are not a race. They stack: prompt engineering shapes what one call reads, context engineering picks what enters that call, loop engineering wraps the call in a bounded iteration.
Loops come in sizes. A big loop is agentic RAG (plan, act, observe, replan, many turns). A small loop is a single ask-answer-continue turn. This article is about the smallest useful loop, sitting on question parsing.
2. The loop fills a fixed schema
Before the loop, name the fields it will fill.
Question parsing writes into a fixed set of typed fields, defined once for the whole series and consumed deterministically by retrieval and generation. Vol.1 ships with these:
keywords: content noun phrases for retrieval detectors.intent: a bounded enum (factual,listing,section_retrieval,open_scoped,open_corpus_wide).retrieval.section_hint: a section name or number to filtertoc_df.retrieval.layout_hint:"table","figure", or"glossary"when the answer sits in a specific layout.structural_hints.pages_hint: an explicit page list the user pinned in the question.- (
sheets_hint/slides_hintfor Vol.2 formats, unused here.)
These names are not article-local. They live in the docintel.question module and every downstream brick (retrieval detectors, dispatcher, generation schema lookup) reads them. Add a field and the whole pipeline changes. That is a design decision, not a JSON tweak.
The loop’s only job is to fill one of these fields when the parser cannot fill it alone. It never invents a new one.
The flow, six steps and one loop-back arrow:

In code, the same flow on the premium question from the intro:
# 1-2. First parse from raw question + doc context.
parsed = parse_question(
raw="what is the premium?",
doc_context=doc_context, # doc_type='insurance_policy', toc_df=..., ...
)
# What comes back:
assert parsed.keywords == ["premium"]
assert parsed.intent == "factual"
assert parsed.retrieval.section_hint is None # <-- the missing field
assert parsed.structural_hints is None
# 3. Detect the missing field.
if parsed.retrieval.section_hint is None and _topic_looks_sectioned(parsed, doc_context):
question_to_user = (
f"I don't see a '{parsed.keywords[0].title()}' section in this policy. "
"Where should I look?"
)
user_reply = ask_user(question_to_user) # UI round-trip, free-form answer
# 4-5. Re-parse with the enriched question.
parsed = parse_question(
raw=f"{parsed.original_question} (look under {user_reply})",
doc_context=doc_context,
)
# By now, section_hint is filled from the user's reply.
plan = dispatch(parsed) # step 6, unchanged
Notice what changes across the two parse_question(...) calls: only the field that was missing. Every other field of ParsedQuestion stays where it was. dispatch(parsed) at the end is the exact same call it would have been without a loop, running against the same schema.
Not every field is mandatory: no TOC means no section_hint, and that is fine as long as another field (like pages_hint) scopes the search. The engineering work is picking which field to ask about, given the doc profile, and how to phrase the question for the user’s context.
3. Three cases, three missing fields
Each case below names the one field the parser cannot fill from the raw question alone, the doc profile that flags the gap, and the plain question that fills it. Same schema across the three, no new field invented.
3.1 Missing section_hint: topic not in the TOC
Picture an insurance analyst opening a policy she has never seen before. Forty-seven pages, four TOC entries, nothing standing out at a glance. She types the question she would otherwise answer with ten minutes of scrolling:
“What is the premium for the first quarter?”
The parser reads the question alongside what document parsing already knows about the file:
doc_type: insurance_policyn_pages: 47toc_dfnames General Information, Coverages, Exclusions, Endorsements. Nothing that carries the word Premium.
On its first pass, parse_question(...) fills what it can:
keywordscomes back as["premium"].intentcomes back asfactualbecause the answer is one number, not a summary or a list.retrieval.section_hintrefuses to fill.
The last one is what stops the pipeline. The user’s topic does not correspond to any TOC label, and the parser will not invent one. Rather than forward a half-filled row to retrieval, it holds and asks.
One question goes back to the analyst:
“I don’t see a ‘Premium’ section in this policy. Where should I look?”
She types whatever comes first. “General Info” if she is rushing. “The general one” if she is unsure of the exact label. “Try general information” if she is being precise. The wording does not matter. The same LLM that parsed the original question re-runs on the enriched string, resolves whichever variant she typed to “General Information”, and writes it into section_hint on the second pass. Every other field on the row stays exactly where the first parse left it.
From that point the pipeline is deterministic again. section_filter_active fires downstream because section_hint is now set. Retrieval reads only the pages inside General Information on toc_df (page 3, in this policy). Generation reads a crisp passage instead of forty-seven pages of noise. The analyst gets the number back in seconds.
3.2 Missing pages_hint: multi-position topic
A different desk, a different pipeline surface. A paralegal asks the assistant to look at a contract she needs to file before the end of the day:
“What is the client’s name?”
The doc profile confirms a contract, forty-seven pages long. The TOC lists numbered clauses but never spells out a Parties header. On the first pass:
keywordscomes back as["client name"].intentcomes back asfactual.structural_hints.pages_hintstaysNone.
The last one is the trap. On a contract, the client’s name lives in three canonical positions:
- the cover page;
- the running header at the top of every page;
- the signatory block at the end.
Forwarding the question without a page hint sends retrieval into the whole document and drags in every boilerplate mention of “the client” in between. The right passage is on page 1. The signal-to-noise ratio collapses before generation ever runs.
The parser writes back:
“Contracts often carry the client’s name in a few places (cover, header, signatories). Where do you want me to look?”
The paralegal answers “cover”, or “page 1”, or “the first page”. The exact phrasing does not matter. On the second parse, the LLM resolves whichever variant she typed into pages_hint: [1]. keywords and intent stay where they were. pages_hint_active fires downstream, retrieval reads page 1 and only page 1, and the LLM generates against one clean candidate.
3.3 Missing pages_hint: no TOC on a long doc
A researcher opens an internal risk paper and asks the assistant:
“Summarize the risk section.”
The doc profile flags a research_paper, thirty-two pages, and toc_df is empty. Visually the paper has section headings, but the parsing brick could not extract them cleanly (nested styling, inconsistent numbering, whatever caused the miss). That kills section_hint before the loop can even try. There is no TOC entry to bind to.
But it does not kill pages_hint. The researcher may know roughly where in the paper the risk discussion lives, and a rough page range is all retrieval needs to narrow the search. That gives the parser one useful thing to ask about:
“This paper has no clean table of contents. Do you know roughly which pages cover the risk section?”
Two answers send the pipeline to two very different places:
- “No idea” keeps
pages_hint: None. The pipeline honestly falls back to a full-document scan. Retrieval reads all thirty-two pages, generation runs on the widest possible context. Expensive, but at least the user was told what would happen. - “Around the middle” resolves to
pages_hint: [11, 12, 13, ..., 22]on the second parse.pages_hint_activefires. Retrieval reads one third of the document instead of all of it. Generation sees a shorter, more relevant context. The summary arrives faster and the LLM bill is a third of what it would have been.
It is the same brick chain running on a smaller working set.
Notice what stays constant across the three. Every field the loop touches (section_hint, pages_hint) already existed on the schema. The loop’s contribution is choosing which one to ask about given the doc profile. Same downstream code, same dispatcher, same activation flags. Nothing new to plumb through the pipeline.
4. Where the loop fits, and why it is not agentic RAG
The loop sits inside question parsing. The rest of the pipeline never sees it: retrieval only starts once question parsing has produced a full-confidence ParsedQuestion, whether on the first pass or after one clarification turn.

Retrieval and generation see one interface: a filled ParsedQuestion. Whether the pipeline needed a turn or not is invisible to them. The loop is small, engineered, and lives entirely inside the question brick’s edge with the user.
That placement is also why the small loop looks like an agent turn but is not one:
- One iteration, not many. The loop fires once, the user picks, the pipeline continues. No plan, no replan, no self-critique inside the loop.
- Engineer-designed, not LLM-planned. The candidate values, the target field, the fallback default: all authored code paths driven by the doc profile. The LLM writes the “question_to_user” string and picks the proposed default. That is all.
- Grounded in real state. The loop only fires on gaps the parser can name (topic not in TOC, multi-position topic, no TOC). No open-ended “let me think about this” introspection. Aligned with what Zylos Research calls grounded self-correction: anchored in execution results, not in the model second-guessing itself.
- Bounded latency. A big agentic loop can spin for tens of seconds. This one adds one round-trip and one LLM call, then either continues or hands control to the user.
The distinction matters because the industry framing of loop engineering often assumes the multi-turn agentic case. On the question side of a single-document pipeline, the loop that pays off is the smallest one that exists.
5. Out of scope
Three things this article deliberately does not cover.
- The multi-turn agentic loop. When the pipeline itself plans and re-plans across many LLM calls, that is Volume 4’s territory (agentic bricks with tools).
- Verification / evaluator loops after generation. Answer-generated-then-critiqued patterns (see the schema-validation retry in Article 8C) are their own loops downstream. Different receiver.
- Cross-document / corpus scoping. “Which document?” rather than “which section?” is a
CorpusContextproblem, Volume 2.
Each of these is a different loop, at a different scope, for a different receiver, using the same vocabulary at a different scale.
6. Sources and further reading
The loop-engineering framing arrived in 2026 from several sources at once, converging on the same term after context engineering had settled the vocabulary.
The primary industry anchors:
- Sydney Runkle (LangChain), “The Art of Loop Engineering”: blog post, June 2026.
- MindStudio, “What Is Loop Engineering? The New Meta for AI Coding Agents”: blog post, June 2026.
- mem0, “Loop Engineering for AI Agents: Memory-First Design”: blog post.
The foundation the loops build on:
- Anthropic, “Building Effective AI Agents”: research post. The base patterns (evaluator-optimizer, reflection) and the temperance rule (add loops only when they demonstrably improve outcomes).
- Yao et al., “ReAct: Synergizing Reasoning and Acting in Language Models”: arXiv 2210.03629. The founding reason-act-observe loop.
- Madaan et al., “Self-Refine: Iterative Refinement with Self-Feedback”: arXiv 2303.17651. Generate → self-feedback → refine.
The series articles that code the pieces this one frames:
- When RAG users ask vague questions (Article 6bis). The clarification loop’s mechanics:
ClarificationRequest,ClarificationDefault, the ask-then-learn cache. - Context Engineering for RAG (Article 7bis). The pipeline-wide context-engineering framing this article extends into loop-engineering.