Skip to content

prompt_sections_lab

Low-level lab — build a system prompt from sections you write yourself.

This is a lab, not a rung. It works one level below Agent and PromptingCapability: those compile a prompt from contributed sources and are what an application should use. PromptBuilder is the machinery underneath, and it is public because a deployment sometimes needs to build a prompt outside a turn — to render one for review, to diff two, or to test a section alone.

  • Lines: ~120
  • Prerequisites: none
  • Key concepts: PromptBuilder, PromptSection, section priority, empty sections
symfonic examples add prompt_sections_lab
python -m prompt_sections_lab

What it shows

A section is a protocol, not a base class. PromptSection is @runtime_checkable with four attributes and one coroutine, so the three sections in this file prove they conform with isinstance and inherit nothing. The concrete sections the framework ships are internal on purpose: what is public is the shape, so the sections you write are first-class rather than second-best.

Priority orders the output, not the list. Lower is earlier. The lab adds Memory first and it renders last, which is the one thing worth getting wrong once here rather than in a prompt nobody can read.

A section with nothing to say says nothing. An empty section that still renders its heading costs a delimiter and tells the model the store was consulted and found empty — a different claim from not having consulted it.

Where it came from

Extracted from e2e_basic_agent, which is archived. That example was four demos in one file: this one, stream-event processing, a capability timeout, and an agent run. Three of the four reached into internal submodules (symfonic.core.prompt.sections, symfonic.core.streaming.event_processor), and the agent demo was already journey_stateless.

Keeping it would have meant promoting those submodules to the public surface — turning implementation detail into a permanent commitment to hold an internal shape still. The lesson worth keeping is here instead, rewritten against the contract rather than against the framework's own section classes.

Import from the package root

symfonic.core.prompt, symfonic.core.prompt.blocks and symfonic.core.prompt.blocks.sources are public, and each defines an __all__ that is the contract. Their submodules are internal. A project that imports from a submodule will fail its own generated tests/test_public_api_imports.py.