Courses / AWS in Action: AI edition

Session 6 of 10

Triage agent: outage check, try the fix, escalate to an IT queue

Notes

Student Notes — Chapter 6: Triage agent: outage check, try the fix, escalate to an IT queue

AWS in Action: AI edition · Chapter 6

Before you start

Nothing is needed to read this chapter or to run its offline checks beyond Python and the development tools listed in the course repository’s requirements-dev.txt. The offline checks use a fake model and a fake topic and read local files, so they need no AWS account and cost nothing. The live steps (deploying the stack, confirming an email address, sending real messages to the model) are done from the AWS console first, with CloudShell as the alternative. They need an AWS account, a sign-in as an IAM or Identity Center user and not the root user (as the root user a Bedrock step stops with an access-denied message that names the root user), a Region that offers Amazon Bedrock and S3 Vectors, a budget alert, the models Amazon Nova Lite and Amazon Titan Text Embeddings V2, the chapter folder on your computer, and a mailbox you can read; they create real resources and cost money, so you delete the stack at the end. The numbered setup steps are in SETUP.md at the top of the course repository, and this chapter’s checklist is in chapters/06-triage/README.md under “Before you try”. This chapter builds on chapter 5. Recap: a tool is an action you describe to the model, and the model answers with a request to run it; your function runs the tool and sends the result back, as a JSON result when it worked or an error result when it did not; and a stack built from a template is created and deleted as one unit. Have paper or a notes app open for the Flashcards and the Model answer.

Three ways to follow this chapter, the same as before, and you can mix them:

  • Watch: read the chapter and follow the build on screen. Nothing needed.
  • Copy and run: copy the folder chapters/06-triage/ and run its checks.
  • Build with AI: open chapters/06-triage/prompts/, run each prompt in an AI coding tool, then run that prompt’s check. The checks for the whole chapter are in chapters/06-triage/prompts/CHECK.md.

Summary

This chapter teaches the helpdesk to work through a problem in steps and to hand hard cases to a person. A model with tools inside a loop is called an agent: the function asks the model what to do next, runs the tool it asks for, sends the result back, and repeats until the model answers. You write that loop by hand with the Converse API, with three tools: one that checks whether a service is down, one that points to a known fix, and one that emails the IT team through an SNS topic. The code limits the loop with a step cap and a hand-over rule, and the escalation email marks chat text as unverified and breaks its links. You extend chapter 5’s template, try an easy case and a hard case, and delete the stack.

Key concepts

Term Definition
Agent loop The function asks the model what to do next, runs the tool it asks for, gives the model the result, and repeats until the model answers.
Agent A model that can use tools inside such a loop.
Step One model call in the loop, together with the tools it asked for. The function counts steps.
Step cap The most model calls one request may make. Here it is six, and the code enforces it.
Hand-over rule The code’s own rule: when the loop cannot finish, it sends one escalation email and returns escalated: true with a reason.
Escalation Handing a problem the helpdesk cannot settle to a person, here by email to the IT team.
SNS topic A named channel your code sends a message to. Everyone subscribed to it receives the message.
Subscription An address, here an email, that receives what is sent to a topic. An email subscription delivers nothing until its owner confirms it.
Status list The JSON the status tool reads, passed as the ServiceStatus parameter. It marks each service up or down.
Known fix A page to point the person at for a common problem: password, VPN or laptop.
UNVERIFIED line The first line of the escalation email, saying the text below came from the chat page and the model and should not be trusted.

Explanations

Orientation: where this chapter sits

Chapter 5 gave the helpdesk one action. This chapter lets it choose its next step, inside limits you set. In the finished helpdesk the model reaches a triage agent that works through a problem in steps:

The finished helpdesk assistant: an employee uses a chat page that calls a helpdesk API in front of a hosted model, which reaches an IT knowledge base, ticket tools and a triage agent, with sign-in, deployment and monitoring around the API.

Today you build the triage agent part of the picture.

Try this first: an employee writes “My VPN will not connect.” What should the helpdesk do first? Answer with general tips from memory and stop; check whether the VPN is down, tell the person, and email the IT team if it is; or email the IT team about each message that mentions a problem. Pick one and say why. The chapter builds one of them.

By the end you can:

  • explain how the loop, the three tools and the limits turn a message into an answer or a hand-over;
  • read the handler and say what each part is for;
  • try an easy case and a hard case, confirm the email, and delete the stack.

Part 1. The goal: hard cases reach a person

