AI service delivery coordinator

An AI coordinator for service projects. It turns a client request into a plan that a person edits and approves, sets the project up in Asana and HubSpot, then watches delivery and proposes what to do about blockers, missing input and slipping dates.

portfolio projectThis is a portfolio project. I designed and built it myself, to the standard I would hold client work to. No client commissioned it, and the agency and clients in the demo are invented.

What it is
An agent system for service businesses and agencies. A request goes in, an approved plan comes out, and delivery is monitored in Asana and HubSpot.
Type
Portfolio project, built by Filip. Not a client engagement.
Stack
Python 3.12, FastAPI, Reflex, Celery and Redis, PostgreSQL, LangGraph, OpenAI models, Docker Compose.
Status
v1.0 released. The repository is private. There is a walkthrough on YouTube, and a live demo on request.
keel / delivery coordinator / film
A short film about the project.

~6,800tests

Unit and integration tests, from the project’s README.

source: project README

85browser tests

Playwright in Chromium. CI also runs both Docker Compose stacks.

source: project README

The problem

When a client sends a request, someone on your team has to turn it into a plan: what to do, in what order, by whom, by when. They set the project up in the project tool and the client up in the CRM. Then, for weeks, they watch for the comment that says work is blocked, the task that is overdue and the date that has quietly moved, and decide what to do about each one.

That is coordination work. It is slow because it is made of reading, planning and re-planning. It is risky because the mistakes show: a date promised without checking the dependencies, a project created twice, a message sent that nobody reviewed.

A language model can read a request and draft a plan. It should not choose dates, write to your client’s CRM or send anything on your behalf without a person agreeing first. So I set myself a narrower problem than “automate project management”: let the model do the reading and drafting, and build the system so that nothing reaches Asana or HubSpot until a person approves.

What I built

The system follows one loop, and a person sits at each point where something would leave it.

  1. Request. Someone on the team, the operator, enters a client request in the chat.
  2. Plan. The Planning agent drafts milestones, tasks, dependencies, roles, assumptions and missing information. Plain code computes every date.
  3. Edit and approve. A person edits the plan freely. The system warns about problems, such as date order, cycles and tasks past their milestone, but never overrides the edit. Approval records who approved which version.
  4. Set up. Only then is the project created in Asana, with sections per milestone, tasks with dates and dependencies, and the client’s company, contact and deal in HubSpot. Each object is created exactly once.
  5. Watch and propose. Changes in Asana come back as events. Plain code detects overdue tasks, moved dates and deleted tasks, and forecasts which milestones and which target date are at risk. The agents read new comments for blockers and requests for input, then propose corrective or recovery options and follow-up drafts. A person approves each one before anything is written.

It handles four kinds of issue: a blocker, missing input, a schedule change and a date at risk. Four agents share the work, and one of them makes no model call at all.

The four agents
AgentWhat it doesModel call
OrchestratorDecides which step follows which event, and whether the project may take itNone
PlanningReads the request, drafts the plan and lists assumptions and missing informationA draft, plus one repair if needed
MonitoringClassifies comments; suggests corrective options for blockers and recovery options for dates at riskClassify and suggest
CommunicationDrafts follow-ups for the team or the client. Always draftsA draft

How it works

The diagram shows the route one project takes. Grey is the system working, orange is a person deciding and green is a state reached. The approval gate sits in the middle of it, and nothing on the lower half of the drawing happens until it has been passed.

Behind it, the API never calls a vendor or a model inside a request. Anything slow or external is a job, run by workers from a queue stored in the database. The interface talks to the API only, server to server. Each step is stored as four separate records: what the AI reasoned, what it proposed, who approved it and what the side effect did.

