Manage intents and examples

Create, update, replace, list, and delete intents and their example phrasings.

An intent is a named group of example phrases that represent one thing a user might ask for, such as tracking an order or requesting a refund. The intents you write and their examples decide how well a Router classifies messages. This page shows how to create, read, update, and delete intents and their examples, how to avoid overwriting someone else's changes, and how to write examples that match well.

The examples use the connection values from the quickstart and an agent key with write access. Follow one tab throughout the page.

Create or replace an intent

Writing an intent with PUT creates it, or replaces the one with that name. It is idempotent: sending the same request twice produces the same intent, although each write returns a new revision. It returns 201 for a new intent and 200 for a replacement, and a replaced intent keeps its place in the list. In Python this is define, and in the CLI it is intents add NAME. Fields you leave out are cleared.

intent = router.define(
    "refund",
    description="The customer wants their money back.",
    target=Target("agent", "billing-agent"),
    min_score=0.75,
    metadata={"owner": "support"},
    examples=["i want a refund", "refund my purchase"],
)
print(intent.name, intent.example_count, intent.revision)
const { result: intent } = await router.upsertIntent("refund", undefined, undefined, {
  description: "The customer wants their money back.",
  target: { type: "agent", ref: "billing-agent" },
  minScore: 0.75,
  metadata: { owner: "support" },
  examples: ["i want a refund", "refund my purchase"],
});
console.log(intent.name, intent.exampleCount, intent.revision);
ROUTER_API="${IRIS_INTENT_ROUTER_URL%/}/iris/data/v1/intent/routers/$IRIS_INTENT_ROUTER_ID"
curl --fail-with-body --silent --show-error \
  -X PUT "$ROUTER_API/intents/refund" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
    "description": "The customer wants their money back.",
    "target": {"type": "agent", "ref": "billing-agent"},
    "minScore": 0.75,
    "metadata": {"owner": "support"},
    "examples": ["i want a refund", "refund my purchase"]
  }' | jq -c '{name, exampleCount, revision}'
iris intent-router intents add refund \
  --description "The customer wants their money back." \
  --target '{"type": "agent", "ref": "billing-agent"}' \
  --min-score 0.75 --metadata owner=support \
  --example "i want a refund" --example "refund my purchase"

An intent takes up to 256 examples, each up to 2,048 characters. Phrasings already on the intent that your request omits are removed after the new ones are written, so a concurrent match never sees an empty intent.

Add several intents

Add up to 100 intents in one request. They are added in the order you send them, after the intents the Router already has. An intent whose name already exists is replaced in place, and every other intent is left alone. Nothing is written if any intent is invalid. In Python this is define_many. The CLI's intents add --file reads a JSON or YAML file that holds a list of intents, or a mapping with an intents list.

added = router.define_many(
    [
        {"name": "track_order", "examples": ["where is my order", "track my package"]},
        {"name": "cancel_order", "examples": ["cancel my order", "i changed my mind"]},
    ]
)
for item in added:
    print(item.name, item.example_count)
const { result } = await router.addIntents({
  intents: [
    { name: "track_order", examples: ["where is my order", "track my package"] },
    { name: "cancel_order", examples: ["cancel my order", "i changed my mind"] },
  ],
});
for (const item of result.items) console.log(item.name, item.exampleCount);
curl --fail-with-body --silent --show-error \
  -X POST "$ROUTER_API/intents" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"intents": [
    {"name": "track_order", "examples": ["where is my order", "track my package"]},
    {"name": "cancel_order", "examples": ["cancel my order", "i changed my mind"]}
  ]}' | jq -c '.items[] | {name, exampleCount}'
cat > intents.json <<'JSON'
{"intents": [
  {"name": "track_order", "examples": ["where is my order", "track my package"]},
  {"name": "cancel_order", "examples": ["cancel my order", "i changed my mind"]}
]}
JSON
iris intent-router intents add --file intents.json

Read and list intents

Reading an intent returns its configuration, exampleCount, timestamps, and revision. It doesn't include the examples, which you list separately.

Listing returns intents with their configuration and example counts, in creation order. Pass limit (default 50, maximum 200). When more intents follow, the response includes nextCursor: pass it back as cursor for the next page. The Python intents() and examples() methods follow the cursor for you and return every item, and the CLI's --all does the same.

for item in router.intents():
    print(item.name, item.example_count)
const { result: page } = await router.listIntents(50);
for (const item of page.items) console.log(item.name, item.exampleCount);
console.log("next cursor:", page.nextCursor);
curl --fail-with-body --silent --show-error \
  "$ROUTER_API/intents?limit=50" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" > intents.json
jq -c '.items[] | {name, exampleCount}' intents.json
jq -r '"next cursor: \(.nextCursor)"' intents.json
iris intent-router intents list --limit 50

Update an intent's configuration

Update the description, target, minScore, or metadata as a merge patch, without touching or re-embedding the examples.

  • An omitted field stays unchanged.
  • null clears description, target, minScore, or metadata.
  • A metadata map replaces the whole existing map.
  • An empty body is rejected.
patched = router.update(
    "refund",
    min_score=None,
    target=Target("agent", "refunds-agent"),
)
print(patched.name, patched.target, patched.min_score, patched.revision)
const { result: patched } = await router.updateIntent("refund", undefined, {
  minScore: null,
  target: { type: "agent", ref: "refunds-agent" },
});
console.log(patched.name, patched.target, patched.minScore, patched.revision);
curl --fail-with-body --silent --show-error \
  -X PATCH "$ROUTER_API/intents/refund" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"minScore": null, "target": {"type": "agent", "ref": "refunds-agent"}}' \
  | jq -c '{name, target, minScore, revision}'
