
Agents for Humans: Building an Autonomous Issue-Triage Agent with Strands and Amazon Bedrock AgentCore
A practical, beginner-friendly walkthrough of how to build an agent that watches a GitHub/GitLab/Jira issue queue and triages it on its own — using the Strands Agents SDK and Amazon Bedrock AgentCore. Built for the AWS "Agents for Humans" hackathon.
Introduction
Most teams have the same quiet problem: issues pile up faster than anyone
has time to read them. Someone has to open each one, decide how bad it is,
figure out who should fix it, label it, and comment — and that job usually
belongs to "whoever has a spare fifteen minutes," which means it doesn't
really belong to anyone.
has time to read them. Someone has to open each one, decide how bad it is,
figure out who should fix it, label it, and comment — and that job usually
belongs to "whoever has a spare fifteen minutes," which means it doesn't
really belong to anyone.
This article walks through how to actually build an agent that does that
job for you, using two things:
job for you, using two things:
- Strands Agents SDK — an open-source Python framework for building
agents that use tools. - Amazon Bedrock AgentCore — the AWS platform that runs, remembers, and
observes agents in production.
If you can write a Python function, you can build this.
What is Strands Agents SDK?
Strands lets you build an agent by giving it a model and a list of tools —
plain Python functions the agent can call when it decides it needs to.
plain Python functions the agent can call when it decides it needs to.
1
2
3
4
5
6
7
from strands import Agent, tool
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers."""
return a + b1
2
3
agent = Agent(tools=[add_two_numbers])
agent("What is 4 plus 5?")The agent decides on its own whether it needs the tool, calls it, and uses
the result — you never write the "if user asks about math, call this"
logic yourself.
the result — you never write the "if user asks about math, call this"
logic yourself.
What is Amazon Bedrock AgentCore?
AgentCore is where that agent actually goes to run for real. It has three
pieces that matter here:
pieces that matter here:
- Runtime — hosts your agent behind a real, invokable, serverless
endpoint. - Memory — a long-term store your agent can write to and search,
across sessions. - Observability — automatic tracing of every invocation, with no
extra logging code.
The Problem We're Solving
We're building IssueOps — an agent that:
- Reads a new issue.
- Decides its severity and type.
- Suggests a fix and an owner.
- Labels, assigns, and comments — for real.
- Remembers the decision, so it recognizes the same bug next time.
How It Works
1
2
3
4
5
6
7
8
New issue appears
↓
Orchestrator agent
↓
Triage agent → Fix-suggester agent
↓
Labels + comment posted, decision saved to memory
Step 1: Give the agent real tools, not toy ones
Every action IssueOps can take is a plain Python function, decorated with
@tool, that calls a real API:1
2
3
4
5
6
7
8
def add_labels(repo: str, issue_number: int, labels: list[str]) -> str:
"""Add labels to a GitHub issue."""
gh_repo = client().get_repo(repo)
issue = gh_repo.get_issue(issue_number)
issue.add_to_labels(*labels)
return f"added labels {labels} to {repo}#{issue_number}"No mocks. When the agent calls this, a real label appears on a real issue.
Step 2: Use agents as tools, not one giant prompt
This is the part that trips people up. A single prompt trying to classify
severity, search code, and write prose all at once gets muddy fast. Strands
lets you wrap a whole sub-agent as a tool for a bigger one:
severity, search code, and write prose all at once gets muddy fast. Strands
lets you wrap a whole sub-agent as a tool for a bigger one:
1
2
3
4
5
6
7
8
9
10
def suggest_fix(repo: str, issue_summary: str) -> str:
"""Propose a fix approach and likely owner for an issue."""
agent = Agent(
system_prompt="You are a fix-suggestion specialist...",
tools=[search_code, get_codeowners],
)
result = agent(f"Repo: {repo}\nIssue: {issue_summary}")
return str(result)Now your top-level orchestrator can call
other tool — it doesn't need to know a whole second agent is running
inside it.
suggest_fix exactly like any
other tool — it doesn't need to know a whole second agent is running
inside it.
1
2
3
4
orchestrator = Agent(
tools=[triage_issue, compile_release_notes],
)Step 3: Add memory, so it stops triaging cold every time
This is where AgentCore Memory comes in. Before deciding on severity, the
agent checks whether it's seen something like this before:
agent checks whether it's seen something like this before:
1
2
3
4
5
6
7
records = memory_client.retrieve_memories(
memory_id=MEMORY_ID,
namespace=f"/triage/{repo}",
query=issue_summary,
top_k=3,
)And after deciding, it writes the decision back:
1
2
3
4
5
6
memory_client.create_event(
memory_id=MEMORY_ID,
actor_id=repo,
messages=[(f"Issue: severity={severity}", "ASSISTANT")],
)The payoff shows up in the agent's own comment on a later issue: "This is
a recurring issue, as seen in Issue #1." That line comes straight out of
memory recall — not a hardcoded string.
a recurring issue, as seen in Issue #1." That line comes straight out of
memory recall — not a hardcoded string.
Step 4: Deploy it to AgentCore Runtime
A local script is a demo. A deployed agent is a product. Wrapping the
orchestrator for AgentCore Runtime takes a handful of lines:
orchestrator for AgentCore Runtime takes a handful of lines:
1
2
3
4
5
6
7
8
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
def invoke(payload: dict) -> str:
return str(orchestrator(payload.get("prompt", "")))Then:
1
2
3
agentcore configure --entrypoint app.py
agentcore deployThat's it — your agent now has a real invocable endpoint, running
serverless, with Observability traces flowing into CloudWatch automatically.
serverless, with Observability traces flowing into CloudWatch automatically.
Why Not Just Trigger Triage Manually?
Consider this:
1
2
3
4
5
6
Human notices issue
↓
Human runs "triage this issue" command
↓
Agent triages itThat's still a chore — someone has to remember to run it. The better shape
is:
is:
1
2
3
4
5
6
Agent polls the repo continuously
↓
Finds an untriaged issue on its own
↓
Triages it, unattendedA background loop that checks for anything missing a severity label and
triages it automatically is the difference between "a tool you operate"
and "an agent that runs itself." That loop is a handful of lines around
the same tools you already built — no new agent logic required.
triages it automatically is the difference between "a tool you operate"
and "an agent that runs itself." That loop is a handful of lines around
the same tools you already built — no new agent logic required.
Bonus: Don't Lock Your Agent to One Tracker
If you want this to scale past one team, don't call GitHub's API directly
from your tools. Define an interface instead:
from your tools. Define an interface instead:
1
2
3
4
5
class IssueTracker(ABC):
def list_open_issues(self, project, limit): ...
def add_labels(self, project, issue_id, labels): ...
def assign(self, project, issue_id, assignee): ...Then GitHub, GitLab, and Jira can each implement it. Swapping which one is
active becomes a config value, not a rewrite.
active becomes a config value, not a rewrite.
Final Thoughts
The agent doesn't have to do everything. It has to do the repetitive,
judgment-heavy part reliably, remember what it's already decided, and
leave the genuinely ambiguous calls to a human. That's the whole shape of
a good background agent — and it's buildable in an afternoon with Strands
and Amazon Bedrock AgentCore. 🤖☁️
judgment-heavy part reliably, remember what it's already decided, and
leave the genuinely ambiguous calls to a human. That's the whole shape of
a good background agent — and it's buildable in an afternoon with Strands
and Amazon Bedrock AgentCore. 🤖☁️
Full source for IssueOps, the agent built in this walkthrough:
github.com/A-dvika/IssueOps
github.com/A-dvika/IssueOps
#agents-for-humans
#strands-agents
#amazon-bedrock-agentcore
#amazon-bedrock
#ai-agents
#python
#strands-agents
#amazon-bedrock-agentcore
#amazon-bedrock
#ai-agents
#python
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article