mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
docs: ADR-0041 — landlord-heating classification targets a complete taxonomy
grill-with-docs output for PRD #1361 Class A. The band-G baselines are not a fabric/rebaseline bug (walls are correct — landlord asserted as-built); they are a target-vocabulary gap in the landlord-description heating classifier. The LLM maps unbounded free-text onto a closed internal taxonomy, but that taxonomy had 9 archetypes vs ~13 RdSAP families, so it overflowed into "Gas CPSU"/storage (the "convector" collision sent direct-acting room heaters to off-peak storage 403 → SAP ~10). - ADR-0041: fix the taxonomy not the mapper; unmapped input -> None (keep lodged EPC, surface to user), never a forced archetype; heat pumps modellable via a default Table 4a code (no PCDB id); community heating via codes 301/302/304 with a documented DLF-1.50 / boiler-source default. - CONTEXT.md: sharpen Landlord-Description Classification and the four distinct vocabularies (landlord / recognised-internal / lodged / SAP code). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ee9a1c4d90
commit
72e1e67c6d
2 changed files with 107 additions and 0 deletions
|
|
@ -101,6 +101,10 @@ _Avoid_: energy assessment, site survey, field survey, Domna survey, Hestia surv
|
|||
Property data supplied by a landlord that may correct or supplement the public EPC for a single Property; triggers Rebaselining when applied; not applicable when Site Notes are present.
|
||||
_Avoid_: patches (deprecated), corrections, manual EPC, edits
|
||||
|
||||
**Landlord-Description Classification**:
|
||||
Resolving a **Landlord Description** (unbounded free-text a landlord supplies for one component — "CWI" / "Cav filled" / "cavity insulated" all name one thing) onto a **Recognised Internal Description** via an LLM classifier, persisted in the `landlord_*_overrides` table (`source=classifier`) as a reviewed cache. Four vocabularies are kept **distinct** and must not be conflated: a **Landlord Description** (unbounded input); a **Recognised Internal Description** (the closed target taxonomy — e.g. a `MainHeatingSystemType` archetype — each binding to a Simulation Overlay); a **Lodged Description** (the gov-EPC `main_heating[].description` rendering, e.g. "Room heaters, electric" — only an example of which system *types* occur, never a map key); and the **SAP main heating code** (Table 4a/4b, what the calculator consumes). The classifier maps Landlord → Recognised Internal → SAP code. When it cannot confidently place the text it emits **`None`** (no overlay → the lodged EPC stands, surfaced to the user as "no suitable match"), **never the nearest wrong archetype** — the target taxonomy must be complete enough that a real system always has a correct home, so the classifier never overflows into a garbage-drawer archetype (ADR-0041).
|
||||
_Avoid_: "the LLM mapper is unreliable" (the failure mode is a too-small target taxonomy, not LLM language ability); conflating the landlord input vocabulary with the gov-EPC lodged rendering or the RdSAP entry-tool catalogue; treating a deterministic dict as a *replacement* for the LLM rather than a reviewed cache of its output
|
||||
|
||||
### Modelling
|
||||
|
||||
**Effective EPC**:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
# Landlord-heating classification targets a complete, modellable taxonomy; unmapped input is no-override
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
Landlord supplementary data describes a dwelling's components in **unbounded
|
||||
free-text** — a wall is "cavity insulated" / "CWI" / "Cav filled"; a heating
|
||||
system is "communal gas boiler" / "Quantum storage" / a boiler make. An LLM
|
||||
classifier maps that free-text onto a **closed internal taxonomy** of recognised
|
||||
descriptions, each of which binds to a Simulation Overlay applied to
|
||||
`EpcPropertyData` (ADR-0032). For heating the taxonomy is the
|
||||
`MainHeatingSystemType` enum, mapped to a representative SAP Table 4a/4b code by
|
||||
`main_heating_system_overlay`, with coherent companions dragged from the code
|
||||
(ADR-0035). The LLM is the right tool for the unbounded-free-text problem; the
|
||||
deterministic dicts in legacy `asset_list` were a *reviewed cache of LLM output*,
|
||||
not a replacement, and the `landlord_*_overrides` table already serves as that
|
||||
verified cache (keyed `portfolio_id + description`).
|
||||
|
||||
The taxonomy was **too small** — 9 heating archetypes against a ~13-family RdSAP
|
||||
main-heating taxonomy. With no correct target the classifier force-picked the
|
||||
nearest wrong archetype, and treated **"Gas CPSU" as a garbage drawer**: oil and
|
||||
solid-fuel room heaters, community heating, and `"boiler: a rated na"` all
|
||||
classified to Gas CPSU; the word "convector" in `"electric (direct acting) room
|
||||
heaters: panel, convector or radiant heaters"` pulled it to `"Electric storage
|
||||
heaters, convector"` (code 403, off-peak storage) instead of `"Electric room
|
||||
heaters"` (691, direct-acting, single-rate).
|
||||
|
||||
Folded into the Effective EPC, a single-rate dwelling modelled as off-peak
|
||||
storage scores **SAP ~10 (band G)**. This is the true cause of **PRD #1361
|
||||
Class A** — 14 properties lodged in band C/D rebaselining to band G — which had
|
||||
been mis-attributed to a fabric "no insulation (assumed)" assumption and the
|
||||
pre-SAP10 rebaseline. The walls are correct (landlord asserted as-built); the
|
||||
heating is the crater. Exemplar **property 718066**: lodged SAP 57, landlord
|
||||
described "electric (direct acting) room heaters: panel, convector or radiant"
|
||||
(correct code 691, single-rate), mis-stored as code 403 → live SAP **9.9**. The
|
||||
stored override was classified `source=classifier` on 2026-06-20, **before** the
|
||||
691 "Electric room heaters" archetype existed (commit ada2bc07, 2026-06-29) — so
|
||||
it is also stale.
|
||||
|
||||
## Decision
|
||||
|
||||
The fix is the **target taxonomy, not the mapper**. The LLM stays.
|
||||
|
||||
1. **The classifier targets a complete, modellable taxonomy.** Expand
|
||||
`MainHeatingSystemType` + `_MAIN_HEATING_CODES` to cover every RdSAP
|
||||
main-heating family the calculator can score: room heaters by fuel
|
||||
(gas / oil / solid), warm air, **heat pumps**, and **community heating**. Each
|
||||
maps to a representative Table 4a/4b code; coherent companions drag from the
|
||||
code per ADR-0035, so adding an archetype is still "just add its code".
|
||||
|
||||
2. **Unmapped free-text → `None`, never a forced archetype.** The overlay already
|
||||
returns `None` (no overlay → the lodged EPC heating stands) for any value not
|
||||
in `_MAIN_HEATING_CODES`; the classifier must *emit* Unknown/`None` when it
|
||||
cannot confidently place the text, rather than the nearest wrong archetype.
|
||||
"Gas CPSU" (or any other archetype) is never a fallback. The `None` mapping is
|
||||
persisted and surfaced to the user as "no suitable match" for later edit. A
|
||||
forced archetype overwrites a correct lodged cert; `None` (keep lodged) is the
|
||||
only safe and honest behaviour.
|
||||
|
||||
3. **Heat pumps are modellable without a PCDB index** — a "model unknown" heat
|
||||
pump maps to a default Table 4a heat-pump code (211–224) with the table's
|
||||
default seasonal efficiency; no `main_heating_index_number` is required.
|
||||
|
||||
4. **Community heating is modelled via its explicit codes** — 301 (boiler
|
||||
community) / 302 (CHP + boilers) / 304 (electric heat-pump community), which
|
||||
the catalogue strings disambiguate ("community boilers only" / "community CHP
|
||||
and boilers" / "community heat pump"). The calculator (Appendix C §C3.2
|
||||
distribution loss, Table 12c DLF, Table 12 source factors) and Bill Derivation
|
||||
(`HEAT_NETWORK` indicative rate) already support these. **Load-bearing
|
||||
assumption:** when the landlord text does not supply them, the **DLF defaults
|
||||
to Table 12c 1.50** and the source to a boiler — community SAP is *sensitive*
|
||||
to the DLF (1.0 vs 1.5 is a large swing), so this default is documented here
|
||||
and surfaced as an assumption. Coarse "communal heating" with **no named
|
||||
source** falls to `None` rather than inventing a heat-network model.
|
||||
|
||||
5. **Re-classify the stale `source=classifier` overrides** once the taxonomy is
|
||||
complete, so the band-G properties pick up their correct code.
|
||||
|
||||
## Considered Options
|
||||
|
||||
- **Replace the LLM with a deterministic catalogue map** — rejected: landlord
|
||||
input is unbounded free-text, not a closed vocabulary. The LLM is the right
|
||||
front door; determinism belongs as a downstream *verified* cache (the override
|
||||
table), built from LLM output + review, exactly as `asset_list` was.
|
||||
- **Keep the 9-member enum, tune the LLM prompt** — rejected: with no correct
|
||||
target the LLM must pick a wrong archetype regardless of prompt. Target
|
||||
completeness is the fix; prompt quality is secondary.
|
||||
- **Force unmapped input to a conservative archetype** (e.g. direct-acting
|
||||
electric) — rejected: any forced archetype overwrites a correct lodged cert.
|
||||
- **Defer community heating to `None`** (grill option b) — rejected in favour of
|
||||
modelling the three explicit community codes, since calculator + bill already
|
||||
support heat networks and the catalogue strings disambiguate the cases; only
|
||||
coarse unnamed "communal heating" falls to `None`.
|
||||
|
||||
This extends [ADR-0032](0032-landlord-override-epc-overlay.md) (the override
|
||||
overlay) and [ADR-0035](0035-coherent-heating-system-synthesis.md) (coherent
|
||||
companions drag from the code). The visible baseline shift it produces is
|
||||
correct Rebaselining per [ADR-0039](0039-override-aware-rebaselining.md). It
|
||||
**corrects the stated cause of PRD #1361 Class A** (classifier taxonomy gap, not
|
||||
fabric/rebaseline).
|
||||
Loading…
Add table
Reference in a new issue