Write a Binding¶
For an application team that wants its agent in its channel: at the end, a Binding in your namespace connects your agent to your channel, through the backend and Slack connection the platform team runs.
Before you start¶
- The platform team runs Agent Kourier, an AgentBackend for your agent's runtime and a
ChatConnection for Slack, and lists your namespace in both resources'
allowedNamespaces. - The Agent Kourier bot is a member of your channel, and you have the channel's ID (it starts with
C; a private channel created before March 2021 may start withG). - If the backend verifies tokens, a Secret in your namespace holds the Binding's token under the key
token. Create the Binding identity explains the account, how to obtain its token, and how to store it. For a backend that trustsX-User-Id, that guide explains how to choose a stable name instead.
Write it¶
apiVersion: agentkourier.dev/v1alpha1
kind: Binding
metadata:
name: payments-assistant
namespace: payments
spec:
agent:
backendRef: # (1)!
namespace: agent-kourier
name: kagent
namespace: payments # (2)!
name: payments-agent
identity:
tokenSecretRef: # (3)!
name: payments-agent-token
key: token
chat:
connectionRef:
namespace: agent-kourier
name: slack
channel: C0123456789
output: live # (4)!
threadReplies: all # (5)!
acknowledgment:
enabled: true # (6)!
- The platform team's AgentBackend. A reference to another namespace works only if that resource lists your
namespace in
allowedNamespaces. - The agent's namespace and name on the backend, not in your cluster.
- The token every turn of this Binding is sent with. Without a front door that verifies it, use
userIdinstead, which is sent asX-User-Id. livestreams the answer as the agent writes it;finalposts it once, when the turn ends.allmakes every person's reply in a thread Agent Kourier owns a turn.mentiontakes only the replies that mention the bot.acknowledgmentadds a temporary reaction to each accepted message while its turn is processing. It defaults to enabled, with the platform's own reaction (Slack:hourglass_flowing_sand); setenabled: falseto omit it, or setemojito another bare Slack reaction name such aseyes.
Load it¶
The broker reads its resources from custom resources, the chart's default, or, if the platform team chose the quick route, from files, usually a ConfigMap
(see Choose a config route). Ask which. With the Helm chart, add the Binding under
config.bindings, keyed by its name, with its namespace:
Then helm upgrade. On the ConfigMap route, by default (config.rollOnChange), the chart rolls the pod when the config changes; with custom resources the chart renders the Binding as an object and the broker reads the change at its next check, every 10 seconds.
If the platform team manages its own ConfigMap (config.existingConfigMap), add the Binding's YAML there as a file
ending in .yaml, and ask them to list your namespace in rbac.secretNamespaces so the broker may read your Secret.
With custom resources, the platform team's chart values may render the Binding as above, or you apply it yourself in your own
namespace, where it is a Binding object:
kubectl apply --dry-run=server -f binding.yaml # the schema and CEL rules of the CRD, with nothing stored
kubectl apply -f binding.yaml
It needs two things. The platform team lists your namespace in the allowedNamespaces of the ChatConnection (and of the AgentBackend, if it is in another
namespace). You make, in your own namespace, a Role that names your token Secret and a RoleBinding to the broker's ServiceAccount
(the how-to has both). Until they exist the status says NamespaceNotAllowed or
CredentialsUnreadable. A connectionRef to a connection of another namespace names that namespace.
Check it¶
Mention the bot in the channel. A thread opens and the agent answers in it. If nothing happens, see Troubleshoot an install.
With custom resources, read the Binding's status first:
kubectl -n payments get akbinding payments-assistant
kubectl -n payments wait --for=condition=Ready akbinding/payments-assistant --timeout=120s
READY is True when the broker accepted the Binding and its checks passed. A reason in REASON says what to fix: see
Binding status and A Binding is not accepted. Never type
kubectl get bindings: that is a core Kubernetes resource, not this one.
The leader also checks the Binding by itself, within about 30 seconds of the reload or when a pass already running ends: agentkourier_binding_ready{binding="<namespace>/<name>"} is 1 when the agent's card can be fetched and the bot is in the channel. If it is 0, see A Binding is not ready.
One channel takes one Binding, on both routes, and what happens to a second one differs: with files the whole config fails to load, and with custom
resources the younger Binding says ChannelConflict and the rest carry on. Who holds a channel
says how it is decided, and what an older Binding that you edit onto a younger one's channel does.
Variations¶
- Let the agent ask questions in the thread: set
interactions.askUser: true. See Let the agent ask questions. - Investigate the alerts a bot posts to the channel: add a
botMessagetrigger. See Investigate Alertmanager alerts for a worked trigger, or Start investigations from any bot's messages for another tool's bot. - Keep threads around for less than 90 days: set
session.threadTTL, for example720h. - Rating buttons on answers: set
feedback.enabled: true. Investigations that a trigger started always carry them. A turn that another bot's mention started counts as an answer. - Choose what a live reply shows while the agent works: set
chat.progresstosteps,steps-and-activity(the default) oractivity. The default adds aWorking…card when 3 seconds pass with nothing new;activityshows one card (a line in an alert thread) that counts the steps and names no tool;stepsis the cards and lines alone. See Output. - Turn off other bots' mentions: any bot or webhook in the channel can start a turn by mentioning Agent Kourier. Set
chat.botMentions: noneto stop that. See A bot's mention. - Let the agent notify people: declare them under
chat.mentions, each with thenamethe agent writes and theuserID it stands for. See Let the agent notify people. - Change the processing reaction: set
chat.acknowledgment.emojito a bare Slack reaction name. The reaction is removed when the turn ends; a question removes it before the thread waits for an answer, and the answer gets its own reaction.
Every field, with its default, is in the Binding reference.
Let the agent notify people¶
When a turn's reply says @payments-owner, Agent Kourier posts one more message in the thread, after the reply, that
mentions that user. Each person named gets a message of their own.
maxMentionsPerTurn must be from 1 to 10, and 0 is refused at load. To notify nobody, leave mentions out.
- The agent is told the names. A session's first prompt ends with a line that lists them. It is not said again: a thread that started before you added a name does not get it, and neither does an agent that opens a new session of its own and answers in it. A first turn that fails once the agent has started on it still counts as the first, and an agent that did not keep the line is not told again. The line goes out again on any turn that finds no context recorded for the session: for example after the agent answers that it no longer has the task, as Agent Kourier then resets the session, and on every turn of an agent that replies with no context ID. Tell the agent in its own instructions if that matters.
- Only the text of a reply counts. A turn that completes or pauses for an answer mentions the people its
final text names. A question the agent asks through a pause, which Agent Kourier posts as a prompt, is not read for names, so an
agent that wants someone to answer it has to name them in its reply text. A name in a code span or a fence does not count, though a name in a table does, even where Slack shows the
table as code. A fence line has only white space before its marks, so a fence opened after a list marker or a
>is not code in what Agent Kourier sends to Slack, or here: a name under it counts, and a closing line with no marker of its own opens a block that hides what follows. A name that runs straight into more letters, as in@samさん, is a different name and does not count, and neither does one that ends where a long reply was cut._@sam_and~~@sam~~count, and@sam/@leenames both people. A/after a name otherwise leaves it a name only before white space or the end of the line:@sam/x,cd @sam/..and(@sam/)read as paths, name nobody and are not counted asundeclared. Nothing else up to the next white space,|or backtick is a name either, so@acme/x,@samnames nobody.@sam@leereads as an address and names nobody, as does a name straight after a word,/,.,=,?,&,#,%,+or\, and neither is counted asundeclared. A name after a:otherwise counts, so thatOwner:@samdoes. A name after a URL with://in it counts only once something ends the URL: white space,|,<,>, a backtick,),]or non-ASCII punctuation, such as—or。. Sohttps://x.test,@samnames nobody and is not counted asundeclared, while a)or]in the URL itself can let a name in it count. In a URL with no://, such asmailto:x,@sam, a name can count after most punctuation. A reply long enough to be cut can also notify someone for a name Slack does not show, as Slack's limit counts the text after escaping. A turn that was cut short, failed, was stopped or was rejected mentions nobody, and neither does a name pastmaxMentionsPerTurn. Nor does a turn whose final text could not be written, even if a stream had already shown the name. - One mention for each person. A person is mentioned once a turn however often the reply names them, and two names for one
user are one person. A task that pauses can run on under the same turn, for example when it is answered outside Slack: its later text
mentions only people the turn has not mentioned yet, and
maxMentionsPerTurnholds across both. A mention Agent Kourier gave up on posting still counts, and is not tried again. Only people still inmentionsare counted, so changing the list in between can take the turn over the cap, and a name given a newusernotifies that user. - Declare lasting owners, not a rota.
useris a fixed ID, so when a person moves on, it goes stale. Slack accepted a mention of an ID that does not exist with no error, so nothing tells you. Agent Kourier is not a paging tool, and user groups cannot be declared. - Keep the person in the channel. Agent Kourier has not been tested with a mention of someone who is not a member of it.
A name the Binding does not declare notifies nobody, and agentkourier_mentions_total counts it as undeclared, which is
how to spot a typo or a name the model made up. Security has what hostile text
can do with this, and what an agent is never allowed to do.
To link to a Slack message, the agent writes the message's URL in its reply. Slack shows it as its own reference to that message, so this needs no setting.