Skip to main content

How to Configure Interjections

๐Ÿ“‹ Prompt ยท ๐Ÿ’ฌ Conversation

Interjections are messages the harness adds to the running turn, between the agent's model calls. Each comes from an interjection source that watches the transcript: one speaks up when a tool keeps failing, another has a separate reviewer read the trajectory and steer. The agent reads them as advisory and keeps the user's request as its objective.

A source can speak at two points of every model and tool iteration:

HookWhen it runs
before_model_callBefore every model call of the turn, the first included, with the transcript the model is about to receive
after_model_callOnce the model's response and the results of the tools it called are in the transcript

What a source says is appended to the transcript as a developer message starting with [interjection: <source>], and is saved with the rest of the transcript. After-call messages follow the tool results, so each tool result stays next to its call. Sources speak in configuration order. A source that fails is logged and skipped. With no source active the turn skips both hooks.

Enable Interjectionsโ€‹

{
"agent_configuration": {
"interjections_provider_configuration": { "enabled": true },
"repeated_tool_failure_interjection_source_configuration": { "enabled": true },
"reviewer_interjection_source_configuration": { "enabled": true, "reviewer_agent_identifier": "reviewer" }
}
}
za agents capabilities interjection-sources list
za agents capabilities interjection-sources configure <source-name>
za agents capabilities interjection-sources settings

Provider Settingsโ€‹

interjections_provider_configuration:

SettingTypeDefaultDescription
enabledboolfalseRun the active sources at both hooks. With at least one source active, the system prompt gets a ## Interjections section telling the model what these messages are
include_sources / exclude_sourceslistnullSource filtering, as on every provider

Interjection Sourcesโ€‹

SourceCLI nameHookSpeaks when
Repeated Tool Failurerepeated-tool-failureafterOne tool failed a number of times in a row
ReviewerreviewerbeforeA failure streak, or every N tool steps; a separate agent reviews the transcript
Attempt Plateauattempt-plateauafterThe last attempts at a scored task were all rejected
Attempt Transcript Recorderattempt-transcript-recorderafterNever; it saves the transcript next to each committed attempt
RefinerefineafterEvery N model calls, when a gate agent finds the turn worth a refinement

All sources are disabled by default.


Repeated Tool Failureโ€‹

Tells the agent to change approach when the same tool fails several times in a row. A failure is a tool result that starts with Error in executing tool. A success, or a failure of another tool, ends the streak. The source speaks once each time the streak crosses another multiple of the threshold.

za agents capabilities interjection-sources configure repeated-tool-failure

repeated_tool_failure_interjection_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the source on
max_consecutive_failuresint3Failures in a row of one tool before it speaks
messagestr or nullnullReplace the built-in text. Placeholders: {tool_name}, {count}, {last_error}

Reviewerโ€‹

A separate agent, with a clean context, reads the transcript and replies with a diagnosis and two or three directions to try. Its reply is the interjection. The stuck agent does not review itself.

za agents capabilities interjection-sources configure reviewer

reviewer_interjection_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the source on
on_consecutive_tool_failuresint or null3Review when one tool has failed this many times in a row. null turns this trigger off
every_n_tool_stepsint or nullnullAlso review every N tool steps, failures or not. null turns this trigger off
reviewer_agent_identifierstr or nullnullAgent in agent_setups.json that reviews. null runs a fresh copy of the current agent
max_transcript_charsint24000Characters from the end of the rendered transcript given to the reviewer
instructionsstr or nullnullReplace the built-in reviewer instructions

Point reviewer_agent_identifier at a lean agent with tools and memory off. A copy of the current agent starts every provider it has for each review.


Attempt Plateauโ€‹

Part of the attempts gate. When the last attempts at a scored task were all rejected, tells the agent the line of changes has plateaued and to try a different kind of change. It speaks once per crossed multiple of the threshold, and stays silent on turns that matched no rubric or when no attempt store is enabled.