request to delivery
How a request moves through the delivery coordinatorAn operator enters a client request. The Planning agent drafts a plan and plain code sets every date. A person edits the plan, and the system warns but never overrides the edit. A person approves one exact version, or declines and nothing is written. Only after approval is the project created in Asana and the client and deal in HubSpot, each exactly once. Delivery is then watched through Asana webhooks and a periodic check; plain code finds slips and forecasts their impact. Agents propose options and follow-up drafts, a person approves or discards each one, and an approved change is written to Asana once. The watching continues.noyeskeeps watchingA client request comes inAn operator enters it in chatPlanning agent drafts a planMilestones, tasks, roles, gapsPlain code sets every dateA person edits the planWarnings appear; the edit isnever overriddenA person approves a versionNames the operator and theexact plan versionAsana and HubSpot set upProject, client and deal,each created exactly onceDelivery is watchedWebhooks plus a periodic checkPlain code finds slips andforecasts their impactAgents propose what to doOptions and follow-up draftsfor blockers, gaps and slipsA person decides on each oneApprove or discardThe approved change is writtento Asana, onceDeclinedNothing is written

The system worksA person decidesState reachedDashed: it repeats for as long as the project runs

See it run

Everything below comes from the demo. It runs the whole system with stand-in versions of Asana and HubSpot and recorded agent answers, so it needs no accounts, keys or spend, and nothing a visitor does can reach a real external system. It is seeded with an invented small agency and five clients at different stages.

core loop, silent
The core loop in 21 seconds, silent: a request becomes a draft plan, a person approves it, and only then does the project reach Asana and HubSpot.
walkthrough, 3 minutes
Play the 3-minute walkthrough. Title card: AI Service Delivery Coordinator, from request to plan, approval, Asana and HubSpot.
Press play to load the video from YouTube’s privacy-enhanced player. Nothing is requested from YouTube until you do. Open it on YouTube instead.
project page / plan v2
Screenshot of the coordinator’s project page showing a draft plan, version 2, edited by a person, for a bakery’s grand-opening campaign. A banner reads 9 warnings to check before approval, and buttons offer Edit plan, Approve plan and Decline project. Below, tasks are listed with their roles, durations, dates and the task each one follows.

A plan a person has edited. Look at the banner: nine warnings, and the Approve button is still there. The system points out problems in the edit and leaves the decision to the person. Approval will name this exact version.

approval inbox
Screenshot of the approval inbox, titled Awaiting your decision. It lists one plan waiting, with 9 warnings, and one draft follow-up comment waiting, for a surf school’s bookings campaign.

The approval inbox. A plan and a drafted follow-up comment are waiting. Neither has been written to Asana or HubSpot, and neither will be until a person decides.

delivery panel / blocker
Screenshot of a project’s delivery panel. A blocker reported in a task comment is shown with its impact if it stays blocked 5 more working days, and suggested actions: ask the client for access to a shared folder, or remove a dependency. Each suggestion has a button to draft it or use it.

A blocker on a delivered project. The impact line, “if it stays blocked 5 more working days”, is worked out by plain code from the task dependencies. The agent only suggests what to do about it, and each suggestion starts as a draft.

Six decisions, and the evidence for each

Each decision answers a way these systems usually go wrong. The note beside it says what the project does about it. The repository is private, so I describe the evidence rather than link to it.

Nothing reaches Asana or HubSpot before a person approves

This is the rule the whole system is built around. An approval names the operator and the exact plan version, and if the plan is edited afterwards it goes back to planning. Because reasoning, proposal, approval and side effect are stored separately, you can answer “who agreed to this?” afterwards.

The model drafts; plain code does the dates

A language model is good at reading a request and drafting a plan, and unreliable at arithmetic and constraints. So the model only interprets and drafts. Dates, dependencies, scheduling back from a target date, the impact of a slip and the forecast are ordinary Python, and whatever the model returns must be validated before anything uses it.

Every write to a client system happens exactly once

An automation that retries a failed call can create the same project twice. Here each external write has a deterministic idempotency key, and its attempt is saved before the call is made. If the outcome is unknown, because of a timeout or a worker killed mid-call, the system looks at the vendor before trying again. A failure is shown as a failure, with what to check. Nothing is reported as synced unless every action succeeded.

