Inspiration

Small community associations (food banks, local nonprofits, neighborhood associations) coordinate volunteers by hand — spreadsheets, WhatsApp groups, phone calls. Matching availability, skills, shift capacity, and travel distance manually is slow, error-prone, and impossible to justify when someone disputes their assignment. We wanted an agent that does this coordination work end-to-end, not just chat about it, while keeping a human in control of every message that actually goes out.

What it does

NeighborLink turns a messy CSV of volunteers (availability, skills, location) into an optimized, explainable, ready-to-publish schedule for a community operation. A coordinator can also type a natural-language instruction (e.g. "prioritize volunteers who live close to the site"), which a Strands agent interprets into a bounded, structured decision that measurably changes the plan — without ever making the underlying optimization non-deterministic. Nothing is sent to a real volunteer until the coordinator explicitly approves.

How we built it

  • Frontend: React + TypeScript SPA, installable PWA, deployed on AWS Amplify Hosting.
  • Backend: FastAPI on EC2 (nginx + PM2), PostgreSQL on RDS, multi-tenant by organization.
  • Auth: Amazon Cognito (JWT).
  • Agent: Strands Agents SDK, deployed on Amazon Bedrock AgentCore Runtime. Two agents: one orchestrates three tools (optimize_schedule, validate_plan, explain_plan); a second, lightweight structured-output-only agent interprets the coordinator's free-text instruction into a bounded decision (a proximity weight multiplier, or a clarification request).
  • Optimizer: a deterministic CP-SAT solver (Google OR-Tools) — hard constraints (availability, skill, capacity, no double-booking) plus weighted soft criteria (preference, proximity, fairness).
  • Notifications: real email via Gmail SMTP, WhatsApp via Infobip (simulated in this demo) — both gated behind explicit human approval.

Challenges we ran into

Keeping the system judged on reliability (deterministic scheduling) while still letting a natural-language instruction meaningfully steer the outcome. We solved this by scoping the LLM to a single narrow, schema-bounded decision (InstructionDecision), with a deterministic keyword-based fallback if the model call fails — so a live demo never breaks because of a model hiccup, and identical input always produces an identical plan.

What we learned

How to keep an LLM's non-determinism contained to a single, auditable decision point instead of letting it leak into the core business logic — and why that separation matters for both reliability and explainability to end users.

What's next

Multi-operation scheduling, real WhatsApp delivery, and richer fairness criteria across recurring operations.

Built With

Share this project:

Updates

Submission history