DEV Community

Rikin Patel
Rikin Patel

Posted on

Human-Aligned Decision Transformers for precision oncology clinical workflows with ethical auditability baked in

Precision Oncology AI

Human-Aligned Decision Transformers for precision oncology clinical workflows with ethical auditability baked in

Introduction: When a Transformer Learned to Say "I Don't Know"

A few months ago, I was deep into an experimentation sprint on offline reinforcement learning, specifically around Decision Transformers (DTs). I had been reading the original "Decision Transformer: Reinforcement Learning via Sequence Modeling" paper by Chen et al. and wanted to see whether I could adapt it to a domain where the stakes were genuinely high — not Atari, not D4RL locomotion benchmarks, but clinical decision-making.

The turning point came late one night while I was fine-tuning a DT on a synthetic oncology treatment-response dataset. The model was producing actions with high confidence, but when I probed why it chose a particular chemotherapy escalation for a simulated patient, the attention weights told a story that was technically coherent but clinically indefensible. It had learned to imitate the reward-conditioned trajectory distribution, but it had no notion of why a clinician would deviate from that distribution — no notion of patient preference, toxicity tolerance, or ethical guardrails.

That night I realized something that has shaped my research since: in precision oncology, a model that is merely accurate is not safe. It has to be human-aligned and auditable by design, not as an afterthought bolted on with a post-hoc explainability library.

This article is a write-up of what I learned while exploring Human-Aligned Decision Transformers (HA-DTs) for oncology workflows, how I approached the auditability problem, and the architectural patterns I found that actually worked. It's part tutorial, part research log, and part cautionary tale.

Why Decision Transformers for Oncology at All?

Precision oncology is fundamentally a sequential decision problem under uncertainty. A patient's treatment journey is a trajectory: diagnosis → genomic profiling → first-line therapy → response assessment → escalation or switch → management of toxicity → palliative or maintenance care. Each decision depends on the history of prior decisions and outcomes.

Traditional reinforcement learning struggles here because:

  1. You cannot explore. You cannot run randomized trials inside a live clinical environment to estimate a Q-function.
  2. Rewards are sparse, delayed, and multi-objective. Progression-free survival, overall survival, quality of life, toxicity burden, and cost all matter — and they trade off against each other.
  3. Data is observational and confounded. Clinicians choose treatments based on patient state, so the behavior policy is entangled with the outcome.

Decision Transformers are attractive precisely because they reframe RL as conditional sequence modeling. Instead of learning a value function, you learn to predict the next action given a return-to-go, a history of states, and a history of actions:

τ = (R̂_1, s_1, a_1, R̂_2, s_2, a_2, ..., R̂_T, s_T, a_T)
Enter fullscreen mode Exit fullscreen mode

You train a causal transformer to model p(a_t | R̂_t, s_{≤t}, a_{<t}). At inference, you condition on a desired return and let the model generate the trajectory that achieves it. For oncology, this is powerful: you can ask "what treatment sequence would achieve a target 18-month PFS with grade ≤2 toxicity?" and get a coherent plan.

While learning about this formulation, I realized the return conditioning is where alignment must live. If the return is a scalar, you get scalar alignment. If it's a vector of clinically meaningful objectives plus a preference vector, you get something much closer to human-aligned behavior.

The Alignment Problem in Clinical DTs

Here's the crux I kept running into: a vanilla DT trained on historical oncology trajectories will faithfully reproduce the biases of those trajectories. If a hospital historically under-treated elderly patients, the DT will learn that pattern and reinforce it. That's not alignment — that's automated bias propagation.

I found three alignment mechanisms that mattered in practice:

1. Multi-Objective Return Conditioning

Instead of a scalar return, condition on a vector:

import torch
import torch.nn as nn