An employee writes “My VPN will not connect.” A good helpdesk does not guess. It first checks whether the VPN is down. If the service is up, it points to a known fix. If neither settles the problem, it emails the IT team. The reply lists the steps that ran and says whether a person was told. Easy cases are fixed; hard cases reach a person.

The code for this chapter is in the course repository under chapters/06-triage/. The new function is chapters/06-triage/app/triage/handler.py, its tests are in chapters/06-triage/tests/test_triage.py and chapters/06-triage/tests/test_triage_stack.py, and the stack description is chapters/06-triage/template.yaml. Chapter 5’s files are still there. A prompts/ folder holds one prompt per build step with a check for each. This chapter adds an endpoint, POST /triage, next to /ask and /tickets. Use curl to talk to it.

The address is open. /triage has no sign-in. Anyone who has its address can run it, each message is billed to your account, and a message can cause an email to your inbox. Sign-in arrives in chapter 7. Until then, keep the throttle in the template, put no real names or real problems in a test message, and delete the stack as soon as you have finished.

Part 2. From one tool call to a loop

A repair technician checks the fault, tries a fix, and calls the supervisor if it is still broken. A chef tastes, adjusts, tastes again, and serves when it is right. In both, the next step depends on what the last step found. An agent loop works like that. The function asks the model what to do next; if the model asks for a tool, the function runs it and gives the model the result; then it asks again. This repeats until the model answers. A model that can use tools inside such a loop is called an agent.

Chapter 5 was one round trip: ask, run the tool, answer. The loop is the same round trip, repeated until the job is done. The model chooses the next step; your code runs it. In Amazon Bedrock the model does not call the tool itself. With the Converse API your application code runs each tool the model asks for, then continues the conversation with a message that holds the tool result.

The function asks the model what to do next. If the model asks for a tool, the function runs it and counts one step; if the step cap is reached the code hands over with one email to the IT team, otherwise it asks the model again. If the model asks for no tool, it answers the person.

Read it from the first box: ask the model what to do next. If it does not ask for a tool, it answers the person, fixed or handed over. If it asks for a tool, the function runs that tool (a check, a fix or an escalation) and counts one step. If the step cap has been reached, the code hands over with one email to the IT team. If not, the function asks the model again. The loop ends in an answer or in a hand-over.

Each pass through the loop reads the model’s reason for stopping. When the reason is tool_use, the function runs each requested tool and sends the results back in one message. Any other reason means the model has answered. The prompt chapters/06-triage/prompts/03-the-loop-and-its-limits.md writes the loop.

Part 3. Build the loop: a platform or by hand

You can build an agent in more than one way. The AWS documentation says Amazon Bedrock Agents, now Bedrock Agents Classic, is no longer open to new customers, and points to Amazon Bedrock AgentCore for similar capabilities. AgentCore is described as a platform for building, deploying and operating agents. This chapter uses neither. It writes the loop by hand: chapter 5’s round trip inside a for loop, in one function, with the Converse API.

Our reason is that each step then shows in the code, the logs and the tests, and you see what any agent platform has to do. That is our choice, not an AWS statement. The function is chapters/06-triage/app/triage/handler.py. It needs no new service beyond an SNS topic.

Part 4. Tool 1: is the service down?

A receptionist asked “is the printer working?” looks at the status board before answering. The first tool, check_service_status, is that glance. It takes a service name, such as vpn, and returns whether the service is up or down.

The status board here is a list the template sets, the ServiceStatus parameter, written as JSON, for example {"vpn": "down"}. The default marks the VPN down and email, the wiki and printing up. To move the outage, change the parameter and update the stack. The core of the tool:

table = json.loads(SERVICE_STATUS)
# …
if status is None:
    raise ToolError("There is no status for …")

A service with no entry, an empty name or an unreadable list becomes an error result, so the model hears the problem and can decide. A real helpdesk would ask a status page here. The loop stays the same. The list is a parameter because a template cannot put rows into a table without a custom resource, which this lab avoids.

Part 5. Tool 2: try a known fix

Some problems have an answer on a page already. The second tool, try_known_fix, points to one of chapter 4’s policy pages. There are three known fixes: password, vpn and laptop. A fix is a page to point at, not an action the code takes: the code does not reset anything.

KNOWN_FIXES = {"password": …, "vpn": …, "laptop": …}
# …
"issue": {"type": "string",
          "enum": sorted(KNOWN_FIXES)},

The tool’s input schema lists the three issues as the allowed values, using ordinary JSON schema. If the model asks for anything else, the function answers with an error result that says to escalate, so the loop reaches the third tool.

Part 6. Tool 3: escalate by email

