Session 3. Structured outputs — Wed 16 Sep

Conclusion

You turned model text into data: ResearchAnswer with four fields, and parse_research_answer as the only way in.

What you did

The failure you handled

A repair budget that runs out. One call, one corrective retry, then a typed refusal with needs_human_review: true and no citations. Never an unbounded loop.

Nothing raised in that last case, and that is the point — the flag and the two llm_call lines are the only evidence, which is why both exist.

What to carry forward

You also saw the limit of the shape: a valid ResearchAnswer can still be wrong, so citations are verified against retrieval and an unsupported question is refused before a call is spent.

Into session 4

Session 4 assumes you read a trace without help, treat model output as untrusted input, and reach for a typed refusal instead of None. It gives the model tools. Every argument it passes needs exactly the treatment you just gave its output.