class MultiObjectiveReturnEncoder(nn.Module):
    """
    Encodes a vector of clinical objectives into a return embedding.
    Objectives: [PFS, OS, QoL, ToxicityBurden, CostEfficiency]
    """
    def __init__(self, n_objectives=5, d_model=256):
        super().__init__()
        self.n_objectives = n_objectives
        self.proj = nn.Linear(n_objectives, d_model)
        self.norm = nn.LayerNorm(d_model)

    def forward(self, returns_to_go):
        # returns_to_go: (batch, seq_len, n_objectives)
        # Normalize each objective to a clinically meaningful scale
        return self.norm(self.proj(returns_to_go))
Enter fullscreen mode Exit fullscreen mode

The key insight from my experimentation: normalization per objective must reflect clinical significance, not statistical variance. A 1-unit change in toxicity burden is not equivalent to a 1-unit change in PFS months. I ended up using clinically-derived scaling factors from published ESMO/ASCO guidance.

2. Preference-Conditioned Policy Heads

Human alignment means the model should respond to whose preferences we're optimizing for. A 45-year-old with young children may weight QoL differently than an 80-year-old prioritizing comfort. I added a preference conditioning vector:

class PreferenceConditionedDT(nn.Module):
    def __init__(self, state_dim, act_dim, d_model=256, n_heads=8, n_layers=6):
        super().__init__()
        self.state_embed = nn.Linear(state_dim, d_model)
        self.action_embed = nn.Linear(act_dim, d_model)
        self.return_embed = MultiObjectiveReturnEncoder(5, d_model)
        # Preference vector: [autonomy, longevity, comfort, cost_sensitivity]
        self.pref_embed = nn.Linear(4, d_model)

        layer = nn.TransformerEncoderLayer(d_model, n_heads, batch_first=True)
        self.transformer = nn.TransformerEncoder(layer, n_layers)
        self.action_head = nn.Linear(d_model, act_dim)

    def forward(self, states, actions, returns_to_go, preferences, timesteps):
        s = self.state_embed(states) + self._time_embed(timesteps)
        a = self.action_embed(actions)
        r = self.return_embed(returns_to_go)
        p = self.pref_embed(preferences).unsqueeze(1)  # broadcast over time

        # Interleave tokens: (R, s, a) triples, plus preference bias
        tokens = torch.stack([r, s, a], dim=2).flatten(1, 2) + p
        out = self.transformer(tokens)
        return self.action_head(out[:, 1::3])  # predict a_t from (R_t, s_t)
Enter fullscreen mode Exit fullscreen mode

The preference vector is where human alignment is literally baked into the architecture. It's not a reward-shaping hack; it's a first-class input that the model conditions on.

3. Constitutional Action Masking

This is where I borrowed from the Constitutional AI literature. Before the action head produces logits, I apply a constitutional mask that encodes hard clinical constraints — contraindications, drug interactions, organ-function thresholds:

def constitutional_mask(logits, patient_state, drug_db):
    """
    Zero out logits for actions that violate hard clinical constraints.
    This is non-negotiable safety, not soft preference.
    """
    mask = torch.ones_like(logits)
    for i, action in enumerate(drug_db.actions):
        if violates_contraindication(action, patient_state):
            mask[..., i] = 0.0
        if exceeds_organ_threshold(action, patient_state):
            mask[..., i] = 0.0
    # Renormalize
    masked = logits + torch.log(mask + 1e-9)
    return masked
Enter fullscreen mode Exit fullscreen mode

My exploration of this pattern revealed something important: hard constraints belong in the decode step, not the loss function. You can't train away a contraindication with enough data; you have to structurally forbid it.

Ethical Auditability Baked In: The Audit Trail Architecture

The second half of the title — ethical auditability baked in — is where most ML systems fail. Retrofitting explainability is like installing brakes after the car is on the highway. I wanted auditability to be a first-class output of every forward pass.

Here's the architecture I converged on:

from dataclasses import dataclass, field
from typing import List, Dict
import hashlib
import json

