Related Blogs
Ready to Build Something Amazing?
Join thousands of developers building the future with Orkes.
Join thousands of developers building the future with Orkes.
Previously, I showed you how to build an AI agent in Conductor. Now, I want to show you how to build a simple customer success agent with Agentspan — our open-source runtime designed not just for building agents, but for running them reliably in production. To get you started, I made a short video showing you how the agents runs and what it looks like before we dive deeper into the technicalities.
A quick overview: the success agent runs in the terminal, waits for user input, carries out its tasks and decision-making autonomously, and escalates to a human when needed because there are certain tasks we don’t want agents handling entirely on their own.

If you've heard about customer success but aren't sure what it actually involves, here's a quick breakdown of the role AI can play in simplifying it.
A customer success agent is the person or system that's responsible for a customer's experience after they've signed up.
Customer success as a system is actually pretty large and can include a lot of different aspects depending on your product and industry, on your resources and needs. Responding to customer issues, checking account health, answering questions, opening tickets, monitoring onboarding, escalating problems internally and a lot more.
As you can imagine, some parts of that workflow are a really good fit for artificial intelligence.
A lot of customer success work is fundamentally about communication, retrieving information, understanding context, and deciding what action to take next. Which AI is getting better at daily.
Here are some things an AI customer success agent can do for you::
And probably the biggest benefit is speed. Especially when retrieving information and making decisions or suggestions based on that information and on your goals.
A human support rep might spend quite a bit of time going between Slack, support tickets, CRM systems, billing history, and internal notes just to understand what's going on when a customer complains.
An AI agent can pull all of that context together almost instantly.
That doesn't mean you should automate the entire process though. In my experience it's much better to start small and grow from there and see where you might benefit the most from an agentic system.
So for this post I'm showing you how you can build a simple customer success AI agent that can:
Here's the agent working through an example, a customer whose exports keep failing, from start to finish. This agent runs in the terminal, but you can always build a UI for it as well. You can also see the agent running at the top of the article:
$ python agent.py
CustomerSuccessAgent
input: "Customer CUST-4821 says their data exports keep failing"
→ get_hubspot_data("CUST-4821")
Sekro · contract $48,000 · health score 62 · owner jane@yourcompany.com
→ get_slack_data("CUST-4821")
3 recent messages in #sekro
"our exports have been failing since Tuesday"
"Still broken — our CFO is asking questions"
→ open_zendesk_ticket(
subject="Sekro — data exports failing since Tuesday",
priority="high"
)
✓ ZD-99182 created (high priority)
Done in 3 steps.
So in a few seconds the agent pulled up the account, read through the Slack history, figured out this was a frustrated high-value customer, and opened a high-priority Zendesk ticket.
If you want to check out the code, I put it on Github.
For this example I included some sample data in the tools because it makes the demo much easier to understand and lets you focus on the actual agent logic.
The first thing our agent needs is context, so that it gets an understanding of the customer first.
So let’s start by giving the model access to customer account information from HubSpot:
@tool
def get_hubspot_data(customer_id: str) -> dict:
Now the agent can actually start understanding what's happening.
This gives your agent important information like contract value, lifecycle stage, account ownership
Next, a lot of important information about an issue is hidden in slack messages. So next we're going to give the agent access to recent Slack messages too:
@tool
def get_slack_data(customer_id: str) -> dict:
Now the agent can actually start understanding what's happening a bit more. It can see frustration, urgency, repeated issues, whether someone already replied internally and generally get a much better understanding of the situation.
For example this message is a pretty strong signal that the issue is serious:
"Still broken and our CFO is asking questions"
And this is one of the places where LLMs become genuinely useful because they’re very good at reading messy conversational context and extracting meaning from it quickly.
And now that the agent can gather information we want it to do something so it's a lot more helpful. The next tool will give it access to open issue tickets in Zendesk.
@tool
def open_zendesk_ticket(subject: str, description: str, priority: str = "high") -> dict:
Like this the agent can gather information, understand recent conversations your team had with this customer, decide what actions to take, and then perform said action.
There's also one more tool worth adding. Opening a ticket isn't always the right move. Sometimes the agent should hand things off to a person. So we'll give it a way to escalate to a human when confidence is low or the issue is high-stakes:
@tool(approval_required=True)
def escalate(reason: str, recommended_action: str) -> dict:
The approval_required=True flag here means the escalation waits for a human to sign off before it actually goes through. This is your human-in-the-loop task. When the agent decides to escalate, it pauses in the terminal and asks you directly:
============================================================
HUMAN APPROVAL REQUIRED
============================================================
[Summary of who the customer is, what's wrong,
what the agent already did, and why it's escalating]
Mark as resolved? (y/n):
Press y and the agent marks it resolved and prints a final summary. Press n and it leaves the issue open and stops. It's a small thing but I like it a lot. It allows me to have control over the agent while still keeping the agent as an actual AI agent.
This is the part that shapes how the agent behaves, the AI prompt/instructions. How good your agent is depends on how good the model is and how good your instructions are. As time goes on models will likely just get better so instructions won't have to be as great (I imagine), but for now they do. It's also a great idea to test your prompts, I wrote a short post on how you can do that too.
INSTRUCTIONS = """
You are a customer success agent for a SaaS company.
...
"""
The workflow is intentionally simple.
First the agent checks the customer’s HubSpot data to understand the account.
Then it reviews recent Slack messages to get more context.
And finally it decides whether it should open a support ticket.
That structure is important because it forces the model to gather context before taking action.
In this example the model would probably recognize two things pretty quickly:
So opening a Zendesk ticket becomes the reasonable next step.
And because we're using tools, the model is now interacting with systems and performing actions.
Now you can define your agent, essentially build it up. With a good framework you don't need a lot of code to make that happen, especially for simpler agents.
agent = Agent(
name="CustomerSuccessAgent",
model="anthropic/claude-sonnet-4-20250514",
tools=[get_hubspot_data, get_slack_data, open_zendesk_ticket],
instructions=INSTRUCTIONS,
max_turns=5
)
The code here is pretty straightforward. You can clearly see that you give it a name, the model it will use, the tools the agent can actually use, the instructions (your AI prompt), and how many reasoning steps it's allowed to have. The last one is not required, but it's a good habit. You can't predict exactly how an agent will behave and you can potentially run into a situation where the agent just runs forever. You don't want that. So you can give it how many times it can go over its own reasoning. For this one I set it to 5.
Now you can actually run the agent to see what it can do. The code on Github is a bit more involved because I made it more user friendly in the terminal, but the essential code to running your agent is the following:
with AgentRuntime() as runtime:
result = runtime.run(agent, "the agent input")
result.print_result()
Once you run this, the model will reason through the problem step by step, decide which tools to call, gather context and then decide what action to take.
Here is what the agent code looks like put together. You can see what the file looks like and where all the pieces of the code go:
from agentspan.agents import Agent, AgentRuntime, tool
# ---------------------------------------------------------------------------
# Tools for the agent
# ---------------------------------------------------------------------------
@tool
def get_hubspot_data(customer_id: str) -> dict:
"""Get account details for a customer from HubSpot.
Returns plan tier, contract value, lifecycle stage, and account owner.
"""
return {
"customer_id": customer_id,
"company": "Sekro",
"contract_value": 48000,
"lifecycle_stage": "customer",
"health_score": 62,
"account_owner": "jane@yourcompany.com",
}
@tool
def get_slack_data(customer_id: str) -> dict:
"""Get the 100 most recent Slack messages from the customer's shared channel."""
return {
"customer_id": customer_id,
"channel": "#sekro",
"messages": [
{"user": "bob@sekro.com", "text": "Hey, our exports have been failing since Tuesday", "ts": "2025-06-10T09:12:00Z"},
{"user": "jane@yourcompany.com", "text": "Looking into it now!", "ts": "2025-06-10T09:15:00Z"},
{"user": "bob@asekro.com", "text": "Still broken — our CFO is asking questions", "ts": "2025-06-11T14:03:00Z"},
],
}
@tool
def open_zendesk_ticket(subject: str, description: str, priority: str = "high") -> dict:
"""Open a support ticket in Zendesk.
Call this AT MOST ONCE. If the issue needs another ticket, escalate instead.
"""
return {
"ticket_id": "ZD-99182",
"subject": subject,
"priority": priority,
"status": "open",
"url": "https://support.yourcompany.zendesk.com/tickets/99182",
}
@tool(approval_required=True)
def escalate(reason: str, recommended_action: str) -> dict:
"""Escalate to a human agent.
Use this when confidence is low, when the issue is high-stakes, or when no available action seems right.
"""
return {"status": "escalated", "reason": reason, "recommended_action": recommended_action}
# ---------------------------------------------------------------------------
# Agent prompt
# ---------------------------------------------------------------------------
INSTRUCTIONS= """
You are a customer success agent for a SaaS company.
Your goal is to keep the customer happy.
You wull be given a customer ID and a description of their issue. Work through the issue step by step:
1. Get the customer's Hubspot data to understand their tier and importance
2. Check their Slack history to understand the full context
3. Based on what you learn, either open a Zendesk ticket or escalate to a human
Rules:
- Open a Zendesk ticket AT MOST ONCE. If you already opened one, escalate instead.
- If you have low confidence at any point, escalate immediately.
- High-value customers (contracts > $25k) should be escalated if the issue is unresolved after 2 steps.
"""
# ---------------------------------------------------------------------------
# Define the Agent
# ---------------------------------------------------------------------------
agent = Agent(
name="CustomerSuccessAgent",
model="anthropic/claude-sonnet-4-20250514",
tools=[get_hubspot_data, get_slack_data, open_zendesk_ticket, escalate],
instructions=INSTRUCTIONS
max_turns=5
)
# ---------------------------------------------------------------------------
# Run the Agent
# ---------------------------------------------------------------------------
if __name__ == "__main__":
with AgentRuntime() as runtime:
result = runtime.run(agent, "the agent input")
result.print_result()
to run this all you need to do is the following (or you can just clone the repo and run the following):
pip install agentspan anthropic
export ANTHROPIC_API_KEY=...
agentspan server start`
this will start your server at http://localhost:6767.
The mock data always returns the same Sekro account, but how the agent reasons about it changes a lot based on what you tell it. A few worth playing with:
# Should open a ticket straightforwardly
python agent.py "Sekro — user can't find the export button, first time asking"
# Should escalate immediately (high stakes)
python agent.py "Sekro — full data loss reported, legal team involved"
# Should escalate because of low confidence (vague description)
python agent.py "Sekro — something seems off with their account, not sure what"
# A different kind of issue entirely
python agent.py "Sekro — disputing their last invoice, threatening to cancel"
And that’s a working customer success agent: one that gathers context, reasons over it, and takes a real action. It doesn’t require a huge amount of code, but it still gives you the durability and reliability that come with Conductor.
The thing I’d really stress is that this is a starting point, not a finished product. It’s also only one piece of a much larger customer success system.
Real customer success has a lot more moving parts. But the nice thing about building it this way is that you can grow into those moving parts one tool at a time.
You could add a billing lookup, a churn-risk check, or a step that drafts a reply for a human to approve. Each one becomes another tool the agent can reach for, while the core loop stays the same. And because the infrastructure is open source, you’re not locked into a black box.
That’s the whole reason I like starting small here. You get something useful running quickly, and you’re never more than one tool away from making it better.
So my suggestion: clone the repo, run it against the sample data, and then swap in one tool that's real for your team. Once you've done that, you've basically got the pattern for everything else.
And if you do build on it, the next thing worth your time is getting your prompt right. That's where most of an agent's behavior actually comes from. I wrote a short post on testing your prompts that pairs well with this one.
Happy building.