Salus-7B

Salus-7B is a specialized medical language model introduced in Salus: Strategic Diagnostic Testing for Complex Diagnosis via Multi-Agent Reinforcement Learning (ICML 2026). It is built on Qwen2.5-7B-Instruct and trained with supervised fine-tuning (SFT) followed by Group Relative Policy Optimization (GRPO).

Salus addresses complex diagnosis as a sequential evidence-gathering process. It decomposes diagnostic reasoning into three functional roles:

  1. Differential Reasoner — produces a differential diagnosis from the available patient record.
  2. Strategic Controller — decides whether to request more evidence or make a final diagnosis.
  3. Workup Proposer — recommends the next auxiliary examinations.

The model is optimized for these three fixed instructions. It is not intended to be used as a general-purpose chat model, and paraphrasing the system prompts may substantially reduce performance.

Model details

  • Base model: Qwen2.5-7B-Instruct
  • Architecture: Qwen2 causal language model
  • Parameters: approximately 7.6B
  • Training: SFT followed by multi-agent GRPO
  • Primary task: sequential diagnostic testing for complex clinical cases
  • Primary prompt language: Chinese
  • License: Apache 2.0

The GRPO stage uses structured rewards to calibrate evidence-seeking behavior, discourage premature diagnostic closure, and improve differential diagnosis. See the paper page and code repository for details.

Supported instructions

The following system prompts and input formats should be used exactly as shown.

1. Differential Reasoner

System prompt:

你是一名专业医生,负责根据提供的病人信息,进行鉴别诊断。

User prompt:

<病历>
{patient_record}
</病历>

The final answer should contain a line-separated differential diagnosis.

2. Strategic Controller

System prompt:

你是一名专业医生,负责根据提供的病人信息和鉴别诊断,决定医生下一步是继续进行辅助检查,还是直接给出最终诊断。

User prompt:

<病历>
{patient_record}
</病历>

<鉴别诊断>
{differential_diagnosis}
</鉴别诊断>

The final answer must be one of:

继续辅助检查

or:

您的诊断结果为:{diagnosis}

3. Workup Proposer

System prompt:

你是一名专业医生,负责根据提供的病人信息和鉴别诊断,决定医生下一步要进行哪些辅助检查

User prompt:

<病历>
{patient_record}
</病历>

<鉴别诊断>
{differential_diagnosis}
</鉴别诊断>

The final answer normally uses the following format:

请求进行以下辅助检查:
- {examination_1}
- {examination_2}

Usage

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "ShuohaoGao-THU/Salus-7B"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    torch_dtype="auto",
    device_map="auto",
)


def generate(system_prompt: str, user_prompt: str) -> str:
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ]
    text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
    )
    inputs = tokenizer(text, return_tensors="pt").to(model.device)

    with torch.inference_mode():
        output_ids = model.generate(
            **inputs,
            max_new_tokens=4096,
            do_sample=True,
            temperature=0.7,
            top_p=0.8,
            top_k=20,
            repetition_penalty=1.05,
        )

    new_ids = output_ids[0, inputs.input_ids.shape[1]:]
    return tokenizer.decode(new_ids, skip_special_tokens=True)


system_prompt = "你是一名专业医生,负责根据提供的病人信息,进行鉴别诊断。"
patient_record = "在此处填写病历"
user_prompt = f"<病历>\n{patient_record}\n</病历>"

response = generate(system_prompt, user_prompt)
print(response)

Parsing the three outputs

The model may place its reasoning before a </reason> marker. Always remove this reasoning prefix before parsing the task-specific result.

def extract_final_answer(response: str) -> str:
    """Return the content after the final </reason> marker."""
    return response.rsplit("</reason>", 1)[-1].strip()

Parse a differential diagnosis

The Differential Reasoner returns one diagnosis per line:

def parse_differential_diagnosis(response: str) -> list[str]:
    final_answer = extract_final_answer(response)
    diagnoses = [
        line.strip()
        for line in final_answer.splitlines()
        if line.strip()
    ]
    if not diagnoses:
        raise ValueError("The model returned an empty differential diagnosis.")
    return diagnoses

Parse the controller decision

The Strategic Controller must either request more examinations or provide a final diagnosis:

from typing import Literal, TypedDict


class ControllerResult(TypedDict):
    action: Literal["continue", "finalize"]
    diagnosis: str | None


def parse_controller_decision(response: str) -> ControllerResult:
    final_answer = extract_final_answer(response)

    if final_answer == "继续辅助检查":
        return {"action": "continue", "diagnosis": None}

    prefix = "您的诊断结果为"
    if final_answer.startswith(prefix):
        diagnosis = final_answer[len(prefix):].lstrip(":: ").strip()
        if not diagnosis:
            raise ValueError("The model selected finalization without a diagnosis.")
        return {"action": "finalize", "diagnosis": diagnosis}

    raise ValueError(f"Invalid controller output: {final_answer!r}")

Parse auxiliary examinations

The Workup Proposer returns one recommended examination per line:

import re


def parse_auxiliary_examinations(response: str) -> list[str]:
    final_answer = extract_final_answer(response)

    header = "请求进行以下辅助检查"
    if final_answer.startswith(header):
        final_answer = final_answer[len(header):].lstrip(":: \r\n")

    examinations = [
        re.sub(r"^(?:[-*•]+|\d+[.、)])\s*", "", line.strip())
        for line in final_answer.splitlines()
        if line.strip()
    ]
    examinations = [item for item in examinations if item]
    if not examinations:
        raise ValueError("The model returned no auxiliary examinations.")
    return examinations

For the complete sequential workflow, first call the Differential Reasoner, pass its output to the Strategic Controller, and call the Workup Proposer only when the controller returns 继续辅助检查. Append newly obtained examination results to the patient record before starting the next round.

Results

On the complex cases in CompDiag-Bench, Salus-7B achieves 83.64% Top-1 diagnostic accuracy, compared with 71.38% for DeepSeek-V3.2 and 80.30% for GPT-5.2, as reported in the paper.

These results are specific to the benchmark protocol described by the authors and should not be interpreted as evidence of clinical safety or real-world diagnostic performance.

Limitations and safety

  • Salus-7B supports only the three task-specific instructions documented above.
  • The model may produce incorrect, incomplete, or clinically unsafe diagnoses and examination recommendations.
  • Outputs may vary with prompt formatting, decoding settings, patient-record quality, and available context.
  • The model has not been validated for autonomous clinical use.
  • It must not replace evaluation, judgment, or treatment by qualified healthcare professionals.
  • Do not provide identifiable patient information to the model without appropriate authorization and privacy safeguards.

This model is released for research purposes. Users are responsible for evaluating its suitability, safety, privacy implications, and regulatory requirements in their own setting.

Citation

@inproceedings{gao2026salus,
  title={Salus: Strategic Diagnostic Testing for Complex Diagnosis via Multi-Agent Reinforcement Learning},
  author={Gao, Shuohao and Chen, Xuanzhong and Luo, Lingxiao and Ding, Zilin and Han, Rong and Jiang, Rui and Chen, Ting},
  booktitle={Forty-third International Conference on Machine Learning},
  year={2026}
}

Acknowledgements

Salus-7B is based on Qwen2.5-7B-Instruct. Please also follow the license and acceptable-use requirements of the base model.

Downloads last month
164
Safetensors
Model size
8B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for ShuohaoGao-THU/Salus-7B

Base model

Qwen/Qwen2.5-7B
Finetuned
(3043)
this model
Quantizations
1 model