Action Policies¶
Base Action Policy¶
polymathera.colony.agents.patterns.actions.policies.BaseActionPolicy(agent, action_map=None, action_providers=[], io=None)
¶
Bases: ActionPolicy
Base class for action policies with dataflow and nested policy support.
Provides: - Automatic action dispatcher creation - Integration with agent capabilities - Nested policy execution with scope inheritance - Dispatch with automatic Ref resolution
Subclasses implement plan_step to produce the next action or child policy.
The base execute_iteration handles:
- Delegating to active child policies
- Executing actions returned by plan_step
- Setting up child policies returned by plan_step
TODO: For example, we can orchestrate iterative reasoning to follow the pattern
(PLAN → ACT → REFLECT → CRITIQUE → ADAPT) by adding AgentCapabilities that
implement each step as an action executor, and then implementing plan_step
to select the next action based on the current state. This can be enforced by:
- Restricting available actions in the action dispatcher depending on the
last completed step, or
- Using an ActionPolicy subclass that implements the iterative pattern by
overriding execute_iteration to enforce the sequence of steps, and
only calling plan_step to get parameters for each step, or
- Prompting the LLM planner with this workflow.
Example
class MyPolicy(BaseActionPolicy):
io = ActionPolicyIO(
inputs={"query": str},
outputs={"result": dict}
)
async def plan_step(self, state) -> Action | None:
# Return None when policy is complete
if state.custom.get("done"):
return None
# Return an Action to execute
return Action(
action_id="analyze_001",
action_type="analyze",
parameters={"query": state.scope.get("query")}
)
# Or return an ActionPolicy for nested execution
# return ChildPolicy(self.agent)
Source code in src/polymathera/colony/agents/patterns/actions/policies.py
execute_iteration(state)
async
¶
Execute one iteration of this policy.
This method is @hookable so memory capabilities can observe iterations.
Calls plan_step to get next action, then dispatches it.
For hierarchical composition (nested policies), spawn child agents
instead of nesting policies. Use self.agent.spawn_child_agents().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
ActionPolicyExecutionState
|
Execution state for this policy (all mutable state lives here) |
required |
Returns:
| Type | Description |
|---|---|
ActionPolicyIterationResult
|
Iteration result |
Source code in src/polymathera/colony/agents/patterns/actions/policies.py
1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 | |
plan_step(state)
async
¶
Produce the next action to execute.
Override this method to implement policy-specific planning logic.
For hierarchical composition, spawn child agents instead of nesting
policies. Use self.agent.spawn_child_agents() with appropriate
action policies for child agents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
ActionPolicyExecutionState
|
Execution state for this policy |
required |
Returns:
| Type | Description |
|---|---|
Action | None
|
|
Action | None
|
|
Example
async def plan_step(self, state) -> Action | None:
phase = state.custom.get("phase", "act")
if phase == "act":
action = self._get_next_action(state)
if action is None:
state.custom["policy_complete"] = True
return None
state.custom["phase"] = "process"
return action
elif phase == "process":
# Do some processing without dispatching an action
self._process_results(state)
state.custom["phase"] = "act"
return None # Skip iteration, continue policy
Source code in src/polymathera/colony/agents/patterns/actions/policies.py
action_executor decorator¶
polymathera.colony.agents.patterns.actions.policies.action_executor(action_key=None, *, input_schema=None, output_schema=None, reads=None, writes=None, exclude_from_planning=False, planning_summary=None, tags=None)
¶
Decorator to turn any method into an action executor.
Automatically infers input/output schemas from type hints if not provided.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action_key
|
str | ActionType | None
|
Key identifying the action type. If None, uses method name. |
None
|
input_schema
|
type[BaseModel] | None
|
Optional Pydantic model for input validation. If None, inferred from method signature. |
None
|
output_schema
|
type[BaseModel] | None
|
Optional Pydantic model for output validation. If None, inferred from return type hint. |
None
|
reads
|
list[str] | None
|
List of scope variable names this action reads. |
None
|
writes
|
list[str] | None
|
List of scope variable names this action writes. |
None
|
exclude_from_planning
|
bool
|
If True, this action is not exposed to the LLM planner. Use this for actions that are only meant to be invoked programmatically in response to events (e.g., game moves in response to spawned agent events). Default is False. |
False
|
tags
|
frozenset[str] | None
|
Optional domain/modality tags for this action (e.g., frozenset({"memory", "expensive"})). Used for future per-action tag-based filtering and grouping. |
None
|
Example
@action_executor()
async def route_query(
self,
query: str,
max_results: int = 10
) -> list[str]:
'''Route query to find relevant pages.'''
...
@action_executor(writes=["analysis_result"])
async def analyze_pages(
self,
page_ids: list[str],
goal: str
) -> AnalysisResult:
'''Analyze pages for the given goal.'''
...
# Event-driven action not visible to planner
@action_executor(exclude_from_planning=True)
async def submit_move(self, game_id: str, move: dict) -> None:
'''Submit move in response to game event.'''
...
Source code in src/polymathera/colony/agents/patterns/actions/policies.py
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | |