How Henri calls a model

Yingchen Liu
The tools, provider chain and API call behind one Henri lesson: a quiz, a chapter move and a picture travel with the conversation, and the next provider can answer when the first one does not.
- LLM tools
- Provider fallback
- API design

01
What the tutor asks a model to do
Henri is a personal AI tutor. A learner names a topic and the app builds a syllabus of conversational lessons, questions and quizzes, rather than opening a general chat.
A lesson reply can be an explanation, a quiz, a decision to move to the next chapter, or a picture. Those are tools the model can call. The app sends the lesson so far, the learner’s words, and the tools it is willing to trust.
The call also has to survive the provider. Claude on Amazon Bedrock is the usual first stop. If that call fails, the same lesson can be asked again through another provider, with that provider’s own key and its own way of accepting a structured result.
02
The problem
A learner’s message can need a quiz, a decision about the next chapter, and sometimes a picture. If the model writes all of that as one paragraph, the app has to guess which sentence is the question and which sentence is the picture prompt.
Providers fail in different places. Bedrock can be down while OpenRouter still serves Claude. Claude can be down in both places, and the lesson still needs an answer from a different model. One API call, aimed at one provider, ends the lesson at the first failure.
The providers also disagree about structured output. One accepts a JSON schema. Another accepts JSON mode and needs the schema written into the prompt. Another needs the schema in the prompt and no response format at all. The lesson’s meaning has to stay the same while the call changes to fit the provider.
03
One lesson, asked in a way the app can trust
Architecture
The app sends the lesson and a small set of tools. If that provider does not answer, the same lesson moves to the next provider.
The request
What does the app ask the model to do?
The lesson so far, plus the actions the product is willing to trust.
- 01
Send the lesson so far
The system prompt stays fixed. Finished chapters become one learning record. The current chapter stays, with the learner’s words and the chapter state on each new turn.
- 02
Offer three tools
The model can set a quiz, decide to move on or switch chapter, or ask for a picture. Each tool has a fixed set of arguments.
- 03
Check the arguments
The app accepts a tool when the arguments match that shape, then stores a quiz, a chapter move or a picture.
The backup
What if that provider does not answer?
The lesson stays the same. The next provider uses its own key.
- 01
Start with Claude on Bedrock
A normal lesson asks Claude Haiku on Amazon Bedrock first. That attempt always runs.
- 02
Ask Claude through OpenRouter
If Bedrock fails, the next step asks Claude Haiku through OpenRouter, with OpenRouter’s own key.
- 03
Ask DeepSeek with thinking off
If Claude is unavailable on both, official DeepSeek V4 Pro answers with thinking turned off.
How I implemented it
The teaching system prompt is the stable part of every lesson call. Its sections cover the message structure, a curriculum that stays fixed once teaching starts, the teaching philosophy, a short welcome, one chapter at a time, what to do when the learner is confused, response length, tone, and a usage guide for each tool. On Claude that prompt is the cached prefix, so later turns in the same lesson reuse it. The tree prompt asks for 12 to 40 chapters, grouped under a few top-level branches. The questionnaire prompt stops once it knows the learner’s motivation, starting point, goal and preferred approach, usually after two or three rounds.
The tool definitions match the evaluation harness used to compare teaching models. generate_quiz returns a true-or-false or a choice question, and is_final marks the question that can complete the chapter. evaluate_understanding either advances with reveal_next or switches with switch_to. generate_image asks for one picture of the idea being taught. The evaluation request also defines show_html_widget. The iOS lesson omits that tool, because the phone has no renderer for it. The app checks the arguments against that tool’s schema before it stores the result. A quiz that the learner has not answered is removed from the history before the next ordinary message, because the provider requires every tool call to be followed by its result.
Finished chapters are folded before the model sees them. The saved conversation stays complete. The copy sent to the model replaces each finished chapter with one learning record: three to eight notes about what the learner understood. If that record was never written, the model sees a short completed-chapter marker. A record that is already in that form is left as it is. The opening turn that carries the questionnaire and the curriculum stays. On the adaptive lesson, the chapter being taught keeps every turn. On the earlier lesson path, that chapter keeps the latest 30 turns, and the window is lengthened when it would otherwise start on a tool result whose tool call had fallen outside it. Tool calls that lost their results are removed, so the provider still receives a valid sequence.
Uploaded pages are handled on the copy, too. PDF and file blocks are left out of the compressed teaching history. In the evaluation harness, the original upload becomes a placeholder once the curriculum exists. When the chapter changes, the previous chapter’s pages come off and only the current chapter’s pages are attached for that call. That change stays in memory, so the cached prompt prefix is unchanged.
A normal lesson uses one chain. Claude Haiku on Bedrock is first. Claude Haiku through OpenRouter is next, and that step is omitted when its key is missing. Official DeepSeek V4 Pro is last, with thinking turned off, because that is the setting under which the teaching evaluation selected it. Each attempt has its own timeout, and the whole chain has a deadline. The first attempt always runs. A later attempt runs only when enough of that deadline remains.
Each attempt receives the schema in the way that provider can accept: the native JSON schema, JSON mode with the full schema added to the prompt, or the full schema in the prompt alone. Required fields, types and allowed values travel with the schema on the prompt path.
Two other jobs use a shorter chain of their own. Adaptive teaching starts on official DeepSeek with high reasoning, then falls back to Claude Haiku on OpenRouter. Changing the learning plan starts on GPT-5.6 Terra at medium reasoning through OpenAI, then the same model on Azure, and then the normal lesson chain. A missing key skips that step. The saved call records the provider, the model and whether a later step answered.
04
The decision
I treated every model call as the same three parts: a system prompt, the message list, and the tools for that phase. The questionnaire returns JSON, either more questions or two to four learning paths. The tree call returns the curriculum as an object. While the learner is still editing that plan, the tools are remove, add_node and start_learning. The live lesson then drops those tools and offers generate_quiz, evaluate_understanding and generate_image.
An ordinary learner turn is two blocks. user_message holds what the learner wrote. learning_state follows it with the current chapter, its status, the next chapter, progress, visual coverage, and the task to stay on this chapter until it is complete. The first teaching turn also sends questionnaire_context, the knowledge_tree and first_node, then the teaching order. A quiz answer comes back as the tool result for generate_quiz. A chapter tap arrives as a system_event, together with the tool call the app has already made.
The decisions that shaped the system
- 01
Make the product actions into tools
A quiz, a chapter move and a picture each have a name and a fixed set of arguments, so the app can store them without guessing at a paragraph.
- 02
Keep one chain of providers
The lesson stays the same while the call moves from Bedrock to OpenRouter and then to DeepSeek. Each step has its own credential and is skipped when that credential is missing.
- 03
Record which provider answered
The saved call names the provider and the model, and whether a later step in the chain had to answer.
05
What improved
A quiz, a chapter move and a picture arrive as checked tool results, so the lesson can store them and continue. When Bedrock fails, OpenRouter can still serve Claude. When Claude fails in both places, DeepSeek can still answer. The call record shows which provider served that reply, so a provider failure stays separate from a malformed tool result.
06
What I learned
The prompt is one part of the call. The tools, the lesson history and the provider chain decide whether the learner gets a result the app can continue. A fallback model is useful when production keeps the setting the evaluation measured. On the last step of a normal lesson, DeepSeek stays with thinking turned off for that reason.
Takeaways
- Turn the actions the product must trust into tools with a fixed shape.
- Send the lesson so far, then move that same lesson down a chain of providers.
- Record which provider answered, including when a later step in the chain was used.