What this is

No new concepts in this lesson. This is a checkpoint: a small, real script built entirely out of ideas from Lessons 1 through 11, combined into one thing. If you can read lesson.py and understand why every piece is there, you've mastered the Beginner tier. If any piece feels unfamiliar, that's a sign to revisit the lesson it came from before continuing to Intermediate.

What it does

Generates a two-sentence, atmospheric "mystery" story opening for each of several different characters, all in a consistent style, using one batch call instead of a loop.

Where each piece came from

FROM LESSON 11
model = init_chat_model("google_genai:gemini-3.5-flash-lite", temperature=0.9)

Lesson 11 (provider chosen by string) and Lesson 10 (temperature, along with its honest caveat: this specific model ignores it).

FROM LESSON 5
examples = [...]
example_prompt = ChatPromptTemplate.from_messages([...])
few_shot_prompt = FewShotChatMessagePromptTemplate(examples=examples, example_prompt=example_prompt)

Lesson 5. Two worked examples teach the model the exact style we want (two sentences, atmospheric, no dialogue) far more precisely than describing it in words would.

FROM LESSON 3
prompt = ChatPromptTemplate.from_messages([
("system", "... {genre} ..."),
few_shot_prompt,
("human", "Character: {character_name}"),
])

Lesson 3 (templates) and Lesson 4 (two blanks in one template: {genre} and {character_name}).

FROM LESSON 4
mystery_prompt = prompt.partial(genre="mystery")

Lesson 4's .partial(). Every story in this run shares the same genre, so it's locked in once, and only character_name needs to be supplied per call from here on.

FROM LESSON 8
def add_word_count(opening: str) -> dict:
return {"opening": opening, "word_count": len(opening.split())}

Lesson 8's RunnableLambda pattern: our own plain function, doing something no built-in LangChain piece does for us.

FROM LESSON 6
chain = mystery_prompt | model | StrOutputParser() | RunnableLambda(add_word_count)

Lesson 6 (chaining with |), Lesson 7 (StrOutputParser, so the model's AIMessage becomes plain text before our word-count function ever sees it), and Lesson 8 again (wiring our function in as the last step).

FROM LESSON 9
results = chain.batch([{"character_name": name} for name in characters])

Lesson 9. Three different characters, one .batch() call, run concurrently instead of three separate .invoke() calls in a loop.

Try this yourself

Without looking anything up:

  • Change genre="mystery" to a different genre, and add matching new examples in examples, does the style shift accordingly?
  • Add a fourth name to characters, does the batch handle it with no other changes needed?
  • Swap RunnableLambda(add_word_count) for a different custom function of your own, say, one that counts sentences instead of words.

If you can make these changes confidently, you're ready for the Intermediate tier, starting at Lesson 13.