Logic Flows
Build conditional loops and multi-step agent execution
Overview
Logic flows enable agents to execute multiple steps based on conditions. Instead of single-shot execution, agents can loop, make decisions, and execute complex workflows using the LogicRunner class.
LogicRunner Class
The core class for managing conditional agent loops:
from agentic import LogicRunner, LogicConfig, LogicCondition
config = LogicConfig(
logic_id="my_loop",
max_iterations=10,
stop_conditions=[...], # List of LogicCondition objects
loop_until_conditions=[...]
)
runner = LogicRunner(agent_runner, context, patterns, config)
# Execute with streaming events
async for event in runner.run_stream("initial input"):
if isinstance(event, StepCompleteEvent):
print(f"Step {event.result.iteration} done")
LogicConfig
Configuration for logic flow execution:
from agentic import LogicConfig, ProcessingMode
config = LogicConfig(
logic_id="research_loop",
max_iterations=20, # Safety limit
stop_conditions=[...], # Conditions to stop loop
loop_until_conditions=[...], # Loop until satisfied
break_on_error=True, # Stop on agent errors
processing_mode=ProcessingMode.THREAD, # Default processing mode
context_health_checks=[...] # Monitor context health
)
LogicCondition
Define conditions for when to stop or continue looping:
Basic Structure
from agentic import LogicCondition
condition = LogicCondition(
pattern_set="default", # Pattern set name
pattern_name="done", # Pattern/text to match
match_type="contains", # "contains" | "equals" | "regex"
target="response", # "response" | "reasoning" | "tool_output" | "context:{key}"
evaluation_point="auto" # When to evaluate (see below)
)
Match Types
1. Contains (Pattern Detection)
Check if a specific pattern appears in the output:
# Stop when "done" pattern detected in response
LogicCondition(
pattern_set="default",
pattern_name="done",
match_type="contains",
target="response"
)
2. Equals (Exact Match)
Check if text exactly equals a value:
# Stop when context key equals "complete"
LogicCondition(
pattern_set="default",
pattern_name="complete",
match_type="equals",
target="context:status"
)
3. Regex (Pattern Matching)
Use regular expressions for complex pattern matching:
# Stop when confidence score >= 95% appears
LogicCondition(
pattern_set="default", # Not used for regex
pattern_name=r"confidence:\s*(\d+)%",
match_type="regex",
target="response"
)
Target Options
"response"- Check agent's response segment"reasoning"- Check reasoning segments"tool_output"- Check tool execution results"context:key"- Check specific context key (e.g.,"context:task_status")
Evaluation Points
Control when conditions are evaluated during agent execution. This is crucial for early stopping and fine-grained control:
# Common evaluation points:
"auto" # Smart default (context → step_complete, patterns → llm_complete)
"llm_chunk" # On every LLM chunk as it streams (real-time, for early exit)
"llm_complete" # After LLM generation completes
"tool_detected" # When tool pattern detected in output
"tool_finished" # After tool execution completes
"step_complete" # After entire agent step completes (most common)
"any_event" # On every event (most frequent, use sparingly)
# Advanced evaluation points (for fine-grained control):
"pattern_start" # When a pattern starts being detected
"pattern_end" # When a non-tool pattern completes
"pattern_content"# During pattern content streaming
"tool_start" # When tool execution begins
"tool_output" # During tool output streaming
"context_write" # When context is written
"error" # When an error occurs
"status" # On status changes
Example: Early Exit on Streaming
# Stop immediately when "ABORT" appears in LLM stream
LogicCondition(
pattern_set="default",
pattern_name=r"ABORT",
match_type="regex",
target="response",
evaluation_point="llm_chunk" # Check during streaming!
)
Helper Functions
Convenient functions for common loop patterns:
Loop N Times
from agentic import loop_n_times
runner = loop_n_times(agent_runner, context, patterns, n=5)
async for event in runner.run_stream("Execute 5 iterations"):
if isinstance(event, StepCompleteEvent):
result = event.result
print(f"Iteration {result.iteration}: {result.status}")
Loop Until Pattern
from agentic import loop_until_pattern
runner = loop_until_pattern(
agent_runner,
context,
patterns,
pattern_set="default",
pattern_name="done",
target="response",
max_iterations=20
)
async for event in runner.run_stream("Complete task"):
if isinstance(event, StepCompleteEvent):
print(f"Step {event.result.iteration} done")
Loop Until Regex
from agentic import loop_until_regex
runner = loop_until_regex(
agent_runner,
context,
patterns,
regex_pattern=r"confidence:\s*([8-9]\d|100)%", # 80-100%
target="response",
max_iterations=10
)
async for event in runner.run_stream("Refine until confident"):
pass
Stop on Error
from agentic import stop_on_error
runner = stop_on_error(
agent_runner,
context,
patterns,
max_iterations=100
)
# Will stop immediately on any agent error
Context Health Monitoring
Monitor context size and prevent runaway memory usage:
from agentic import ContextHealthCheck, LogicConfig
config = LogicConfig(
logic_id="monitored_loop",
max_iterations=50,
context_health_checks=[
# Warn if any context key exceeds 100KB
ContextHealthCheck(
check_type="size",
key_pattern="*",
threshold=100_000,
action="warn",
evaluation_point="step_complete"
),
# Stop if llm_output versions exceed 50
ContextHealthCheck(
check_type="version_count",
key_pattern="llm_output:*",
threshold=50,
action="stop",
evaluation_point="step_complete",
max_versions_limit=10000
)
]
)
Health Check Types
"size"- Monitor byte size of context values"version_count"- Monitor number of versions stored for keys
Key Pattern Matching
"*"- All keys"llm_output:*"- All keys starting with prefix"tool_result:*"- Glob pattern matching
Actions
"warn"- Emit ContextHealthEvent but continue"stop"- Stop the logic loop immediately
Stop vs Loop-Until Conditions
Stop Conditions
Terminate the loop when condition is met:
config = LogicConfig(
logic_id="example",
stop_conditions=[
# Stop if "ERROR" appears
LogicCondition(
pattern_set="default",
pattern_name=r"ERROR",
match_type="regex",
target="response"
)
]
)
Loop-Until Conditions
Continue looping until condition is satisfied:
config = LogicConfig(
logic_id="example",
loop_until_conditions=[
# Loop until "COMPLETE" appears
LogicCondition(
pattern_set="default",
pattern_name=r"COMPLETE",
match_type="regex",
target="response"
)
]
)
Real-World Example: Research Agent
Agent that iteratively researches until comprehensive answer is found:
from agentic import (
LogicRunner, LogicConfig, LogicCondition,
ContextHealthCheck, ProcessingMode
)
# Configure research loop
config = LogicConfig(
logic_id="research_agent",
max_iterations=10,
# Stop on errors
break_on_error=True,
# Stop conditions
stop_conditions=[
# Stop if agent says DONE
LogicCondition(
pattern_set="default",
pattern_name="done",
match_type="contains",
target="response"
),
# Stop if comprehensive answer detected
LogicCondition(
pattern_set="default",
pattern_name=r"(?=.*sources)(?=.{500,})", # 500+ chars with "sources"
match_type="regex",
target="response",
evaluation_point="llm_complete"
)
],
# Monitor context health
context_health_checks=[
ContextHealthCheck(
check_type="size",
key_pattern="*",
threshold=500_000, # 500KB limit
action="stop"
)
],
processing_mode=ProcessingMode.THREAD
)
runner = LogicRunner(agent_runner, context, patterns, config)
# Execute research loop
async for event in runner.run_stream("Research quantum computing applications"):
if isinstance(event, StepCompleteEvent):
print(f"Research iteration {event.result.iteration} complete")
print(f"Response: {event.result.segments.response[:100]}...")
elif isinstance(event, StatusEvent):
print(f"Status: {event.message}")
elif isinstance(event, ContextHealthEvent):
print(f"Context health warning: {event.check_type}")
Batch Mode
For synchronous execution without streaming:
# Collect all results at once
results = runner.run("Execute task")
for i, result in enumerate(results):
print(f"Iteration {i + 1}: {result.status}")
if result.segments.response:
print(f" Response: {result.segments.response}")
run() method cannot be called from an async context. Use run_stream() in async code.
Best Practices
- Always set max_iterations - Prevent infinite loops with reasonable limits
- Use evaluation_point wisely -
"llm_chunk"for early exit,"step_complete"for accuracy - Monitor context health - Prevent memory issues with health checks
- Set break_on_error=True - Stop immediately on agent errors
- Combine stop and loop-until - Use both for robust flow control
- Use specific evaluation points - Don't rely solely on "auto" for critical conditions
- Test conditions thoroughly - Verify regex patterns and thresholds before production
Event Handling
LogicRunner emits various events during execution:
from agentic.events import (
StatusEvent, StepCompleteEvent, ContextHealthEvent,
LLMChunkEvent, LLMCompleteEvent, ToolEndEvent
)
async for event in runner.run_stream(user_input):
if isinstance(event, StatusEvent):
print(f"[{event.status}] {event.message}")
elif isinstance(event, StepCompleteEvent):
print(f"Iteration {event.result.iteration} done")
elif isinstance(event, ContextHealthEvent):
print(f"Health: {event.key} - {event.check_type}")
elif isinstance(event, LLMChunkEvent):
print(event.chunk, end="") # Stream LLM output
Next Steps
- Agent System - Configure agents for logic flows
- Multi-Agent - Coordinate multiple agents with logic flows
- Events - Handle flow events in detail
- Patterns - Define patterns for conditions