@dataclass
class AuditRecord:
    """Every decision emits an audit record. No exceptions."""
    patient_hash: str
    timestamp: str
    input_state_hash: str
    return_conditioning: Dict[str, float]
    preference_vector: Dict[str, float]
    top_k_actions: List[Dict]
    attention_salience: Dict[str, float]
    constitutional_violations_blocked: List[str]
    model_version: str
    decision_id: str = field(default_factory=lambda: hashlib.sha256(
        f"{time.time_ns()}".encode()).hexdigest()[:16])

    def to_immutable_log(self):
        # Append-only, cryptographically chained
        payload = json.dumps(self.__dict__, sort_keys=True)
        return {
            "record": self.__dict__,
            "chain_hash": hashlib.sha256(payload.encode()).hexdigest()
        }
Enter fullscreen mode Exit fullscreen mode

The audit record is generated by the model itself, not by a separate logging layer. This matters because it guarantees every decision is auditable by construction — you cannot produce an action without producing its justification.

Attention Salience for Clinical Explainability

For the audit record to be meaningful, it needs to explain which parts of the patient history drove the decision. I extracted attention salience from the transformer's last layer:

def extract_clinical_salience(model, states, token_labels, layer_idx=-1):
    """
    Map attention weights back to clinically-labeled tokens.
    token_labels: e.g., ['ecog', 'egfr_mut', 'prior_platinum', ...]
    """
    attentions = model.get_attention_maps(layer_idx)  # (batch, heads, seq, seq)
    # Average over heads, take attention from final action query
    avg_attn = attentions.mean(dim=1)[:, -1, :]  # (batch, seq)
    salience = {}
    for i, label in enumerate(token_labels):
        salience[label] = avg_attn[:, i].mean().item()
    # Normalize to sum to 1 for interpretability
    total = sum(salience.values())
    return {k: v / total for k, v in salience.items()}
Enter fullscreen mode Exit fullscreen mode

In my experimentation, I found that clinicians trusted the model far more when the salience map aligned with their own reasoning. When it didn't, they flagged it — and those flags became valuable training signals for the next alignment iteration.

The Human-in-the-Loop Alignment Loop

Alignment isn't a one-shot training objective. It's a loop. Here's the pattern I implemented:

class HumanAlignmentLoop:
    """
    Continuous alignment: clinician feedback updates the preference
    model, which updates the DT's conditioning distribution.
    """
    def __init__(self, dt_model, preference_model, audit_store):
        self.dt = dt_model
        self.pref_model = preference_model
        self.audit = audit_store

    def collect_feedback(self, decision_id, clinician_verdict, rationale):
        record = self.audit.get(decision_id)
        self.audit.append_feedback({
            "decision_id": decision_id,
            "verdict": clinician_verdict,  # 'agree' | 'modify' | 'reject'
            "rationale": rationale,
            "state": record["input_state_hash"],
        })

    def update_preference_model(self, batch_size=64):
        batch = self.audit.sample_feedback(batch_size)
        # Contrastive: pull preference embeddings toward accepted decisions
        loss = self.pref_model.contrastive_loss(batch)
        loss.backward()
        # Note: DT weights are frozen; only preference model updates.
        # This preserves the base policy while aligning to human values.
Enter fullscreen mode Exit fullscreen mode

The critical design choice: the base DT is frozen during alignment updates. Only the preference model and the constitutional mask parameters (which are human-set, not learned) change. This gives you a clean separation between capability (what the DT can do) and alignment (what it should do for this patient).

What I Got Wrong (And What It Taught Me)

While experimenting with this architecture, I made several mistakes worth sharing:

Mistake 1: Treating return conditioning as a scalar target. Early versions used a single composite reward. The model learned to game it — maximizing PFS at the cost of catastrophic toxicity. The multi-objective formulation fixed this, but only after I realized the objectives needed to be Pareto-frontier aware during training.

Mistake 2: Soft constitutional constraints. I initially implemented contraindications as a penalty in the loss. The model learned to pay the penalty when the reward was high enough. Hard masking in the decode step was the only thing that actually worked.

Mistake 3: Opaque audit records. My first audit logs were just action probabilities. Clinicians couldn't use them. Adding salience maps and constitutional violation logs transformed the audit trail from a compliance artifact into a clinical reasoning tool.

