Skip to content

hylo-agents

Hylo intelligence layer: agent composition, reasoners, baseline agents

Repository hylo-harness, packages/hylo-agents. Licence: Apache-2.0. Depends on: hylo-core.

Registered entry points
Group Name Object
agents baseline.nearest_object hylo.agents.baseline:NearestObjectAgent
agents baseline.planar_ik hylo.agents.baseline:PlanarIKAgent
agents baseline.random hylo.agents.baseline:RandomAgent
agents composable.object_reach hylo.agents.composable:make_object_reach_agent
agents memory.steered hylo.agents.memory_steer:MemorySteeredAgent
agents noise.steered hylo.agents.noise_steer:NoiseSteeredAgent
agents planner.subgoal hylo.agents.planner:SubgoalAgent
agents policy hylo.agents.policy_agents:PolicyAgent
agents shadow hylo.agents.shadow:ShadowAgent
agents skills hylo.agents.skills:SkillAgent
agents steered hylo.agents.steering:SteeredAgent
agents temporal_ensemble hylo.agents.policy_agents:TemporalEnsembleAgent
agents testing.landmark hylo.agents.composable:make_landmark_agent
agents testing.p_controller hylo.agents.baseline:PControllerAgent
choosers chooser.rule_color hylo.agents.object_reasoner:RuleColorChooser
envs world_model hylo.agents.world_model:WorldModelEnvironment
instruments judge.trace hylo.agents.judges:TraceSuccessJudge
planners planner.identity hylo.agents.planner:IdentityPlanner
reasoners testing.rule_landmark hylo.agents.reasoner:LandmarkReasoner
Module Summary
hylo.agents Hylo intelligence layer: agents, reasoners, policies
hylo.agents.baseline Baseline agents. They implement the Agent protocol directly (no reasoner, no memory)
hylo.agents.composable ComposableAgent: Reasoner (slow, event-triggered) + goal-conditioned Policy (every step) + Memory
hylo.agents.critic Chunk value critic (RFC-0002 L3): a small learned Q(s, chunk) that re-ranks the N chunks a frozen VLA samples
hylo.agents.judges Success judges trained on our own traces (RFC-0004 A3; the instrument RFC-0003 M5 needs)
hylo.agents.memory_steer Action-memory steering for a frozen flow VLA (RFC-0003 M1; mechanism after Retrieve-then-Steer, arXiv 2605.10094)
hylo.agents.noise_steer Noise-space steering (RFC-0004 W3): an offline, retrieval-based counterpart of DSRL for a frozen flow VLA
hylo.agents.object_reasoner Language-conditioned object selection with spatial memory
hylo.agents.planner Subgoal planning around any Agent (RFC-0002 L2)
hylo.agents.policy_agents Agents that run a policy (core.policy.ChunkPolicy) given by configuration
hylo.agents.reasoner A rule-based reference reasoner (the Reasoner contract is hylo.core.reasoning)
hylo.agents.shadow Shadow mode (RFC-0004 W4; roadmap v0.3): evaluate a candidate on real hardware without letting it act
hylo.agents.skills SkillAgent (RFC-0003 M3): apply verified skills from a SkillLibrary to the agent it wraps, per episode
hylo.agents.steering Steering a frozen stochastic policy (flow-matching / diffusion VLAs) without touching its weights
hylo.agents.world_model World model (RFC-0004 W2): an action-conditioned model of the environment, learned from our own traces

hylo.agents

Hylo intelligence layer: agents, reasoners, policies.

META module-attribute

META = {'contracts': {'envs': 'hylo.env/v0.1', 'agents': 'hylo.agent/v0.1', 'reasoners': 'hylo.reasoner/v0.1', 'choosers': 'hylo.chooser/v0.1', 'planners': 'hylo.planner/v0.1', 'instruments': 'hylo.instrument/v0.1'}, 'maturity': 'implemented', 'plugins': {'agents/planner.subgoal': {'maturity': 'unverified'}, 'agents/shadow': {'maturity': 'unverified'}, 'agents/skills': {'maturity': 'unverified'}, 'agents/memory.steered': {'maturity': 'unverified', 'notes': 'the LeRobot variant (lerobot.memory_steered) has run'}, 'agents/noise.steered': {'maturity': 'unverified', 'notes': 'the LeRobot variant (lerobot.noise_steered) has run'}, 'planners/planner.identity': {'maturity': 'unverified'}, 'envs/world_model': {'maturity': 'unverified', 'notes': 'world-model pre-screening not yet used in a campaign'}}}

hylo.agents.baseline

Baseline agents. They implement the Agent protocol directly (no reasoner, no memory).

PControllerAgent

PControllerAgent(kp: float = 2.0, kd: float = 0.0, horizon: int = 1, execution_steps: int | None = None)

