Skip to content

Commit bc53808

Browse files
andreisavuclaude
andauthored
Refactor sections to use factory functions instead of subclasses (#7)
Convert QuestionSection, GameRulesSection, HintsSection, and LuckyDiceSection from class-based definitions to factory functions (build_question_section, build_game_rules_section, build_hints_section, build_lucky_dice_section). This simplifies the code by directly returning configured MarkdownSection instances rather than creating thin wrapper subclasses. https://claude.ai/code/session_01VcXtbqjLRs8dMZM5y1Fbi1 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 625be17 commit bc53808

4 files changed

Lines changed: 143 additions & 153 deletions

File tree

src/trivia_agent/sections.py

Lines changed: 101 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
11
"""Prompt sections for the trivia agent.
22
33
This module provides reusable prompt sections that compose the trivia agent's
4-
system prompt. Each section encapsulates related content and tools, enabling
4+
system prompt. Each section is created via a factory function, enabling
55
modular prompt construction.
66
77
Key WINK features demonstrated:
8-
- **Custom sections with typed parameters**: QuestionSection uses QuestionParams
9-
to inject the player's question into the prompt template.
10-
- **Progressive disclosure**: GameRulesSection starts collapsed (SUMMARY visibility)
11-
and expands when the agent calls read_section('rules').
12-
- **Tools attached to sections**: HintsSection bundles the hint_lookup tool, so
13-
it's only available when the hints section is included.
14-
- **Tool policies**: LuckyDiceSection uses SequentialDependencyPolicy to enforce
15-
that pick_up_dice must be called before throw_dice.
8+
- **Custom sections with typed parameters**: build_question_section() creates
9+
a section that uses QuestionParams to inject the player's question.
10+
- **Progressive disclosure**: build_game_rules_section() creates a section that
11+
starts collapsed (SUMMARY visibility) and expands when the agent calls
12+
read_section('rules').
13+
- **Tools attached to sections**: build_hints_section() bundles the hint_lookup
14+
tool, so it's only available when the hints section is included.
15+
- **Tool policies**: build_lucky_dice_section() uses SequentialDependencyPolicy
16+
to enforce that pick_up_dice must be called before throw_dice.
1617
- **Task examples**: build_task_examples_section() provides multi-step workflow
1718
demonstrations for proper tool sequencing.
1819
1920
Usage:
2021
To build a prompt with these sections, include them in a PromptTemplate::
2122
2223
from trivia_agent.sections import (
23-
QuestionSection,
24-
GameRulesSection,
25-
HintsSection,
26-
LuckyDiceSection,
24+
build_question_section,
25+
build_game_rules_section,
26+
build_hints_section,
27+
build_lucky_dice_section,
2728
build_task_examples_section,
2829
)
2930
3031
template = PromptTemplate(
3132
sections=[
32-
QuestionSection(),
33-
GameRulesSection(),
34-
HintsSection(),
35-
LuckyDiceSection(),
33+
build_question_section(),
34+
build_game_rules_section(),
35+
build_hints_section(),
36+
build_lucky_dice_section(),
3637
build_task_examples_section(),
3738
]
3839
)
@@ -128,23 +129,24 @@ class EmptyParams:
128129
# =============================================================================
129130

130131

131-
class QuestionSection(MarkdownSection[QuestionParams]):
132-
"""Section that displays the player's trivia question.
132+
def build_question_section() -> MarkdownSection[QuestionParams]:
133+
"""Build a section that displays the player's trivia question.
133134
134-
This section renders the current question being asked by the player. It uses
135-
a simple template with a single ${question} placeholder that gets replaced
136-
with the actual question text from QuestionParams.
135+
Creates a MarkdownSection that renders the current question being asked by
136+
the player. It uses a simple template with a single ${question} placeholder
137+
that gets replaced with the actual question text from QuestionParams.
137138
138139
The section is registered under the key "question", so parameters should be
139140
passed as {"question": QuestionParams(question="...")}.
140141
141-
Attributes:
142-
_params_type: QuestionParams - the parameter type for this section.
142+
Returns:
143+
MarkdownSection[QuestionParams]: A configured section ready to be included
144+
in a PromptTemplate. The section key is "question".
143145
144146
Example:
145147
Include in a prompt template::
146148
147-
template = PromptTemplate(sections=[QuestionSection(), ...])
149+
template = PromptTemplate(sections=[build_question_section(), ...])
148150
149151
Render with a specific question::
150152
@@ -156,32 +158,30 @@ class QuestionSection(MarkdownSection[QuestionParams]):
156158
The default_params provides an empty question string, so the section can
157159
render even without explicit parameters (useful for template validation).
158160
"""
159-
160-
_params_type = QuestionParams
161-
162-
def __init__(self) -> None:
163-
super().__init__(
164-
title="Question",
165-
key="question",
166-
template="${question}",
167-
default_params=QuestionParams(question=""),
168-
)
161+
return MarkdownSection[QuestionParams](
162+
title="Question",
163+
key="question",
164+
template="${question}",
165+
default_params=QuestionParams(question=""),
166+
)
169167

170168

171-
class GameRulesSection(MarkdownSection[EmptyParams]):
172-
"""Game rules section demonstrating progressive disclosure.
169+
def build_game_rules_section() -> MarkdownSection[EmptyParams]:
170+
"""Build a game rules section demonstrating progressive disclosure.
173171
174-
This section contains the complete rules for the secret trivia game but starts
175-
in SUMMARY visibility mode. The agent initially sees only a brief summary hint
176-
and can expand the full content by calling read_section('rules').
172+
Creates a MarkdownSection containing the complete rules for the secret trivia
173+
game. The section starts in SUMMARY visibility mode, so the agent initially
174+
sees only a brief summary hint and can expand the full content by calling
175+
read_section('rules').
177176
178177
Progressive disclosure is useful for:
179178
- Reducing initial prompt size and token usage
180179
- Letting the agent decide when it needs detailed information
181180
- Keeping the main prompt focused on the immediate task
182181
183-
Attributes:
184-
_params_type: EmptyParams - this section has no dynamic content.
182+
Returns:
183+
MarkdownSection[EmptyParams]: A configured section ready to be included
184+
in a PromptTemplate. The section key is "rules".
185185
186186
Section key: "rules"
187187
@@ -191,8 +191,8 @@ class GameRulesSection(MarkdownSection[EmptyParams]):
191191
Add to a prompt template for on-demand rules access::
192192
193193
template = PromptTemplate(sections=[
194-
QuestionSection(),
195-
GameRulesSection(), # Shows summary until agent expands
194+
build_question_section(),
195+
build_game_rules_section(), # Shows summary until agent expands
196196
])
197197
198198
The agent sees in the prompt::
@@ -207,14 +207,10 @@ class GameRulesSection(MarkdownSection[EmptyParams]):
207207
The rules explain the four secret categories (number, word, color, phrase)
208208
and instruct the agent to give concise answers from its skill knowledge.
209209
"""
210-
211-
_params_type = EmptyParams
212-
213-
def __init__(self) -> None:
214-
super().__init__(
215-
title="Game Rules",
216-
key="rules",
217-
template="""## Secret Trivia Game Rules
210+
return MarkdownSection[EmptyParams](
211+
title="Game Rules",
212+
key="rules",
213+
template="""## Secret Trivia Game Rules
218214
219215
You are the host of a secret trivia game. Your job is to answer trivia questions
220216
using secret knowledge that only you possess.
@@ -239,29 +235,30 @@ def __init__(self) -> None:
239235
- The secrets are in your skill knowledge - trust what you know
240236
- If asked about something that's not a secret, say you don't know
241237
""",
242-
summary=(
243-
"Game rules available. Use read_section('rules') to review "
244-
"how the secret trivia game works."
245-
),
246-
visibility=SectionVisibility.SUMMARY,
247-
default_params=EmptyParams(),
248-
)
238+
summary=(
239+
"Game rules available. Use read_section('rules') to review "
240+
"how the secret trivia game works."
241+
),
242+
visibility=SectionVisibility.SUMMARY,
243+
default_params=EmptyParams(),
244+
)
249245

