Buckets:
| """ | |
| Specialist Node — Tier-adaptive clinical reasoning with Chain-of-Thought. | |
| Design patterns: | |
| - Model Tiering: routes to Qwen3.5-9B (fast) or Qwen3.6-27B (deep) | |
| - Reflexion: accepts critic feedback for iterative refinement | |
| - Anti-Hallucination: system prompt strictly forbids inventing treatments | |
| The specialist produces a structured recommendation with explicit | |
| reasoning sections (Findings → Staging → Treatment → Recommendation). | |
| """ | |
| import logging | |
| from typing import Dict, Any | |
| from .state import AgentState | |
| from .tools import call_tier_model, get_tier_spec | |
| logger = logging.getLogger(__name__) | |
| # --------------------------------------------------------------------------- | |
| # Prompt Engineering | |
| # --------------------------------------------------------------------------- | |
| _SYSTEM_PROMPT_TEMPLATE = """\ | |
| You are an expert clinical oncologist operating as part of the OncoAgent system. | |
| Your task is to analyze the patient case and provide the most appropriate clinical | |
| next steps based STRICTLY on the provided guidelines. | |
| MODEL TIER: {tier_name} ({tier_description}) | |
| DIAGNOSTIC RIGOR POLICY: | |
| 1. You MUST verify if a definitive diagnosis (e.g., pathology report, biopsy) exists. | |
| 2. If diagnostic evidence is missing or inconclusive, your PRIMARY recommendation | |
| MUST be the specific diagnostic procedure needed (e.g., "Esperar informe de biopsia", | |
| "Realizar legrado diagnóstico"). | |
| 3. You are STRICTLY FORBIDDEN from assuming cancer exists or jumping to treatment | |
| protocols (surgery, chemo, radiation) if the pathology is not confirmed in the input. | |
| ANTI-HALLUCINATION POLICY: | |
| 1. If the information is NOT explicitly in the guidelines, reply ONLY with: | |
| "Información no concluyente en las guías provistas." | |
| 2. Do NOT invent dosages or protocols. | |
| OUTPUT FORMAT (use this exact structure): | |
| ## Hallazgos Clínicos | |
| [Summary of current patient presentation] | |
| ## Validación Diagnóstica | |
| [State if pathology/biopsy is present and confirmed. If missing, specify what is needed.] | |
| ## Análisis de Estadificación | |
| [Map findings to staging ONLY if diagnosis is confirmed. Otherwise, state why it's not possible.] | |
| ## Opciones de Manejo | |
| [List clinical next steps or treatment options ONLY if appropriate for the diagnostic stage.] | |
| ## Recomendación Final | |
| [The absolute next step for the clinician with confidence level] | |
| Provide your recommendation in Spanish, clearly citing the guidelines. | |
| IMPORTANT: Output your recommendation DIRECTLY. Do NOT wrap it in <think> tags.""" | |
| _USER_PROMPT_TEMPLATE = """\ | |
| Patient Information: | |
| - Original Text: {clinical_text} | |
| - Cancer Type: {cancer_type} | |
| - Stage: {stage} | |
| - Mutations: {mutations} | |
| Clinical Guidelines Context: | |
| {context} | |
| {api_evidence} | |
| {critic_feedback_section} | |
| Based ONLY on the guidelines above, what are the recommended clinical next steps?""" | |
| def _build_specialist_prompt( | |
| state: AgentState, | |
| ) -> tuple[str, str]: | |
| """Build the system and user prompts for the specialist. | |
| Incorporates critic feedback if this is a retry iteration. | |
| Args: | |
| state: Current LangGraph state. | |
| Returns: | |
| Tuple of (system_prompt, user_prompt). | |
| """ | |
| tier = state.get("selected_tier", 1) | |
| spec = get_tier_spec(tier) | |
| system_prompt = _SYSTEM_PROMPT_TEMPLATE.format( | |
| tier_name=spec.name, | |
| tier_description=spec.description, | |
| ) | |
| entities = state.get("extracted_entities", {}) | |
| context = "\n---\n".join(state.get("rag_context", [])) | |
| api_evidence = state.get("api_evidence_context", []) | |
| # Format API evidence if available | |
| api_section = "" | |
| if api_evidence: | |
| api_section = "Additional Evidence (Genomic/Trials):\n" + "\n".join(api_evidence) | |
| # Inject critic feedback for retry iterations | |
| critic_feedback = state.get("critic_feedback", "") | |
| critic_attempts = state.get("critic_attempts", 0) | |
| feedback_section = "" | |
| if critic_attempts > 0 and critic_feedback: | |
| feedback_section = ( | |
| f"\n⚠️ PREVIOUS ATTEMPT FEEDBACK (attempt {critic_attempts}):\n" | |
| f"The following issues were identified in your previous recommendation. " | |
| f"Please address them in this revision:\n{critic_feedback}\n" | |
| ) | |
| user_prompt = _USER_PROMPT_TEMPLATE.format( | |
| clinical_text=state.get("clinical_text", ""), | |
| cancer_type=entities.get("cancer_type", "Unknown"), | |
| stage=entities.get("stage", "Unknown"), | |
| mutations=", ".join(entities.get("mutations", [])), | |
| context=context, | |
| api_evidence=api_section, | |
| critic_feedback_section=feedback_section, | |
| ) | |
| return system_prompt, user_prompt | |
| # --------------------------------------------------------------------------- | |
| # Specialist Node | |
| # --------------------------------------------------------------------------- | |
| def specialist_node(state: AgentState) -> Dict[str, Any]: | |
| """Generate a clinical recommendation using the tier-adaptive model. | |
| If critic feedback exists in the state (retry iteration), the feedback | |
| is injected into the prompt so the model can self-correct. | |
| Args: | |
| state: Current LangGraph state. | |
| Returns: | |
| State update with clinical_recommendation and reasoning_trace. | |
| """ | |
| context = state.get("rag_context", []) | |
| tier = state.get("selected_tier", 1) | |
| attempt = state.get("critic_attempts", 0) | |
| # Guard: no context available | |
| if not context: | |
| return { | |
| "clinical_recommendation": ( | |
| "Información no concluyente en las guías provistas. " | |
| "No se encontró evidencia relevante en la base de datos clínica." | |
| ), | |
| "reasoning_trace": "No RAG context available — safe fallback triggered.", | |
| } | |
| system_prompt, user_prompt = _build_specialist_prompt(state) | |
| spec = get_tier_spec(tier) | |
| logger.info( | |
| "Specialist invoking %s (attempt %d, context chunks: %d)", | |
| spec, attempt + 1, len(context), | |
| ) | |
| try: | |
| recommendation = call_tier_model( | |
| tier=tier, | |
| system_prompt=system_prompt, | |
| user_prompt=user_prompt, | |
| ) | |
| # Build reasoning trace for the critic | |
| reasoning_trace = ( | |
| f"Tier: {spec.name} ({spec.model_id})\n" | |
| f"Attempt: {attempt + 1}\n" | |
| f"Context chunks: {len(context)}\n" | |
| f"API evidence items: {len(state.get('api_evidence_context', []))}\n" | |
| f"Recommendation length: {len(recommendation)} chars" | |
| ) | |
| except RuntimeError as exc: | |
| logger.error("Specialist inference failed: %s", exc) | |
| recommendation = ( | |
| "Error en el sistema de inferencia. " | |
| "No se pudo generar la recomendación clínica en este momento." | |
| ) | |
| reasoning_trace = f"INFERENCE ERROR: {exc}" | |
| # Detect if model returned the safe phrase | |
| if "información no concluyente" in recommendation.lower(): | |
| recommendation = "Información no concluyente en las guías provistas." | |
| return { | |
| "clinical_recommendation": recommendation, | |
| "reasoning_trace": reasoning_trace, | |
| } | |
Xet Storage Details
- Size:
- 7.13 kB
- Xet hash:
- 991dcbb6a3d988fc445ec9b4ccf39a40568da974e86b0c932a8f16172b655b21
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.