iris intent-router intents update refund \
  --target '{"type": "agent", "ref": "refunds-agent"}'

Clearing minScore makes the intent use the Router's threshold again. The CLI's intents update can't send null, so the CLI example changes only the target. To clear a field, use another client.

Avoid overwriting concurrent changes

Every intent has a revision that changes with each write to the intent. Read the intent, then send its revision as expectedRevision with your change, so the write succeeds only if nobody changed the intent in between. This example changes the description and leaves the rest of the intent alone. A conditional PUT works the same way but replaces the whole intent.

current = router.intent("refund")
changed = router.update(
    "refund",
    description="The customer wants a refund or a chargeback reversed.",
    expected_revision=current.revision,
)
print(changed.name, changed.revision)
const { result: current } = await router.getIntent("refund");
const { result: changed } = await router.updateIntent("refund", undefined, {
  description: "The customer wants a refund or a chargeback reversed.",
  expectedRevision: current.revision,
});
console.log(changed.name, changed.revision);
REVISION=$(curl --fail-with-body --silent --show-error \
  "$ROUTER_API/intents/refund" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" | jq -er .revision)
curl --fail-with-body --silent --show-error \
  -X PATCH "$ROUTER_API/intents/refund" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --arg rev "$REVISION" '{
    description: "The customer wants a refund or a chargeback reversed.",
    expectedRevision: $rev
  }')" | jq -c '{name, revision}'
REVISION=$(iris --json intent-router intents show refund | jq -er .revision)
iris intent-router intents update refund \
  --description "The customer wants a refund or a chargeback reversed." \
  --expected-revision "$REVISION"

A stale revision fails with 412 and the type /errors/revision-conflict. Read the intent again, reapply your change, and retry. Because every write returns a new revision, a retry of a conditional write that already succeeded also fails with 412: read the intent to check. Adding or deleting examples through the example operations doesn't change the revision.

Manage examples

Add phrasings to an existing intent. Each request takes 1 to 256 examples and adds them after the existing ones. Repeating a phrasing keeps its place and doesn't embed it again.

for example in router.add_examples("refund", ["send my money back", "i was charged twice"]):
    print(example.example_id, example.text)
const { result: added } = await router.addExamples("refund", {
  examples: ["send my money back", "i was charged twice"],
});
for (const example of added.items) console.log(example.exampleId, example.text);
curl --fail-with-body --silent --show-error \
  -X POST "$ROUTER_API/intents/refund/examples" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"examples": ["send my money back", "i was charged twice"]}' | jq -c '.items[] | {exampleId, text}'
iris intent-router examples add refund "send my money back" "i was charged twice"

The response lists each example with its exampleId. List an intent's examples in the order they were written, with the same limit and cursor pagination as intents.

examples = router.examples("refund")
for example in examples:
    print(example.example_id, example.text)
const { result: examples } = await router.listExamples("refund", 50);
for (const example of examples.items) console.log(example.exampleId, example.text);
curl --fail-with-body --silent --show-error \
  "$ROUTER_API/intents/refund/examples?limit=50" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" > examples.json
jq -c '.items[] | {exampleId, text}' examples.json
iris --json intent-router examples list refund --limit 50 > examples.json
jq -c '.items[] | {exampleId, text}' examples.json

Read or delete one example by its identifier. Reading an identifier from another intent returns 404. Deleting an example the intent doesn't hold succeeds and changes nothing, and deleting under an intent that doesn't exist returns 404.

router.remove_example("refund", examples[0].example_id)
await router.deleteExample("refund", examples.items[0].exampleId);
EXAMPLE_ID=$(jq -er '.items[0].exampleId' examples.json)
curl --fail-with-body --silent --show-error \
  -X DELETE "$ROUTER_API/intents/refund/examples/$EXAMPLE_ID" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY"
EXAMPLE_ID=$(jq -er '.items[0].exampleId' examples.json)
iris intent-router examples delete refund "$EXAMPLE_ID" --yes

Delete an intent

Deleting an intent removes it and all its examples. Deleting a missing intent succeeds.

router.remove("cancel_order")
await router.deleteIntent("cancel_order");
curl --fail-with-body --silent --show-error \
  -X DELETE "$ROUTER_API/intents/cancel_order" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY"
iris intent-router intents delete cancel_order --yes

Clear a Router

Clearing removes every intent and example and keeps the Router. The scope is required, and INTENTS is the only value. There is no default, so a request can't empty a Router by accident. Clearing also removes the quickstart's intents, which the Match queries examples create again before they run.

router.clear()
await router.clear({ scope: "INTENTS" });
curl --fail-with-body --silent --show-error \
  -X POST "$ROUTER_API/clear" \
  -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"scope": "INTENTS"}'
iris intent-router clear --scope intents --yes

Write good examples

  • Vary the wording. Include short and long phrasings, and the vocabulary your users really use.
  • Keep each example about one thing. An example that mixes two requests pulls the intent toward both.
  • Separate overlapping intents. If two intents keep matching the same queries, add examples that distinguish them, or raise their minScore.
  • Add examples from real misses. When a query returns the wrong intent or none, add it as an example of the right intent.

See Match queries for how scores and thresholds interact with examples.

RATE THIS PAGE
Back to top ↑