Tebaki — About the Project

Inspiration

Tebaki started with a frustration I have seen repeatedly in my own city, Addis Ababa: people notice problems in their neighborhoods, try to report them and don't have a suitable way to do so and when they do, they hear nothing back.

The problem is not that residents do not care. It is that reporting channels are often fragmented, follow-up is difficult, and small civic teams do not have enough people to keep every case moving. A report can disappear into an inbox, get duplicated by several residents, or remain unresolved without anyone knowing who should chase it next.

I wanted to build something that treated a resident's report as the beginning of a process, not the end of one. The name Tebaki means “guardian” in Amharic. The idea is simple: a good civic system should help watch over a community's concerns and keep them moving toward a response.

What it does

Tebaki helps residents and small civic teams turn local reports into accountable cases.

A resident can submit an issue with a description, category, and location. The Guardian agent triages the report immediately and records its confidence and reasoning. When several residents report the same problem, the system can identify nearby reports, group them into one case, and avoid sending duplicate complaints.

The agent workflow then prepares a factual complaint using the available city regulations and contact information. Personal information is redacted before filing. A human operator reviews the evidence, edits the draft if needed, and explicitly approves or drops the action.

After approval, Tebaki files the case through the configured city channel, records the result, tracks the response deadline, and follows up when the SLA is missed. The system keeps a timeline of what happened so residents and operators can see the difference between a submitted report, a drafted case, a filed complaint, and an actual outcome.

The public impact view shows anonymized neighborhood-level information: what issues are being reported, how many residents corroborated them, which cases remain open, and which cases need attention.

How I built it

The core is a multi-agent workflow built with Strands Agents. Each agent has a focused responsibility:

  • The Guardian handles triage, severity, confidence, and reasoning.
  • The Clusterer groups nearby reports using geographic clustering.
  • The Drafter prepares a cited, privacy-redacted complaint.
  • The Coordinator recommends in-app actions for residents and stewards.
  • The Filer connects an approved case to a city channel.
  • The Chaser monitors deadlines and records escalation activity.

The workflow is exposed through a FastAPI service. The React and TypeScript web app provides the resident intake form, map, case dossier, evidence drawer, review queue, public impact view, proof screen, and run replay.

For production, the engine runs in AWS AgentCore with Amazon Bedrock, DynamoDB, and Secrets Manager. CloudFront and S3 serve the web application, while API Gateway and a signing Lambda keep AWS authentication away from the browser. DynamoDB stores reports, decisions, complaints, SLA state, and agent run history so an approval can survive a restart.

The city integration is pack-based. Each city pack defines its boundaries, categories, regulations, contacts, SLAs, and delivery channel. The current Addis Ababa setup includes a verified Bole pilot polygon and an honestly labelled city-level fallback elsewhere. Gmail SMTP is available for human-approved delivery, while the sandbox channel provides a deterministic environment for testing and demonstrations.

Challenges I ran into

The hardest part was making the agent workflow reliable enough to be useful, rather than simply impressive in a screenshot.

Early Bedrock runs exposed malformed tool-use sequences from the model. I had to tighten the prompts, validate tool inputs, improve the resume path, and make failures visible instead of allowing a broken stream to take down the API request.

I also had to work around compatibility issues in the Python 3.14 test environment. Some Strands synchronous bridges and FastAPI TestClient paths could hang, so those runtime-loop tests were isolated while the compatible tests continued to run. This forced me to distinguish between a real application failure and a test-harness problem.

Deployment introduced another set of practical problems. Docker permissions, ARM64 image builds, ECR pushes, AgentCore runtime updates, IAM permissions, and browser-safe request signing all had to work together. A service that worked locally was not automatically a service that a judge could reach through a public URL.

Addis Ababa also required honesty. Complete sub-city boundary data and verified municipal filing routes were not available everywhere, so I chose to show a narrow verified Bole pilot and label the rest as city-level fallback rather than claim coverage I could not prove.

There were plenty of product bugs along the way too: empty review pages, reports not appearing immediately after submission, the map requiring an API key, filing failures, and pages losing their state after an error. Fixing these was a reminder that a working agent is only half of a product. The other half is making its work understandable to a person using it.

Accomplishments that I'm proud of

I am proud that Tebaki does more than classify a report. It connects multiple resident voices to a single accountable case and continues working after the initial filing.

I built a real human approval boundary into the workflow. The system can triage, cluster, draft, and prepare an action, but it cannot send an external complaint without an explicit operator decision.

I made the agent's work visible through lifecycle timelines, evidence views, run replay, citations, confidence, privacy flags, and delivery labels. This makes it possible to ask not only “What did the model say?” but “What did the system do, why did it do it, and what happened next?”

I also have a deployed AgentCore runtime, a browser-safe API path, durable DynamoDB state, an Addis-specific city pack, real SMTP delivery support, and a deterministic sandbox flow for repeatable SLA escalation demonstrations.

What I learned

I learned that agentic software needs clear boundaries. Giving an agent a role is not enough; the inputs, tools, permissions, outputs, and handoff points all need to be explicit.

I learned that persistence and observability are product features. A paused decision that survives a restart, a timeline that explains each step, and a visible failure state build more trust than a clever one-off response.

I learned that geographic and operational truth matters. A smaller verified pilot is more credible than broad coverage with invented contacts or unclear delivery behavior.

I also learned that public-interest software has to respect the people represented in its data. Privacy redaction, untrusted input handling, human approval, and clear real-versus-simulated labels are not optional polish. They are part of the product's responsibility.

What's next for Tebaki

The next step is to move from a strong pilot into a repeatable civic workflow.

I want to expand verified Addis sub-city coverage carefully, confirm additional city contacts, and add more delivery adapters only when their behavior can be documented and tested. I also want to make the public impact view useful to community groups, not just judges and operators. I also aim to expand this app beyond Addis and into cities with more robust reporting infrastructure, this would benefit them quite a lot.

Longer term, Tebaki could support volunteer stewards, neighborhood organizations, schools, libraries, and small nonprofits that need help coordinating local action but cannot afford a full-time operations team.

The guiding principle will remain the same: agents should reduce the follow-up burden on people who are already carrying their communities, while keeping important decisions understandable and under human control.

Built With

Share this project:

Updates

Submission history