Bases: AgentBase

Test double ("testing.p_controller"): proportional control toward state['goal'] in testing.point_reach.

spec

spec() -> AgentSpec

act

act(observation: Observation) -> ActionChunk

RandomAgent

RandomAgent(dim: int = 2, seed: int = 0)

Bases: AgentBase

spec

spec() -> AgentSpec

act

act(observation: Observation) -> ActionChunk

PlanarIKAgent

PlanarIKAgent(kp: float = 0.5, l1: float = 0.3, l2: float = 0.25, max_step: float = 0.05)

Bases: AgentBase

Analytic 2-link IK + joint-space P control for mujoco.planar_reach.

spec

spec() -> AgentSpec

act

act(observation: Observation) -> ActionChunk

NearestObjectAgent

NearestObjectAgent(kp: float = 0.3)

Bases: AgentBase

No language, no memory: drives the fingertip to the object nearest to it in the current observation (mujoco.table_reach). Once objects are occluded it has nothing to go on.

policy instance-attribute

policy = PlanarIKGoalPolicy(kp=kp)

spec

spec() -> AgentSpec

act

act(observation: Observation) -> ActionChunk

hylo.agents.composable

ComposableAgent: Reasoner (slow, event-triggered) + goal-conditioned Policy (every step) + Memory.

This is the reference composition, not a kernel concept. An end-to-end VLA implements Agent directly; a VLA can also be the policy here, with an LLM/VLM as the reasoner. Components declare which observation keys they read, so the same agent works on any environment.

DEFAULT_MEMORY module-attribute

DEFAULT_MEMORY = {'type': 'sqlite', 'params': {'path': ':memory:'}}

GoalPolicy

Bases: Protocol

name instance-attribute

name: str

semantics instance-attribute

semantics: ActionSemantics

action_dim instance-attribute

action_dim: int

position_key instance-attribute

position_key: str

required_state instance-attribute

required_state: tuple[str, ...]

act

act(obs: Observation, subgoal: ndarray) -> ActionChunk

PGoalPolicy

PGoalPolicy(kp: float = 3.0, kd: float = 0.0)

Planar velocity P-controller toward a task-space subgoal (testing.point_reach).

semantics class-attribute instance-attribute

semantics = ActionSemantics(control_mode='planar_vel', frame='world')

action_dim class-attribute instance-attribute

action_dim = 2

position_key class-attribute instance-attribute

position_key = 'pos'

required_state class-attribute instance-attribute

required_state = ('pos', 'vel')

name instance-attribute

name = f'p_goal(kp={kp},kd={kd})@0.1'

act

act(obs: Observation, subgoal: ndarray) -> ActionChunk

PlanarIKGoalPolicy

PlanarIKGoalPolicy(kp: float = 0.3, l1: float = 0.3, l2: float = 0.25, max_step: float = 0.05)

Analytic 2-link IK toward a task-space subgoal, joint-delta output (mujoco planar arms).

semantics class-attribute instance-attribute

semantics = ActionSemantics(control_mode='joint_delta', frame='base')

action_dim class-attribute instance-attribute

action_dim = 2

position_key class-attribute instance-attribute

position_key = 'tip_pos'

required_state class-attribute instance-attribute

required_state = ('qpos', 'tip_pos')

name instance-attribute

name = f'planar_ik_goal(kp={kp})@0.1'

ik

ik(x: float, y: float) -> np.ndarray

act

act(obs: Observation, subgoal: ndarray) -> ActionChunk

ComposableAgent

ComposableAgent(policy: GoalPolicy, reasoner: Reasoner, memory: MemoryStore | None = None, replan_every: int = 10, reach_tol: float = 0.06)

working instance-attribute

working = WorkingMemory()

spec

spec() -> AgentSpec

reset

reset(ctx: EpisodeContext) -> None

act

act(observation: Observation) -> ActionChunk

on_step

on_step(result: StepResult) -> None

on_episode_end

on_episode_end(outcome: EpisodeOutcome) -> None

make_landmark_agent

make_landmark_agent(kp: float = 3.0, memory: dict | None = DEFAULT_MEMORY, replan_every: int = 10) -> ComposableAgent

Factory for configs. The memory backend is resolved through the plugin registry, so this package never imports a memory implementation (memory=None disables long-term memory).

make_object_reach_agent

make_object_reach_agent(kp: float = 0.3, chooser: dict | None = None, memory: dict | None = DEFAULT_MEMORY, replan_every: int = 10) -> ComposableAgent

Language-conditioned object reaching: an ObjectReasoner picks the instructed object with a pluggable chooser (rule-based or LLM, resolved from the reasoners plugin group), remembers objects in spatial memory while they are visible, and an IK policy drives the arm to it.

