Skip to content

Dynamic Topology

Modify the graph structure during execution.

Overview

Dynamic topology changes the graph or remaining plan after an agent step. A policy can add or remove edges, skip work, reroute execution, or stop the run.

TopologyAction

A TopologyAction describes modifications to apply after an agent executes:

Field Type Description
early_stop bool Stop execution immediately
early_stop_reason str \| None Reason for early stop
add_edges list[tuple[str, str, float]] Edges to add (source, target, weight)
remove_edges list[tuple[str, str]] Edges to remove
skip_agents list[str] Agents to skip entirely
force_agents list[str] Force-execute previously skipped agents
condition_skip_agents list[str] Skip agents via conditional edge logic
condition_unskip_agents list[str] Unskip conditionally skipped agents
insert_chains list[tuple[str, str]] Insert agent chains after a node
new_end_agent str \| None Change the final agent
trigger_rebuild bool Force plan rebuild

Topology Hooks

Register functions that return TopologyAction after each agent step:

from gmas.execution import MACPRunner, RunnerConfig, TopologyAction, StepContext

def my_hook(ctx: StepContext, graph) -> TopologyAction | None:
    """Skip the reviewer if the writer is confident."""
    if ctx.agent_id == "writer" and "CONFIDENT" in (ctx.response or ""):
        return TopologyAction(skip_agents=["reviewer"])
    return None

config = RunnerConfig(
    adaptive=True,
    enable_dynamic_topology=True,
    topology_hooks=[my_hook],
)

runner = MACPRunner(llm_caller=llm_caller, config=config)
result = runner.run_round(graph)

Async Topology Hooks

For hooks that need async operations:

async def async_hook(ctx: StepContext, graph) -> TopologyAction | None:
    # Async logic here
    return TopologyAction(skip_agents=["some_agent"])

config = RunnerConfig(
    adaptive=True,
    enable_dynamic_topology=True,
    async_topology_hooks=[async_hook],
)

Early Stopping

Stop execution based on conditions:

from gmas.execution import EarlyStopCondition

# Stop when an agent outputs a specific keyword
config = RunnerConfig(
    adaptive=True,
    early_stop_conditions=[
        EarlyStopCondition.on_keyword("FINAL_ANSWER"),
        EarlyStopCondition.on_token_limit(max_tokens=5000),
        EarlyStopCondition.on_agent_count(max_agents=5),
    ],
)

Combining Conditions

# Stop when ANY condition is met
condition = EarlyStopCondition.combine_any([
    EarlyStopCondition.on_keyword("DONE"),
    EarlyStopCondition.on_token_limit(10000),
])

# Stop when ALL conditions are met
condition = EarlyStopCondition.combine_all([
    EarlyStopCondition.on_keyword("REVIEWED"),
    EarlyStopCondition.on_agent_count(min_agents_executed=3),
])

Custom Conditions

def check_quality(ctx: StepContext) -> bool:
    return ctx.response is not None and len(ctx.response) > 100

condition = EarlyStopCondition.on_custom(
    condition=check_quality,
    reason="Sufficient output quality",
    after_agents=["researcher"],  # only check after these agents
)

Conditional Edges

Define edges that are only traversed when a condition is met:

from gmas.core import AgentProfile
from gmas.builder import build_property_graph

graph = build_property_graph(agents, workflow_edges=[
    ("router", "analyst"),
    ("router", "creative"),
], query="...")

# Only traverse to creative if the router decides
graph.set_edge_condition("router", "creative", condition="has_keyword:creative")
graph.set_edge_condition("router", "analyst", condition="has_keyword:analytical")

Graph Modification Methods

Direct graph modifications at runtime:

# Add a new agent mid-execution
new_agent = AgentProfile(agent_id="fact_checker", display_name="Fact Checker")
graph.add_node(new_agent, connections_to=["writer"])

# Add/remove edges
graph.add_edge("fact_checker", "writer", weight=0.9)
graph.remove_edge("researcher", "writer")

# Disable nodes (present but not executed)
graph.disable("draft_agent")

# Re-enable
graph.enable("draft_agent")

# Set execution boundaries
graph.set_start_node("researcher")
graph.set_end_node("writer")