250246

251-
class HintsSection(MarkdownSection[EmptyParams]):
252-
"""Hints section that bundles the hint_lookup tool.
247+
def build_hints_section() -> MarkdownSection[EmptyParams]:
248+
"""Build a hints section that bundles the hint_lookup tool.
253249
254-
This section demonstrates attaching tools to sections. When this section is
255-
included in a prompt template, the hint_lookup tool automatically becomes
256-
available to the agent. This pattern keeps tools co-located with their
257-
documentation and usage instructions.
250+
Creates a MarkdownSection that demonstrates attaching tools to sections.
251+
When this section is included in a prompt template, the hint_lookup tool
252+
automatically becomes available to the agent. This pattern keeps tools
253+
co-located with their documentation and usage instructions.
258254
259255
The hint_lookup tool allows the agent to retrieve hints for each secret
260256
category without revealing the actual answer. Available categories are:
261257
number, word, color, and phrase.
262258
263-
Attributes:
264-
_params_type: EmptyParams - this section has no dynamic content.
259+
Returns:
260+
MarkdownSection[EmptyParams]: A configured section ready to be included
261+
in a PromptTemplate. The section key is "hints".
265262
266263
Section key: "hints"
267264
@@ -274,8 +271,8 @@ class HintsSection(MarkdownSection[EmptyParams]):
274271
Include to enable hint functionality::
275272
276273
template = PromptTemplate(sections=[
277-
QuestionSection(),
278-
HintsSection(), # Adds hint_lookup tool to available tools
274+
build_question_section(),
275+
build_hints_section(), # Adds hint_lookup tool to available tools
279276
])
280277
281278
The agent can then call hint_lookup to help stuck players::
@@ -285,43 +282,40 @@ class HintsSection(MarkdownSection[EmptyParams]):
285282
286283
Note:
287284
Tools attached to sections are only available when the section is part
288-
of the active prompt. Remove HintsSection to disable hint functionality.
285+
of the active prompt. Remove the hints section to disable hint functionality.
289286
"""
290-
291-
_params_type = EmptyParams
292-
293-
def __init__(self) -> None:
294-
super().__init__(
295-
title="Hints",
296-
key="hints",
297-
template="""## Hint System
287+
return MarkdownSection[EmptyParams](
288+
title="Hints",
289+
key="hints",
290+
template="""## Hint System
298291
299292
If a player is stuck, you can provide hints using the hint_lookup tool.
300293
Hints give clues without revealing the actual answer.
301294
302295
Available hint categories: number, word, color, phrase
303296
""",
304-
visibility=SectionVisibility.FULL,
305-
tools=(hint_lookup_tool,),
306-
default_params=EmptyParams(),
307-
)
297+
visibility=SectionVisibility.FULL,
298+
tools=(hint_lookup_tool,),
299+
default_params=EmptyParams(),
300+
)
308301

309302

310-
class LuckyDiceSection(MarkdownSection[EmptyParams]):
311-
"""Lucky Dice mini-game section demonstrating tool policies.
303+
def build_lucky_dice_section() -> MarkdownSection[EmptyParams]:
304+
"""Build a Lucky Dice mini-game section demonstrating tool policies.
312305
313-
This section showcases SequentialDependencyPolicy, which enforces tool call
314-
ordering. The throw_dice tool cannot be called until pick_up_dice has been
315-
called first. If the agent tries to throw without picking up, the policy
316-
blocks the call and returns an error message.
306+
Creates a MarkdownSection that showcases SequentialDependencyPolicy, which
307+
enforces tool call ordering. The throw_dice tool cannot be called until
308+
pick_up_dice has been called first. If the agent tries to throw without
309+
picking up, the policy blocks the call and returns an error message.
317310
318311
Tool policies are useful for:
319312
- Enforcing proper operation sequences (e.g., connect before query)
320313
- Preventing invalid state transitions
321314
- Teaching agents correct workflows through guardrails
322315
323-
Attributes:
324-
_params_type: EmptyParams - this section has no dynamic content.
316+
Returns:
317+
MarkdownSection[EmptyParams]: A configured section ready to be included
318+
in a PromptTemplate. The section key is "dice".
325319
326320
Section key: "dice"
327321
@@ -339,8 +333,8 @@ class LuckyDiceSection(MarkdownSection[EmptyParams]):
339333
Include to enable the dice mini-game::
340334
341335
template = PromptTemplate(sections=[
342-
QuestionSection(),
343-
LuckyDiceSection(), # Adds dice tools with ordering policy
336+
build_question_section(),
337+
build_lucky_dice_section(), # Adds dice tools with ordering policy
344338
])
345339
346340
Correct tool sequence::
@@ -360,21 +354,17 @@ class LuckyDiceSection(MarkdownSection[EmptyParams]):
360354
The policy state resets between agent sessions. Each new session
361355
requires pick_up_dice to be called before throw_dice.
362356
"""
357+
# Policy: throw_dice requires pick_up_dice to have been called first
358+
dice_policy = SequentialDependencyPolicy(
359+
dependencies={
360+
"throw_dice": frozenset({"pick_up_dice"}),
361+
}
362+
)
363363

364-
_params_type = EmptyParams
365-
366-
def __init__(self) -> None:
367-
# Policy: throw_dice requires pick_up_dice to have been called first
368-
dice_policy = SequentialDependencyPolicy(
369-
dependencies={
370-
"throw_dice": frozenset({"pick_up_dice"}),
371-
}
372-
)
373-
374-
super().__init__(
375-
title="Lucky Dice",
376-
key="dice",
377-
template="""## Lucky Dice Mini-Game
364+
return MarkdownSection[EmptyParams](
365+
title="Lucky Dice",
366+
key="dice",
367+
template="""## Lucky Dice Mini-Game
378368
379369
Players can roll the lucky dice for bonus points! But there's a rule:
380370
you must pick up the dice before you can throw it.
@@ -386,11 +376,11 @@ def __init__(self) -> None:
386376
The throw_dice tool has a policy that enforces this ordering.
387377
If someone asks to roll the dice, make sure to pick it up first!
388378
""",
389-
visibility=SectionVisibility.FULL,
390-
tools=(pick_up_dice_tool, throw_dice_tool),
391-
policies=(dice_policy,),
392-
default_params=EmptyParams(),
393-
)
379+
visibility=SectionVisibility.FULL,
380+
tools=(pick_up_dice_tool, throw_dice_tool),
381+
policies=(dice_policy,),
382+
default_params=EmptyParams(),
383+
)
394384