A mailing list takes one message and gives a copy to each member; a noticeboard with a sign-up sheet tells everyone who signed up. An SNS topic is a channel like that: a named channel your code sends a message to. A subscription is an address, here an email, that receives what is sent. The AWS documentation describes a topic as a logical access point and communication channel, and says publishers send messages to it and subscribers receive them. Your code sends once and SNS delivers to each subscriber.

The third tool, escalate, publishes a short summary to the topic:

sns.publish(TopicArn=TOPIC_ARN,
            Subject="Helpdesk escalation",
            Message=…)

The address that receives the email is the EscalationEmail parameter. It has no default and no address is written in any file; you pass your own when you deploy. An email subscription delivers nothing until its owner confirms it: AWS sends a confirmation message, and you must choose its link. Until then the subscription shows PendingConfirmation in place of its ARN, and escalation emails are not delivered. The function’s role may publish to this one topic.

What goes into the email matters. The text the person typed and the text the model wrote are untrusted. The email opens with a line that says so, and the code breaks links in what follows. This is an illustrative body:

UNVERIFIED: the text below comes from the chat
page and the model. Do not follow links in it.
Summary: see evil[.]example, https[:]//…

Dots between letters and the colon and slashes of a link are broken, because many mail programs turn even a bare domain or an address into a link. Look-alike characters are folded to plain ones first, and invisible characters are dropped. The IT team can still read the link, but it is not clickable. The subject is fixed text, and the summary and the person’s message are cut to a fixed length before they are sent.

Part 7. Limits: the step cap and the hand-over

A repair visit with a limit says: two tries, then the supervisor is called. A cooking timer says: when it rings, you stop and serve what you have. A step cap is that rule for the loop: the most model calls one request may make. The hand-over rule is the code’s own rule for when the loop cannot finish: it sends one email to the IT team and says so in the reply. The cap belongs to the code, not to the model. The cap here, six, is our design choice, not an AWS limit.

MAX_STEPS = 6  # model calls per request
for _ in range(MAX_STEPS):
    # … ask the model, run the tools it asks for
return hand_over(state, "step_cap")

A request also sends at most one escalation email, whatever the model asks for, so a prompt cannot fill the inbox. A second request to escalate gets a note that the hand-over has already happened. The code hands over in three cases, and the reply says escalated: true with the reason:

Reason What went wrong
step_cap Six model calls passed with no answer
no_tool_request The model stopped for a tool but asked for no tool
model_error A model call failed after work had started

If the hand-over email fails too, the reply is an error, not a promise that someone was told. A model call that fails on the very first step, before any work, is an error and nobody is emailed.

Three situations show where the limits matter.

Situation 1: the model that will not stop.

steps used:  6 of 6, still no answer
handed over: reason = step_cap

The model keeps asking for tools and does not answer, and each round is a paid model call. Cause: nothing but a limit ends a model that keeps asking. The step cap ends the loop after six calls, and the code emails the IT team.

Situation 2: the planted link.

Chat text:   a link to a look-alike site
Email text:  …reset at https[:]//evil[.]example

A message carries a link made to look official, and the email goes to IT staff who trust it. Cause: chat text can say anything, and the email is a place where a person acts. The link arrives broken and under the UNVERIFIED line, so it is not clickable and not trusted.

Situation 3: the email that does not arrive.

reply:         "escalated": true
inbox:         empty
subscription:  PendingConfirmation

The reply says the IT team was told, but nothing is in the inbox. Cause: the subscription was not confirmed. The escalated field is a statement about the code, not about a person reading an email. Open AWS’s confirmation message and choose its link, then check the topic with aws sns list-subscriptions-by-topic --topic-arn "$TOPIC": a confirmed subscription lists a full ARN.

This table turns the three into a routine:

Symptom What it usually means What you do
The reply says escalated: true with the reason step_cap The model did not settle the problem in six calls Read the function logs and the steps list; improve the system text or the tools
escalated: true but no email The subscription is unconfirmed, or the mail is in a junk folder Confirm the subscription; check the topic
The email text looks odd, with brackets in links The chat text had a link, and the code broke it on purpose Check with the person; do not repair the link

What this chapter leaves open, and where the course deals with it:

What is still open What the helpdesk will do Built in
Anyone with the address can run the triage and trigger email Sign-in and roles Chapter 7
The status list is fixed text, not a live check A real status source in a real helpdesk Beyond this course
Nobody watches cost, errors or the alarm Logs, cost per ticket and an alarm Chapter 9

Part 8. Extend the template, test, delete

