104 lines
5.5 KiB
Markdown
104 lines
5.5 KiB
Markdown
# OrdinaceAgentEmail — agent na žádosti o recept
|
|
|
|
Hledá ve schránce **ordinace@buzalkova.cz** e-maily, kde pacient žádá
|
|
o předepsání léku (recept), vytěžuje pacienta + požadované léky a pacienta
|
|
ověřuje v kartotéce Medicusu.
|
|
|
|
## Stav: testovací režim (read-only)
|
|
|
|
`recepty_agent.py` zatím jen načte `NEWEST_N` (= 5) nejnovějších mailů
|
|
z Inboxu, klasifikuje je Claude modelem a vypíše report do konzole
|
|
a `_log_recepty.txt`. **Ve schránce nic nemění** (žádné kategorie,
|
|
přesuny, odpovědi), žádný `state.json`.
|
|
|
|
## Tok
|
|
|
|
1. **Graph API** — `newest_inbox_messages()`: N nejnovějších mailů z Inboxu,
|
|
bez filtru na přílohy (`$orderby receivedDateTime desc` — bez filtru
|
|
funguje, na rozdíl od kombinace s `hasAttachments`, viz EmailAgent).
|
|
2. **AI klasifikace + vytěžení (Claude `claude-haiku-4-5`)** — pro každý mail
|
|
JSON: `je_zadost_o_recept`, `pacient` (může se lišit od odesílatele —
|
|
příbuzní píší za pacienta), `rodne_cislo` (přesně jak je v textu),
|
|
`datum_narozeni`, `leky[]` (nazev + poznamka), `poznamka`, `duvod`.
|
|
3. **Ověření v Medicusu (`MedicusLookup`)** — celá kartotéka se načte do
|
|
paměti (KAR ~6300 pacientů + kontakty z KARKONTAKT: ~70 e-mailů,
|
|
~4150 telefonů; TYP: 1=pevná, 2=mobil, 3=e-mail). Párování v pořadí
|
|
spolehlivosti:
|
|
1. **RČ** z textu mailu (Medicus ukládá RČ bez lomítka) — jednoznačné,
|
|
2. **e-mail odesílatele** proti KARKONTAKT,
|
|
3. **telefon z textu mailu** proti KARKONTAKT (jen číslice, bez +420 —
|
|
telefonů je v kartotéce hodně, často rozhodne i duplicitní jména),
|
|
4. **jméno** — bez diakritiky, bez ohledu na pořadí slov (Jaroslav Klíma
|
|
= Klíma Jaroslav); při více kandidátech zúžení datem narození
|
|
(z `datum_narozeni` nebo odvozeným z nesedícího RČ).
|
|
Výstup: `[SHODA RČ/E-MAIL/JMÉNO/JMÉNO+DATUM]` s detaily pacienta
|
|
(RČ, datum narození, pojišťovna, idpac, příznak vyřazení), nebo
|
|
`[NENALEZEN]`.
|
|
4. **Nejednoznačnost (více pacientů stejného jména, např. otec a syn)** —
|
|
`resolve_by_prescriptions()`: načte nestornované recepty kandidátů
|
|
z tabulky `RECEPT` (`STORNO <> 'T'`, posledních `RECEPT_MONTHS` = 24
|
|
měsíců) a rozhodne podle shody požadovaných léků s historií:
|
|
1. **Deterministicky** — `_drug_matches()`: substring oběma směry
|
|
(„tadalafil" ~ „TADALAFIL ACCORD") + prefix prvních slov od 5 znaků
|
|
(„Concord" ~ „CONCOR"). Jediný kandidát s nejvyšším nenulovým
|
|
skóre vyhrává → `[SHODA JMÉNO+LÉKY V HISTORII]`.
|
|
2. **Claude fallback** — když deterministika nerozhodne (nikdo/více se
|
|
shodou), model dostane požadované léky + seznamy předepsaných léků
|
|
kandidátů a rozhodne i přes generika/účinné látky → `[SHODA
|
|
JMÉNO+LÉKY+AI]`. Když ani AI nerozhodne → `[NEROZHODNUTO]`
|
|
+ výpis kandidátů k ruční kontrole.
|
|
5. Report + cena AI za běh (~0,04 Kč/mail).
|
|
|
|
## Sdílená infrastruktura
|
|
|
|
- `EmailAgent/graph_mail.py` — import přes `sys.path` (stejná app registrace,
|
|
Mail.Read Application). Credentials natvrdo tam.
|
|
- `Knihovny/medicus_db.py` — Firebird připojení k Medicusu (DSN podle názvu
|
|
počítače, na Z230 → `reporter:c:\medicus\medicus.fdb`).
|
|
- `ANTHROPIC_API_KEY` z `Medevio/.env`.
|
|
|
|
## Vytvoření požadavku v Medeviu — `medevio_recept.py`
|
|
|
|
Funkce pro agenta: jakmile je pacient + léky správně identifikován, založí mu
|
|
v Medeviu požadavek **„Recept na léky"**, aby ho lékař viděl (Medevio kontrolujeme
|
|
průběžně, e-mail zřídka).
|
|
|
|
```python
|
|
from medevio_recept import vytvor_recept
|
|
rid = vytvor_recept(rodne_cislo="730920/8104",
|
|
nazev_leku="Euthyrox 100 µg",
|
|
poznamka="docházejí mi léky")
|
|
```
|
|
|
|
Co se stane (vše odchyceno z webu Medevia 2026-06-13, ověřeno na testovacím pacientovi):
|
|
1. **RČ → patient UUID** přes MySQL `medevio.medevio_pacient` (`identification_number` → `patient_id`).
|
|
2. `fillECRFForm` (prázdný) → `createPatientRequestWithoutReservation` → založí „Recept na léky".
|
|
3. `createClinicPatientRequestNote` → obě pole do **interní poznámky** (formátováno „Název léků / Poznámka").
|
|
4. `assignTagToPatientRequest` → štítek **CLAUDE** (`pridat_stitek=False` vypne).
|
|
|
|
**Proč interní poznámka, ne dotazník:** lékařský přístup neumí vyplnit pacientský
|
|
ECRF dotazník smysluplně (z lékařské strany má jen 1 pole `nazev-leku`), proto obsah
|
|
jde do interní poznámky (viditelná jen pro ordinaci).
|
|
|
|
Auth: Bearer token z `Medevio/token.txt` (dlouhodobý). Test: `python medevio_recept.py`
|
|
(založí testovací Recept na Vladkovi `0210db7b-…`). Pro test bez DB lookup je parametr
|
|
`patient_uuid=`. Mutace + konstanty jsou v `Medevio/medevio_api_notes.md`.
|
|
|
|
## Známé limity / TODO
|
|
|
|
- E-mailových kontaktů je v kartotéce málo (~70 z 6300 pacientů) — párování
|
|
e-mailem zabere zřídka; telefonů je ~4150, proto se vytěžuje i telefon
|
|
z textu mailu. Do budoucna by šlo e-mail odesílatele po ručním potvrzení
|
|
do KARKONTAKT doplňovat.
|
|
- Párování jménem vyžaduje přesnou shodu množiny slov — překlepy ve jméně
|
|
nenajde (kandidát: fuzzy matching / nabídka podobných jmen).
|
|
- Zatím bez označování mailů, bez summary e-mailu, bez odpovědi pacientovi —
|
|
kandidáti na další krok (vzor: `EmailAgent/faktury_agent.py`).
|
|
- Bez idempotence (žádný state) — testovací běhy čtou vždy posledních N mailů.
|
|
|
|
## Spuštění
|
|
|
|
```powershell
|
|
python U:\ordinaceprojekt\OrdinaceAgentEmail\recepty_agent.py
|
|
```
|