"""Adaptive RAG mixin module.""" from __future__ import annotations from .shared import * class AdaptiveRAGAnswerEntryMixin: async def answer_question( self, *, question: str, asked_by: Optional[int], asked_via: str, project_id: Optional[int] = None, source_chat_id: Optional[str] = None, session_id: Optional[int] = None, conversation_key: Optional[str] = None, force_new_session: bool = False, thinking_mode: str = THINKING_MODE_NORMAL, action_mode: str = ACTION_MODE_AUTO, sync_requirement: bool = True, ) -> dict[str, Any]: started = time.perf_counter() clean_q = str(question or "").strip() external_conversation_key = self._build_external_conversation_key( asked_via=asked_via, source_chat_id=source_chat_id, conversation_key=conversation_key, ) active_thinking_mode = self._normalize_thinking_mode(thinking_mode) deep_thinking = active_thinking_mode == THINKING_MODE_DEEP asked_via_lower = str(asked_via or "").strip().lower() deep_retrieval_steps = DEEP_RETRIEVAL_STEPS deep_vector_top_k = DEEP_VECTOR_TOP_K if asked_via_lower == "feishu": # 飞书链路优先低时延,保留 deep 能力但收紧预算。 deep_retrieval_steps = min(DEEP_RETRIEVAL_STEPS, 3) deep_vector_top_k = min(DEEP_VECTOR_TOP_K, 8) if not clean_q: return self._build_response_payload( answer="问题为空,无法检索。", sources=[], domains_used=[], response_time_ms=0, db_query_ms=0, vector_search_ms=0, db_timeout=False, vector_timeout=False, qa_id=None, session_id=None, turn_index=1, relevance_max=0.0, query_type=QUERY_TYPE_UNKNOWN, confidence=0.0, missing_fields=["question"], thinking_mode=active_thinking_mode, ) 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), ) 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_row = await self._ensure_session_state(session_id=session_id) effective_q, rewrite_meta = self._rewrite_question_with_state( question=clean_q, conversation=conversation, session_state=state_row, ) query_type = self._classify_query_type(effective_q) requested_action_mode = self._normalize_action_mode(action_mode) effective_action_mode = self._resolve_action_mode( requested_mode=requested_action_mode, question=clean_q, effective_question=effective_q, ) if ( bool(sync_requirement) and asked_via_lower != "feishu" and effective_action_mode in {ACTION_MODE_WRITE, ACTION_MODE_READ_WRITE} ): ack_result = await self._handle_ack_first_requirement( question=clean_q, effective_question=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, query_type=query_type, action_mode=effective_action_mode, started_at=started, ) if ack_result is not None: return ack_result forced_deep_reason = "" if not deep_thinking: forced_deep_reason = self._forced_deep_reason( question=clean_q, effective_question=effective_q, conversation=conversation, ) if forced_deep_reason and self._should_force_deep_for_channel( asked_via=asked_via, question=clean_q, effective_question=effective_q, reason=forced_deep_reason, ): deep_thinking = True active_thinking_mode = THINKING_MODE_DEEP else: forced_deep_reason = "" clarification_answer = self._build_clarification_question( original_question=clean_q, effective_question=effective_q, session_state=state_row, ) clarification_result = await self._maybe_return_clarification_response( clarification_answer=clarification_answer, 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, ) if clarification_result is not None: return clarification_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, ) if not deep_thinking: deterministic_result = await self._run_deterministic_query( question=effective_q, project_id=project_id, query_type=query_type, ) 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, ) direct_answer = None if not deep_thinking: direct_answer = self._build_snapshot_direct_answer( effective_q, system_snapshot=system_snapshot, user_snapshot=user_snapshot, ) 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, ) if direct_result is not None: return direct_result is_snapshot_direct_question = self._is_snapshot_direct_question(effective_q) if not deep_thinking and not is_snapshot_direct_question: glm_semantic_result = await self._try_glm_project_semantic_answer(clean_q=effective_q, project_id=project_id) 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, ) 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, thinking_mode=thinking_mode, active_thinking_mode=active_thinking_mode, deep_thinking=deep_thinking, forced_deep_reason=forced_deep_reason, deep_retrieval_steps=deep_retrieval_steps, deep_vector_top_k=deep_vector_top_k, system_snapshot=system_snapshot, user_snapshot=user_snapshot, started=started, )