Repository navigation
Conversation
Test CoverageTotal: 71.06% (20085 / 28266 lines)
|
solarsys
force-pushed
the
feat/autoimprove-knowledge
branch
2 times, most recently
from
October 23, 2026 09:18
c039647 to
6fbdb2d
Compare
…ion, report) A run now ends with a report that explains the selected model and how the search got there, built from the decision log alone. - Decision log: every trial records the proposer's rationale and expectation (written before it runs), the trial it was changed from and what changed. LLM proposers are asked for both. - Selection: among the trials that beat the baseline consistently across folds, the most interpretable one within one paired SE of the best (the one-SE rule, select_simplest). Models sit on an interpretability ladder (SearchSpace.rung): logistic regression, depth-1 XGBoost (additive, now in the space), shallow trees and MLPs, then deeper trees and sequence models. one_se=False keeps the highest-lower-bound rule. - Plain words: every model has a description and every setting a meaning (Param.meaning, SearchSpace.explain). - AutoImprover.attribute(): reverts each changed setting of the selected trial one at a time and scores it on the same folds. - evaluate_on_test() also scores the highest-scoring candidate when a simpler one was selected, and saves test.json. - AutoImprover.report() / load_run(): HTML, Markdown and JSON with the summary, task, model and why (candidates, ladder, price of interpretability, attribution), test comparison and reasoning trace (timeline, every trial with hypothesis, change, result and verdict, single-setting comparisons, lessons). Statements are labelled measured, tested or hypothesis; numbers in lessons that match nothing in the log are flagged (unverified_numbers). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
A run's report now explains how the selected model decides and how it would perform in clinical use, and lists what a clinician must decide. - pyhealth.tasks.TaskCard and the optional BaseTask.task_card() hook: the task's design choices with their rationale, and a feature dictionary (meaning, clinical group, expected direction of effect). - pyhealth.metrics.clinical: alert_rate_metrics (sensitivity, PPV and number needed to evaluate when the top 5/10/20% of risk is flagged), net_benefit (decision curve), calibration_bins, subgroup_metrics and clinical_summary; counts below 11 patients are suppressed. - AutoImprover(task_card=...); explain(): exact TreeSHAP for trees, integrated gradients for torch models (exact for logistic regression); importance by clinical group, top features with effect shapes in bins of at least 11 patients, learned vs expected direction, and the top-feature overlap of the fold models. - evaluate_on_test(subgroups=...) adds the clinical summary per config. - flags() and approve(): a missing card, undescribed top features, effects opposite to the expected direction, poor calibration and subgroup gaps; decisions are kept in review.json. - The report gains How it decides, Performance in clinical terms, Clinician review and a TRIPOD+AI reporting checklist; load_run reads the new files. The eICU example has a task card and age/sex subgroups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
…d test) New inputs are written as JSON specs over PyHealth's event log, never as code. FeatureSpace validates a spec against a fixed set of operators (count, exists, days_since, stat, n_distinct, trend, blocks_with, ratio, diff, band, ge, any, cross, column), compiles it at each sample's prediction time t with windows that end strictly before t, and writes its column name and plain-language definition from the spec itself. Every spec carries its clinical rationale and expected direction; invalid specs are refused with a reason. - The future-blind test computes each spec on the full record and on the record as known at t (later rows removed, values known only later blanked via known_at, derived events rebuilt); any difference rejects it. - FeatureSpace.check reports definition, coverage and the test result; augment adds an "engineered" tensor field (EngineeredDataset) whose imputation and scaling run_config_cv refits on each fold's training part (fold_parts). - Optional BaseTask hooks: time_key, feature_vocabulary() and derived_events(patient, t) with derived_event_types. Class-level defaults, so set_task cache keys are unchanged. - examples/autoimprove_features_mimic3.py checks one hand-written feature on synthetic MIMIC-III and measures its paired CV gain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
…g, review gate) With a FeatureSpace, AutoImprover searches inputs as well as settings. - A trial is a config plus a feature set; trials.jsonl records the specs, the group a trial added, whether it was accepted, and any suspicion. - Cycles alternate (feature_cycles=(2, 1)): feature cycles hold the selected model and propose groups of specs, accepted only if their paired gain over the accepted set clears the lower bound (forward selection); model cycles hold the accepted features. Specs failing the future-blind test are refused before training; rejected groups never become the result. - Proposers: LLMProposer.propose_features (operator menu, task card, accepted features, earlier groups), random specs without a rationale, and QueueProposer for a ranked list without an LLM. - confirm(): the leading candidates are re-scored on new folds with several seeds and must clear confirm_se (2 SE) there. - prune(): accepted groups whose removal costs under one SE are dropped; the pruned selection is the result, and the ablation records what each group added. - Suspicion check (a feature that alone has ROC-AUC above suspicion_auc), review gate (features with no rationale, an opposite direction or a suspicion are held back until approved; the test also scores the model without them) and early_stop for clearly worse trials. - The report gains Features and Confirmation sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
After confirmation, the "Why this model" sentence compared the selection with the top candidate on the search folds, although the one-SE rule chose on the confirmation folds. A run could then read "costs +0.022 ± 0.006 (better in 5/5 folds), which is within noise". It now quotes the confirmation scores, says why the rule preferred the selection (a lower rung or fewer engineered features), and names the trial it was pruned from. The trace verdicts follow the same scores. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
… report) A KnowledgeBase turns published evidence into ranked, justified feature candidates without ever seeing patient data. - Sources: curated tables (add_table_evidence; effects parsed from text such as "RR 1.94 (1.61-2.32)"), documents (tables of abstracts, text files, PDFs when pypdf is installed) and PubMed via E-utilities, with queries built from the task card's text only and kept for audit. - extract(): LLM records with the sentence that states each effect; the source is set from the document, never by the LLM. - verify(): a record is kept only if its source was gathered, its quote is in that source, and its effect size and CI are in the quote; directions of ratio effects are set from the value. - map(): each predictor becomes have (an existing column), build (a spec validated by the FeatureSpace, carrying its citations and the literature's direction) or not in data (a data request), with a prior of strength x population match x data support that only orders candidates. - directions() (ambiguous when sources disagree), ranked_specs() for QueueProposer or the LLM's new "evidence" strategy, gap_report() and benchmark_range(). - AutoImprover(knowledge=kb): feature cycles add the "evidence" strategy; the report gains a Literature section and feature citations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
…when selected KnowledgeBase.fetch_pubmed(pmids) adds a review's references by id (only the ids are sent); search_pubmed uses it. A feature view now calls the task's derived_events hook only for a derived event type, not for every table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
A quoted 'MD -0.37' or 'AUC 84%' was rejected because numbers were matched without sign and percentages were not read as proportions. Found on the ARCHES literature: 7 of 101 real records were rejected this way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
A review exported from Excel stores PMIDs as =HYPERLINK(url, "id"); the documents were cited by the whole formula. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
When candidates passed the search bound but none was confirmed, the report said the kept baseline 'beat the starting pipeline'. It now says how many trials passed the search and that none kept its gain on the confirmation folds. Found on the ARCHES literature arm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
On ARCHES, LLM feature turns were wasted repeating features tried more than 12 trials earlier, which the prompt no longer showed. The prompt now lists every feature tried so far, the retry names the repeated features, and trial notes are labelled as notes rather than as space clipping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
'1 if either 1 if there was any X ..., else 0 or ... is above 0, else 0' becomes '1 if there was any X ... or there was any Y ..., else 0'. Found in a feature selected on ARCHES, whose definitions go to clinician review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
solarsys
force-pushed
the
feat/autoimprove-knowledge
branch
from
October 25, 2026 15:20
6fbdb2d to
ff6447d
Compare
Collaborator
|
/review |
AI Reviewsuggestion: human review This PR is too large for AI review (over 300 files or 200000 diff characters), so it is labeled |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1320, then #1319, #1318, #1317 and #1316. Review the commits from
autoimprove: literature knowledgeon.Why
The feature space is large: operators × code groups × windows × thresholds. Published predictors are the best guide to what to try first. A clinician also wants each feature's rationale backed by evidence, not by the LLM's memory. LLMs invent references, though, so every piece of evidence has to be checked.
What changes
New
pyhealth/autoimprove/knowledge.py:Sources:
add_table_evidence): one row per predictor and effect. Effects are parsed from text such asRR 1.94 (1.61–2.32)orOR 2.0–4.1.pypdfis installed.kb.queriesfor audit.extract(). The LLM returns records with the exact sentence that states each effect. Each record's source is set from the document, never by the LLM.verify()keeps a record only if:An invented citation, a misquote or a made-up number is rejected by construction. The direction of a ratio effect is set from its value.
map(fs, card). The LLM sees only the task card, the feature menu and column names. Each predictor becomes one of:Each gets a prior of strength × population match × data support. The prior only orders candidates; the feature search decides what is kept.
Outputs:
directions(), which is ambiguous when sources disagree (e.g. controller use);ranked_specs(), forQueueProposeror the LLM;gap_report();benchmark_range();save()/load().AutoImprover(knowledge=kb). Feature cycles gain an "evidence" strategy, which shows the LLM the untried published predictors with their citations and priors. The report gains a Literature section (the citation-check counts, the gap report and the published performance range) and a citations column in Features.Fixes from the first real run (ARCHES pediatric asthma)
The ARCHES experiment (v2 proposal PR 5) built a knowledge base from 16 curated rows, 22 review abstracts and 64 reference abstracts, then ran four feature-search arms. Running it turned up these issues, each fixed in its own commit with a test:
fetch_pubmed(pmids)adds a review's references by id; only the ids are sent.MD -0.37orAUC 84%failed, because numbers were matched without sign and percentages were not read as proportions. This affected 7 of 101 records.=HYPERLINK(url, "id")were used whole as document ids.any()definitions. '1 if either 1 if there was any X …, else 0 or … is above 0, else 0' now reads '1 if there was any X … or there was any Y …, else 0'. Selected features' definitions go to clinician review.Tests
tests/core/test_autoimprove_knowledge.py(10 tests):33831622.0give clean PMIDs; fetching by id sends only the ids.QueueProposer(kb.ranked_specs())keeps the planted feature, and the report shows the literature and citations.All 65 autoimprove and metrics tests pass, as do the doctests, ruff and
tools/check_pr_rules.py.🤖 Generated with Claude Code
https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T