AWS Builder Center

Concurd - MultiAgent System

A behind-the-scenes look at building Concord, a live multi-agent AI platform, and the AWS lessons that came with making Bedrock, Lambda, API Gateway, and DynamoDB work together under hackathon pressure.

I made an AI agent perform live in front of an audience, and Bedrock's tool loop humbled me

Nobody really tells you this about building AI agents: getting one to answer a question is easy.
Getting that agent to actually work while a room full of people is watching, expecting it to call tools correctly, stream its output, and let someone take control halfway through the task?
Completely different sport.
That was my job at First Commit with Concord, and honestly, I learned way more about building reliable agent systems than I expected to.
The AWS docs gave me the what. This is the stuff I actually wish I'd known before starting.

The actual problem

Concord lets multiple people watch and interact with a live AI agent session together.
There's one driver at a time. Everyone else can watch what the agent is doing in real time — including streamed output and tool calls — instead of just sitting there waiting for the final response.
And spectators can request control, branch off into their own session, work independently, and eventually offer their result back to the original session.
My part was making sure that whatever the model decided to do actually made it to everyone watching correctly and consistently.
No pressure.

Step one: talk to Bedrock alone first

I deliberately didn't start with Lambda.
I didn't start with API Gateway.
I didn't start with DynamoDB.
I started with a tiny script on my laptop that called Amazon Bedrock's  ConverseStream  API directly.
Nothing else.
That turned out to be one of the best decisions I made during the hackathon.
Bedrock's tool-use loop has a very specific flow. The model starts streaming a response, decides it needs a tool, and then stops with a  stop_reason  of  tool_use  instead of finishing the response.
At that point, your application has to:
  1. Detect the tool call.
  1. Execute the tool itself.
  1. Take the result.
  1. Send that result back as another turn.
  1. Let the model continue from there.
On paper, that sounds pretty straightforward.
The first time you see the model suddenly stop streaming because it wants a tool, though, your immediate reaction is basically:
"Wait... why did it just stop talking?"
Testing this separately meant I could prove that the model interaction itself worked before adding the rest of the system around it.
Later, when something broke inside Lambda, I already knew the Bedrock side wasn't the problem.
Under a hackathon deadline, being able to eliminate entire categories of problems quickly is huge.

Step two: fake the expensive part

Here's another decision that saved us a lot of time.
Our hackathon credits hadn't landed yet, and Bedrock isn't something I wanted to casually hammer with requests just to test infrastructure.
So I mocked it.
Not a vague fake that returned  "hello world" .
I mocked the same shape and event flow that my actual Bedrock integration would produce.
Then I built the Lambda handler, broadcasting logic, and everything downstream against that mock.
No AWS calls.
No waiting for someone else's infrastructure.
No unnecessary spending.
When the actual credits became available, replacing the mock with the real Bedrock call was basically a couple of changes.
Everything else stayed the same.
That was a pretty useful lesson:
If a dependency is expensive, rate-limited, or not available yet, understand its interface and mock the interface — not just the output.
You can keep building while everyone else is waiting for access.

Then came the bug that only appears when there's an audience

This was probably my favorite lesson from the whole thing.
We were using API Gateway's WebSocket Management API to push updates to individual connected clients.
And if a client disconnects, the API can return a  410 Gone .
Which makes sense.
The connection is gone.
The problem is what happens if you're broadcasting to a bunch of people.
Imagine five people are watching the same session.
Four are happily connected.
One person closes their laptop.
Your broadcast loop tries to send that person an update.
Boom.
 410 Gone .
If you handle that badly, one dead connection can interfere with the entire broadcast.
And that's a terrible failure mode.
The person who closed their laptop shouldn't be able to crash the experience for everyone who is still watching.
The fix is pretty simple once you understand what's happening:
handle connection failures individually.
If one connection is gone, remove/ignore it and keep broadcasting to everyone else.
It sounds obvious now.
It definitely didn't feel obvious the first time I watched it happen during testing.
Real-time systems have a funny way of turning completely normal human behavior — like closing a browser tab — into an edge case.

What actually worked surprisingly well

One thing I really appreciated about Bedrock's Converse API was the consistency of the interface across models.
I could develop the agent loop using a cheaper model and later move toward a stronger model for the actual demo without having to redesign the entire integration.
The model ID changed.
The surrounding logic didn't.
For a four-day hackathon where every hour matters, that kind of consistency is incredibly useful.
The less time you spend rewriting integrations, the more time you can spend actually building the product.

And then there was DynamoDB

The driver-lock system sounds much more complicated when you describe it as a feature.
"Multiple users can watch an agent, but exactly one person can control it at any given time, and control can be handed off safely."
Sounds like something that needs a whole distributed locking system.
It didn't.
DynamoDB's conditional writes handled the important part.
The idea was essentially:
Update the driver only if the current state is still what I expect it to be.
That condition is what prevents two people from successfully grabbing the wheel at the same time.
No separate locking service.
No unnecessary infrastructure.
Just the database enforcing the state transition.
Sometimes the simplest solution really is the best one.

What I'd do again

If I were starting this project from scratch, I'd follow roughly the same order:
1. Isolate the scariest dependency.
Get the model/tool loop working by itself.
2. Understand its exact event flow.
Don't just verify that you get a final response. Understand what happens when the model calls a tool, stops streaming, receives the result, and continues.
3. Mock it.
Once you understand the interface, build everything around a fake implementation.
4. Add real-time delivery.
Only then introduce WebSockets, broadcasting, connection management, etc.
5. Expect people to disconnect.
Because they will.
Design your system around that from the beginning.
6. Keep state transitions atomic.
For things like driver handoff, let the database enforce the condition rather than relying on application-level assumptions.

The biggest takeaway

The biggest thing I learned wasn't really about Bedrock, Lambda, or DynamoDB.
It was about isolating uncertainty.
When you're building something complicated under a deadline, there are going to be a lot of things that can break.
Your job isn't to somehow prevent all of them.
It's to make sure you can quickly figure out which thing is actually broken.
Test the model separately.
Mock expensive dependencies.
Understand your streaming events.
Handle dropped connections.
Make critical state transitions atomic.
Then put everything together.
That way, when the demo starts and something inevitably goes wrong, you're debugging a specific problem instead of staring at six AWS services wondering which one decided to ruin your day.
And yes, I would absolutely debug a  410 Gone  at 2 AM again.
Probably.
Maybe.
10/10 hackathon experience. Would build a live AI agent in front of an audience again.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article