noteguard-agent / CLAUDE.md
github-actions[bot]
Deploy 9aa839066bbf99a8ada733b41479a39770b3bb83 from main
eb83689
|
Raw
History Blame Contribute Delete
4.76 kB

NoteGuard β€” the trust layer for clinical AI

What this is

NoteGuard de-identifies NHS clinical free-text so LLM agents can use it safely, and proves the privacy with a measured number. This repo is the agent slice: a LangGraph ReAct agent (Gemini + Tavily) wrapped so the model and tools only ever see de-identified text; real identifiers are restored only in the final clinician-facing answer.

It started at the {Tech: Europe} London AI Hackathon. The 1.0 line is pruned to the components that actually ship in the deployed Space (see CHANGELOG.md).

Architecture

deidentify_in β†’ agent (Gemini + Tavily) β†’ reidentify_out β†’ compute_trust

  • The guarantee (non-negotiable): nothing downstream of deidentify_in may receive PHI. assert_clean() raises if any identifier remains. Never weaken or bypass this β€” it is the whole point of the project.
  • Tavily is public-guidance grounding only (NICE/NHS). Never send patient text to it.
  • compute_trust audits de-identification: NoteGuard.scan_pii(deid_text) flags PII the vault/NER passes missed (vault-independent, so it works on pasted notes), plus orphaned surrogate tokens for reversibility. The trust panel reports only this β€” no answer-quality metrics (faithfulness/sources were removed).

Key files

  • src/deid.py β€” de-id core: NHS-aware rules + vault-from-CSV, consistent surrogates, DOB date-shift, reidentify, assert_clean, and scan_pii (the panel audit). Keep the rule/vault layer std-lib only. Presidio + spaCy NER sit behind the _Detector interface (the [nlp] extra, NOTEGUARD_SPACY_MODEL, default en_core_web_md) and now ship in the Docker image to catch free-text names β€” but they degrade gracefully to a no-op stub when absent (CI, local).
  • agent/graph.py β€” the graph; exposed as noteguard for langgraph dev.
  • app/api.py β€” FastAPI backend: GET / (serves index.html), GET /health, POST /process (full UI payload), POST /summarise (compact legacy).
  • app/static/index.html β€” single-file clinician web UI (vanilla JS, no build step).
  • streamlit_app.py β€” Streamlit demo (no API keys required; rule layer only).
  • Dockerfile β€” HF Spaces Docker config; uvicorn on port 7860.
  • eval/run_eval.py β€” LangSmith evals: zero_phi_to_model (must be 1.0) + faithfulness.
  • langgraph.json, .env.example.
  • docs/tool_card.md β€” Five Safes, bias & fairness, use cases out of scope.
  • docs/report.md β€” ATRS Tier 1 + Tier 2 record.

Commands

  • De-id demo (no keys): python src/deid.py
  • Install: pip install -e ".[dev]"
  • Clinician web UI: uvicorn app.api:app --reload --port 8000 (or make run) then open http://localhost:8000
  • Serve agent (Agent Chat UI): langgraph dev then open: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
  • Evals: python -m eval.run_eval
  • Deploy: push to main β†’ .github/workflows/deploy-hf.yml mirrors it onto the HF Space chaeyoona/noteguard-agent (needs HF_TOKEN repo secret); HF rebuilds from Dockerfile (Docker SDK, app_port: 7860).
  • Env: copy .env.example β†’ .env; fill GOOGLE_API_KEY, TAVILY_API_KEY, LANGSMITH_API_KEY; set LANGSMITH_TRACING=true.

Components

Gemini (reasoning) + Tavily (public-guidance grounding) are the external services. LangGraph orchestrates the pipeline; LangSmith provides traces + evals. Superlinked (retrieval) and n8n (a proxy workflow) were hackathon-era integrations that never ran in the deployed Space and were removed in 1.0.

Dataset

NHSEDataScience/synthetic_clinical_notes (Hugging Face, MIT, fully synthetic). Build the vault from patients.csv via load_known_from_csv() so leakage is measured against ground truth. Dataset has mojibake β€” _fix_mojibake handles it.

Conventions

  • Python 3.10+. Keep src/deid.py std-lib only.
  • Verify/round any number shown in the demo.
  • Model id lives in NOTEGUARD_MODEL (default google_genai:gemini-2.5-flash).
  • Versions drift: if a langgraph/langsmith/create_react_agent import fails, adjust to the installed version rather than pinning blindly.
  • src/__init__.py exports only NoteGuard, DeidResult, load_known_from_csv.
  • pyproject.toml is the single dependency source; requirements.txt is removed.
  • Linting: ruff check + ruff format (not black).

HF Spaces notes

  • The Docker image installs only the runtime deps in pyproject.toml; streamlit and dev extras are not part of the deployed image.
  • Set GOOGLE_API_KEY, TAVILY_API_KEY, LANGSMITH_API_KEY in Space Secrets.

Ethics

Pseudonymised β‰  anonymous (still personal data under UK GDPR). Synthetic β‰  real β€” frame as methodology. Clinician stays in the loop and signs off.