Templates in this course are cumulative: chapter 6 holds the resources chapter 5 held, plus what it adds. Chapter 5’s twenty-four resources stay, and the CloudFront distribution gains a /triage path. The file is chapters/06-triage/template.yaml, and it adds eight resources and two parameters, EscalationEmail and ServiceStatus:

Piece What it is for Name in the template
Topic The channel that emails the IT team EscalationTopic
Subscription The email address on the topic EscalationSubscription
Log group Where the function writes its logs TriageFunctionLogGroup
Function role What the function may do TriageFunctionRole
Function Runs the loop and the three tools TriageFunction
API integration Connects the route to the function TriageIntegration
Route POST /triage TriageRoute
Invoke permission Lets the API call the function TriageInvokePermission

The function role is a staff badge with a short list of doors: it may write its own logs, call the chosen model, and publish to the one topic. It has no table permission, because this function does not use the tickets table. The topic and function are tagged chapter: session-06, project and environment like the other resources that can carry a tag; the subscription has no tags property. The topic is named helpdesk-dev-escalations from the Project and Environment parameters, and the model permission follows the inference-profile route chosen in chapter 2. The template adds one output you use: EscalationTopicArn. The prompts chapters/06-triage/prompts/01-escalation-topic.md and chapters/06-triage/prompts/05-triage-endpoint.md build the stack changes, and chapters/06-triage/prompts/04-triage-tests.md writes the tests: an easy case, a hard case, the step cap and the limits on what one request can send.

Costs money. The live steps create real resources, each message calls the model, usually several times, and some messages send an email. Deploy, try and delete in one sitting.

aws cloudformation deploy \
  --stack-name helpdesk-dev-ch6 \
  --template-file template.yaml \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides EscalationEmail=<your-email> Project=helpdesk Environment=dev

Replace the placeholder with an address you can read. Read the outputs into variables, as in chapter 5, and upload the chat page and the policy pages. Then open the message AWS sent to your address and choose the confirmation link, and check the subscription. Then send an easy case and a hard case:

curl -s -X POST "$SITE/triage" -H "content-type: application/json" -d '{"message": "I forgot my email password."}'
curl -s -X POST "$SITE/triage" -H "content-type: application/json" -d '{"message": "My VPN will not connect."}'

The first should check that email is up, point to the password reset page and report "escalated": false; no email arrives. The second finds the VPN marked down in the status list, escalates, reports "escalated": true, and one email arrives. The steps list in each reply names the tools that ran, in order. The model chooses its own steps, so the wording and the steps can vary from run to run. A message may be at most 1000 characters. These commands are also in chapters/06-triage/README.md.

A hotel guest checks out and the room is cleared; a room left occupied keeps being charged. A stack you do not delete keeps costing money. The bucket still holds the chat page and the policy pages, and the documentation says a bucket can be deleted only if it has no objects in it, so empty it first:

aws s3 rm "s3://$BUCKET" --recursive
aws cloudformation delete-stack --stack-name helpdesk-dev-ch6
aws cloudformation wait stack-delete-complete --stack-name helpdesk-dev-ch6

The wait command polls until the stack status is DELETE_COMPLETE, so when it returns the stack is gone. Deleting the stack removes the topic and its subscription, and the rest of what the stack made, because nothing has a Retain deletion policy.

Practice: what do you do?

Decide each one before reading the call: what you do, and what the employee sees.

Problem Our call and reason
An employee writes “I forgot my email password.” and email is up The model checks email, finds it up, points to the password reset page, and no email is sent
An employee writes “My VPN will not connect.” and the VPN is marked down The model finds the VPN down, escalates, and one email arrives
The model keeps asking for tools and does not answer The step cap ends the loop after six calls, and the code emails the IT team
A message carries a link made to look official The email marks the text unverified and breaks the link
The reply says escalated: true and no email arrives The subscription is probably unconfirmed: confirm it, then check the topic
You finish the chapter Empty the bucket, delete the stack and wait until it is gone

Flashcards

Say the answer aloud before revealing it. Speaking is a different skill from reading, so practise both.

