Pattern catalog¶
This document maps agentic AI patterns to their implementations in the Agentic Runtime Platform codebase. For each pattern it identifies the specific files, classes, and functions involved, so you can locate how a pattern works without reading the full codebase. Patterns whose implementations have since been removed from the repository are listed separately at the end.
Table of contents¶
- Orchestration patterns
- 1. Tool use
- 2. Task decomposition
- 3. Capability matching
- 4. Multi-agent coordination
- 5. Bounded iteration
- Prompting patterns
- 6. Chain-of-thought scaffolding
- Retrieval patterns
- 7. Hybrid retrieval
- 8. Conversational memory
- Routing patterns
- 9. Circuit breaker
- Evaluation patterns
- 10. Self-reflection
- Safety patterns
- 11. Prompt injection defense
- Previously implemented (removed)
Orchestration patterns¶
1. Tool use¶
Category: Orchestration
Implementation: agentic-workflows-v2/agentic_v2/agents/base.py -- BaseAgent._bind_tools(), _handle_tool_calls(), _get_tool_schemas()
How It Works: During initialization, BaseAgent._bind_tools() iterates the global ToolRegistry and binds every tool whose tier is at or below the agent's configured default_tier. When the LLM response contains tool_calls, _handle_tool_calls() dispatches each call to the matching bound tool, executes it, and injects the result back into conversation memory as a "tool" role message so the LLM can incorporate it in the next iteration.
Code Reference: agentic-workflows-v2/agentic_v2/agents/base.py (_bind_tools, _handle_tool_calls)
Example:
# Tier-filtered binding at agent init
async def _bind_tools(self) -> None:
max_tier = self.config.default_tier.value
for tool in self.tools.list_tools():
if tool.tier <= max_tier:
self._bound_tools[tool.name] = tool
2. Task decomposition¶
Category: Orchestration
Implementation: agentic-workflows-v2/agentic_v2/agents/orchestrator.py -- OrchestratorAgent._parse_output(), decompose_task()
How It Works: The OrchestratorAgent sends the task description and a list of registered agents (with their capabilities) to the LLM via a system prompt requesting structured JSON decomposition. The LLM returns a JSON plan with subtasks, each declaring required capabilities and dependencies. The orchestrator parses this with _extract_json() (delegating to balanced-brace extraction in json_extraction.py) and builds SubTask objects with dependency edges for downstream scheduling.
Code Reference: agentic-workflows-v2/agentic_v2/agents/orchestrator.py (decompose_task, _parse_output)
Example:
{
"subtasks": [
{
"id": "generate",
"description": "Generate the code",
"capabilities": ["code_generation"],
"dependencies": [],
"parallel_group": 1
},
{
"id": "review",
"description": "Review the generated code",
"capabilities": ["code_review"],
"dependencies": ["generate"],
"parallel_group": 2
}
]
}
3. Capability matching¶
Category: Orchestration
Implementation: agentic-workflows-v2/agentic_v2/agents/capabilities.py -- CapabilitySet.score_match(), get_agent_capabilities()
How It Works: Each agent declares capabilities via mixin classes (CodeGenerationMixin, CodeReviewMixin, etc.) that return a CapabilitySet. When the orchestrator needs to assign an agent to a subtask, score_match() computes a 0.0-1.0 match score by averaging min(1.0, agent_proficiency / required_proficiency) across all required capability types. get_agent_capabilities() walks the agent's MRO to aggregate capabilities from all mixin bases. The orchestrator builds a ranked candidate list sorted by score and stores fallback chains for resilience.
Code Reference: agentic-workflows-v2/agentic_v2/agents/capabilities.py (score_match, get_agent_capabilities)
Example:
# Scoring: proficiency ratio averaged across requirements
def score_match(self, required: "CapabilitySet") -> float:
total_score = 0.0
for cap_type, req_cap in required.capabilities.items():
our_cap = self.capabilities.get(cap_type)
if our_cap:
total_score += min(1.0, our_cap.proficiency / max(0.01, req_cap.proficiency))
return total_score / len(required.capabilities)
4. Multi-agent coordination¶
Category: Orchestration
Implementation: agentic-workflows-v2/agentic_v2/agents/orchestrator.py -- OrchestratorAgent.execute_as_dag(), _execute_plan(); agentic-workflows-v2/agentic_v2/engine/dag_executor.py -- DAGExecutor.execute()
How It Works: The orchestrator registers specialized agents, decomposes the task, assigns agents via capability scoring, and then executes the resulting plan. The preferred path is execute_as_dag(), which builds a DAG object from subtask dependencies and delegates to the DAGExecutor. The DAG executor implements Kahn's algorithm, tracking in-degrees and scheduling steps via asyncio.wait(FIRST_COMPLETED) for maximum parallelism. A fallback chain in _execute_plan() tries alternative agents if the primary assignment fails.
Code Reference: agentic-workflows-v2/agentic_v2/agents/orchestrator.py (execute_as_dag, _execute_plan); agentic-workflows-v2/agentic_v2/engine/dag_executor.py (DAGExecutor.execute)
Example:
# DAG executor: Kahn's algorithm with asyncio parallelism
ready = deque([name for name, deg in in_degree.items() if deg == 0])
while len(completed) < len(dag.steps):
while ready and len(running) < max_concurrency:
step_name = ready.popleft()
tasks.add(asyncio.create_task(run_step(step_name)))
done, tasks = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
# ... decrement downstream in-degrees, unlock ready steps
5. Bounded iteration¶
Category: Orchestration
Implementation: agentic-workflows-v2/agentic_v2/workflows/loader.py -- loop_max parsing; agentic-workflows-v2/agentic_v2/workflows/definitions/iterative_review.yaml -- review_rework_loop step (loop_until + loop_max)
How It Works: Workflow steps can declare a loop_until expression and a loop_max integer. The workflow loader parses loop_max (default 3, minimum 1) and attaches it to the StepDefinition. At runtime, the step re-executes until the loop_until condition is satisfied or loop_max iterations are reached, preventing infinite agent loops. The shipped iterative_review workflow demonstrates this: its review_rework_loop step reviews and reworks generated code until the review status is approved or the iteration cap trips, with a post-loop escalation_notice step that fires only when the loop never approved.
Code Reference: agentic-workflows-v2/agentic_v2/workflows/loader.py (loop_max parsing); agentic-workflows-v2/agentic_v2/workflows/definitions/iterative_review.yaml (review_rework_loop)
Example:
# iterative_review.yaml -- bounded review/rework loop
- name: review_rework_loop
agent: tier3_reviewer
depends_on: [implement]
loop_until: >-
${steps.review_rework_loop.outputs.review_status} in ['APPROVED', 'APPROVED_WITH_NOTES']
loop_max: ${inputs.max_review_rounds}
Prompting patterns¶
6. Chain-of-thought scaffolding¶
Category: Prompting
Implementation: All 7 persona files in agentic-workflows-v2/agentic_v2/prompts/*.md (architect, coder, orchestrator, planner, reviewer, tester, validator) -- ## Reasoning Protocol sections
How It Works: Every agent persona includes a ## Reasoning Protocol section with a domain-specific multi-step cognitive workflow that instructs the LLM to reason through the task before generating output. These are not generic "think step-by-step" prompts -- each is tailored to the persona's function. For example, the coder persona uses stack-identification-first reasoning, while the reviewer, tester, and validator personas each define their own review, test-design, and verification protocols.
Code Reference: agentic-workflows-v2/agentic_v2/prompts/coder.md (## Reasoning Protocol section; the other six personas follow the same convention)
Example:
## Reasoning Protocol (from coder.md)
Before generating your response:
1. Identify the target stack from inputs and select the matching language/framework conventions
2. Read any existing code, review findings, or rework instructions to understand what must change
3. Plan the file structure: which files to create vs. modify, and in what dependency order
4. For each file, determine imports, types, and error handling before writing implementation
5. Verify all artifacts are complete — no TODOs, no missing imports, no placeholder logic
Retrieval patterns¶
7. Hybrid retrieval¶
Category: Retrieval
Implementation: agentic-workflows-v2/agentic_v2/rag/retrieval.py -- BM25Index, reciprocal_rank_fusion(), HybridRetriever
How It Works: The HybridRetriever combines two independent retrieval signals. Dense retrieval embeds the query via EmbeddingProtocol and searches a VectorStoreProtocol backend using cosine similarity. Keyword retrieval uses a pure-Python BM25Index implementing Okapi BM25 with k1=1.5 and b=0.75, including proper IDF with Laplace smoothing. Both ranked lists are merged via reciprocal_rank_fusion(), which computes score = sum(1/(k+rank)) with k=60 (per Cormack et al. 2009) and deduplicates by chunk_id. This dual-signal approach improves recall over either method alone.
Code Reference: agentic-workflows-v2/agentic_v2/rag/retrieval.py (BM25Index, reciprocal_rank_fusion, HybridRetriever.retrieve)
Example:
# HybridRetriever.retrieve() -- dual signal + RRF fusion
async def retrieve(self, query: str, *, top_k: int = 5) -> list[RetrievalResult]:
dense_results = await self.dense_only(query, top_k=top_k)
bm25_results = self._bm25.search(query, top_k=top_k)
results = reciprocal_rank_fusion(
[dense_results, bm25_results], k=self._rrf_k, top_k=top_k,
)
return results
8. Conversational memory¶
Category: Retrieval
Implementation: agentic-workflows-v2/agentic_v2/agents/memory.py -- ConversationMemory class
How It Works: ConversationMemory maintains a sliding window of messages (default 50) with a token budget (default 8000 tokens). When the window exceeds either limit, _summarize_and_trim() compacts older messages into textual summaries, preserving the system prompt and most recent messages. Summaries are prepended to the LLM message list as system context by get_messages(). The summary budget is itself bounded (max_summaries=5) to prevent unbounded growth. Token counting uses a len(text) // 4 heuristic with an optional pluggable token_counter callable for precise counting.
Code Reference: agentic-workflows-v2/agentic_v2/agents/memory.py (ConversationMemory, _summarize_and_trim, get_messages)
Example:
# Automatic summarization when window overflows
def add(self, role: str, content: str, **kwargs) -> ConversationMessage:
msg = ConversationMessage(role=role, content=content, **kwargs)
self.messages.append(msg)
if len(self.messages) > self.max_messages or self.total_tokens > self.max_tokens:
self._summarize_and_trim()
return msg
Routing patterns¶
9. Circuit breaker¶
Category: Routing
Implementation: agentic-workflows-v2/agentic_v2/models/model_stats.py -- ModelStats, CircuitState; agentic-workflows-v2/agentic_v2/models/smart_router.py -- SmartModelRouter
How It Works: Each model has a per-model ModelStats instance with a CircuitState FSM: CLOSED (normal) -> OPEN (after 5 consecutive failures) -> HALF_OPEN (after the recovery timeout). The SmartModelRouter layers production hardening on top: health-weighted selection scoring success_rate x 0.6 + latency_score x 0.2 + recency_score x 0.2, adaptive cooldowns with base x 1.5^consecutive_failures capped at 600s, per-provider bulkhead semaphores preventing cascade failures, probe lock serialization for HALF_OPEN recovery, cross-tier degradation preferring cheaper tiers first, and rate-limit header parsing. Latency is measured with time.monotonic() to avoid wall-clock jumps.
Code Reference: agentic-workflows-v2/agentic_v2/models/model_stats.py (CircuitState, ModelStats, check_circuit); agentic-workflows-v2/agentic_v2/models/smart_router.py (SmartModelRouter, call_with_fallback)
Example:
# Circuit state transition in ModelStats
def record_failure(self, error_type: str = "unknown") -> None:
self._consecutive_failures += 1
if self.circuit_state == CircuitState.HALF_OPEN:
self.circuit_state = CircuitState.OPEN # failed recovery probe
elif self._consecutive_failures >= self._failure_threshold:
self.circuit_state = CircuitState.OPEN # trip the breaker
Evaluation patterns¶
10. Self-reflection¶
Category: Evaluation
Implementation: agentic-workflows-v2/agentic_v2/agents/capabilities.py -- SelfReflectionMixin; agentic-workflows-v2/agentic_v2/agents/coder.py -- CoderAgent.reflect()
How It Works: SelfReflectionMixin declares the SELF_REFLECTION capability type and defines an abstract async reflect(output, criteria) method. CoderAgent inherits both CodeGenerationMixin and SelfReflectionMixin, and provides a concrete reflect() implementation that sends the generated code back to the LLM with a reflection prompt requesting a JSON critique with needs_revision, issues, and optional revised_output fields. The reflection result is parsed via extract_json() for structured consumption. This enables the agent to critique its own output before returning it.
Code Reference: agentic-workflows-v2/agentic_v2/agents/capabilities.py (SelfReflectionMixin); agentic-workflows-v2/agentic_v2/agents/coder.py (CoderAgent.reflect)
Example:
# CoderAgent.reflect() -- self-critique via LLM
async def reflect(self, output: str, criteria: str = "correctness") -> dict[str, Any]:
reflection_prompt = (
f"Review the following code for {criteria} issues. "
f"Return a JSON object with 'needs_revision' (bool), "
f"'issues' (list of strings), and optionally 'revised_output' (str).\n\n"
f"```\n{output}\n```"
)
self._memory.add_user(reflection_prompt)
response = await self._get_model_response()
content = response.get("content", "")
return extract_json(content)
Safety patterns¶
11. Prompt injection defense¶
Category: Safety
Implementation: agentic-workflows-v2/agentic_v2/rag/context_assembly.py -- frame_content(), TokenBudgetAssembler with frame_results=True
How It Works: Retrieved documents may contain adversarial content designed to hijack LLM behavior (e.g., "Ignore all previous instructions..."). The frame_content() function wraps each chunk in <retrieved_context> / </retrieved_context> delimiter tags, signaling to the LLM that the enclosed content is untrusted user-provided data and should not be interpreted as instructions. The TokenBudgetAssembler applies this framing by default (frame_results=True) when assembling retrieval results, and accounts for the framing overhead in its token budget calculation. The system prompt instructs the model to treat content within these tags as data only.
Code Reference: agentic-workflows-v2/agentic_v2/rag/context_assembly.py (frame_content, TokenBudgetAssembler)
Example:
# Delimiter framing for prompt injection defense
CONTEXT_DELIMITER_START = "<retrieved_context>"
CONTEXT_DELIMITER_END = "</retrieved_context>"
def frame_content(content: str) -> str:
return f"{CONTEXT_DELIMITER_START}\n{content}\n{CONTEXT_DELIMITER_END}"
# Applied automatically during assembly
if self._frame_results:
framed = RetrievalResult(
content=frame_content(result.content),
score=result.score,
...
)
Cross-reference: pattern to file index¶
| Pattern | Primary file(s) | Key symbols |
|---|---|---|
| Tool use | agents/base.py |
_bind_tools, _handle_tool_calls |
| Task decomposition | agents/orchestrator.py |
decompose_task, _parse_output |
| Capability matching | agents/capabilities.py |
score_match, get_agent_capabilities |
| Multi-agent coordination | agents/orchestrator.py, engine/dag_executor.py |
execute_as_dag, _execute_plan; DAGExecutor.execute |
| Bounded iteration | workflows/loader.py, workflows/definitions/iterative_review.yaml |
loop_max parsing; review_rework_loop |
| Chain-of-thought scaffolding | prompts/*.md (all 7) |
## Reasoning Protocol sections |
| Hybrid retrieval | rag/retrieval.py |
BM25Index, reciprocal_rank_fusion, HybridRetriever |
| Conversational memory | agents/memory.py |
ConversationMemory, _summarize_and_trim |
| Circuit breaker | models/model_stats.py, models/smart_router.py |
CircuitState, ModelStats; SmartModelRouter, call_with_fallback |
| Self-reflection | agents/capabilities.py, agents/coder.py |
SelfReflectionMixin; CoderAgent.reflect |
| Prompt injection defense | rag/context_assembly.py |
frame_content, TokenBudgetAssembler |
All file paths above are relative to agentic-workflows-v2/agentic_v2/.
Previously implemented (removed)¶
The following patterns were implemented by files that have since been removed from the repository — principally the deep_research.yaml workflow and the two antagonist persona prompts. They are documented here for historical reference only; there is no current in-repo implementation to cite. Git history contains the removed files if you want to resurrect one.
- Tree-of-thought (removed) —
deep_research.yaml'shypothesis_tree_tot_roundNsteps had atier3_reasoneragent build a search plan as a tree of hypotheses with confirming and disconfirming evidence paths, forcing exploration of multiple reasoning branches per round. - ReAct (removed) —
deep_research.yaml'sretrieval_react_roundNsteps ran a Reason+Act loop: atier2_researcheragent alternated between reasoning about what evidence to gather and invokingweb_search/http_get/context_storetools. - Chain-of-verification (removed) —
deep_research.yaml'scove_verify_roundNsteps had atier3_revieweragent independently re-verify claims from the parallel analyst steps against fresh evidence, producing a verification score and unresolved questions for the next round. - Adversarial review (removed) — two orthogonal antagonist personas (
antagonist_implementation.md, FMEA-style failure-mode enumeration;antagonist_systemic.md, pre-mortem systemic risk analysis) attacked plans from non-overlapping angles. - Confidence gating (removed) —
deep_research.yaml'scoverage_confidence_audit_roundNsteps computed a multi-dimensional confidence index and emitted agate_passedboolean; later rounds ran onlywhen:the gate had not yet passed. The shippediterative_reviewandconsensus_reviewworkflows use related (but simpler) status- and agreement-based gates. - Domain-adaptive recency (removed) —
deep_research.yamlaccepted adomainenum and arecency_window_daysinput that propagated to every retrieval step, so fast-moving domains required more recent sources than stable ones.