hylo.agents.critic

Chunk value critic (RFC-0002 L3): a small learned Q(s, chunk) that re-ranks the N chunks a frozen VLA samples.

V-GPS-style test-time steering, scaled to CPU and to the data we already have: - data: our own traces. Each agent.chunk event plus the observation before it is one (state, chunk) sample; the target is the discounted success-to-go gamma**(steps until success), 0 if the episode failed (Monte Carlo, no bootstrapping: the behaviour policy is the policy being steered). - features: proprio state, episode progress, task one-hot, a summary of the chunk's executed part (net translation / rotation, gripper level and flips), optionally a coarse camera thumbnail. - model: an ensemble of small numpy MLPs; selection maximises mean - beta * std (pessimism against the critic's own extrapolation, the role Cal-QL's conservatism plays in V-GPS). Weights are .npz + JSON metadata (no pickle), so the critic loads in any venv.

agent: {type: remote, params: {ref: hylo.policies.lerobot.critic_agent:make_critic_steered, params: {
    repo_id: ..., steering: {n_samples: 4, execution_steps: 25}, critic: {path: critics/l10-v1, beta: 0.5}}}}

STATE_KEYS module-attribute

STATE_KEYS = ('eef_pos', 'eef_axis_angle', 'gripper_qpos')

FeatureConfig

Bases: Contract

state_keys class-attribute instance-attribute

state_keys: tuple[str, ...] = STATE_KEYS

prefix_steps class-attribute instance-attribute

prefix_steps: int = Field(default=10, ge=1)

horizon class-attribute instance-attribute

horizon: int = Field(default=520, ge=1)

tasks class-attribute instance-attribute

tasks: tuple[str, ...] = ()

image_camera class-attribute instance-attribute

image_camera: str | None = None

image_px class-attribute instance-attribute

image_px: int = Field(default=8, ge=2, le=32)

ChunkCritic

ChunkCritic(features: FeatureConfig, mean: ndarray, std: ndarray, members: list[dict[str, ndarray]], meta: dict[str, Any] | None = None)

meta instance-attribute

meta = dict(meta or {})

predict

predict(x: ndarray) -> tuple[np.ndarray, np.ndarray]

score_chunks

score_chunks(obs: Observation, chunks: ndarray, *, step: int, task_id: str, execution_steps: int, beta: float = 0.0) -> tuple[np.ndarray, np.ndarray, np.ndarray]

save

save(path: str | Path) -> str

load classmethod

load(path: str | Path) -> 'ChunkCritic'

fit classmethod

fit(x: ndarray, y: ndarray, features: FeatureConfig, *, weights: ndarray | None = None, members: int = 5, hidden: int = 64, epochs: int = 60, lr: float = 0.001, l2: float = 0.0001, batch: int = 256, seed: int = 0, meta: dict[str, Any] | None = None) -> 'ChunkCritic'

CriticConfig

Bases: Contract

path instance-attribute

path: str

beta class-attribute instance-attribute

beta: float = Field(default=0.5, ge=0.0, le=5.0)

within_vote class-attribute instance-attribute

within_vote: bool = True

min_samples class-attribute instance-attribute

min_samples: int = Field(default=2, ge=1)

CriticSteeredAgent

CriticSteeredAgent(sampler: Any, steering: Any = None, critic: CriticConfig | dict[str, Any] | None = None)

Bases: SteeredAgent

SteeredAgent whose sample-and-select step picks the chunk the critic scores highest.

critic_cfg instance-attribute

critic_cfg = critic if isinstance(critic, CriticConfig) else CriticConfig.model_validate(critic)

critic instance-attribute

critic = ChunkCritic.load(self.critic_cfg.path)

spec

spec()

reset

reset(ctx: EpisodeContext) -> None

chunk_features

chunk_features(chunk: ndarray, execution_steps: int, prefix_steps: int) -> np.ndarray

Summary of the part of a (T, 7) chunk that will actually run: net 6-D motion over the prefix and the executed part, mean gripper command over each, and the number of gripper sign flips.

state_features

state_features(state: dict[str, Any], step: int, task_id: str, images: dict[str, Any] | None, cfg: FeatureConfig) -> np.ndarray

hylo.agents.judges

Success judges trained on our own traces (RFC-0004 A3; the instrument RFC-0003 M5 needs).

A judge decides "is the task done?" from what a real robot has: camera frames, proprio state and the task. It never sees privileged simulator state, the episode length or the termination reason (in LIBERO an episode ends the moment it succeeds, so length alone would leak the answer).

Model: the critic's numpy MLP ensemble over proprio + task one-hot + grayscale thumbnails of each camera. Trained by the "judge.trace" learner in hylo-learn (hylo.learn.judges).