za agents capabilities interjection-sources configure attempt-plateau

attempt_plateau_interjection_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the source on
max_consecutive_rejectionsint3Rejected attempts in a row before it speaks
messagestr or nullnullReplace the built-in text. Placeholders: {count}, {scores}, {best}

Attempt Transcript Recorderโ€‹

Part of the attempts gate. It never speaks: after a tool batch that committed an attempt, it saves the transcript up to that point next to the attempt (transcript.json in the file store), so you, a reviewer or the agent can later read how the attempt came about.

za agents capabilities interjection-sources configure attempt-transcript-recorder

attempt_transcript_recorder_interjection_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the source on

Refineโ€‹

The harness side of refinement. Every every_n_model_calls model calls, and at most once per cooldown_seconds, a gate agent reads the harness (memories, skills, instructions, sub-agents) and the transcript so far, and answers whether this checkpoint holds something worth keeping. On yes, the refiner runs with the gate's instructions and the interjection lists what was kept. On no, nothing else happens: a rejected checkpoint costs one gate call. The source is off when the refiner has nowhere to write.

za agents capabilities interjection-sources configure refine

refine_interjection_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the source on
every_n_model_callsint8Model calls between two gate reviews within a turn
cooldown_secondsfloat120.0Minimum time between two gate reviews
gate_agent_identifierstr or nullnullAgent in agent_setups.json that decides. null runs a fresh copy of the current agent; prefer a lean agent with tools and memory off
gate_instructionsstr or nullnullReplace the built-in gate instructions. The gate must reply with a JSON object {"should_refine": bool, "rationale": str, "instructions": str}

Custom Interjection Sourcesโ€‹

A source subclasses InterjectionSource and overrides before_model_call, after_model_call or both. Each receives an InnerLoopContext and returns the message text, or None to stay silent:

FieldTypeDescription
transcriptList[ChatCompletion]The turn's transcript at this hook
remaining_tool_stepsintTool steps the turn has left
responseChatCompletion or nullThe model's response; set in after_model_call only
from typing import Optional

from zav.pydantic_compat import BaseModel

from zav.agents_sdk import AgentDependencyFactory, AgentDependencyRegistry
from zav.agents_sdk.adapters.interjections import InterjectionSource
from zav.agents_sdk.adapters.llm_models.chat_turn_request import InnerLoopContext


class StepBudgetInterjectionSourceConfiguration(BaseModel):
enabled: bool = False
warn_at: int = 3


class StepBudgetInterjectionSource(InterjectionSource):
source_name = "step_budget"

def __init__(self, enabled: bool, warn_at: int) -> None:
self.enabled = enabled
self.warn_at = warn_at

async def before_model_call(self, context: InnerLoopContext) -> Optional[str]:
if context.remaining_tool_steps != self.warn_at:
return None
return f"{self.warn_at} tool steps left. Start writing the answer."


class StepBudgetInterjectionSourceFactory(AgentDependencyFactory):
@classmethod
def create(
cls,
step_budget_interjection_source_configuration: (
StepBudgetInterjectionSourceConfiguration
) = StepBudgetInterjectionSourceConfiguration(),
) -> StepBudgetInterjectionSource:
configuration = step_budget_interjection_source_configuration
return StepBudgetInterjectionSource(configuration.enabled, configuration.warn_at)


AgentDependencyRegistry.register(StepBudgetInterjectionSourceFactory)

The source is configured like the built-in ones, as step_budget_interjection_source_configuration. za agents capabilities interjection-sources add <name> scaffolds the file.

Hooks in a Custom Agentโ€‹

The interjections provider is the built-in agent's implementation of InnerLoopHooks (zav.agents_sdk.adapters.llm_models.chat_turn_request). A custom agent passes its own to the chat client: client.complete(..., hooks=my_hooks). Both methods take an InnerLoopContext and return a list of ChatCompletion messages to append, empty for none.