PMFlow/backend/app/services/adaptive_rag_service/mixin_answer_mainline.py

1091 lines
48 KiB
Python

"""Adaptive RAG answer mainline orchestration helpers."""
from __future__ import annotations
from .shared import *
from .internal_query_context import InternalContextPacket, internal_context_block_reason
class AdaptiveRAGAnswerMainlineMixin:
@staticmethod
def _mirror_internal_context_to_query_semantics(
*,
query_semantics: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]] = None,
target_resolution: Optional[dict[str, Any]] = None,
query_plan: Optional[dict[str, Any]] = None,
) -> dict[str, Any]:
mirrored = dict(query_semantics or {}) if isinstance(query_semantics, dict) else {}
if isinstance(operation_contract, dict):
mirrored["operation_contract"] = dict(operation_contract)
if isinstance(target_resolution, dict):
mirrored["target_resolution"] = dict(target_resolution)
if isinstance(query_plan, dict):
mirrored["query_plan"] = dict(query_plan)
return mirrored
@staticmethod
def _normalize_runtime_consultant_mode_preference(value: Optional[str]) -> Optional[str]:
text = str(value or "").strip().lower()
if text in {"clarify", "advise", "challenge", "brainstorm", "execute"}:
return text
return None
@staticmethod
def _internal_answer_contract_for_query_type(query_type: str) -> str:
normalized = str(query_type or QUERY_TYPE_UNKNOWN).strip().lower()
if normalized == QUERY_TYPE_EXISTENCE:
return "yes_no_with_evidence"
if normalized == QUERY_TYPE_COUNT:
return "count_scalar"
if normalized == QUERY_TYPE_LIST:
return "entity_list"
if normalized == QUERY_TYPE_STATUS:
return "entity_snapshot"
if normalized == QUERY_TYPE_EXPLAIN:
return "explanation_with_citations"
if normalized == QUERY_TYPE_DIAGNOSE:
return "diagnosis_with_evidence"
if normalized == QUERY_TYPE_COMPARE:
return "comparison_with_basis"
return "clarify_or_fail"
@staticmethod
def _has_resolved_followup_rewrite(rewrite_meta: Optional[dict[str, Any]]) -> bool:
if not isinstance(rewrite_meta, dict):
return False
mode = str(rewrite_meta.get("mode") or "").strip().lower()
return mode in {
"state_subject",
"state_entities",
"followup_anchor_recent_reply",
"followup_anchor_meta",
}
@staticmethod
def _mirror_followup_state_to_query_semantics(
*,
query_semantics: Optional[dict[str, Any]],
session_state: Optional[QASessionState],
rewrite_meta: Optional[dict[str, Any]],
) -> dict[str, Any]:
semantics = dict(query_semantics or {}) if isinstance(query_semantics, dict) else {}
if not isinstance(rewrite_meta, dict):
return semantics
rewrite_mode = str(rewrite_meta.get("mode") or "").strip().lower()
if rewrite_mode not in {"state_subject", "state_entities"}:
return semantics
entity_ids: list[int] = []
if isinstance(getattr(session_state, "last_entity_ids", None), list):
for value in list(getattr(session_state, "last_entity_ids", None) or []):
try:
entity_ids.append(int(value))
except Exception:
continue
payload = {
"rewrite_mode": rewrite_mode,
"last_subject": str(getattr(session_state, "last_subject", "") or "").strip() or None,
"last_entity_ids": entity_ids[:6],
}
semantics["session_followup_context"] = payload
return semantics
@staticmethod
def _internal_entity_type_for_subject_focus(subject_focus: str) -> str:
normalized = str(subject_focus or "").strip().lower()
if normalized == "people":
return "user"
if normalized in {"project", "task", "signal", "feature"}:
return normalized
return "unknown"
def _build_internal_query_plan(
self,
*,
question: str,
project_id: Optional[int],
query_semantics: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]] = None,
target_resolution: Optional[dict[str, Any]] = None,
) -> Optional[dict[str, Any]]:
semantics = query_semantics if isinstance(query_semantics, dict) else {}
operation = operation_contract if isinstance(operation_contract, dict) else (
semantics.get("operation_contract") if isinstance(semantics.get("operation_contract"), dict) else {}
)
target = target_resolution if isinstance(target_resolution, dict) else (
semantics.get("target_resolution") if isinstance(semantics.get("target_resolution"), dict) else {}
)
source_policy = str(
semantics.get("effective_source_policy") or semantics.get("source_policy") or ""
).strip().lower()
if source_policy != "internal":
return None
query_type = str(
operation.get("query_type")
or semantics.get("query_type")
or QUERY_TYPE_UNKNOWN
).strip().lower() or QUERY_TYPE_UNKNOWN
subject_focus = self._query_subject_focus(semantics) if hasattr(self, "_query_subject_focus") else "unknown"
entity_type = str(
target.get("target_entity_type")
or self._internal_entity_type_for_subject_focus(subject_focus)
).strip().lower() or "unknown"
scope_hint = self._query_scope_hint(semantics) if hasattr(self, "_query_scope_hint") else "none"
target_ref = str(target.get("target_ref") or "").strip() or None
filters: list[dict[str, Any]] = []
if entity_type == "project" and target_ref and target_ref.startswith("project:"):
try:
resolved_project_id = int(target_ref.split(":", 1)[1])
except Exception:
resolved_project_id = None
if resolved_project_id is not None:
filters.append(
{
"field": "project_id",
"operator": "equals",
"value": resolved_project_id,
"value_source": "target_resolution",
}
)
elif entity_type == "project":
mention_collector = getattr(self, "_collect_lookup_terms", None)
if callable(mention_collector):
operation_mentions = mention_collector(
question=question,
query_semantics=semantics,
operation_contract=operation,
target_resolution=target,
limit=3,
)
else:
operation_mentions = [
str(item or "").strip()
for item in list(operation.get("object_mentions") or [])
if str(item or "").strip()
]
if operation_mentions:
for raw in operation_mentions[:3]:
filters.append(
{
"field": "semantic_label",
"operator": "equals_or_alias",
"value": raw,
"value_source": "operation_contract",
}
)
elif entity_type == "feature" and target_ref:
filters.append(
{
"field": "feature_ref",
"operator": "equals",
"value": target_ref,
"value_source": "target_resolution",
}
)
elif entity_type == "config" and target_ref:
filters.append(
{
"field": "config_ref",
"operator": "equals",
"value": target_ref,
"value_source": "target_resolution",
}
)
return {
"plan_version": "v2" if operation or target else "v1",
"query_type": query_type,
"entity_type": entity_type,
"answer_contract": self._internal_answer_contract_for_query_type(query_type),
"target_ref": target_ref,
"filters": filters,
"scope": {
"tenant_bound": True,
"project_id": int(project_id) if project_id is not None else None,
"scope_hint": scope_hint,
},
"retrieval_policy": {
"facts_first": True,
"allow_fallback_text": True,
},
"verifier_checks": [
"contract_matches_query_type",
"evidence_backed",
],
}
def _build_internal_query_context_packet(
self,
*,
question: str,
project_id: Optional[int],
session_id: Optional[int],
conversation_scope_key: Optional[str],
reply_to_message_id: Optional[str],
root_message_id: Optional[str],
route_decision: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]],
target_resolution: Optional[dict[str, Any]],
query_plan: Optional[dict[str, Any]],
) -> InternalContextPacket | None:
normalize_route_decision = getattr(self, "_normalize_query_route_decision", None)
normalized_route = (
normalize_route_decision(route_decision)
if callable(normalize_route_decision)
else (dict(route_decision or {}) if isinstance(route_decision, dict) else {})
)
if str(normalized_route.get("effective_source_policy") or "").strip().lower() != "internal":
return None
return InternalContextPacket.model_validate(
{
"packet_version": "v1",
"question": str(question or "").strip(),
"scope": {
"tenant_id": int(getattr(self, "tenant_id", 0) or 0),
"project_id": int(project_id) if project_id is not None else None,
"session_id": int(session_id) if session_id is not None else None,
"conversation_scope_key": str(conversation_scope_key or "").strip() or None,
"reply_to_message_id": str(reply_to_message_id or "").strip() or None,
"root_message_id": str(root_message_id or "").strip() or None,
},
"route_decision": dict(normalized_route),
"operation_contract": dict(operation_contract or {}) if isinstance(operation_contract, dict) else {},
"target_resolution": dict(target_resolution or {}) if isinstance(target_resolution, dict) else {},
"query_plan": dict(query_plan or {}) if isinstance(query_plan, dict) else {},
"executor_summary": {},
"verifier_summary": {},
"uncertainties": [],
}
)
async def _maybe_return_internal_context_precheck_response(
self,
*,
question: str,
effective_question: str,
asked_by: Optional[int],
asked_via: str,
project_id: Optional[int],
session_id: Optional[int],
context_session_id: Optional[int],
turn_index: int,
state_row: Optional[QASessionState],
rewrite_meta: Optional[dict[str, Any]],
thinking_mode: str,
query_type: str,
route_decision: Optional[dict[str, Any]],
query_semantics: Optional[dict[str, Any]],
internal_context_packet: Any,
) -> Optional[dict[str, Any]]:
packet_block = internal_context_block_reason(
internal_context_packet,
route_decision=route_decision,
)
if packet_block is None:
return None
reason_code, missing_fields = packet_block
guard_builder = getattr(self, "_build_internal_answer_contract_guard_payload", None)
if not callable(guard_builder):
return None
guard_payload = guard_builder(
query_type=query_type,
reason_code=reason_code,
missing_fields=list(missing_fields or []),
)
persist_fast_answer = getattr(self, "_persist_fast_answer", None)
if callable(persist_fast_answer):
return await persist_fast_answer(
question=question,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
fast_result={
"answer": str(guard_payload.get("answer") or "").strip(),
"sources": list(guard_payload.get("sources") or []),
"missing_fields": list(guard_payload.get("missing_fields") or []),
"reason_code": str(guard_payload.get("reason_code") or "").strip() or reason_code,
"query_type": str(query_type or QUERY_TYPE_UNKNOWN).strip() or QUERY_TYPE_UNKNOWN,
"confidence": 0.0,
"db_query_ms": 0,
"response_time_ms": 0,
"prompt_variant_id": "internal_context_guard",
},
session_id=session_id,
external_session_id=context_session_id,
turn_index=turn_index,
query_type=query_type,
missing_fields=list(guard_payload.get("missing_fields") or []),
state_row=state_row,
effective_question=effective_question,
rewrite_meta=rewrite_meta or {},
thinking_mode=thinking_mode,
answer_mode=str(guard_payload.get("answer_mode") or ANSWER_MODE_CLARIFY),
route_decision=route_decision,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
return dict(guard_payload or {})
@classmethod
def _extract_prompt_variant_from_policy_version(cls, policy_version: Optional[str]) -> Optional[str]:
raw = str(policy_version or "").strip()
if not raw:
return None
for part in raw.split("|"):
normalized = str(part or "").strip()
if not normalized.startswith("prompt:"):
continue
value = normalized.split(":", 1)[1].strip().lower()
return value or None
return None
async def _resolve_anchor_prompt_variant(
self,
*,
anchor_resolution: Optional[FollowupAnchorResolution],
conversation_scope_key: Optional[str],
) -> Optional[str]:
resolver = getattr(self, "_resolve_anchor_qa_record", None)
if not callable(resolver):
return None
try:
anchor_row = await resolver(
anchor_resolution=anchor_resolution,
conversation_scope_key=conversation_scope_key,
)
except Exception:
return None
if anchor_row is None:
return None
return self._extract_prompt_variant_from_policy_version(getattr(anchor_row, "policy_version", None))
def _apply_chat_external_fact_scene_policy(
self,
*,
question: str,
project_id: Optional[int],
scene_semantics: Optional[dict[str, Any]],
anchor_prompt_variant: Optional[str] = None,
) -> dict[str, Any]:
_ = question
normalized = dict(scene_semantics or {})
scene_key = str(normalized.get("scene_key") or "default").strip().lower() or "default"
anchor_variant = str(anchor_prompt_variant or "").strip().lower()
if scene_key in {"ai_pmo", "ai_pmo_correction"} and anchor_variant in {"external_trend", "industry_trend"}:
scene_key = anchor_variant
normalized["snapshot_focus"] = "none"
normalized["deep_intent"] = "none"
if scene_key == "external_trend" and project_id is None:
scene_key = "industry_trend"
if scene_key in {"external_trend", "industry_trend"}:
normalized["external_fact_chat"] = True
normalized["scene_key"] = scene_key
return normalized
def _plan_query_runtime(
self,
*,
question: str,
effective_question: str,
query_type: str,
scene_semantics: Optional[dict[str, Any]] = None,
requested_thinking_mode: str,
enforce_scene_prompt_only: bool,
consultant_mode_preference: Optional[str] = None,
) -> QueryPlannerDecision:
normalize_thinking_mode = getattr(
self,
"_normalize_thinking_mode",
lambda value: THINKING_MODE_DEEP if str(value or "").strip().lower() == THINKING_MODE_DEEP else THINKING_MODE_NORMAL,
)
normalized_thinking_mode = normalize_thinking_mode(requested_thinking_mode)
normalized_query_type = str(query_type or QUERY_TYPE_UNKNOWN).strip().lower()
normalized_consultant_mode = self._normalize_runtime_consultant_mode_preference(
consultant_mode_preference
)
fast_path_candidate = normalized_query_type in {
QUERY_TYPE_EXISTENCE,
QUERY_TYPE_COUNT,
QUERY_TYPE_LIST,
QUERY_TYPE_STATUS,
}
normalized_question = str(question or "").strip()
normalized_effective = str(effective_question or "").strip()
normalized_scene_semantics = scene_semantics if isinstance(scene_semantics, dict) else {}
deep_intent = str(normalized_scene_semantics.get("deep_intent") or "none").strip().lower()
force_deep_by_semantics = deep_intent in {
"correction_turn",
"correction_escalation",
"engine_process_query",
"runtime_state_query",
}
if enforce_scene_prompt_only:
retrieval_mode = ANSWER_MODE_RETRIEVAL_DEEP if normalized_thinking_mode == THINKING_MODE_DEEP else ANSWER_MODE_RETRIEVAL_NORMAL
return QueryPlannerDecision(
answer_mode=retrieval_mode,
thinking_mode=THINKING_MODE_DEEP if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else THINKING_MODE_NORMAL,
allow_fast_path=False,
retrieval_answer_mode=retrieval_mode,
budget=RetrievalBudget(
max_steps=DEEP_RETRIEVAL_STEPS if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else AGENT_MAX_RETRIEVAL_STEPS,
vector_top_k=DEEP_VECTOR_TOP_K if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else DEFAULT_TOP_K,
),
reason="scene_prompt_only",
)
if fast_path_candidate and not force_deep_by_semantics:
if normalized_consultant_mode:
return QueryPlannerDecision(
answer_mode=ANSWER_MODE_RETRIEVAL_NORMAL,
thinking_mode=THINKING_MODE_NORMAL,
allow_fast_path=False,
retrieval_answer_mode=ANSWER_MODE_RETRIEVAL_NORMAL,
budget=RetrievalBudget(max_steps=AGENT_MAX_RETRIEVAL_STEPS, vector_top_k=DEFAULT_TOP_K),
reason=f"consultant_mode:{normalized_consultant_mode}",
)
return QueryPlannerDecision(
answer_mode=ANSWER_MODE_FAST_PATH,
thinking_mode=THINKING_MODE_NORMAL,
allow_fast_path=True,
retrieval_answer_mode=ANSWER_MODE_RETRIEVAL_NORMAL,
budget=RetrievalBudget(max_steps=AGENT_MAX_RETRIEVAL_STEPS, vector_top_k=DEFAULT_TOP_K),
reason="deterministic_query_type",
)
retrieval_mode = ANSWER_MODE_RETRIEVAL_DEEP if (normalized_thinking_mode == THINKING_MODE_DEEP or force_deep_by_semantics) else ANSWER_MODE_RETRIEVAL_NORMAL
return QueryPlannerDecision(
answer_mode=retrieval_mode,
thinking_mode=THINKING_MODE_DEEP if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else THINKING_MODE_NORMAL,
allow_fast_path=False,
retrieval_answer_mode=retrieval_mode,
budget=RetrievalBudget(
max_steps=DEEP_RETRIEVAL_STEPS if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else AGENT_MAX_RETRIEVAL_STEPS,
vector_top_k=DEEP_VECTOR_TOP_K if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else DEFAULT_TOP_K,
),
reason="requested_thinking_mode" if retrieval_mode == ANSWER_MODE_RETRIEVAL_DEEP else "default_retrieval",
)
async def _prepare_answer_runtime(
self,
*,
clean_q: str,
asked_by: Optional[int],
asked_via: str,
project_id: Optional[int],
source_chat_id: Optional[str],
session_id: Optional[int],
conversation_key: Optional[str],
force_new_session: bool,
action_mode: str,
source_message_id: Optional[str] = None,
source_sender_open_id: Optional[str] = None,
thread_context: Optional[dict[str, Any]] = None,
source_policy: Optional[str] = None,
precomputed_query_semantics: Optional[dict[str, Any]] = None,
route_decision: Optional[dict[str, Any]] = None,
) -> dict[str, Any]:
external_conversation_key = self._build_external_conversation_key(
asked_via=asked_via,
source_chat_id=source_chat_id,
conversation_key=conversation_key,
source_sender_open_id=source_sender_open_id,
sender=None,
thread_context=thread_context,
)
self._set_active_external_qa_context(
source_chat_id=source_chat_id,
source_message_id=source_message_id,
source_sender_open_id=source_sender_open_id,
conversation_scope_key=external_conversation_key,
thread_context=thread_context,
)
session_id, turn_index = await self._touch_session_for_new_record(
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
question=clean_q,
preferred_session_id=session_id,
force_new_session=bool(force_new_session),
external_conversation_key=external_conversation_key,
)
context_session_id: Optional[int]
if session_id is not None:
context_session_id = int(session_id)
elif asked_by is None:
context_session_id = self._external_session_id_from_key(external_conversation_key)
else:
context_session_id = None
conversation = await self._load_recent_conversation(
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
session_id=session_id,
external_session_id=context_session_id,
limit=10,
)
state_session_id = int(session_id) if session_id is not None else (
int(context_session_id) if context_session_id is not None else None
)
state_row = await self._ensure_session_state(session_id=state_session_id)
pending_selection = self._consume_pending_clarification_selection(
question=clean_q,
session_state=state_row,
)
resolved_question = str(pending_selection.get("effective_question") or clean_q).strip() if isinstance(
pending_selection, dict
) else clean_q
consumed_source_policy_hint = (
str(pending_selection.get("source_policy_hint") or "").strip().lower() or None
if isinstance(pending_selection, dict)
else None
)
anchor_resolution = await self._resolve_followup_anchor(
question=resolved_question,
session_id=state_session_id,
context_session_id=context_session_id,
session_state=state_row,
conversation_scope_key=external_conversation_key,
source_chat_id=source_chat_id,
source_sender_open_id=source_sender_open_id,
source_message_id=source_message_id,
thread_context=thread_context,
)
effective_q, rewrite_meta = self._rewrite_question_with_state(
question=resolved_question,
conversation=conversation,
session_state=state_row,
anchor_resolution=anchor_resolution,
)
query_semantics = dict(precomputed_query_semantics or {}) if isinstance(precomputed_query_semantics, dict) else {}
should_refresh_route_preview = bool(
isinstance(pending_selection, dict)
and (
resolved_question != clean_q
or consumed_source_policy_hint
)
)
if should_refresh_route_preview:
query_semantics = {}
route_decision = None
route_preview = await self.preview_query_route_decision(
question=effective_q,
asked_via=asked_via,
project_id=project_id,
source_chat_id=source_chat_id,
source_message_id=source_message_id,
conversation_scope_key=external_conversation_key,
reply_to_message_id=str((thread_context or {}).get("reply_to_message_id") or "").strip() or None,
root_message_id=str((thread_context or {}).get("root_message_id") or "").strip() or None,
source_policy_hint=consumed_source_policy_hint or source_policy,
query_semantics=query_semantics,
) if should_refresh_route_preview or not isinstance(route_decision, dict) or not query_semantics else {
"query_semantics": self._apply_route_decision_to_query_semantics(
query_semantics=query_semantics,
route_decision=route_decision,
),
"route_decision": self._normalize_query_route_decision(route_decision),
}
query_semantics = (
dict(route_preview.get("query_semantics") or {})
if isinstance(route_preview.get("query_semantics"), dict)
else {}
)
operation_contract = None
operation_contract_builder = getattr(self, "_build_operation_contract", None)
if callable(operation_contract_builder):
operation_contract = operation_contract_builder(
question=effective_q,
query_semantics=query_semantics,
)
query_semantics = self._mirror_internal_context_to_query_semantics(
query_semantics=query_semantics,
operation_contract=operation_contract,
)
internal_query_plan = self._build_internal_query_plan(
question=effective_q,
project_id=project_id,
query_semantics=query_semantics,
operation_contract=operation_contract,
)
query_semantics = self._mirror_internal_context_to_query_semantics(
query_semantics=query_semantics,
query_plan=internal_query_plan,
)
route_decision = (
dict(route_preview.get("route_decision") or {})
if isinstance(route_preview.get("route_decision"), dict)
else {}
)
query_type = str(
(operation_contract or {}).get("query_type")
or query_semantics.get("query_type")
or QUERY_TYPE_UNKNOWN
).strip().lower() or QUERY_TYPE_UNKNOWN
conversation_text = "\n".join(
[
f"Q: {str(row.get('question') or '').strip()}\nA: {str(row.get('answer') or '').strip()}"
for row in conversation[-4:]
if isinstance(row, dict)
]
) or "无"
scene_semantics = await self._resolve_answer_scene_semantics(
question=clean_q,
effective_question=effective_q,
asked_via=asked_via,
message_intent=str(query_semantics.get("message_intent") or query_semantics.get("intent") or "").strip().lower() or None,
query_type=query_type,
conversation_text=conversation_text,
source_policy=str(
route_decision.get("effective_source_policy")
or query_semantics.get("effective_source_policy")
or query_semantics.get("source_policy")
or source_policy
or ""
).strip().lower()
or None,
project_id=project_id,
query_semantics=query_semantics,
)
anchor_prompt_variant = await self._resolve_anchor_prompt_variant(
anchor_resolution=anchor_resolution,
conversation_scope_key=external_conversation_key,
)
scene_semantics = self._apply_chat_external_fact_scene_policy(
question=resolved_question,
project_id=project_id,
scene_semantics=scene_semantics,
anchor_prompt_variant=anchor_prompt_variant,
)
if bool(scene_semantics.get("requires_freshness")):
query_semantics["external_freshness_only"] = True
query_semantics["retrieval_domains"] = ["url"]
query_semantics["source_policy"] = "external"
query_semantics["effective_source_policy"] = "external"
route_decision = self._normalize_query_route_decision(
{
**route_decision,
"route_mode": str(route_decision.get("route_mode") or "external"),
"effective_source_policy": "external",
"decision_origin": str(route_decision.get("decision_origin") or "freshness_guard"),
"reply_contract": {
"mode": "explicit_source",
"head": "外部实时查询:",
"tail_hint": None,
},
"query_profile": query_semantics.get("query_profile"),
"probe_snapshot_summary": route_decision.get("probe_snapshot_summary"),
"probe_snapshot": route_decision.get("probe_snapshot"),
}
)
requested_action_mode = self._normalize_action_mode(action_mode)
effective_action_mode = await self._resolve_action_mode_async(
requested_mode=requested_action_mode,
question=resolved_question,
effective_question=effective_q,
asked_via=asked_via,
)
return {
"session_id": session_id,
"turn_index": turn_index,
"context_session_id": context_session_id,
"conversation": conversation,
"state_row": state_row,
"resolved_question": resolved_question,
"effective_q": effective_q,
"rewrite_meta": rewrite_meta,
"query_type": query_type,
"query_semantics": query_semantics,
"retrieval_domains": list(query_semantics.get("retrieval_domains") or []),
"message_intent": str(query_semantics.get("message_intent") or query_semantics.get("intent") or "").strip().lower() or None,
"scene_semantics": scene_semantics if isinstance(scene_semantics, dict) else {},
"effective_action_mode": effective_action_mode,
"anchor_resolution": anchor_resolution,
"route_decision": route_decision,
}
async def _execute_base_answer_mainline(
self,
*,
clean_q: str,
asked_by: Optional[int],
asked_via: str,
project_id: Optional[int],
source_chat_id: Optional[str],
session_id: Optional[int],
turn_index: int,
context_session_id: Optional[int],
conversation: list[dict[str, str]],
state_row: Optional[QASessionState],
effective_q: str,
rewrite_meta: dict[str, Any],
query_type: str,
query_semantics: dict[str, Any],
retrieval_domains: list[str],
scene_semantics: dict[str, Any],
thinking_mode: str,
active_thinking_mode: str,
deep_thinking: bool,
enforce_scene_prompt_only: bool,
deep_retrieval_steps: int,
deep_vector_top_k: int,
started: float,
message_intent: Optional[str],
mixed_signal_part: Optional[str],
anchor_resolution: Optional[FollowupAnchorResolution] = None,
consultant_mode_preference: Optional[str] = None,
route_decision: Optional[dict[str, Any]] = None,
) -> dict[str, Any]:
operation_contract = (
dict(query_semantics.get("operation_contract") or {})
if isinstance(query_semantics.get("operation_contract"), dict)
else {}
)
operation_contract_builder = getattr(self, "_build_operation_contract", None)
if not operation_contract and callable(operation_contract_builder):
operation_contract = operation_contract_builder(
question=effective_q,
query_semantics=query_semantics,
)
if isinstance(operation_contract, dict):
query_semantics["operation_contract"] = operation_contract
query_type = str(
operation_contract.get("query_type")
or query_type
or QUERY_TYPE_UNKNOWN
).strip().lower() or QUERY_TYPE_UNKNOWN
planner = self._plan_query_runtime(
question=clean_q,
effective_question=effective_q,
query_type=query_type,
scene_semantics=scene_semantics,
requested_thinking_mode=active_thinking_mode,
enforce_scene_prompt_only=enforce_scene_prompt_only,
consultant_mode_preference=consultant_mode_preference,
)
active_thinking_mode = planner.thinking_mode
deep_thinking = planner.retrieval_answer_mode == ANSWER_MODE_RETRIEVAL_DEEP
deep_retrieval_steps = int(planner.budget.max_steps or deep_retrieval_steps)
deep_vector_top_k = int(planner.budget.vector_top_k or deep_vector_top_k)
forced_deep_reason = ""
if not deep_thinking and not planner.allow_fast_path:
deep_intent = str((scene_semantics or {}).get("deep_intent") or "none").strip().lower()
if deep_intent != "none":
forced_deep_reason = deep_intent
deep_thinking = True
active_thinking_mode = THINKING_MODE_DEEP
clarification_payload = None
if not self._has_resolved_followup_rewrite(rewrite_meta):
clarification_payload = self._build_followup_clarification_payload(
question=clean_q,
anchor_resolution=anchor_resolution,
session_state=state_row,
)
route_clarification_payload = self._build_route_clarification_payload(route_decision=route_decision)
meta_followup_payload = await self._build_meta_followup_payload(
question=clean_q,
anchor_resolution=anchor_resolution,
conversation_scope_key=self._get_active_external_qa_context().get("conversation_scope_key"),
)
clarification_result = await self._maybe_return_clarification_response(
clarification_payload=clarification_payload or route_clarification_payload,
started=started,
clean_q=clean_q,
effective_q=effective_q,
query_type=query_type,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
session_id=session_id,
turn_index=turn_index,
state_row=state_row,
rewrite_meta=rewrite_meta,
context_session_id=context_session_id,
active_thinking_mode=active_thinking_mode,
route_decision=route_decision,
)
if clarification_result is not None:
return clarification_result
meta_followup_result = await self._maybe_return_meta_followup_response(
meta_followup_payload=meta_followup_payload,
started=started,
clean_q=clean_q,
effective_q=effective_q,
query_type=query_type,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
session_id=session_id,
turn_index=turn_index,
state_row=state_row,
rewrite_meta=rewrite_meta,
context_session_id=context_session_id,
active_thinking_mode=active_thinking_mode,
route_decision=route_decision,
)
if meta_followup_result is not None:
return meta_followup_result
system_snapshot = await self.context_cache.get_system_snapshot(
db=self.db,
tenant_id=self.tenant_id,
)
user_snapshot = await self.context_cache.get_user_snapshot(
db=self.db,
tenant_id=self.tenant_id,
user_id=asked_by,
)
prefers_external_retrieval = (
str(query_semantics.get("effective_source_policy") or query_semantics.get("source_policy") or "").strip().lower()
== "external"
)
query_semantics = self._mirror_followup_state_to_query_semantics(
query_semantics=query_semantics,
session_state=state_row,
rewrite_meta=rewrite_meta,
)
target_resolution = (
dict(query_semantics.get("target_resolution") or {})
if isinstance(query_semantics.get("target_resolution"), dict)
else {}
)
target_resolver = getattr(self, "_resolve_internal_target", None)
if not prefers_external_retrieval and callable(target_resolver):
target_resolution = await target_resolver(
question=effective_q,
project_id=project_id,
query_semantics=query_semantics,
system_snapshot=system_snapshot,
operation_contract=operation_contract,
)
query_semantics = self._mirror_internal_context_to_query_semantics(
query_semantics=query_semantics,
target_resolution=target_resolution,
)
patch_scene_semantics = getattr(self, "_patch_scene_semantics_with_target_resolution", None)
if callable(patch_scene_semantics):
scene_semantics = patch_scene_semantics(
scene_semantics=scene_semantics,
operation_contract=operation_contract,
target_resolution=target_resolution,
)
internal_query_plan = self._build_internal_query_plan(
question=effective_q,
project_id=project_id,
query_semantics=query_semantics,
operation_contract=operation_contract,
target_resolution=target_resolution,
)
query_semantics = self._mirror_internal_context_to_query_semantics(
query_semantics=query_semantics,
query_plan=internal_query_plan,
)
active_context = (
self._get_active_external_qa_context()
if hasattr(self, "_get_active_external_qa_context")
else {}
)
# Keep query_semantics as a compatibility mirror only; internal execution truth moves to the packet.
internal_context_packet = self._build_internal_query_context_packet(
question=effective_q,
project_id=project_id,
session_id=session_id,
conversation_scope_key=str(active_context.get("conversation_scope_key") or "").strip() or None,
reply_to_message_id=str(active_context.get("reply_to_message_id") or "").strip() or None,
root_message_id=str(active_context.get("root_message_id") or "").strip() or None,
route_decision=route_decision,
operation_contract=operation_contract,
target_resolution=target_resolution,
query_plan=internal_query_plan,
)
precheck_response = await self._maybe_return_internal_context_precheck_response(
question=clean_q,
effective_question=effective_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
session_id=session_id,
context_session_id=context_session_id,
turn_index=turn_index,
state_row=state_row,
rewrite_meta=rewrite_meta,
thinking_mode=active_thinking_mode,
query_type=query_type,
route_decision=route_decision,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
if precheck_response is not None:
return precheck_response
if planner.allow_fast_path and not prefers_external_retrieval:
deterministic_result = await self._run_deterministic_query(
question=effective_q,
project_id=project_id,
query_type=query_type,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
if deterministic_result is not None:
return await self._persist_fast_answer(
question=clean_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
fast_result=deterministic_result,
session_id=session_id,
external_session_id=context_session_id,
turn_index=turn_index,
query_type=str(deterministic_result.get("query_type") or query_type),
missing_fields=list(deterministic_result.get("missing_fields") or []),
state_row=state_row,
effective_question=effective_q,
rewrite_meta=rewrite_meta,
thinking_mode=active_thinking_mode,
answer_mode=ANSWER_MODE_FAST_PATH,
route_decision=route_decision,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
internal_model_config_fast_answer = None
internal_model_config_builder = getattr(self, "_build_internal_model_config_fast_answer", None)
if not prefers_external_retrieval and callable(internal_model_config_builder):
internal_model_config_fast_answer = await internal_model_config_builder(
question=effective_q,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
if internal_model_config_fast_answer is not None:
return await self._persist_fast_answer(
question=clean_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
fast_result=internal_model_config_fast_answer,
session_id=session_id,
external_session_id=context_session_id,
turn_index=turn_index,
query_type=QUERY_TYPE_STATUS,
missing_fields=[],
state_row=state_row,
effective_question=effective_q,
rewrite_meta=rewrite_meta,
thinking_mode=active_thinking_mode,
answer_mode=ANSWER_MODE_FAST_PATH,
route_decision=route_decision,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
direct_answer = None
allow_snapshot_direct_answer = planner.allow_fast_path and not prefers_external_retrieval
snapshot_direct_guard = getattr(self, "_should_allow_snapshot_direct_answer", None)
if callable(snapshot_direct_guard):
allow_snapshot_direct_answer = allow_snapshot_direct_answer and snapshot_direct_guard(
scene_semantics=scene_semantics,
operation_contract=operation_contract,
target_resolution=target_resolution,
)
if allow_snapshot_direct_answer:
direct_answer = self._build_snapshot_direct_answer(
effective_q,
system_snapshot=system_snapshot,
user_snapshot=user_snapshot,
snapshot_focus=str((scene_semantics or {}).get("snapshot_focus") or "none"),
)
direct_result = None
if allow_snapshot_direct_answer:
direct_result = await self._maybe_return_snapshot_direct_response(
direct_answer=direct_answer,
started=started,
clean_q=clean_q,
effective_q=effective_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
session_id=session_id,
turn_index=turn_index,
state_row=state_row,
rewrite_meta=rewrite_meta,
context_session_id=context_session_id,
active_thinking_mode=active_thinking_mode,
user_snapshot=user_snapshot,
route_decision=route_decision,
)
if direct_result is not None:
return direct_result
is_snapshot_direct_question = str((scene_semantics or {}).get("snapshot_focus") or "none").strip().lower() != "none"
if planner.allow_fast_path and not prefers_external_retrieval and not is_snapshot_direct_question:
glm_semantic_result = await self._try_glm_project_semantic_answer(
clean_q=effective_q,
project_id=project_id,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
if glm_semantic_result is not None:
return await self._persist_fast_answer(
question=clean_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
fast_result=glm_semantic_result,
session_id=session_id,
external_session_id=context_session_id,
turn_index=turn_index,
query_type=query_type if query_type != QUERY_TYPE_UNKNOWN else QUERY_TYPE_EXPLAIN,
missing_fields=[],
state_row=state_row,
effective_question=effective_q,
rewrite_meta=rewrite_meta,
thinking_mode=active_thinking_mode,
answer_mode=ANSWER_MODE_FAST_PATH,
route_decision=route_decision,
query_semantics=query_semantics,
internal_context_packet=internal_context_packet,
)
return await self._build_retrieval_response(
clean_q=clean_q,
effective_q=effective_q,
asked_by=asked_by,
asked_via=asked_via,
project_id=project_id,
source_chat_id=source_chat_id,
session_id=session_id,
turn_index=turn_index,
context_session_id=context_session_id,
conversation=conversation,
state_row=state_row,
rewrite_meta=rewrite_meta,
query_type=query_type,
query_semantics=query_semantics,
retrieval_domains=retrieval_domains,
scene_semantics=scene_semantics,
thinking_mode=thinking_mode,
active_thinking_mode=active_thinking_mode,
deep_thinking=deep_thinking,
forced_deep_reason=forced_deep_reason or str(planner.reason or ""),
deep_retrieval_steps=deep_retrieval_steps,
deep_vector_top_k=deep_vector_top_k,
system_snapshot=system_snapshot,
user_snapshot=user_snapshot,
started=started,
message_intent=message_intent,
mixed_signal_part=mixed_signal_part,
answer_mode=planner.retrieval_answer_mode,
consultant_mode_preference=consultant_mode_preference,
route_decision=route_decision,
internal_context_packet=internal_context_packet,
)