instrument: {type: judge.trace, params: {path: "artifact:l10-trace-v2@54a1efadf00d34d8"}}

JudgeConfig

Bases: Contract

state_keys class-attribute instance-attribute

state_keys: tuple[str, ...] = STATE_KEYS

cameras class-attribute instance-attribute

cameras: tuple[str, ...] = ('agentview', 'wrist')

image_px class-attribute instance-attribute

image_px: int = Field(default=8, ge=2, le=32)

tasks class-attribute instance-attribute

tasks: tuple[str, ...] = ()

TraceSuccessJudge

TraceSuccessJudge(path: str)

SuccessJudge instrument over a sealed judge.trace artifact.

path instance-attribute

path = str(path)

model instance-attribute

model = ChunkCritic.load(path)

cfg instance-attribute

cfg = JudgeConfig.model_validate(self.model.meta['judge'])

name instance-attribute

name = f'judge.trace[{Path(path).name}]@0.1'

judge

judge(episode: EpisodeTrace) -> Judgement

judge_features

judge_features(state: dict[str, Any], images: dict[str, Any], task_id: str, cfg: JudgeConfig) -> np.ndarray

imaged_frames

imaged_frames(trace: EpisodeTrace, cameras: tuple[str, ...]) -> list[tuple[int, dict[str, Any]]]

(observation index, payload) of every observation that carries all cameras.

hylo.agents.memory_steer

Action-memory steering for a frozen flow VLA (RFC-0003 M1; mechanism after Retrieve-then-Steer, arXiv 2605.10094).

At every decision the sampler computes a retrieval key inside its own forward pass and calls back here: 1. retrieve the top-K verified segments by cosine similarity (same task by default) above a similarity floor; 2. drop candidates whose chunk is inconsistent with the others (median pairwise DTW over the executed prefix); 3. aggregate the rest into an elite prior with softmax-over-similarity weights (weighted mean for motion, weighted vote for the gripper, nearest neighbour when the vote is split); 4. map retrieval confidence (similarity, agreement) to a flow start time t0: confident -> small t0 -> strong guidance; nothing usable -> t0 = 1, i.e. the unchanged policy. The sampler then integrates the policy's own velocity field from x_t0 = (1 - t0) prior + t0 noise, so the current observation still decides the action: memory biases the sample, it never replays it.

MemorySteerConfig

Bases: Contract

k class-attribute instance-attribute

k: int = Field(default=10, ge=1, le=64)

same_task class-attribute instance-attribute

same_task: bool = True

sim_floor class-attribute instance-attribute

sim_floor: float = Field(default=0.9, ge=-1.0, le=1.0)

temperature class-attribute instance-attribute

temperature: float = Field(default=0.05, gt=0)

dtw_mad class-attribute instance-attribute

dtw_mad: float = Field(default=2.0, gt=0)

compare_steps class-attribute instance-attribute

compare_steps: int | None = Field(default=None, ge=1)

t0_min class-attribute instance-attribute

t0_min: float = Field(default=0.1, ge=0.0, le=1.0)

t0_max class-attribute instance-attribute

t0_max: float = Field(default=0.7, ge=0.0, le=1.0)

fixed_t0 class-attribute instance-attribute

fixed_t0: float | None = Field(default=None, ge=0.0, le=1.0)

dispersion_scale class-attribute instance-attribute

dispersion_scale: float = Field(default=0.5, gt=0)

gripper_dim class-attribute instance-attribute

gripper_dim: int = -1

MemorySteeredAgent

MemorySteeredAgent(sampler: Any, steering: Any = None, memory: ActionMemory | dict[str, Any] | None = None, memory_steer: MemorySteerConfig | dict[str, Any] | None = None)

Bases: SteeredAgent

SteeredAgent whose samples start from an action-memory prior (needs a PriorSampler).

memory instance-attribute

memory = registry.build('memory', memory) if isinstance(memory, dict) else memory

mcfg instance-attribute

mcfg = memory_steer if isinstance(memory_steer, MemorySteerConfig) else MemorySteerConfig.model_validate(memory_steer or {})

spec

spec()

reset

reset(ctx: EpisodeContext) -> None

dtw

dtw(a: ndarray, b: ndarray) -> float

Multivariate dynamic time warping with Euclidean step cost, normalised by path length bound (len(a)+len(b)).

elite_prior

elite_prior(cands: list[tuple[ActionSegment, float]], cfg: MemorySteerConfig, horizon: int, compare: int) -> PriorDecision

Filter, aggregate and score retrieved candidates (already above the similarity floor).

verified_decision_points

verified_decision_points(trace: Any, *, splits: tuple[str, ...] = ('search', 'search_val')) -> Iterator[dict[str, Any]]

Policy chunks of a successful development-split episode whose observation has camera images.

