In AgentForge, the PromptProcessor
class coordinates how prompt templates are read, validated, and dynamically populated with data. While most users won’t need to alter these methods directly, understanding them can be helpful if you want to override or deeply customize prompt logic in your own classes.
Key Responsibilities:
- Format Validation: Ensures that any loaded prompt dictionary has proper keys (
system
,user
) and that each prompt section is a string or dictionary of sub-sections. - Variable Extraction: Identifies placeholders like
{variable_name}
in prompt templates. - Data Checking: Validates that required placeholders actually exist and have non-empty values in your agent’s data.
- Rendering: Substitutes placeholders with actual values and optionally unescapes braces (
/{.../}
→{...}
).
Typical Flow
When you run an agent, AgentForge:
- Loads the prompt YAML into a dictionary.
- Passes it to
PromptProcessor
for structure checks. - Renders each section (
system
anduser
) by substituting placeholders with your agent’sdata
. - Validates the final result so it isn’t empty.
class PromptProcessor:
"""
A utility class for handling dynamic prompt templates. It supports extracting variables from templates,
checking for required variables, and rendering templates with provided data.
"""
pattern = r\"\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}\"
def __init__(self):
self.logger = Logger(name=self.__class__.__name__)
pattern
: A regex ({variable_name}
) for detecting placeholders in your prompt strings.logger
: An instance of AgentForge’s logger, used for warnings, errors, and debug messages.
Purpose: Confirms that prompts
is a dictionary with exactly two keys: system
and user
. Also checks if those entries are either strings or dictionaries (sub-sections).
def check_prompt_format(self, prompts):
# Raises ValueError if format is incorrect
Usage
Called internally by AgentForge to ensure your prompt YAML is valid. If you define multi-section prompts, they must be nested under system
or user
.
Purpose: Returns a list of placeholder variable names from a single string template.
def extract_prompt_variables(self, template: str) -> list:
...
Example:
variables = processor.extract_prompt_variables("Hello, {user_name}. Today is {day_of_week}.")
# variables = ["user_name", "day_of_week"]
Purpose: Checks if all required placeholders in prompt_template
are present and not empty in data
. Returns prompt_template
if valid, None
otherwise.
def handle_prompt_template(self, prompt_template: str, data: dict) -> str | None:
...
Behavior
- Grabs placeholders with
extract_prompt_variables
. - If each placeholder in
data
is non-empty, returnsprompt_template
. - Otherwise returns
None
, indicating a missing or empty variable.
Purpose: Performs the actual substitution of placeholders with data, then unescapes braces.
def render_prompt_template(self, template: str, data: dict) -> str:
...
Process
- Regex Substitution: For each
{variable}
, replace withdata[variable]
if it exists, or leave the placeholder if it doesn’t. - Unescape Braces: Replaces
/{.../}
patterns with{...}
, letting you embed literal braces in your prompt.
Example:
template = "Hello, {user_name}. Your ID is /{id/}."
data = {"user_name": "Alice", "id": 1234}
rendered = processor.render_prompt_template(template, data)
# "Hello, Alice. Your ID is {id}."
(Here, the user intentionally kept {id}
unrendered by escaping braces.)
Purpose: Renders both system
and user
sections of a prompt dictionary, supporting multi-section sub-prompts.
def render_prompts(self, prompts, data):
...
Behavior
- If
system
oruser
is a string, treat it as one “Main” sub-prompt. - If it’s a dictionary, each key is a named sub-section.
- For each sub-section, first calls
handle_prompt_template
; if valid, callsrender_prompt_template
. Otherwise logs a message indicating a missing variable. - Joins sub-sections with newlines.
Example:
prompts = {
"system": {
"Intro": "You are an assistant.",
"Rules": "Please be concise."
},
"user": "Hello, {user_name}!"
}
data = {"user_name": "Alice"}
rendered = processor.render_prompts(prompts, data)
print(rendered["system"])
# "You are an assistant.\nPlease be concise."
print(rendered["user"])
# "Hello, Alice!"
Purpose: Ensures the final system
and user
strings aren’t empty after rendering.
def validate_rendered_prompts(self, rendered_prompts):
...
- Raises a
ValueError
if either is blank (""
or whitespace).
Purpose: Converts /{.../}
→ {...}
so you can deliberately keep braces in your final text without them being interpreted as placeholders.
def unescape_braces(template: str) -> str:
...
While you can call these methods directly if you’re building something unusual, AgentForge typically calls them automatically when an agent runs. Under the hood:
- Check Prompt Format (from loaded YAML).
- Render each prompt section.
- Validate that the final prompt is not empty.
Example:
from agentforge.agent import Agent
class GreetingAgent(Agent):
def load_additional_data(self):
self.template_data["user_name"] = "Alice"
# Prompt file might say:
# system: "You are a greeting agent."
# user: "Hello, {user_name}!"
When agent.run()
is called, AgentForge uses PromptProcessor
to fill in {user_name}
automatically.
- Custom Placeholders: If you want a different placeholder style (e.g.,
<<var>>
), you might subclass and changepattern
. - Conditional Rendering: If you need advanced logic for “sub-prompts,” e.g., only show certain text if
data["is_vip"]
is true. - Multi-Language: If you prefer different code fences or advanced ways of escaping braces.
IMPORTANT NOTE: Overriding prompt logic can break backward compatibility with existing YAML files. Tread carefully!!!
Suppose you have a prompt dictionary:
prompts = {
"system": "You are a helpful assistant.",
"user": {
"Greet": "Hello, {user_name}.",
"Ask": "How are you doing today, {user_name}?"
}
}
And data:
data = { "user_name": "Alice" }
check_prompt_format(prompts)
: Confirms keys =system
,user
.render_prompts(prompts, data)
:- For
system
: It's a string → "Main" sub-prompt ="You are a helpful assistant."
.handle_prompt_template("You are a helpful assistant.", data)
→ returns same string since no placeholders.render_prompt_template(...)
→"You are a helpful assistant."
.
- For
user
: It's a dict with sub-sectionsGreet
,Ask
.handle_prompt_template("Hello, {user_name}.", data)
→ placeholders =[user_name]
, found in data, so returns the template.render_prompt_template("Hello, {user_name}.", data)
→"Hello, Alice."
- Same for
Ask
→"How are you doing today, Alice?"
- Joins sub-sections with newlines:
User
prompt ="Hello, Alice.\nHow are you doing today, Alice?"
- For
validate_rendered_prompts(...)
: Bothsystem
anduser
are non-empty.- Final results are:
{
"system": "You are a helpful assistant.",
"user": "Hello, Alice.\nHow are you doing today, Alice?"
}
- Use Consistent Variable Names: Stick to
[a-zA-Z_][a-zA-Z0-9_]*
to avoid unexpected parse failures. - Be Mindful of Escaped Braces:
/{something/}
will not be replaced with data. If you see these patterns, they’ll remain as literal braces in the final output. - Keep Sub-Prompts Manageable: Large YAML with too many sub-sections can become confusing. Possibly break them into smaller agent files or keep them well-documented.
The PromptProcessor
in AgentForge underpins how prompts are rendered and validated. Most developers will benefit from letting the framework handle prompt substitution. However, if you need deeper customization—like unique placeholder styles or conditional logic—understanding this utility is key. Proceed carefully, as changing the prompt flow can break existing templates.
Need Help?
- Email: contact@agentforge.net
- Discord: Join our Discord Server