|
1 | | -# MomConnect Intent Classifier |
| 1 | +## MomConnect Intent Classifier |
2 | 2 |
|
3 | | -The **MomConnect Intent Classifier** labels inbound messages from mothers into high-level intents. |
4 | | -It is used by the Department of Health to triage and route messages appropriately (e.g. *service feedback*, *sensitive exits like baby loss or opt-outs*, etc.). |
| 3 | +A machine learning service to classify inbound user messages for the *MomConnect*. This service identifies user intents such as service feedback and sensitive topics like baby loss, enabling the platform to provide appropriate and timely responses. |
5 | 4 |
|
6 | | -⚠️ **Note:** This service is **internal only** and not exposed outside the cluster. |
7 | 5 |
|
8 | | ---- |
| 6 | +### How It Works: A Hybrid Approach to Intent Classification |
| 7 | +This classifier uses a modern, two-stage process to understand user messages with both accuracy and precision. This design ensures that broad categories are handled robustly by a data-driven model, while critical, specific cases are managed with transparent and reliable rules. |
9 | 8 |
|
10 | | -## Features |
| 9 | +_A simplified flowchart of the classification process._ |
11 | 10 |
|
12 | | -- **Sentence embeddings** (via [SentenceTransformers](https://www.sbert.net/)) |
13 | | -- **Linear classifier head** trained on embeddings |
14 | | -- **Threshold-based decisioning** per intent |
15 | | -- **Policy-driven biasing**: |
16 | | - - *Sensitive exits (baby loss, opt-out)* → prioritise **recall** |
17 | | - - *Service feedback* → prioritise **precision** |
18 | | - - *Noise/Spam* → strict filtering |
19 | | -- **Automatic enrichment**: |
20 | | - - Service feedback → sentiment polarity (positive/negative/neutral) |
21 | | - - Sensitive exit → bereavement vs generic opt-out |
22 | | -- **Review band**: low-confidence predictions are flagged as `NEEDS_REVIEW` |
| 11 | +1. Broad Understanding (Machine Learning): First, the user's message is converted into a sophisticated numerical representation (an "embedding") using a SentenceTransformer model. A trained machine learning model then reads this embedding to classify the message into one of four broad parent categories: `FEEDBACK`, `SENSITIVE_EXIT`, `NOISE_SPAM`, or `OTHER`. |
23 | 12 |
|
24 | | ---- |
| 13 | +2. Specific Details (*Enrichment Rules*): Once the main category is known, the system applies a set of precise rules to determine the specific sub-intent. |
25 | 14 |
|
26 | | -## Development |
| 15 | + - If the message is `FEEDBACK`, a sentiment analysis model determines if it's a `COMPLIMENT` or `COMPLAINT`. |
27 | 16 |
|
28 | | -This project uses [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management. |
| 17 | + - If it's `SENSITIVE_EXIT`, carefully curated patterns detect if it's about `BABY_LOSS` or an `OPTOUT` request. |
29 | 18 |
|
30 | | -1. Ensure you’re running **Python 3.11+**: |
31 | | - ```bash |
32 | | - python --version |
33 | | -Install dependencies: |
34 | 19 |
|
35 | | -bash |
36 | | -Copy |
37 | | -Edit |
38 | | -poetry install |
39 | | -Running the Flask worker |
40 | | -Set the environment variables and start the app: |
41 | 20 |
|
42 | | -bash |
43 | | -Copy |
44 | | -Edit |
45 | | -export NLU_USERNAME=your-username |
46 | | -export NLU_PASSWORD=your-password |
| 21 | +### Model Development & Training |
| 22 | +This section is for anyone involved in managing the data, training the model, or evaluating its performance. |
47 | 23 |
|
48 | | -poetry run flask --app src.application run |
49 | | -Code Quality |
50 | | -Autoformat + Linting: |
| 24 | +**Data & Schema** |
51 | 25 |
|
52 | | -bash |
53 | | -Copy |
54 | | -Edit |
55 | | -poetry run ruff format . |
56 | | -poetry run ruff check . |
57 | | -poetry run mypy --install-types |
58 | | -Tests: |
| 26 | +The ground truth for this model lives in `src/data/nlu.yaml`. This file contains all the training examples, which are then processed by `src/data/build_datasets.py` to create the final training files. |
59 | 27 |
|
60 | | -bash |
61 | | -Copy |
62 | | -Edit |
63 | | -poetry run pytest |
64 | | -Regenerating Embeddings |
65 | | -Delete the existing embeddings JSON in src/data/ |
| 28 | + - To regenerate the training data from the source YAML files, run |
| 29 | + ```bash |
| 30 | + make datasets # Which runs: poetry run python src/data/build_datasets.py --emit-jsonl |
| 31 | + ``` |
| 32 | + This will create clean .jsonl files (e.g., samples.train.jsonl) in the src/mapped_data/ directory. |
| 33 | + |
| 34 | + - To train a new version of the model, run |
| 35 | + ```bash |
| 36 | + make train # Which runs: poetry run python src/train_model.py |
| 37 | + ``` |
| 38 | + This script executes the complete pipeline: it loads the processed data, encodes the text, trains the classifier, and saves all model artifacts to the `src/artifacts/ directory`. |
66 | 39 |
|
67 | | -Update training examples in nlu.yaml |
68 | 40 |
|
69 | | -Run the Flask app: |
| 41 | +### Evaluation & Threshold Tuning |
70 | 42 |
|
71 | | -bash |
72 | | -Copy |
73 | | -Edit |
74 | | -poetry run flask --app src.application run |
75 | | -→ Embeddings will be regenerated. |
| 43 | +Model performance is measured against a hold-out test set (`test.yaml`). Confidence thresholds are defined in `src/artifacts/thresholds.json` and can be tuned by analyzing the model's performance on the validation set to balance the needs of each intent (e.g., prioritizing recall for `SENSITIVE_EXIT`). |
76 | 44 |
|
77 | | -Threshold Tuning |
78 | | -Thresholds live in artifacts/thresholds.json. |
79 | | -They can be tuned with validation data via: |
80 | 45 |
|
81 | | -bash |
82 | | -Copy |
83 | | -Edit |
84 | | -poetry run python src/fit_thresholds.py \ |
85 | | - --model-dir artifacts/ \ |
86 | | - --nlu-path src/data/nlu.yaml \ |
87 | | - --validation-path src/data/validation.yaml |
88 | | -Policy: |
| 46 | +### API, Deployment & Integration |
| 47 | +This section is for engineers responsible for deploying and integrating the service, and for QA who need to test the API. |
89 | 48 |
|
90 | | -Sensitive exits → low threshold (recall focus) |
| 49 | +**Setup and Installation** |
91 | 50 |
|
92 | | -Service feedback → higher threshold (precision focus) |
| 51 | +- Install dependencies: |
| 52 | + ```bash |
| 53 | + make install # Which runs: poetry install |
| 54 | + ``` |
93 | 55 |
|
94 | | -Noise → strict threshold |
| 56 | +- Activate the virtual environment: |
| 57 | + ``` |
| 58 | + poetry shell |
| 59 | + ``` |
95 | 60 |
|
96 | | -Other → balanced |
97 | 61 |
|
98 | | -Review band → learned dynamically from disagreement zone |
| 62 | +#### Running the API Service |
| 63 | +The application is a standard Flask service. Do not use the built-in Flask development server for production. |
99 | 64 |
|
100 | | -Editor Configuration |
101 | | -For VS Code: |
| 65 | +- For production or staging, use a WSGI server like Gunicorn: |
102 | 66 |
|
103 | | -Install the Python and Ruff extensions. |
| 67 | + ``` |
| 68 | + gunicorn --workers 2 --bind 0.0.0.0:5001 src.application:app |
| 69 | + ``` |
104 | 70 |
|
105 | | -Settings: |
| 71 | +- For local development, you can use the Flask dev server: |
106 | 72 |
|
107 | | -"python.linting.mypyEnabled": true |
| 73 | + ``` |
| 74 | + poetry run flask --app src.application run |
| 75 | + ``` |
108 | 76 |
|
109 | | -"python.formatting.provider": "black" |
| 77 | + _You will need to set the `NLU_USERNAME` and `NLU_PASSWORD` environment variables for authentication._ |
110 | 78 |
|
111 | | -"editor.formatOnSave": true |
112 | 79 |
|
113 | | -Release Process |
114 | | -Merge all PRs & complete QA. |
| 80 | +#### API Endpoints |
| 81 | +The service provides two specific, authenticated endpoints. Authentication is handled via HTTP Basic Auth. |
115 | 82 |
|
116 | | -Update release notes in CHANGELOG.md. |
| 83 | +1. **Baby Loss Detection** |
117 | 84 |
|
118 | | -On main branch: |
| 85 | + This endpoint analyzes a message to determine if it relates to baby loss. It is optimized for high recall to ensure sensitive cases are not missed. |
119 | 86 |
|
120 | | -Update version in pyproject.toml |
| 87 | + - Request: GET /nlu/babyloss/ |
| 88 | + - Query Parameters |
| 89 | + |
| 90 | + Parameter Type Required Description |
| 91 | + question string Yes The user's message text to be classified. |
| 92 | + |
121 | 93 |
|
122 | | -Replace UNRELEASED with release version + date in CHANGELOG.md |
| 94 | + - Responses: |
123 | 95 |
|
124 | | -Commit + tag: |
| 96 | + - `200 OK` (Success): The babyloss key will be true if the intent is detected, and false otherwise. |
125 | 97 |
|
126 | | -bash |
127 | | -Copy |
128 | | -Edit |
129 | | -git tag v0.2.1 |
130 | | -git push origin main --tags |
131 | | -Post-release: |
| 98 | + { |
| 99 | + "babyloss": true, |
| 100 | + "model_version": "2025-09-29-v1", |
| 101 | + "parent_label": "SENSITIVE_EXIT", |
| 102 | + "sub_intent": "BABY_LOSS", |
| 103 | + "probability": 0.98, |
| 104 | + "review_status": "CLASSIFIED" |
| 105 | + } |
| 106 | + - `400 Bad Request`: Returned if the question parameter is missing. |
132 | 107 |
|
133 | | -Increment version in pyproject.toml (e.g. 0.2.2.dev0) |
| 108 | +2. **Feedback Detection** |
134 | 109 |
|
135 | | -Add new UNRELEASED header to CHANGELOG.md |
| 110 | + This endpoint analyzes a message to determine if it is a compliment or a complaint. |
136 | 111 |
|
137 | | -Running in Production |
138 | | -A Docker image is published for deployment. |
| 112 | + - Request: GET /nlu/feedback/ |
| 113 | + - Query Parameters |
| 114 | + |
| 115 | + Parameter Type Required Description |
| 116 | + question string Yes The user's message text to be classified. |
| 117 | + |
139 | 118 |
|
140 | | -Required environment variables: |
| 119 | + - Responses: |
141 | 120 |
|
142 | | -Variable Description |
143 | | -NLU_USERNAME Username for API requests |
144 | | -NLU_PASSWORD Password for API requests |
145 | | -SENTRY_DSN Sentry DSN for error reporting |
| 121 | + - `200 OK`: The primary intent key will be COMPLIMENT, COMPLAINT, or None. |
146 | 122 |
|
147 | | -License |
148 | | -MIT |
| 123 | + { |
| 124 | + "intent": "COMPLIMENT", |
| 125 | + "model_version": "2025-09-29-v1", |
| 126 | + "parent_label": "FEEDBACK", |
| 127 | + "probability": 0.99, |
| 128 | + "review_status": "CLASSIFIED" |
| 129 | + } |
149 | 130 |
|
150 | | -yaml |
151 | | -Copy |
152 | | -Edit |
| 131 | + - `401 Unauthorized`: Returned for any endpoint if authentication is incorrect. |
153 | 132 |
|
154 | | ---- |
155 | 133 |
|
156 | | -Do you also want me to add an **API Quickstart** section (sample `curl` + example JSON response) so QA/SxD can test it directly without spinning up notebooks? |
| 134 | +### Testing and Code Quality |
| 135 | +Run these commands to ensure code quality and correctness. |
| 136 | +
|
| 137 | +- Run Unit Tests: |
| 138 | + ``` |
| 139 | + make test # Which runs: poetry run pytest -vv |
| 140 | + ``` |
| 141 | +
|
| 142 | +- Run Static Type Checking: |
| 143 | + ``` |
| 144 | + make typecheck # Which runs: poetry run mypy . |
| 145 | + ``` |
| 146 | +
|
| 147 | +- Run Linter/Formatter: |
| 148 | + ``` |
| 149 | + make lint # Which runs: poetry run ruff check --fix . && poetry run ruff format . |
| 150 | + ``` |
| 151 | +
|
| 152 | +
|
| 153 | +### Production Deployment |
| 154 | +For production, the Dockerfile should be configured to run the application using the Gunicorn command. The number of workers (--workers 4) should be adjusted based on the resources of the environment. The `src/artifacts directory`, which contains the trained model, must be included in the final container image. |
0 commit comments