Yields {"observation": payload, "actions": (T, A), "step", "progress", "task_id", "episode_id", "split"}. Only simulator-verified successes qualify (unverified memory is worse than none).

hylo.agents.noise_steer

Noise-space steering (RFC-0004 W3): an offline, retrieval-based counterpart of DSRL for a frozen flow VLA.

DSRL learns a policy over the flow policy's initial noise and leaves the VLA untouched. Offline, from our own successes: every successful chunk was produced from a known seed, so its exact initial noise is recoverable. The bank (memory_build --store noise) keys those noises by the policy's own visual tokens. At decision time the agent retrieves the nearest same-task noises, averages them (similarity-weighted), rescales the mean to unit variance and hands it to the sampler, which starts the full flow from sqrt(w) * prior noise + sqrt(1 - w) * fresh noise. With w = 0 this is exactly the policy; nothing is retrieved below sim_floor.

Unlike an action prior, the chunk is still produced by the policy's velocity field for the current observation: the noise only biases which mode the flow lands in.

agent: {type: remote, params: {ref: hylo.policies.lerobot.experience_agent:make_noise_agent, params: {
    repo_id: lerobot/smolvla_libero, memory: {type: action_bank, params: {path: "artifact:l10-noise-search-v1@2687768f", frozen: true}},
    noise_steer: {weight: 0.5, k: 5, sim_floor: 0.95}, steering: {n_samples: 1, seed_noise: true}}}}

NoiseSteerConfig

Bases: Contract

weight class-attribute instance-attribute

weight: float = Field(default=0.5, ge=0.0, le=1.0)

k class-attribute instance-attribute

k: int = Field(default=5, ge=1)

same_task class-attribute instance-attribute

same_task: bool = True

sim_floor class-attribute instance-attribute

sim_floor: float = Field(default=0.95, ge=-1.0, le=1.0)

temperature class-attribute instance-attribute

temperature: float = Field(default=0.02, gt=0.0)

NoiseSteeredAgent

NoiseSteeredAgent(sampler: Any, steering: Any = None, memory: ActionMemory | dict[str, Any] | None = None, noise_steer: NoiseSteerConfig | dict[str, Any] | None = None)

Bases: SteeredAgent

memory instance-attribute

memory = registry.build('memory', memory) if isinstance(memory, dict) else memory

ncfg instance-attribute

ncfg = noise_steer if isinstance(noise_steer, NoiseSteerConfig) else NoiseSteerConfig.model_validate(noise_steer or {})

spec

spec()

reset

reset(ctx: EpisodeContext) -> None

noise_prior

noise_prior(hits: list[tuple[Any, float]], cfg: NoiseSteerConfig) -> PriorDecision

hylo.agents.object_reasoner

Language-conditioned object selection with spatial memory.

ObjectReasoner turns an instruction + perceived (or remembered) objects into a subgoal. Which object the instruction refers to is decided by a pluggable Chooser: a rule-based colour matcher here, an LLM/VLM in the hylo-reasoner-llm adapter. Objects are written to spatial memory while visible and recalled from it once they are occluded, so the same code path serves both cases.

FRAME module-attribute

FRAME = 'table'

COLOR_NAMES module-attribute

COLOR_NAMES: dict[str, tuple[float, float, float]] = {'red': (0.85, 0.1, 0.1), 'green': (0.1, 0.7, 0.2), 'blue': (0.15, 0.35, 0.9), 'yellow': (0.95, 0.85, 0.1), 'purple': (0.55, 0.2, 0.75), 'orange': (0.95, 0.5, 0.1)}

RuleColorChooser

Matches an explicit colour word in the instruction to the nearest object colour. Falls back to the nearest object when no colour word is present.

name class-attribute instance-attribute

name = 'chooser.rule_color@0.1'

choose

choose(instruction: str, objects: list[SceneObject], robot_xy: tuple[float, float], obs: Observation | None = None) -> Choice

ObjectReasoner

ObjectReasoner(chooser: Chooser)

required_state class-attribute instance-attribute

required_state = ('tip_pos', 'obj_pos', 'obj_rgb', 'objects_visible')

visibility_key class-attribute instance-attribute

visibility_key = 'objects_visible'

chooser instance-attribute

chooser = chooser

name instance-attribute

name = f'object_reasoner[{chooser.name}]@0.1'

begin

begin(rctx: ReasonerContext) -> None

deliberate

deliberate(obs: Observation, trigger: str, rctx: ReasonerContext) -> Decision

end

end(success: bool | None, rctx: ReasonerContext) -> None

hylo.agents.planner

Subgoal planning around any Agent (RFC-0002 L2).