395385

396386
# =============================================================================

src/trivia_agent/worker.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@
4646
from trivia_agent.models import TriviaRequest, TriviaResponse
4747
from trivia_agent.sections import (
4848
EmptyParams,
49-
GameRulesSection,
50-
HintsSection,
51-
LuckyDiceSection,
5249
QuestionParams,
53-
QuestionSection,
54-
build_task_examples_section, # type: ignore[attr-defined]
50+
build_game_rules_section,
51+
build_hints_section,
52+
build_lucky_dice_section,
53+
build_question_section,
54+
build_task_examples_section, # pyright: ignore[reportUnknownVariableType]
5555
)
5656

5757
if TYPE_CHECKING:
@@ -180,10 +180,10 @@ def build_prompt_template() -> PromptTemplate[TriviaResponse]:
180180
ns="trivia",
181181
key="main",
182182
sections=[ # type: ignore[list-item]
183-
QuestionSection(),
184-
GameRulesSection(), # Progressive disclosure - starts summarized
185-
HintsSection(), # Has attached hint_lookup tool
186-
LuckyDiceSection(), # Lucky Dice mini-game with policy enforcement
183+
build_question_section(),
184+
build_game_rules_section(), # Progressive disclosure - starts summarized
185+
build_hints_section(), # Has attached hint_lookup tool
186+
build_lucky_dice_section(), # Lucky Dice mini-game with policy enforcement
187187
build_task_examples_section(), # Multi-step workflow examples
188188
],
189189
feedback_providers=build_feedback_providers(),
@@ -318,10 +318,10 @@ def prepare(
318318
ns="trivia",
319319
key="main",
320320
sections=[ # type: ignore[list-item]
321-
QuestionSection(),
322-
GameRulesSection(),
323-
HintsSection(),
324-
LuckyDiceSection(), # Lucky Dice mini-game with policy enforcement
321+
build_question_section(),
322+
build_game_rules_section(),
323+
build_hints_section(),
324+
build_lucky_dice_section(), # Lucky Dice mini-game with policy enforcement
325325
build_task_examples_section(), # Multi-step workflow examples
326326
workspace_section, # Session-bound workspace access
327327
],

0 commit comments

Comments
 (0)