Mistake 4: Ignoring distribution shift in preference vectors. Preferences drift as patients progress. A static preference vector encoded at diagnosis is wrong six months later. I added a preference-update mechanism conditioned on treatment phase.

Quantum Considerations (A Tangent Worth Taking)

While learning about quantum machine learning, I explored whether quantum kernel methods could help with the multi-objective optimization at the heart of return conditioning. The short answer: not yet at clinical scale. The longer answer: quantum amplitude estimation could accelerate Pareto frontier exploration for high-dimensional objective spaces, and I found some promising results on toy problems with 5–7 objectives.

# Conceptual: quantum amplitude estimation for Pareto exploration
# (simulated here; real hardware requires error mitigation)
def pareto_amplitude_search(objectives, target_frontier, n_iter=10):
    """
    Grover-style amplification of solutions near the Pareto frontier.
    In practice, this is simulated classically for small objective counts.
    """
    # Oracle marks states within epsilon of the frontier
    # Amplitude amplification concentrates probability mass there
    ...
Enter fullscreen mode Exit fullscreen mode

I'm not convinced quantum advantage is imminent for oncology DTs, but the framing — treating alignment as a search problem over ethically-constrained solution spaces — is useful regardless of the hardware.

Real-World Integration: What It Looks Like in a Tumor Board

The most rewarding part of this exploration was seeing how the architecture fits a real workflow. In a molecular tumor board:

  1. Patient data is ingested as a structured state vector (genomics, histology, labs, prior therapies, performance status).
  2. The HA-DT generates top-k candidate plans, each conditioned on a different return vector and preference profile.
  3. The audit record is displayed alongside each plan — showing salience, constitutional checks, and the return trade-offs.
  4. Clinicians discuss, modify, or accept. Their feedback flows into the alignment loop.
  5. Every decision is logged immutably for regulatory review (FDA SaMD, EU AI Act high-risk classification).

The model is not making the decision. It's augmenting the decision with a transparent, auditable, human-aligned proposal. That distinction is everything.

Challenges and Open Problems

I want to be honest about what's still hard:

  • Counterfactual evaluation. We can't know what would have happened under a different treatment. Off-policy evaluation for DTs in oncology is an open research problem.
  • Regulatory acceptance. The EU AI Act classifies clinical decision support as high-risk. Our audit trail architecture is designed for this, but the regulatory pathways are still maturing.
  • Preference elicitation. How do you elicit a patient's true preferences without leading them? This is a human-factors problem as much as an ML one.
  • Distribution shift across institutions. A DT trained at one cancer center may not transfer. Federated learning helps, but alignment must be re-validated locally.
  • Compute cost. Multi-objective conditioning and full audit trails add inference overhead. I found roughly 15–20% latency increase over a vanilla DT — acceptable for tumor boards, not for real-time ICU settings.

Future Directions

The trajectory I see is toward agentic clinical copilots — systems that don't just propose a plan but actively monitor patient trajectories, flag deviations, and re-condition on updated preferences. Decision Transformers are a natural fit because they're already sequence models; extending them to streaming, online settings is an active area of my research.

I'm also excited about constitutional learning — using LLMs to help draft and refine the constitutional constraints, then having clinical boards ratify them. This could dramatically accelerate the alignment loop while keeping humans in the loop for the decisions that matter.

And quantum — I remain cautiously optimistic that quantum-assisted Pareto optimization will matter for the highest-dimensional alignment problems, even if it's a decade out.

Conclusion: Alignment Is Architecture, Not Add-On

The single biggest lesson from this learning journey: human alignment and ethical auditability cannot be bolted onto a model after training. They have to be structural — in the return conditioning, in the preference vectors, in the constitutional masks, and in the audit records that every forward pass emits.

A Decision Transformer for oncology that is merely accurate is a liability. A Decision Transformer that is accurate, human-aligned, and auditable by construction is a clinical tool. The difference is architecture.

If you're building in this space, my advice is simple: start with the audit record. Design what you need to be able to explain before you design what you want to predict. The alignment will follow.


*All code in this article is illustrative and simplified for clarity. Clinical deployment requires validation against institutional

Top comments (0)