SubgoalAgent asks a Planner to decompose the task instruction into short sequential sub-instructions, feeds the current one to the wrapped agent (typically a VLA) in place of the original instruction, and advances when the planner judges it done or its step budget runs out. Plans and switches are trace events, so failures can be attributed to planning vs execution. The planner's prompt and check cadence are config, i.e. evolvable.

IdentityPlanner

Baseline: one subgoal, the original instruction (SubgoalAgent then behaves like the wrapped agent).

name class-attribute instance-attribute

name = 'planner.identity@0.1'

plan

plan(instruction: str, observation: Observation) -> Plan

done

done(subgoal: Subgoal, observation: Observation) -> tuple[bool, str, dict[str, Any]]

SubgoalAgent

SubgoalAgent(inner: Agent | dict[str, Any], planner: Planner | dict[str, Any] | None = None, check_every: int = 1, final_instruction: str = 'original')

inner instance-attribute

inner = registry.build('agents', inner) if isinstance(inner, dict) else inner

planner instance-attribute

planner = registry.build('planners', planner) if isinstance(planner, dict) else planner or IdentityPlanner()

check_every instance-attribute

check_every = max(1, int(check_every))

final_instruction instance-attribute

final_instruction = final_instruction

spec

spec() -> AgentSpec

reset

reset(ctx: EpisodeContext) -> None

act

act(observation: Observation) -> ActionChunk

current_instruction

current_instruction(observation: Observation) -> str

on_step

on_step(result: StepResult) -> None

on_episode_end

on_episode_end(outcome: EpisodeOutcome) -> None

close

close() -> None

hylo.agents.policy_agents

Agents that run a policy (core.policy.ChunkPolicy) given by configuration.

  • "policy": draw one chunk per decision (seeded noise, so episodes replay and paired comparisons share noise) and execute its first execution_steps actions open loop. The plain way to run any policy plugin.
  • "temporal_ensemble": ACT's inference (Zhao et al. 2023): query the policy every step and execute, for the current step, the exponentially weighted average of every chunk predicted so far that covers it, w_i = exp(-m * i) with i = 0 the oldest prediction. Smoother motion at the price of one query per step.

    agent: {type: policy, params: {policy: {type: policy.bc, params: {path: artifacts/bc}}, execution_steps: 8}} agent: {type: temporal_ensemble, params: {policy: {type: policy.bc, params: {path: artifacts/bc}}, m: 0.1}}

PolicyAgent

PolicyAgent(policy: ChunkPolicy | dict[str, Any], execution_steps: int | None = None, seed_noise: bool = True, noise_scale: float = 1.0)

Bases: SteeredAgent

A policy executed chunk by chunk: SteeredAgent with one sample and no rules.

spec

spec() -> AgentSpec

TemporalEnsembleAgent

TemporalEnsembleAgent(policy: ChunkPolicy | dict[str, Any], m: float = 0.01, seed_noise: bool = True, noise_scale: float = 1.0)

Bases: AgentBase

spec

spec() -> AgentSpec

reset

reset(ctx: EpisodeContext) -> None

on_step

on_step(result: StepResult) -> None

act

act(observation: Observation) -> ActionChunk

hylo.agents.reasoner

A rule-based reference reasoner (the Reasoner contract is hylo.core.reasoning).

A Reasoner is the slow, event-triggered part of an Agent. It turns observations + memory into Decisions (here: a subgoal position). An LLM/VLM reasoner, a LangGraph graph or a DSPy program implements the same protocol; the kernel never sees the difference.

SWEEP module-attribute

SWEEP = [(-0.8, 0.8), (0.8, 0.8), (0.8, 0.4), (-0.8, 0.4), (-0.8, 0.0), (0.8, 0.0), (0.8, -0.4), (-0.8, -0.4), (-0.8, -0.8), (0.8, -0.8)]

FRAME module-attribute

FRAME = 'testing:world'

LandmarkReasoner

LandmarkReasoner(reach_tol: float = 0.06)

Go to the goal if visible; else to a remembered goal; else explore. Remembers what it saw.

Uses working memory within an episode and spatial memory across episodes. A remembered location that turns out to be empty is invalidated (forgotten), not trusted forever.

name class-attribute instance-attribute

name = 'testing.rule_landmark@0.1'

required_state class-attribute instance-attribute

required_state = ('goal', 'goal_visible')

visibility_key class-attribute instance-attribute

visibility_key = 'goal_visible'

reach_tol instance-attribute

reach_tol = reach_tol

begin

begin(rctx: ReasonerContext) -> None

deliberate

deliberate(obs: Observation, trigger: str, rctx: ReasonerContext) -> Decision

end

end(success: bool | None, rctx: ReasonerContext) -> None

hylo.agents.shadow

Shadow mode (RFC-0004 W4; roadmap v0.3): evaluate a candidate on real hardware without letting it act.

