▶ Watch the agent run a full DMTA campaign
An autonomous drug discovery agent that closes the Design → Make → Test → Analyze loop using agentic AI and physical lab instrument integration.
Claude reasons over experimental data to propose compounds, a Hamilton STAR liquid handler dispenses them, an assay station measures potency and ADMET properties, and the agent analyzes results to decide next steps — pausing between iterations for scientist review.
Built on FastAPI, Pydantic, and the Anthropic SDK. Docker and Kubernetes ready.
┌─────────────────────────────────────────────┐
│ FastAPI Service │
│ POST /campaigns GET /campaigns/{id}/run │
└──────────────────────┬──────────────────────┘
│ SSE stream
┌────────▼────────┐
│ DMTAAgent │ ← orchestrates the loop
└──┬──────────┬───┘
│ │
┌────────▼──┐ ┌────▼──────────┐
│ Claude │ │ Instruments │
│ (LLM) │ │ Layer │
└───────────┘ └──┬────────┬───┘
Design + │ │
Analyze Hamilton AssayStation
STAR (TR-FRET +
(Make) ADMET)
(Test)
| Decision | Rationale |
|---|---|
| SSE streaming | Client sees each phase event in real-time; no polling needed |
| Instrument abstraction layer | Swap mock → real SDK without touching agent logic |
| Pydantic throughout | Every LLM response and instrument payload is validated |
| Parallel dispensing | asyncio.gather() fires all Hamilton jobs concurrently |
| Stateless agent | Campaign state lives in Redis (or in-memory for dev); agent is pure function |
- Python 3.12+
- Anthropic API key — get one at console.anthropic.com or ask me for a live demo.
# 1. Install
pip install -r requirements.txt
# 2. Set API key
export ANTHROPIC_API_KEY=sk-ant-...
# 3. Run
uvicorn main:app --reload --port 8000# Start a campaign
curl -X POST http://localhost:8000/campaigns \
-H "Content-Type: application/json" \
-d '{"goal": "JAK2-selective inhibitor: IC50 < 1 nM, >100x selectivity vs JAK1"}'
# Stream agent events (replace {id} with campaign_id from above)
curl -N http://localhost:8000/campaigns/{id}/rundocker build -t dmta-agent .
docker run -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY -p 8000:8000 dmta-agentIn agent/instruments.py, replace the _dispense_real and _real_panel methods:
# Hamilton VENUS SDK
async def _dispense_real(self, job: DispenseJob) -> DispenseResult:
import venus
result = await venus.execute_method("Dispense", job.model_dump())
return DispenseResult(**result)
# LIMS (e.g. Benchling, Dotmatics)
async def _real_panel(self, compounds) -> list[AssayResult]:
async with httpx.AsyncClient() as c:
r = await c.post(f"{self.lims_url}/assay/jak2", json=[...])
return [AssayResult(**x) for x in r.json()]The agent loop in agent/dmta_agent.py does not change.
- Replace in-memory
campaignsdict with Redis - Add JWT auth middleware
- Wire real instrument SDKs in
instruments.py - Set
mock_instruments=Falseinmain.py - Deploy as Kubernetes Deployment + Service
- Add Prometheus metrics on
/metrics