SchedulerRX · Constraint Debugger Agent
A neuro-symbolic agent that turns cryptic CP-SAT INFEASIBLE errors into plain-English diagnoses and only proposes fixes it has re-verified by re-solving the real production solver.
README
SchedulerRX · Constraint Debugger Agent
A neuro-symbolic agent that turns a cryptic CP-SAT INFEASIBLE into a plain-English diagnosis — and only ever proposes fixes it has re-verified by re-solving the real production solver.
Built for the Google for Startups AI Agents Challenge · runs on Google Cloud Vertex AI (Gemini) + Cloud Run.
▶ Live: See the agent reason — /dev-ui · Before/after demo — /demo
TL;DR — it diagnosed and fixed a real production failure
A live Emergency-Medicine residency program's schedule block (~4 weeks, 14 residents) came back from the shipped product as a flat INFEASIBLE — no schedule, no actionable reason. We pointed this agent at the real data (read-only). In ~25 seconds / 47 solver re-solves it:
- isolated the cause to 2 of 14 residents (proving every other resident's time-off was irrelevant),
- produced two minimal, solver-verified fixes — decline 2 time-off requests, or relax 2 shift targets,
- and ruled out a plausible red herring (a data bug that, fixed on its own, doesn't restore feasibility).
The fix was applied; the block solved. → Full case study.
The problem
Constraint solvers are how serious scheduling gets done — residency rotations, nurse rosters, airline crews. When a schedule is over-constrained the solver returns one word, INFEASIBLE, on top of output like:
INFEASIBLE: 'linear: never in domain' — constraint #3787:
vars: [1846, 1847, 1855, 1858, 1859] domain: [6, 7]
No human can act on that. In production it's an engineer's job to decipher — and the engineer still has to figure out what to relax and whether relaxing it actually helps. That recurring, expensive pain is what this agent removes.
What it does
Given an infeasible schedule the agent:
- Diagnoses it — running the real CP-SAT model and explaining, in coordinator English, why it can't be solved.
- Ranks relaxations — a closed set the solver itself authored, ordered least- to most-disruptive.
- Verifies before recommending — every fix is re-solved; a plausible-but-insufficient one is caught and never presented.
- Shows the resulting schedule once a verified fix is applied.
Architecture — neuro-symbolic, solver is ground truth
flowchart LR
U([Coordinator / judge]) --> A
subgraph A[ADK LlmAgent · Gemini on Vertex AI]
direction TB
P["diagnose → explain → rank → VERIFY → present"]
end
A -- "MCP tools (stdio)" --> M
subgraph M[MCP server · agent/mcp_server.py]
T1["diagnose_schedule"]
T2["verify_relaxation"]
end
M --> R[("realsolver — production OR-Tools CP-SAT")]
R -- "report + CLOSED candidate set" --> A
R -. "re-solve EVERY candidate" .-> R
style A fill:#2a2140,stroke:#7c5cff,color:#fff
style M fill:#11314a,stroke:#5b9dff,color:#fff
style R fill:#0f2b16,stroke:#3fb950,color:#fff
The deterministic solver provides ground-truth feasibility; Gemini provides the human-facing translation and ranking. The symbolic layer structurally bounds the LLM — it can only rank candidate IDs the solver authored, and every recommendation is re-solved before a human sees it.
Two kinds of infeasibility — and why the second one matters
- Clean gap — a specific shift no eligible resident can fill. A proto-scan of the model localizes it directly (
agent/diagnostic.py). - Emergent shortfall — no single empty cell; the conflict only exists as the interaction of coverage × availability × per-resident shift targets across the whole block. A static check finds nothing. A relaxation / IIS search (
agent/iis.py) relaxes candidate constraint groups, re-solves, and deletion-filters to the minimal binding set — then proves minimality (remove any element → infeasible again). This is the real production case the toy "find the empty slot" approach can't touch.
Why it's safe (the neuro-symbolic guardrail)
- Bounded action space — candidates are generated by the solver with stable IDs; the LLM can only rank from that closed set and acts by ID, never by parsing prose. A hallucinated ID is dropped.
- Every recommendation is re-solved before it is shown — only feasibility-verified relaxations are presented; a confident-but-wrong suggestion fails the re-solve and is never surfaced.
- It abstains / escalates when it can't ground — a clean gap is localized directly; an emergent one is handed to the IIS search rather than guessed at.
Why it's innovative
- Solver as ground truth, every fix verified by re-solving — not an LLM asserting a fix is good; the optimizer proves it. No hallucinated relaxation can be applied or surfaced.
- IIS search for emergent infeasibilities — finds and proves the minimal fix for failures that have no single localizable cause.
- Generalizes — the same pattern is a template for explainable optimization in any heavily-regulated scheduling domain (residency, nursing, anesthesia, aviation crew): compliance-as-code.
Quickstart
git clone <this repo> && cd schedulerrx-constraint-agent
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python main.py --list # known scenarios
python main.py --diagnose em_block_capacity # emergent case → IIS finds a verified minimal fix
python main.py --diagnose em_block_gap # clean single-gap case
uvicorn server:app --port 8080 # then open /dev-ui (agent) and /demo (calendar)
pytest -q # 11 hermetic solver tests (need the snapshot below; skipped without it)
On the solver snapshot. The production OR-Tools CP-SAT engine is SchedulerRX's proprietary IP; the deployed build vendors a pinned snapshot under
vendor/(not included in this open repo). The open contribution is the agent layer — the neuro-symbolic architecture, the MCP tools, and the relaxation/IIS search. Because the 11 solver tests import that snapshot,pytest -qskips them with an explanation when it's absent (they pass in CI / the maintainer's environment). The easiest way to see it end-to-end is the live links above.
Deploy (Cloud Run · Vertex AI)
./deploy_adk.sh # Gemini via Vertex AI (service-account ADC, no API key)
MIN_INSTANCES=1 ./deploy_adk.sh # pin one warm instance for demo/judging week
Auth is the Cloud Run runtime service account (Vertex aiplatform.user) — no API key, no free-tier cap.
Tech stack
| Solver | OR-Tools CP-SAT 9.15 (proto API for ground-truth diagnosis) — production engine, vendored |
| Agent | Google ADK 1.15 LlmAgent + the prebuilt dev-ui |
| LLM | Gemini 3.5 Flash on Vertex AI (global endpoint; GEMINI_MODEL overridable), auto-retry on transient errors |
| Tools | MCP via fastmcp over stdio (diagnose_schedule, verify_relaxation, list_known_scenarios) |
| Web | FastAPI + uvicorn (/dev-ui ADK UI · /demo before/after calendar) |
| Deploy | Cloud Run (Vertex AI, scale-to-zero or warm-pinned) |
Business case
This is the customer-facing infeasibility-explanation layer for SchedulerRX, an EM-residency scheduling product with an active pilot at an EM residency program and more EM-program demos in the pipeline.
- Healthcare scheduling is heavily ACGME-regulated; duty-hour violations carry accreditation and patient-safety consequences. "Why won't my schedule solve, and what's the safe thing to change?" is a constant question.
- Today, deciphering a solver failure is engineer-time. This turns it into a self-serve answer a coordinator can act on — engineer-hours → seconds, as the case study shows on real data.
- One bounded Gemini call per diagnosis; the heavy lifting is free, deterministic CP-SAT; scale-to-zero. Cost stays flat-to-trivial across programs and blocks.
Repository layout
agent/
realsolver.py builds + solves the real CP-SAT model; diagnose (proto-scan → IIS fallback) + verify
iis.py relaxation / IIS search for emergent infeasibilities (minimal binding set, proven)
diagnostic.py proto-scan ground truth (forced-false vars, unsatisfiable coverage linears)
model.py CP-SAT model primitives shared by the diagnostic proto-scan
mcp_server.py MCP tools (fastmcp, stdio) the agent drives
adk_agent.py the ADK LlmAgent (Gemini on Vertex) + McpToolset
adk_app/ ADK app package (root_agent) for `adk web` / get_fast_api_app
server.py Cloud Run entry — ADK dev-ui (/dev-ui) + the /demo calendar
main.py local CLI (diagnose / verify against the real solver)
vendor/ pinned proprietary solver snapshot (NOT in the open repo)
tests/ hermetic solver tests (no DB, no network, no LLM)
docs/CASE_STUDY.md the real production-failure write-up
deploy_adk.sh · Dockerfile · requirements.txt
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。