177 lines
6.5 KiB
Python
177 lines
6.5 KiB
Python
"""Adaptive RAG mixin module."""
|
|
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
|
|
|
|
class AdaptiveRAGAnswerEntryMixin:
|
|
HIGH_ACCURACY_CHANNELS = {
|
|
"feishu",
|
|
"lark",
|
|
"feishu_group",
|
|
"feishu_dm",
|
|
"pmflow",
|
|
"pmflow_manual",
|
|
"/ai-pmo/process-text",
|
|
"ai-pmo-process-text",
|
|
"ai_pmo",
|
|
"ai_pmo_ui",
|
|
}
|
|
|
|
@classmethod
|
|
def _is_high_accuracy_channel(cls, asked_via: str) -> bool:
|
|
return str(asked_via or "").strip().lower() in cls.HIGH_ACCURACY_CHANNELS
|
|
|
|
@staticmethod
|
|
def _should_enforce_scene_prompt_only(
|
|
*,
|
|
asked_via: str,
|
|
message_intent: Optional[str],
|
|
) -> bool:
|
|
intent = str(message_intent or "").strip().lower()
|
|
return intent == "mixed"
|
|
|
|
@staticmethod
|
|
def _should_trigger_write_ack_gate(
|
|
*,
|
|
sync_requirement: bool,
|
|
asked_via: str,
|
|
effective_action_mode: str,
|
|
message_intent: Optional[str] = None,
|
|
) -> bool:
|
|
intent = str(message_intent or "").strip().lower()
|
|
if intent in {"mixed", "question"}:
|
|
return False
|
|
return (
|
|
bool(sync_requirement)
|
|
and str(asked_via or "").strip().lower() != "feishu"
|
|
and str(effective_action_mode or "").strip().lower() in {ACTION_MODE_WRITE, ACTION_MODE_READ_WRITE}
|
|
)
|
|
|
|
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,
|
|
source_message_id: Optional[str] = None,
|
|
session_id: Optional[int] = None,
|
|
conversation_key: Optional[str] = None,
|
|
source_sender_open_id: Optional[str] = None,
|
|
thread_context: Optional[dict[str, Any]] = None,
|
|
force_new_session: bool = False,
|
|
thinking_mode: str = THINKING_MODE_NORMAL,
|
|
action_mode: str = ACTION_MODE_AUTO,
|
|
sync_requirement: bool = True,
|
|
message_intent: Optional[str] = None,
|
|
mixed_signal_part: Optional[str] = None,
|
|
consultant_mode_preference: Optional[str] = None,
|
|
) -> dict[str, Any]:
|
|
started = time.perf_counter()
|
|
clean_q = str(question or "").strip()
|
|
active_thinking_mode = self._normalize_thinking_mode(thinking_mode)
|
|
deep_thinking = active_thinking_mode == THINKING_MODE_DEEP
|
|
enforce_scene_prompt_only = self._should_enforce_scene_prompt_only(
|
|
asked_via=asked_via,
|
|
message_intent=message_intent,
|
|
)
|
|
deep_retrieval_steps = DEEP_RETRIEVAL_STEPS
|
|
deep_vector_top_k = DEEP_VECTOR_TOP_K
|
|
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,
|
|
)
|
|
|
|
runtime = await self._prepare_answer_runtime(
|
|
clean_q=clean_q,
|
|
asked_by=asked_by,
|
|
asked_via=asked_via,
|
|
project_id=project_id,
|
|
source_chat_id=source_chat_id,
|
|
source_message_id=source_message_id,
|
|
session_id=session_id,
|
|
conversation_key=conversation_key,
|
|
force_new_session=force_new_session,
|
|
action_mode=action_mode,
|
|
source_sender_open_id=source_sender_open_id,
|
|
thread_context=thread_context,
|
|
)
|
|
clean_q = str(runtime.get("resolved_question") or clean_q).strip() or clean_q
|
|
session_id = runtime["session_id"]
|
|
turn_index = int(runtime["turn_index"] or 1)
|
|
context_session_id = runtime["context_session_id"]
|
|
conversation = runtime["conversation"]
|
|
state_row = runtime["state_row"]
|
|
effective_q = str(runtime["effective_q"] or "").strip()
|
|
rewrite_meta = runtime["rewrite_meta"] if isinstance(runtime["rewrite_meta"], dict) else {}
|
|
query_type = str(runtime["query_type"] or QUERY_TYPE_UNKNOWN)
|
|
effective_action_mode = str(runtime["effective_action_mode"] or ACTION_MODE_AUTO).strip().lower()
|
|
anchor_resolution = runtime["anchor_resolution"]
|
|
|
|
if self._should_trigger_write_ack_gate(
|
|
sync_requirement=sync_requirement,
|
|
asked_via=asked_via,
|
|
effective_action_mode=effective_action_mode,
|
|
message_intent=message_intent,
|
|
):
|
|
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
|
|
return await self._execute_base_answer_mainline(
|
|
clean_q=clean_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,
|
|
effective_q=effective_q,
|
|
rewrite_meta=rewrite_meta,
|
|
query_type=query_type,
|
|
thinking_mode=active_thinking_mode,
|
|
active_thinking_mode=active_thinking_mode,
|
|
deep_thinking=deep_thinking,
|
|
enforce_scene_prompt_only=enforce_scene_prompt_only,
|
|
deep_retrieval_steps=deep_retrieval_steps,
|
|
deep_vector_top_k=deep_vector_top_k,
|
|
started=started,
|
|
message_intent=message_intent,
|
|
mixed_signal_part=mixed_signal_part,
|
|
anchor_resolution=anchor_resolution,
|
|
consultant_mode_preference=consultant_mode_preference,
|
|
)
|