Whatever a client writes is treated as untrusted

Client requests, task comments and CRM records can contain instructions aimed at the model. They reach it only as quoted data in the user message, never in the system prompt, and the agents have no tools to call. Jobs between services carry identifiers only, so no untrusted text crosses the queue.

Duplicate, late and out-of-order webhooks change nothing they should not

Asana reports changes through webhooks, which can arrive twice, late or in the wrong order. Events are verified, stored once and processed as jobs, each compared with the stored task state. A periodic check against Asana’s own listing is the source of truth, and a dead webhook is replaced.

Spend is capped in code

Every model call reserves tokens against a per-run cap and a daily cap before the call is made. The limit is enforced where the call happens, not in a prompt asking the model to be economical.

What I tested, and how

The tests are layered the way the system is. The vendors are faked at the HTTP transport layer, so the real Asana and HubSpot adapters run in the tests. Agent behaviour is covered by scenario evals against the live model, graded, under a hard token cap.

The test layers
LayerWhat it coversSize
Unit and integrationDomain rules, use cases against PostgreSQL, adapters against fake HTTP transports, the API, UI state, migrations, workers against RedisAbout 6,800 tests
End to endPlaywright in Chromium, against the running API and UI85 browser tests
ComposeSmoke tests against the built Docker images, for the live-configured stack and the demo stackBoth stacks
Agent evalsAgent scenarios against the live model, graded, under a hard token capRun separately

CI runs lint, format, strict mypy, the tests (a second time at a far-future date, so seeded dates never expire), the browser tests and both Compose stacks.

The layering is enforced by a test, not by convention: the domain and application code import none of SQLAlchemy, FastAPI, Celery, LangGraph, OpenAI or the settings.

The repository is private, so these figures come from the project’s README and you cannot count them on this page.

What it does not do

  • There is no hosted demo. It runs locally with Docker, and a live demo is available on request.
  • The demo is staged. It uses stand-in vendors and recorded agent answers, and its chat accepts the three suggested requests only. The real Asana and HubSpot adapters are tested against faked HTTP transports.
  • Some revisions are not fully synced. Project-level changes, a revised title or target date, stay in the plan and are not yet written to Asana or HubSpot. Tasks and sections are.
  • Recovery has three kinds: remove a dependency, add a task, or wait. Shortening a task or moving a milestone on purpose is not offered yet.
  • A vendor object deleted by hand is not recreated. The sync stops and says what to check.
  • Agent edge cases remain. On a strict chain of tasks, corrective options occasionally offer a task that cannot help.
  • It has not been deployed publicly. Not done: a public host, TLS, a reverse proxy that passes websockets, backups and a secret store. Also not reached by a local demo: retention of stored webhook events, reconcile paging for very large projects, and a few job-queue inefficiencies while workers are down.

On the roadmap and not built: notifications, client email that is drafted, approved and then sent, scope-change detection, conversational commands and a per-project audit history.

What this means for your business

If your team spends its week turning requests into plans and chasing status, this is the shape of what I would build for you: an agent that reads and drafts, plain code that does the arithmetic, and a person who signs off before anything leaves the building. I built it against Asana and HubSpot. If you use other tools, the integration layer is where the work goes, and I would tell you so in my first reply.

AI agents and workflow automation
Agent workflow builds start from €7,500. I would scope the slice of this that matches your workflow, with the approval points you choose.
LLM hardening audit
The spend caps, untrusted-input handling and evals above are what I look for in an existing LLM feature. Audits start from €1,500.
RAG development
For answers drawn from your own documents, see the EU AI Act assistant, my other portfolio project.

Tell me what is eating your team’s week.

Send a short brief: what the work is, who does it now, and what it costs you. I reply within two working days with a rough scope, a price range and whether AI is the right tool for it at all.

Send a brief

Or write to keel.agents@gmail.com.