Guided Agents¶
Experimental & deprecated
GuidedAgent and the journeys/guidelines API are experimental and
deprecated — not recommended for new code and may be removed in a future
release. For new agents prefer ReActAgent with
Pipeline Agents for deterministic control flow.
GuidedAgent is a ReActAgent subclass that narrows context per
turn. Instead of handing the LLM one large static system prompt and every
tool, it matches behavioral rules ("guidelines") against the current
conversation, injects only the rules that apply, and exposes only the tools
those rules unlock. The design is inspired by Parlant-style context narrowing.
How it works¶
Each turn, stream() performs four steps before delegating to the parent ReAct
loop:
- Match guidelines — evaluate each guideline's
whencondition against the conversation using the configured strategy (always,batch_llm, orembedding), then resolveexcludesand sort bypriority. - Build a narrowed system prompt — start from the base
system_promptand inject only the matched guidelines, plus glossary terms mentioned in the conversation and the current journey step. - Gate tools — ungated tools are always available; a gated tool (named by
any guideline's or journey step's
tools) is exposed only when an active guideline or the current journey step unlocks it. - Delegate to
ReActAgent.stream()with the narrowed prompt and tool set, then check whether the active journey should advance.
from latent.agents.guided import GuidedAgent, Guideline
agent = GuidedAgent(
name="support",
model="gpt-4o",
system_prompt="You are a helpful support agent.",
guidelines=[
Guideline(
when="the user reports a billing problem",
then="apologize, then look up their account before answering",
tools=("lookup_account",),
priority=10,
),
],
tools=[lookup_account, search_docs],
)
answer = await agent.ask("I was charged twice this month")
Constructor¶
| Parameter | Type | Default | Purpose |
|---|---|---|---|
name |
str |
— | Agent name. |
model |
str |
— | LLM model id. |
system_prompt |
str \| None |
None |
Base prompt; guidelines are injected on top of it. |
guidelines |
list[Guideline] \| None |
None |
Behavioral rules. |
canned_responses |
list[CannedResponse] \| None |
None |
Pre-approved templates (used in strict mode). |
glossary |
Glossary \| None |
None |
Domain terms injected when mentioned. |
journeys |
list[Journey] \| None |
None |
Multi-step SOPs; names must be unique. |
mode |
"fluid" \| "strict" |
"fluid" |
strict enforces canned responses. |
match_strategy |
"always" \| "batch_llm" \| "embedding" \| None |
None |
Auto-selected: always for ≤5 guidelines, else batch_llm. |
match_threshold |
float |
0.75 |
Similarity threshold for the embedding strategy. |
embedding_model |
str \| None |
None |
Defaults to model. |
infer_relationships |
bool |
False |
Advisory background check for conflicting guidelines (needs ≥2). |
tools |
list[Callable] \| None |
None |
Tool functions to gate. |
GuidedAgent.from_config(name, model, ...) loads guidelines, glossary, canned
responses, and journeys from flows/<name>/guidelines.yaml.
Guidelines¶
Guideline¶
A frozen dataclass expressing a when condition → then behavior rule.
| Field | Type | Default | Purpose |
|---|---|---|---|
when |
str |
— | Condition that activates the rule. |
then |
str |
— | Instruction injected when active. |
id |
str |
auto | Stable hash derived from the other fields if omitted. |
priority |
int |
0 |
Higher priority sorts first. |
tools |
tuple[str, ...] |
() |
Tool names unlocked while this rule is active. |
excludes |
tuple[str, ...] |
() |
Guideline ids suppressed when this one matches. |
from latent.agents.guided import Guideline
refund = Guideline(
when="the user asks for a refund",
then="confirm the order id, then issue the refund",
tools=("issue_refund",),
priority=5,
excludes=("upsell_rule",),
)
CannedResponse¶
A frozen pre-approved response template for strict mode.
| Field | Type | Default | Purpose |
|---|---|---|---|
id |
str |
— | Template identifier. |
template |
str |
— | str.format_map template. |
variables |
dict[str, str] |
{} |
Default substitution values. |
from latent.agents.guided import CannedResponse
greeting = CannedResponse(
id="greeting",
template="Hi {name}, how can I help with your {topic}?",
variables={"topic": "account"},
)
greeting.render({"name": "Dana"}) # -> "Hi Dana, how can I help with your account?"
Glossary¶
A frozen mapping of domain terms to definitions. Terms are injected into the prompt only when they appear in the conversation.
| Field | Type | Purpose |
|---|---|---|
terms |
dict[str, str] |
Term → definition. |
from latent.agents.guided import Glossary
glossary = Glossary(terms={"MRR": "monthly recurring revenue"})
glossary.find_mentioned("what is our MRR?") # -> {"MRR": "monthly recurring revenue"}
GuidelineMatch¶
The result of evaluating a guideline against the conversation.
| Field | Type | Default | Purpose |
|---|---|---|---|
guideline |
Guideline |
— | The evaluated guideline. |
matched |
bool |
— | Whether the condition held. |
reason |
str |
"" |
Optional rationale from the matcher. |
Journeys¶
Journeys model multi-step standard operating procedures (SOPs). The agent walks
the user through ordered Steps, advancing when each step's advance_when
condition is met (checked by a quick LLM call after each turn). Journey progress
is tracked per conversation_id — pass
config={"conversation_id": <id>} so state persists across turns.
Step¶
A frozen dataclass for one journey step.
| Field | Type | Default | Purpose |
|---|---|---|---|
name |
str |
— | Step name. |
instructions |
str |
— | Guidance injected while on this step. |
advance_when |
str |
"" |
Condition to move to the next step. |
tools |
tuple[str, ...] |
() |
Tool names unlocked on this step. |
Journey¶
A frozen dataclass: a named, ordered list of steps (at least one required).
| Field | Type | Purpose |
|---|---|---|
name |
str |
Unique journey name. |
steps |
list[Step] |
Ordered steps. |
JourneyState¶
Mutable runtime state for one active journey conversation.
| Field / member | Type | Purpose |
|---|---|---|
journey |
Journey |
The journey being walked. |
current_step_index |
int |
Index of the active step (default 0). |
completed |
bool |
Whether all steps are done (default False). |
current_step |
Step (property) |
The active step. |
advance() |
-> bool |
Advance one step; returns True if now complete. |
from latent.agents.guided import GuidedAgent, Journey, Step
onboarding = Journey(
name="onboarding",
steps=[
Step(
name="collect_email",
instructions="Ask the user for their work email.",
advance_when="the user has provided an email address",
),
Step(
name="verify",
instructions="Send a verification code and confirm it.",
advance_when="the user confirmed the code",
tools=("send_code",),
),
],
)
agent = GuidedAgent(
name="onboarder",
model="gpt-4o",
journeys=[onboarding],
tools=[send_code],
)
async for event in agent.stream(
[Message(role="user", content="Hi, I'd like to sign up")],
config={"conversation_id": "user-42"},
):
...
Events¶
Beyond the standard Agent Protocol events, GuidedAgent emits
three guided-specific events you can import from latent.protocol:
| Event | When | Fields |
|---|---|---|
GuidelineMatched |
A guideline is matched and injected for the turn. | guideline_id, when, then |
JourneyStepAdvanced |
The active journey moves to a new step. | journey_name, step_name, step_index, conversation_id |
JourneyCompleted |
The active journey finishes its last step. | journey_name, conversation_id |
from latent.protocol import GuidelineMatched, JourneyCompleted, JourneyStepAdvanced
async for event in agent.stream(messages, config={"conversation_id": "user-42"}):
match event:
case GuidelineMatched(guideline_id=gid, when=when):
print(f"matched {gid}: {when}")
case JourneyStepAdvanced(step_name=step):
print(f"advanced to {step}")
case JourneyCompleted(journey_name=name):
print(f"{name} complete")