ShadowAgent executes the active agent and hands the same observation to a shadow (the candidate), whose chunk is never executed. Each decision emits an x.shadow event: how far the shadow's executed prefix would have been from what the robot actually did (mean / max absolute difference over arm dims, gripper agreement), and the shadow's latency. The shadow also receives the real step results, so it follows the episode the active agent produces.

With background=True the shadow runs in a worker thread and its result is emitted at the next decision, so the control loop never waits for it (on a robot the shadow must not add latency). The shadow's own events are prefixed x.shadow. so nothing it emits is mistaken for the active agent's.

agent: {type: shadow, params: {active: {type: ...}, shadow: {type: ...}, background: true}}

ShadowAgent

ShadowAgent(active: dict[str, Any] | Any, shadow: dict[str, Any] | Any, background: bool = False, gripper_dim: int | None = -1)

chunk_index instance-attribute

chunk_index = 0

spec

spec()

reset

reset(ctx: EpisodeContext) -> None

act

act(observation: Observation) -> ActionChunk

on_step

on_step(result) -> None

on_episode_end

on_episode_end(outcome) -> None

close

close() -> None

divergence

divergence(active: ndarray, shadow: ndarray, steps: int, gripper_dim: int | None = -1) -> dict[str, float]

shadow_report

shadow_report(run_dir: str | Path) -> dict[str, Any]

Aggregate a shadow run: how often and how far the candidate would have acted differently, per task.

hylo.agents.skills

SkillAgent (RFC-0003 M3): apply verified skills from a SkillLibrary to the agent it wraps, per episode.

At reset the library is matched against the task (instruction pattern, task tags, env). The best verified skill of each kind is applied for this episode only and undone at its end: - steering: a partial SteeringConfig merged into the nearest SteeredAgent in the wrapped chain; - recovery: trigger rules appended to that SteeredAgent's rules (the steering cap of 4 rules still holds); - plan: subgoal templates, filled with the trigger's slot bindings, given to the nearest SubgoalAgent. Weights skills are not swapped per episode (a policy reload); they are applied as config by the improvement loop. Each application is a skill.applied trace event, so outcomes can be attributed to the skill.

agent: {type: skills, params: {skills: {type: skill_library, params: {path: ..., frozen: true}},
        inner: {type: planner.subgoal, params: {inner: {type: lerobot.steered, params: {...}}}}}}

TemplatePlanner

TemplatePlanner(templates: list[str], binds: dict[str, str], fallback: Any, skill_id: str, max_steps: int = 260)

One-episode planner: subgoals from a plan skill; completion checks delegate to the original planner.

name instance-attribute

name = f'skill.plan[{skill_id}]'

plan

plan(instruction: str, observation: Observation) -> Plan

done

done(subgoal: Subgoal, observation: Observation) -> tuple[bool, str, dict[str, Any]]

SkillAgent

SkillAgent(inner: Agent | dict[str, Any], skills: Any, kinds: tuple[str, ...] = ('steering', 'recovery', 'plan'))

inner instance-attribute

inner = registry.build('agents', inner) if isinstance(inner, dict) else inner

library instance-attribute

library = registry.build('memory', skills) if isinstance(skills, dict) else skills

kinds instance-attribute

kinds = tuple(kinds)

spec

spec() -> AgentSpec

reset

reset(ctx: EpisodeContext) -> None

act

act(observation: Observation) -> ActionChunk

on_step

on_step(result: StepResult) -> None

on_episode_end

on_episode_end(outcome: EpisodeOutcome) -> None

close

close() -> None

hylo.agents.steering

Steering a frozen stochastic policy (flow-matching / diffusion VLAs) without touching its weights.

SteeredAgent wraps any ChunkSampler (core.policy.ChunkPolicy: a "policies" plugin, given as a spec or an object) that can draw action chunks for given noise seeds: - seeded noise: chunk noise is derived from (task key, chunk index, sample index, resample round), so a candidate and its parent face the same randomness on the same init state (common random numbers for paired tests) and every episode is replayable; - sample-and-select: draw N chunks and execute their medoid ("medoid"), or keep the gripper-majority ones first ("gripper_vote_medoid", RoboMonkey-style vote); no extra work when N = 1; - runtime triggers: declarative rules over cheap features (still steps, grasp attempts, sample disagreement) that fire recovery actions (resample with more noise, or back off and reopen the gripper). Everything tunable is data (SteeringConfig), so the improvement loop can propose and gate it as config patches.

Feature module-attribute

Feature = Literal['still_steps', 'grasp_attempts', 'disagreement', 'steps']

ChunkSampler module-attribute

ChunkSampler = ChunkPolicy

TriggerRule

Bases: Contract

name instance-attribute

name: str

feature instance-attribute

feature: Feature

op class-attribute instance-attribute

