Reference
Agents in YAML
On this page
Agents are LLM-backed participants in a channel’s activities. They read the ticket context and conversation history, then respond with a comment.
Where agents come from
An agent belongs to the organization. It is created in the admin console under Apps → Agents, has its own user account, and shows up as a real teammate wherever it posts. An app does not define agents of its own: it names the ones it wants in its triggers, and the same agent can answer on several apps and take part in channels.
Attach one from either end — the app editor’s Agents tab, or an agent’s Deployment tab in the admin console, where a click on an app’s triggers opens the Agents tab’s dialog. Both write the trigger entries described below, so hand-editing the YAML does the same thing.
Skeleton
triggers:
- onCreate:
- agent: triage
- onComment:
- agent: triage
Wiring agents to triggers
In the trigger:
agent: triage # the agent's name, matched case-insensitively
Write the name. Saving the app rewrites it to @Triage:{{U-...}}, which is the agent’s
own user — the same way an app refers to a person. The app then follows the agent through
a rename, and the name you see is refreshed from the agent itself. A plain name keeps
working, which is what app templates and hand-written YAML carry.
An app holds no mode for its agents: the mode is the deployment’s (below). A mode: or
onMention: line an older app still carries is read past.
| Event | Typical use |
|---|---|
onCreate | Triage and respond to the initial activity |
onComment | Reply to follow-up comments; the one event the mode gates |
onEdit | React when attributes change |
onConclude | Write a postmortem summary |
In a channel, rather than on a ticket, an agent is addressed as @name, or by a
platform mention of its linked user where it has one. An agent with no
account cannot be auto-completed, so writing the name is how it is reached.
The mode
An agent has one mode in a channel, and it covers the tickets of the channel’s app as well as the conversation. It is the agent’s own, set on its General tab, unless the channel sets another on the agent’s Deployment tab. Where only the app names the agent, with no deployment to the channel, the agent’s own applies.
| Mode | The agent answers |
|---|---|
| Mention | A message or comment that @mentions it |
| Conversational | Also one that names it |
| Autonomous | Every message and comment, deciding for itself whether to speak |
| Off | Nothing, the app’s events included — it stays deployed and silent |
Only a comment can address an agent, so the mode gates comments: an agent that is not off answers every other event it is on — a new ticket, an edit, a conclusion — unasked.
off exists because an agent linked to a user account follows that account’s
membership, and in a public Teams team every member is in every public channel. There
is no per-channel membership to take the account out of, so the mode is what decides
whether the agent takes part there.
An agent switched off keeps its deployment: the channel still stores its messages and Conclude still receives its events, so the agent reads the conversation it missed when it is switched back on. Off in a channel silences it on the app’s tickets there too.
What the agent sees
For each invocation, the agent receives:
- The app’s
context(if set) anddescriptionas a system-prompt prefix. - The agent’s own instructions, from the admin console.
- Current ticket state — title, body, status, severity, owner, members, deadline, conclusion, and every custom attribute that has a value.
- The complete action history of the activity as alternating user /
assistant /
[system]messages (comments, owner changes, attribute changes, etc.). - Optional lookup data — if the app has a
lookup:endpoint configured for this event, the response body is included as additional context. - The triggering event description (e.g. “new comment from Alice”).
Learning
A channel with learning on keeps what it learns in a knowledge collection it owns (“Learning: #channel”), from three sources:
| Source | Becomes | When |
|---|---|---|
| A closed ticket of the channel’s app | A case file: problem, cause, resolution | On close |
| A thread in the channel | A case file: question, answer, details | Two hours after its last reply; a later reply redoes it |
| The day’s messages in the channel itself | A digest: topics, decisions, open questions | Just after midnight UTC |
Threads and digests need an agent in the channel: that is what makes Conclude store its messages. A channel that learns keeps them for 7 days instead of an hour, and switching learning off purges what it kept, as does removing the app, which switches learning off. A thread with one person in it, or a day of fewer than three messages, is left out, and the model skips what holds nothing worth keeping.
Agents search the collection with search_learning: on the app’s tickets, and in the
channel’s own conversations. Never on a connected ticket or in a connected channel,
which another organization reads. Learning is switched per channel on the admin console’s
Deployment page, which only an organization with beta features on is offered; the app’s YAML
has no setting for it. For now only a channel with an app can be switched on:
one without has no switch, so its conversations are not learned.
Loop protection and rate limits
- Self-loop prevention. An agent never replies to its own comments. Comments authored by an agent are flagged, and the trigger dispatcher skips agent triggers when the source action is from the same agent.
- Cross-agent loop prevention. Any comment posted as a result of a trigger (script or agent) carries a marker that blocks further agent/script dispatch. The chain stops after one hop.
- Per-activity rate limit. Agent comments are throttled to 5 per minute per activity; excess is dropped.
- Per-activity comment cap. The agent’s max comments (or 20 by default) — the agent stops responding once it’s posted this many comments on a single activity, to prevent runaway threads.
Lookup-backed context
If the app has a lookup: endpoint, the agent handler POSTs to it with
{event, activity: {title, email}} and includes the response as extra
context in the prompt. Configure which events trigger lookup via the
on: list:
lookup:
url: https://crm.example.com/conclude-lookup
secret: crm_api_token # references a Secret by name
on: [onCreate, onComment]
timeout: 5s
See Connectors.