| title | Instruction |
|---|---|
| description | One rule the model follows while it speaks, gated by an AI-judged `when` or a code-judged `if`, at agent, flow, step or idle scope. |
| type | reference |
| order | 8 |
An instruction is one sentence the model obeys while it speaks: "never invent prices", "answer in three sentences". It has a kind (must, never, should), a prompt, and two optional tests: when, a string the model judges, and if, a predicate your code judges, which costs no model call. The same shape goes on the agent, a flow, a talk step or the idle speaker; only its place in the configuration changes.
Source: src/types/flow.ts, src/core/Runner.ts (speakRequest), src/core/Prompt.ts (instructionsSection).
interface Instruction<C = unknown, D = unknown> {
id?: string;
kind?: "must" | "never" | "should";
when?: string | string[];
if?: Pred<C, D>;
prompt: Template;
}| Field | Type | Default | Meaning |
|---|---|---|---|
id |
string |
none | Yours. The framework carries it and never reads it. |
kind |
"must" | "never" | "should" |
"should" |
How hard the rule is. must is a hard rule, never a hard ban, should a preference. The word is written into the prompt as is. |
when |
string | string[] |
none | When the rule applies, judged by the model from the conversation. Several strings are alternatives (OR). Rendered into the prompt as text; it never costs a call of its own. |
if |
Pred<C, D> |
none | When the rule applies, judged by code. A function or a JSON ConditionSpec. False: the instruction is left out of this call. |
prompt |
Template |
required | The rule. {{data.x}}, {{context.x}} and {{input.x}} are filled in first. |
| Scope | Where | Applies |
|---|---|---|
| Agent | AgentOptions.instructions |
On every speak call, talk steps and the idle speaker alike. |
| Flow | Flow.instructions |
While a talk step of that flow speaks. |
| Step | TalkStep.instructions |
While that step speaks. |
| Idle | Idle.instructions (idle: { prompt, instructions }) |
While the idle speaker answers. |
The list the model sees is built in this order: agent, then flow, then step. For the idle speaker: agent, then idle. Duplicates are kept: the same sentence in two scopes appears twice.
-
Instructions reach the speak call only. The understand call (routing, mentions, branches, extraction) never sees them.
-
ifis judged by code when the speak request is built, with aPredCtxof{ context, data, input, run, silenced, now }.runis the speaking run; while the idle speaker answers,runis absent andinputisundefinedfor every instruction it judges, both agent-level andidle-level. Writeifpredicates on the agent and onidleso they work without a run. -
whenis not judged by code. It is appended to the line as(apply only when: a OR b)and the model decides. -
Each surviving instruction becomes one line under a
## Instructionsheading, in this exact form:- [should] [Always] Responda em até três frases. - [must] [Always] Reconheça o problema antes de explicar qualquer coisa. (apply only when: a pessoa está irritada)The first bracket is
kind(orshouldwhen absent). The second is the group caption. Every scope goes into one group captioned[Always], so the caption does not say which scope a line came from. -
promptis rendered with the turn'sdata,contextand the run'sinput, then trimmed. A line that renders to nothing is dropped. When no line survives, the whole section is left out. -
Instructions have no effect on movement, extraction or which flow starts. They shape wording only.
-
In a stored flow (
FlowSpec) an instruction is anInstructionSpec: the same fields withifin JSON form.flowSpecSchemalets a model write flow-level instructions and leaves step-level ones out.
import { falai, GeminiProvider } from "@falai/agent";
interface Ctx {
plano: "gratis" | "pro";
horaLocal: number;
}
const f = falai<Ctx>().fields({
duvida: { type: "string", ask: "Pergunte qual é a dúvida, em uma frase." },
});
const agent = f.agent({
name: "Bia",
provider: new GeminiProvider({ apiKey: process.env.GEMINI_API_KEY ?? "", model: "gemini-2.5-flash" }),
instructions: [
{ kind: "never", prompt: "Nunca invente preços ou prazos." },
// The model judges `when` from the conversation.
{ kind: "must", when: "a pessoa está irritada", prompt: "Reconheça o problema antes de explicar qualquer coisa." },
// Code judges `if`; it costs nothing. No `run` here, so read only `context` and `data`.
{
kind: "should",
if: ({ context }) => context.horaLocal >= 18 || context.horaLocal < 9,
prompt: "Avise que o suporte humano volta às 9h.",
},
],
flows: [
f.flow({
id: "duvidas",
name: "Dúvidas",
on: [{ message: [] }], // no examples: the catch-all, starts when no other message flow wins
// Applies to every talk step of this flow.
instructions: [{ kind: "should", prompt: "Responda em até três frases." }],
steps: [
{ id: "qual", collect: ["duvida"] },
{
id: "resposta",
prompt: "Responda a dúvida.",
// Applies to this step only.
instructions: [{ kind: "must", prompt: "Termine perguntando se ficou claro." }],
},
],
}),
],
});
const r = await agent.turn({
sessionId: "demo",
context: { plano: "gratis", horaLocal: 21 },
message: "quanto custa o plano pro?",
});
console.log(r.messages[0]?.text);- Instructions: choosing a scope and a kind.
- Conditions:
whenversusif. - Actions, events, conditions:
Pred,PredCtxandConditionSpec. - Agent:
AgentOptions.instructions,idle,personaandgoal.