1. What is an agent loop? The function asks the model what to do next, runs the tool it asks for, gives the model the result, and repeats until the model answers. Like a repair technician who checks, tries a fix, and calls the supervisor if it is still broken.
2. How is the loop different from chapter 5's round trip? It is the same round trip repeated. Chapter 5 asked, ran one tool and answered; the loop keeps asking, so the next step can depend on what the last tool found. The model chooses the step and your code runs it.
3. Why does this chapter write the loop by hand? Bedrock Agents Classic is documented as no longer open to new customers, and AgentCore is a separate platform. Writing the loop in one function shows each step in the code and logs. That is our reason, not an AWS statement.
4. What does the check_service_status tool read? A status list, the ServiceStatus parameter, written as JSON such as the VPN marked down. A service with no entry becomes an error result. A real helpdesk would ask a status page, and the loop would stay the same.
5. What does the try_known_fix tool do? It points to one of chapter 4's policy pages for password, VPN or laptop. A fix is a page to point at, not an action the code takes. An issue outside the three becomes an error result that says to escalate.
6. What is an SNS topic, and what is a subscription? A topic is a named channel your code sends a message to; a subscription is an address, here an email, that receives what is sent. The code sends once and SNS delivers to each subscriber.
7. Why might the reply say escalated true and yet no email arrive? The subscription may be unconfirmed. An email subscription delivers nothing until its owner chooses the confirmation link, and until then it shows PendingConfirmation instead of an ARN. The field describes the code, not a person reading.
8. How does the escalation email treat the chat text? It opens with an UNVERIFIED line saying the text came from the chat page and the model. The code folds look-alike characters, drops invisible ones and breaks links, so a planted link is readable but not clickable.
9. What is the step cap, and who enforces it? It is the most model calls one request may make, six here, and the code enforces it, not the model. It is our design choice, not an AWS limit. After the cap the code hands over instead of looping on.
10. When does the code hand over by itself, and what does the reply say? When the step cap is reached, when the model stops for a tool but asks for no tool, or when a model call fails after work started. The reply says escalated true with the reason. If the email also fails, the reply is an error.
11. How many escalation emails can one request send? One, whatever the model asks for. A second request to escalate gets a note that the hand-over has already happened. This stops a prompt from filling the inbox.
12. What does chapter 6 add to the template, and how do you delete it cleanly? Eight resources: EscalationTopic, EscalationSubscription, TriageFunctionLogGroup, TriageFunctionRole, TriageFunction, TriageIntegration, TriageRoute and TriageInvokePermission, plus two parameters. Empty the site bucket, run delete-stack, then wait for stack-delete-complete.

Model answer

The core question this chapter answers: “The helpdesk should work through a problem in steps and hand hard cases to a person. How does that work, from the first message to the email?”

Four things a colleague listens for, in this order: a loop · three tools · limits · hand-over. Skip any of these four and the answer is incomplete.

  1. A loop — ask the model, run the tool it asks for, send the result back, and repeat until it answers
  2. Three tools — check the service, try a known fix, escalate; the code runs them
  3. Limits — a cap of six model calls, and one email per request
  4. Hand-over — the escalation email marks chat text unverified and breaks its links; the code hands over itself when the loop cannot finish

The full answer, said aloud:

(A loop) “The function asks the model what to do next. If the model asks for a tool, the function runs it, sends the result back, and asks again. That repeats until the model answers without asking for a tool.

(Three tools) The model has three tools: check whether a service is down, try a known fix by pointing at a policy page, and escalate. The model chooses; my code runs them. An easy case ends with a fix and no email. A hard case ends with an escalation.

(Limits) My code limits the loop. A request makes at most six model calls, and sends at most one escalation email, whatever the model asks for.

(Hand-over) The escalation goes to an SNS topic that emails the IT team, once the address is confirmed. The email opens with an UNVERIFIED line and breaks links in the chat text. If the loop cannot finish, the code hands over itself and the reply says why.“

Missing a point is a failure state. The follow-up question lands where you went quiet: finish without the limits and the next question is what stops the loop, and finish without the hand-over and the next question is what happens when the model gets stuck. Practise until the four come out without prompting, then stop talking and let the questions come.

Why this matters

In a conversation with a colleague: a team that lets a model choose its own next step is asked what stops it, and when a person takes over. The four points answer both, and the triage helpdesk is a specific example.

On the job: an engineer building an assistant that works in steps, a finance owner watching the model bill, an IT lead who needs hard cases to reach the team, and a security reviewer reading the alert email ask versions of the same questions. The step cap, the one-email limit and the unverified marking answer them.

In the rest of this course: chapter 7 adds the sign-in this endpoint is missing, and chapter 9 adds logs, cost and an alarm. A reader who knows these eight parts can predict why each piece is there.

After this unit

Assignment. The chapter project asks you to deploy the stack with your own address, confirm the email, try an easy case and a hard case, change the status list, and delete it. Keep the template: the next chapter extends it. The brief itself is the project file.

What’s next. The next chapter adds sign-in and safety: two roles, masking of pasted secrets, and blocking of off-topic requests.

TechEazy Consulting training material — not affiliated with or endorsed by Amazon Web Services (AWS), Amazon Bedrock or any other company named in this course.