op: Literal['>=', '>', '<=', '<'] = '>='

value instance-attribute

value: float

action instance-attribute

action: Literal['resample', 'backoff']

params class-attribute instance-attribute

params: dict[str, float] = Field(default_factory=dict)

cooldown class-attribute instance-attribute

cooldown: int = Field(default=30, ge=0)

max_fires class-attribute instance-attribute

max_fires: int = Field(default=3, ge=1)

SteeringConfig

Bases: Contract

n_samples class-attribute instance-attribute

n_samples: int = Field(default=1, ge=1, le=8)

selector class-attribute instance-attribute

selector: Literal['first', 'medoid', 'gripper_vote_medoid'] = 'first'

noise_scale class-attribute instance-attribute

noise_scale: float = Field(default=1.0, gt=0, le=3.0)

execution_steps class-attribute instance-attribute

execution_steps: int | None = Field(default=None, ge=1, le=200)

seed_noise class-attribute instance-attribute

seed_noise: bool = True

rules class-attribute instance-attribute

rules: list[TriggerRule] = Field(default_factory=list, max_length=4)

compare_steps class-attribute instance-attribute

compare_steps: int = Field(default=10, ge=1)

still_eps class-attribute instance-attribute

still_eps: float = 0.0015

gripper_closed_below class-attribute instance-attribute

gripper_closed_below: float = 0.06

eef_key class-attribute instance-attribute

eef_key: str = 'eef_pos'

gripper_key class-attribute instance-attribute

gripper_key: str = 'gripper_qpos'

SteeredAgent

SteeredAgent(sampler: ChunkSampler | dict[str, Any], steering: SteeringConfig | dict[str, Any] | None = None)

Bases: AgentBase

sampler instance-attribute

sampler = build_policy(sampler)

cfg instance-attribute

cfg = steering if isinstance(steering, SteeringConfig) else SteeringConfig.model_validate(steering or {})

spec

spec() -> AgentSpec

reset

reset(ctx: EpisodeContext) -> None

on_step

on_step(result: StepResult) -> None

features

features() -> dict[str, float]

act

act(observation: Observation) -> ActionChunk

on_episode_end

on_episode_end(outcome: EpisodeOutcome) -> None

select_chunk

select_chunk(chunks: ndarray, selector: str, k: int) -> tuple[int, float]

Index of the chunk to execute and the sample disagreement (mean distance to the mean prefix).

hylo.agents.world_model

World model (RFC-0004 W2): an action-conditioned model of the environment, learned from our own traces.

StateWorldModel predicts the next proprioceptive / object state from (state, action) with an ensemble of small MLPs (their disagreement is the model's uncertainty), and the probability that the task is complete in a state. WorldModelEnvironment wraps it behind the Environment protocol, so the unchanged kernel can run an agent inside the model: episodes start from initial states recorded in development runs and end when the model says the task is complete or the step budget runs out.

It is an instrument. Its use in the improvement loop is pre-screening (protocol.prescreen): candidates the model predicts to be clearly worse than the parent are dropped before any real rollout; the gate never sees model results. This version models low-dimensional state, so it can host agents that act on state (MuJoCo / test baselines, planners); a VLA acting on camera images needs a video world model (GPU), which plugs into the same protocol later.

env: {type: world_model, params: {path: "artifact:world_model@<digest>"}}     # trained by the "world_model.state"
                                                                    # learner (hylo.learn.world_model)

StateWorldModel

StateWorldModel(keys: list[str], dims: list[int], mean: dict[str, ndarray], std: dict[str, ndarray], dyn: list[dict[str, ndarray]], done: list[dict[str, ndarray]], meta: dict[str, Any])

split

split(v: ndarray) -> dict[str, np.ndarray]

predict

predict(s: ndarray, a: ndarray) -> tuple[np.ndarray, float]

predict_batch

predict_batch(s: ndarray, a: ndarray) -> tuple[np.ndarray, np.ndarray]

predict() for (B, S) states and (B, A) actions: next states (B, S) and ensemble disagreement (B,).

p_done_batch

p_done_batch(s: ndarray) -> np.ndarray

p_done

p_done(s: ndarray) -> float

save

save(path: str | Path) -> None

load classmethod

load(path: str | Path) -> 'StateWorldModel'

WorldModelEnvironment

WorldModelEnvironment(path: str, done_threshold: float = 0.5, init: str = 'recorded')

Environment plugin "world_model": the kernel runs an agent inside a learned StateWorldModel.

path instance-attribute

path = path

model instance-attribute

model = StateWorldModel.load(path)

uncertainty instance-attribute

uncertainty: list[float] = []

spec

spec() -> EnvSpec

reset

reset(task) -> Observation

step

step(action: Action) -> StepResult

close

close() -> None