Adding New Agents
This guide explains how to add new client or therapist agents to PatientHub.
Quickstart: Auto-generate Boilerplate
The examples.create script scaffolds the agent file, prompt template, and __init__.py registration in one command:
# Create a new client agent
patienthub create agent_type=client agent_name=myClient
# Create a new therapist agent
patienthub create agent_type=therapist agent_name=myTherapist
This generates:
patienthub/clients/myClient.py(orpatienthub/therapists/myTherapist.py)data/prompts/client/myClient.yaml(ordata/prompts/therapist/myTherapist.yaml)- Adds the import, registry entry, and config registry entry to the corresponding
__init__.py
The sections below explain each part in detail.
Step 1: Implement the Agent
Client Agent
File ordering convention: imports → config dataclass → response schemas (if any) → agent class.
# patienthub/clients/myClient.py
from omegaconf import DictConfig
from dataclasses import dataclass
from .base import BaseClient
from patienthub.configs import APIModelConfig
@dataclass
class MyClientConfig(APIModelConfig):
"""Configuration for MyClient agent."""
agent_name: str = "myClient"
prompt_path: str = "data/prompts/client/myClient.yaml"
data_path: str = "data/characters/MyClient.json"
data_idx: int = 0
class MyClient(BaseClient):
def __init__(self, configs: DictConfig):
super().__init__(configs) # handles data, prompts, chat_model, build_sys_prompt
def build_sys_prompt(self):
self.messages = [
{"role": "system", "content": self.prompts["sys_prompt"].render(data=self.data)}
]
def generate_response(self, msg: str):
self.messages.append({"role": "user", "content": msg})
res = self.chat_model.generate(self.messages)
self.messages.append({"role": "assistant", "content": res.content})
return res
def reset(self):
self.build_sys_prompt()
self.therapist = None
Therapist Agent
The pattern is identical — inherit from BaseTherapist instead:
# patienthub/therapists/myTherapist.py
from omegaconf import DictConfig
from dataclasses import dataclass
from .base import BaseTherapist
from patienthub.configs import APIModelConfig
@dataclass
class MyTherapistConfig(APIModelConfig):
"""Configuration for MyTherapist agent."""
agent_name: str = "myTherapist"
prompt_path: str = "data/prompts/therapist/myTherapist.yaml"
class MyTherapist(BaseTherapist):
def __init__(self, configs: DictConfig):
super().__init__(configs) # handles prompts, chat_model, build_sys_prompt
def build_sys_prompt(self):
self.messages = [
{"role": "system", "content": self.prompts["sys_prompt"].render()}
]
def generate_response(self, msg: str):
self.messages.append({"role": "user", "content": msg})
res = self.chat_model.generate(self.messages)
self.messages.append({"role": "assistant", "content": res.content})
return res
def reset(self):
self.build_sys_prompt()
self.client = None
Step 2: Register the Agent
If you used examples.create, this is done automatically. Otherwise, edit patienthub/clients/__init__.py (or therapists/__init__.py):
from .myClient import MyClient, MyClientConfig
CLIENT_REGISTRY = {
# ... existing agents ...
"myClient": MyClient,
}
CLIENT_CONFIG_REGISTRY = {
# ... existing configs ...
"myClient": MyClientConfig,
}
Step 3: Create the Prompt Template
# data/prompts/client/myClient.yaml
en:
sys_prompt: |
You are simulating a patient named {{ data.name }}.
Background: {{ data.background }}
Respond as this patient would in a therapy session.
zh:
sys_prompt: |
<在这里输入提示文本>
Step 4: Create Character Data
[
{
"name": "Taylor",
"age": 29,
"background": "Recently moved to a new city for work and is experiencing social anxiety.",
"presenting_problem": "Difficulty meeting new people and persistent worry about social judgment."
}
]
Step 4b: Register a Character Schema
Every client with a character data file must have a Pydantic schema registered in patienthub/schemas/. This validates data at load time and ensures all fields required by your prompts are present.
# patienthub/schemas/myClient.py
from pydantic import Field
from patienthub.schemas.base import BaseCharacter
class MyClientCharacter(BaseCharacter):
name: str = Field(...)
age: int = Field(...)
background: str = Field(...)
presenting_problem: str = Field(...)
Then register it in patienthub/schemas/__init__.py:
from .myClient import MyClientCharacter
CLIENT_SCHEMA_REGISTRY = {
# ... existing schemas ...
"myClient": MyClientCharacter,
}
All fields referenced in your prompt templates (e.g. {{ data.background }}) must be defined in the schema without a default value — use Field(...). This ensures a missing field fails at load time rather than silently producing a broken prompt.
Step 5: Add Tests
Two test suites run automatically for any registered client:
Smoke tests — verify instantiation, prompts, and messages:
uv run python -m pytest patienthub/tests/clients.py -v
Schema validation tests — validate every entry in your character JSON against its registered schema:
uv run python -m pytest patienthub/tests/schemas.py -v
Schema tests also run automatically in CI whenever you push changes to patienthub/clients/, patienthub/schemas/, or data/characters/.
For agent-specific tests:
import pytest
from unittest.mock import patch, MagicMock
from omegaconf import OmegaConf
from patienthub.clients import CLIENT_CONFIG_REGISTRY, CLIENT_REGISTRY
@pytest.fixture
def client():
mock_model = MagicMock()
mock_model.generate.return_value = MagicMock(content="mock response")
with patch("patienthub.utils.models.get_chat_model", return_value=mock_model):
cfg = OmegaConf.structured(CLIENT_CONFIG_REGISTRY["myClient"])
return CLIENT_REGISTRY["myClient"](configs=cfg)
def test_instantiation(client):
assert client is not None
def test_name_is_set(client):
assert client.name == "Taylor"
def test_generate_response(client):
response = client.generate_response("How have you been feeling?")
assert response.content == "mock response"
Step 6: Add Documentation
Create a doc page at docs/docs/components/clients/myagent.md following the style of existing client docs:
# MyClient
> Paper Title (if applicable)
**Venue**: Conference Year (if applicable)
## Overview
Brief description of the method.
## Key Features
- Feature 1
- Feature 2
## Usage
### CLI
\`\`\`bash
patienthub simulate client=myClient
\`\`\`
### Python
\`\`\`python
from patienthub.clients import get_client
client = get_client(agent_name="myClient", lang="en")
response = client.generate_response("How have you been feeling?")
print(response.content)
\`\`\`
## Configuration
| Option | Type | Default | Description |
| ------------ | ------ | ----------------------------------- | ------------------ |
| `prompt_path`| string | `data/prompts/client/myClient.yaml` | Path to prompt file |
| `data_path` | string | `data/characters/MyClient.json` | Path to character file |
| `data_idx` | int | `0` | Character index |
Checklist
Before submitting your new agent:
- Agent class in
patienthub/clients/(ortherapists/) - Config dataclass with
agent_name,prompt_path, and any extra fields - Registered in
CLIENT_REGISTRYandCLIENT_CONFIG_REGISTRYin__init__.py - Prompt YAML created at
data/prompts/client/<agent_name>.yaml - Character data file created (if applicable)
- Pydantic schema created in
patienthub/schemas/with all prompt fields required - Schema registered in
CLIENT_SCHEMA_REGISTRYinpatienthub/schemas/__init__.py - Smoke tests pass:
uv run python -m pytest patienthub/tests/clients.py -v - Schema tests pass:
uv run python -m pytest patienthub/tests/schemas.py -v - Documentation page added
- Works end-to-end:
patienthub simulate client=myClient