851 lines
36 KiB
Python
851 lines
36 KiB
Python
"""Adaptive RAG internal target resolution helpers."""
|
|
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
from .internal_query_context import (
|
|
internal_context_operation_contract,
|
|
internal_context_query_plan,
|
|
internal_context_target_resolution,
|
|
)
|
|
|
|
|
|
class AdaptiveRAGTargetResolutionMixin:
|
|
_TARGET_ENTITY_TYPES = {"project", "feature", "config", "doc", "person", "signal", "unknown"}
|
|
_TARGET_RESOLUTION_MODES = {"deterministic", "llm_rerank", "semantic_filter", "clarify", "none"}
|
|
|
|
@classmethod
|
|
def _normalize_object_mentions(
|
|
cls,
|
|
raw: Any,
|
|
*,
|
|
question: str = "",
|
|
) -> list[str]:
|
|
_ = (cls, question)
|
|
mentions: list[str] = []
|
|
rows = raw if isinstance(raw, list) else []
|
|
for item in rows:
|
|
text = normalize_lookup_fragment(item, limit=32)
|
|
if len(text) >= 2 and text not in mentions:
|
|
mentions.append(text)
|
|
return mentions[:4]
|
|
|
|
def _build_operation_contract(
|
|
self,
|
|
*,
|
|
question: str,
|
|
query_semantics: Optional[dict[str, Any]],
|
|
) -> dict[str, Any]:
|
|
_ = question
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
query_type = str(semantics.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN
|
|
query_profile = dict(semantics.get("query_profile") or {}) if isinstance(semantics.get("query_profile"), dict) else {}
|
|
referenced_objects = {
|
|
str(item or "").strip().lower()
|
|
for item in list(query_profile.get("referenced_objects") or [])
|
|
if str(item or "").strip()
|
|
}
|
|
if query_type in {QUERY_TYPE_EXISTENCE, QUERY_TYPE_UNKNOWN} and referenced_objects.intersection(
|
|
{"model_routing", "system_config"}
|
|
):
|
|
query_type = QUERY_TYPE_STATUS
|
|
object_mentions = self._normalize_object_mentions(
|
|
semantics.get("object_mentions"),
|
|
question=question,
|
|
)
|
|
return {
|
|
"query_type": query_type,
|
|
"answer_contract": (
|
|
self._internal_answer_contract_for_query_type(query_type)
|
|
if hasattr(self, "_internal_answer_contract_for_query_type")
|
|
else "clarify_or_fail"
|
|
),
|
|
"query_profile": query_profile,
|
|
"object_mentions": object_mentions,
|
|
"clarify_policy": "ask_when_target_ambiguous",
|
|
}
|
|
|
|
@staticmethod
|
|
def _referenced_objects(query_semantics: Optional[dict[str, Any]]) -> set[str]:
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
query_profile = semantics.get("query_profile") if isinstance(semantics.get("query_profile"), dict) else {}
|
|
return {
|
|
str(item or "").strip().lower()
|
|
for item in list(query_profile.get("referenced_objects") or [])
|
|
if str(item or "").strip()
|
|
}
|
|
|
|
def _build_session_followup_target(
|
|
self,
|
|
*,
|
|
query_semantics: Optional[dict[str, Any]],
|
|
operation_contract: dict[str, Any],
|
|
) -> Optional[dict[str, Any]]:
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
followup_context = (
|
|
semantics.get("session_followup_context")
|
|
if isinstance(semantics.get("session_followup_context"), dict)
|
|
else {}
|
|
)
|
|
rewrite_mode = str(followup_context.get("rewrite_mode") or "").strip().lower()
|
|
if rewrite_mode not in {"state_subject", "state_entities"}:
|
|
return None
|
|
|
|
entity_ids: list[int] = []
|
|
for value in list(followup_context.get("last_entity_ids") or []):
|
|
try:
|
|
entity_ids.append(int(value))
|
|
except Exception:
|
|
continue
|
|
if len(entity_ids) != 1:
|
|
return None
|
|
|
|
subject_focus = str(semantics.get("subject_focus") or "unknown").strip().lower()
|
|
referenced_objects = self._referenced_objects(semantics)
|
|
if subject_focus != "project" and "project_object" not in referenced_objects:
|
|
return None
|
|
|
|
mentions = list(operation_contract.get("object_mentions") or [])
|
|
resolved_label = (
|
|
str(followup_context.get("last_subject") or "").strip()
|
|
or (str(mentions[0]).strip() if mentions else "")
|
|
or None
|
|
)
|
|
resolved_project_id = entity_ids[0]
|
|
return {
|
|
"target_entity_type": "project",
|
|
"target_ref": f"project:{resolved_project_id}",
|
|
"resolved_label": resolved_label,
|
|
"resolution_mode": "deterministic",
|
|
"binding_source": "session_followup_context",
|
|
"confidence": 0.93 if rewrite_mode == "state_subject" else 0.88,
|
|
"candidate_summary": [],
|
|
}
|
|
|
|
@classmethod
|
|
def _normalize_target_candidate(cls, payload: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
|
|
row = payload if isinstance(payload, dict) else {}
|
|
entity_type = str(row.get("target_entity_type") or row.get("entity_type") or "").strip().lower()
|
|
if entity_type not in cls._TARGET_ENTITY_TYPES:
|
|
return None
|
|
score = max(0.0, min(1.0, float(row.get("score") or 0.0)))
|
|
return {
|
|
"target_entity_type": entity_type,
|
|
"target_ref": str(row.get("target_ref") or "").strip() or None,
|
|
"resolved_label": str(row.get("resolved_label") or row.get("display_name") or "").strip() or None,
|
|
"score": round(score, 4),
|
|
"source_kind": str(row.get("source_kind") or "unknown").strip().lower() or "unknown",
|
|
"match_label": str(row.get("match_label") or "").strip() or None,
|
|
"source_confidence": round(max(0.0, min(1.0, float(row.get("source_confidence") or score or 0.0))), 4),
|
|
}
|
|
|
|
@staticmethod
|
|
def _target_candidate_identity(row: dict[str, Any]) -> str:
|
|
return "|".join(
|
|
[
|
|
str(row.get("target_entity_type") or ""),
|
|
str(row.get("target_ref") or ""),
|
|
str(row.get("resolved_label") or ""),
|
|
]
|
|
)
|
|
|
|
async def _collect_semantic_fact_target_candidates(
|
|
self,
|
|
*,
|
|
mentions: list[str],
|
|
project_id: Optional[int],
|
|
operation_contract: dict[str, Any],
|
|
) -> list[dict[str, Any]]:
|
|
if not mentions:
|
|
return []
|
|
candidates: list[dict[str, Any]] = []
|
|
fact_query = getattr(self, "_query_project_semantic_facts_by_lookup_terms", None)
|
|
if not callable(fact_query):
|
|
return []
|
|
for mention in mentions[:3]:
|
|
fact_rows, _used_keyword = await fact_query(
|
|
query_terms=[mention],
|
|
project_id=project_id,
|
|
limit=10,
|
|
)
|
|
project_ids: list[int] = []
|
|
fact_confidence_by_project: dict[int, float] = {}
|
|
for row in fact_rows:
|
|
try:
|
|
entity_id = int(row.entity_id)
|
|
except Exception:
|
|
continue
|
|
if entity_id not in project_ids:
|
|
project_ids.append(entity_id)
|
|
fact_confidence_by_project[entity_id] = max(
|
|
float(fact_confidence_by_project.get(entity_id) or 0.0),
|
|
float(getattr(row, "confidence", 0.0) or 0.0),
|
|
)
|
|
if not project_ids:
|
|
continue
|
|
project_rows = (
|
|
await self.db.execute(
|
|
select(Project).where(
|
|
and_(
|
|
Project.tenant_id == self.tenant_id,
|
|
Project.id.in_(project_ids),
|
|
)
|
|
)
|
|
)
|
|
).scalars().all()
|
|
for project in project_rows:
|
|
confidence = max(0.88, min(0.99, float(fact_confidence_by_project.get(int(project.id)) or 0.0)))
|
|
candidates.append(
|
|
{
|
|
"target_entity_type": "project",
|
|
"target_ref": f"project:{int(project.id)}",
|
|
"resolved_label": str(project.title or project.name or f"项目#{int(project.id)}"),
|
|
"score": round(min(0.99, confidence + 0.03), 4),
|
|
"source_kind": "semantic_facts",
|
|
"match_label": mention,
|
|
"source_confidence": round(confidence, 4),
|
|
}
|
|
)
|
|
return candidates
|
|
|
|
async def _collect_project_target_candidates(
|
|
self,
|
|
*,
|
|
mentions: list[str],
|
|
project_id: Optional[int],
|
|
) -> list[dict[str, Any]]:
|
|
if not mentions:
|
|
return []
|
|
candidates: list[dict[str, Any]] = []
|
|
similarity_scorer = getattr(self, "_lookup_similarity_score", None)
|
|
for mention in mentions[:3]:
|
|
matched_rows: list[Project] = []
|
|
searcher = getattr(self, "_search_projects_by_keyword", None)
|
|
if callable(searcher):
|
|
matched_rows = await searcher(keyword=mention, project_id=project_id, limit=6)
|
|
else:
|
|
stmt = select(Project).where(Project.tenant_id == self.tenant_id)
|
|
if project_id is not None:
|
|
stmt = stmt.where(Project.id == int(project_id))
|
|
like = f"%{mention}%"
|
|
stmt = stmt.where(
|
|
or_(
|
|
Project.title.ilike(like),
|
|
Project.name.ilike(like),
|
|
Project.description.ilike(like),
|
|
)
|
|
)
|
|
matched_rows = (await self.db.execute(stmt.order_by(Project.updated_at.desc()).limit(6))).scalars().all()
|
|
if not matched_rows:
|
|
stmt = select(Project).where(Project.tenant_id == self.tenant_id)
|
|
if project_id is not None:
|
|
stmt = stmt.where(Project.id == int(project_id))
|
|
project_rows = (
|
|
await self.db.execute(
|
|
stmt.order_by(Project.updated_at.desc(), Project.id.desc()).limit(120)
|
|
)
|
|
).scalars().all()
|
|
scored_rows: list[tuple[float, Project]] = []
|
|
for project in project_rows:
|
|
title = str(project.title or "").strip()
|
|
name = str(project.name or "").strip()
|
|
description = str(project.description or "").strip()
|
|
if callable(similarity_scorer):
|
|
score = max(
|
|
similarity_scorer(mention, title),
|
|
similarity_scorer(mention, name),
|
|
similarity_scorer(mention, description) * 0.8,
|
|
)
|
|
else:
|
|
lowered = mention.lower()
|
|
title_l = title.lower()
|
|
name_l = name.lower()
|
|
description_l = description.lower()
|
|
score = 0.0
|
|
if lowered and (lowered in title_l or lowered in name_l):
|
|
score = 0.9
|
|
elif lowered and lowered in description_l:
|
|
score = 0.76
|
|
if score >= 0.62:
|
|
scored_rows.append((score, project))
|
|
scored_rows.sort(key=lambda item: item[0], reverse=True)
|
|
matched_rows = [row for _, row in scored_rows[:6]]
|
|
for project in matched_rows:
|
|
title = str(project.title or "").strip().lower()
|
|
name = str(project.name or "").strip().lower()
|
|
description = str(project.description or "").strip().lower()
|
|
lowered = mention.lower()
|
|
score = 0.82
|
|
if callable(similarity_scorer):
|
|
score = max(
|
|
0.74,
|
|
similarity_scorer(mention, title) if title else 0.0,
|
|
similarity_scorer(mention, name) if name else 0.0,
|
|
(similarity_scorer(mention, description) * 0.8) if description else 0.0,
|
|
)
|
|
elif lowered and (lowered in title or lowered in name):
|
|
score = 0.91
|
|
elif lowered and lowered in description:
|
|
score = 0.86
|
|
candidates.append(
|
|
{
|
|
"target_entity_type": "project",
|
|
"target_ref": f"project:{int(project.id)}",
|
|
"resolved_label": str(project.title or project.name or f"项目#{int(project.id)}"),
|
|
"score": round(score, 4),
|
|
"source_kind": "project_db",
|
|
"match_label": mention,
|
|
"source_confidence": round(score, 4),
|
|
}
|
|
)
|
|
return candidates
|
|
|
|
def _collect_feature_target_candidates(
|
|
self,
|
|
*,
|
|
question: str,
|
|
mentions: list[str],
|
|
system_snapshot: Optional[dict[str, Any]],
|
|
) -> list[dict[str, Any]]:
|
|
matcher = getattr(self, "_match_feature_catalog_entries", None)
|
|
if not callable(matcher):
|
|
return []
|
|
search_text = " ".join(mentions[:2]) if mentions else question
|
|
rows = matcher(
|
|
question=search_text,
|
|
system_snapshot=system_snapshot,
|
|
limit=4,
|
|
)
|
|
candidates: list[dict[str, Any]] = []
|
|
for row in rows:
|
|
name = str(row.get("name") or "").strip()
|
|
route = str(row.get("route") or "").strip()
|
|
match_score = max(0.0, min(1.0, float(row.get("match_score") or 0.0)))
|
|
if not name and not route:
|
|
continue
|
|
score = min(0.92, max(0.72, 0.62 + match_score * 0.25))
|
|
target_key = route or name
|
|
candidates.append(
|
|
{
|
|
"target_entity_type": "feature",
|
|
"target_ref": f"feature:{target_key}",
|
|
"resolved_label": name or target_key,
|
|
"score": round(score, 4),
|
|
"source_kind": "feature_catalog",
|
|
"match_label": name or target_key,
|
|
"source_confidence": round(match_score or score, 4),
|
|
}
|
|
)
|
|
return candidates
|
|
|
|
def _collect_config_target_candidates(
|
|
self,
|
|
*,
|
|
query_semantics: Optional[dict[str, Any]],
|
|
operation_contract: dict[str, Any],
|
|
) -> list[dict[str, Any]]:
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
query_profile = semantics.get("query_profile") if isinstance(semantics.get("query_profile"), dict) else {}
|
|
referenced_objects = {
|
|
str(item or "").strip().lower()
|
|
for item in list(query_profile.get("referenced_objects") or [])
|
|
if str(item or "").strip()
|
|
}
|
|
if not {"model_routing", "system_config"}.intersection(referenced_objects):
|
|
return []
|
|
mentions = list(operation_contract.get("object_mentions") or [])
|
|
rows: list[dict[str, Any]] = []
|
|
if "model_routing" in referenced_objects:
|
|
rows.append(
|
|
{
|
|
"target_entity_type": "config",
|
|
"target_ref": "config:model_routing",
|
|
"resolved_label": mentions[0] if mentions else "模型配置",
|
|
"score": 0.9,
|
|
"source_kind": "system_config",
|
|
"match_label": mentions[0] if mentions else "模型配置",
|
|
"source_confidence": 0.9,
|
|
}
|
|
)
|
|
elif "system_config" in referenced_objects:
|
|
rows.append(
|
|
{
|
|
"target_entity_type": "config",
|
|
"target_ref": "config:system",
|
|
"resolved_label": mentions[0] if mentions else "系统配置",
|
|
"score": 0.86,
|
|
"source_kind": "system_config",
|
|
"match_label": mentions[0] if mentions else "系统配置",
|
|
"source_confidence": 0.86,
|
|
}
|
|
)
|
|
return rows
|
|
|
|
async def _collect_kg_target_candidates(
|
|
self,
|
|
*,
|
|
question: str,
|
|
mentions: list[str],
|
|
project_id: Optional[int],
|
|
) -> list[dict[str, Any]]:
|
|
resolver = getattr(self, "entity_resolution", None)
|
|
if not callable(resolver):
|
|
return []
|
|
resolution = await resolver(
|
|
question=" ".join(mentions[:2]) if mentions else question,
|
|
project_id=project_id,
|
|
limit=6,
|
|
)
|
|
matched_entities = resolution.get("matched_entities") if isinstance(resolution, dict) else []
|
|
if not isinstance(matched_entities, list) or not matched_entities:
|
|
return []
|
|
ids: list[int] = []
|
|
matched_score: dict[int, float] = {}
|
|
for row in matched_entities:
|
|
if not isinstance(row, dict):
|
|
continue
|
|
try:
|
|
entity_id = int(row.get("entity_id"))
|
|
except Exception:
|
|
continue
|
|
ids.append(entity_id)
|
|
matched_score[entity_id] = max(
|
|
float(matched_score.get(entity_id) or 0.0),
|
|
float(row.get("score") or 0.0),
|
|
)
|
|
if not ids:
|
|
return []
|
|
rows = (
|
|
await self.db.execute(
|
|
select(KGEntity).where(
|
|
and_(
|
|
KGEntity.tenant_id == self.tenant_id,
|
|
KGEntity.id.in_(ids),
|
|
)
|
|
)
|
|
)
|
|
).scalars().all()
|
|
candidates: list[dict[str, Any]] = []
|
|
for row in rows:
|
|
payload = self._safe_dict(row.payload) if hasattr(self, "_safe_dict") else (row.payload if isinstance(row.payload, dict) else {})
|
|
entity_score = max(0.0, min(1.0, float(matched_score.get(int(row.id)) or 0.0)))
|
|
project_ref = payload.get("project_id")
|
|
if project_ref is not None:
|
|
try:
|
|
project_ref = int(project_ref)
|
|
except Exception:
|
|
project_ref = None
|
|
if project_ref is not None:
|
|
candidates.append(
|
|
{
|
|
"target_entity_type": "project",
|
|
"target_ref": f"project:{int(project_ref)}",
|
|
"resolved_label": str(row.display_name or row.entity_key or f"项目#{int(project_ref)}"),
|
|
"score": round(min(0.89, max(0.68, 0.58 + entity_score * 0.25)), 4),
|
|
"source_kind": "knowledge_graph",
|
|
"match_label": str(row.display_name or row.entity_key or "").strip() or None,
|
|
"source_confidence": round(entity_score or 0.68, 4),
|
|
}
|
|
)
|
|
return candidates
|
|
|
|
async def _collect_internal_target_candidates(
|
|
self,
|
|
*,
|
|
question: str,
|
|
project_id: Optional[int],
|
|
query_semantics: Optional[dict[str, Any]],
|
|
system_snapshot: Optional[dict[str, Any]],
|
|
operation_contract: dict[str, Any],
|
|
) -> list[dict[str, Any]]:
|
|
_ = query_semantics
|
|
mentions = list(operation_contract.get("object_mentions") or [])
|
|
providers = await asyncio.gather(
|
|
self._collect_semantic_fact_target_candidates(
|
|
mentions=mentions,
|
|
project_id=project_id,
|
|
operation_contract=operation_contract,
|
|
),
|
|
self._collect_project_target_candidates(
|
|
mentions=mentions,
|
|
project_id=project_id,
|
|
),
|
|
self._collect_kg_target_candidates(
|
|
question=question,
|
|
mentions=mentions,
|
|
project_id=project_id,
|
|
),
|
|
)
|
|
rows: list[dict[str, Any]] = []
|
|
for bucket in providers:
|
|
for item in bucket:
|
|
normalized = self._normalize_target_candidate(item)
|
|
if normalized is not None:
|
|
rows.append(normalized)
|
|
for item in self._collect_feature_target_candidates(
|
|
question=question,
|
|
mentions=mentions,
|
|
system_snapshot=system_snapshot,
|
|
):
|
|
normalized = self._normalize_target_candidate(item)
|
|
if normalized is not None:
|
|
rows.append(normalized)
|
|
for item in self._collect_config_target_candidates(
|
|
query_semantics=query_semantics,
|
|
operation_contract=operation_contract,
|
|
):
|
|
normalized = self._normalize_target_candidate(item)
|
|
if normalized is not None:
|
|
rows.append(normalized)
|
|
|
|
deduped: dict[str, dict[str, Any]] = {}
|
|
for row in rows:
|
|
key = self._target_candidate_identity(row)
|
|
existing = deduped.get(key)
|
|
if existing is None or float(row.get("score") or 0.0) > float(existing.get("score") or 0.0):
|
|
deduped[key] = row
|
|
return sorted(deduped.values(), key=lambda item: float(item.get("score") or 0.0), reverse=True)[:8]
|
|
|
|
async def _rerank_internal_target_candidates(
|
|
self,
|
|
*,
|
|
question: str,
|
|
operation_contract: dict[str, Any],
|
|
candidates: list[dict[str, Any]],
|
|
) -> Optional[dict[str, Any]]:
|
|
if len(candidates) < 2 or not hasattr(self, "llm"):
|
|
return None
|
|
prompt = PROMPT_INTERNAL_TARGET_RESOLUTION.format(
|
|
question=str(question or "")[:600],
|
|
operation_contract=json.dumps(operation_contract, ensure_ascii=False)[:1000],
|
|
candidates=json.dumps(
|
|
[
|
|
{
|
|
"index": idx,
|
|
"target_entity_type": str(item.get("target_entity_type") or ""),
|
|
"target_ref": str(item.get("target_ref") or ""),
|
|
"resolved_label": str(item.get("resolved_label") or ""),
|
|
"score": float(item.get("score") or 0.0),
|
|
"source_kind": str(item.get("source_kind") or ""),
|
|
}
|
|
for idx, item in enumerate(candidates[:4])
|
|
],
|
|
ensure_ascii=False,
|
|
),
|
|
).strip()
|
|
try:
|
|
raw = await self.llm.generate(
|
|
prompt,
|
|
db=self.db,
|
|
tenant_id=self.tenant_id,
|
|
function_name="internal_target_resolution",
|
|
temperature=0.0,
|
|
max_tokens=220,
|
|
use_prompt_profile=False,
|
|
)
|
|
except Exception:
|
|
return None
|
|
parser = getattr(self, "_parse_json_object_safe", None)
|
|
parsed = parser(raw) if callable(parser) else {}
|
|
if not isinstance(parsed, dict):
|
|
return None
|
|
if bool(parsed.get("clarify")):
|
|
return {
|
|
"target_entity_type": "unknown",
|
|
"target_ref": None,
|
|
"resolved_label": None,
|
|
"resolution_mode": "clarify",
|
|
"confidence": round(max(0.0, min(1.0, float(parsed.get("confidence") or 0.0))), 4),
|
|
"candidate_summary": candidates[:4],
|
|
}
|
|
try:
|
|
selected_index = int(parsed.get("selected_index"))
|
|
except Exception:
|
|
return None
|
|
if selected_index < 0 or selected_index >= len(candidates[:4]):
|
|
return None
|
|
selected = dict(candidates[selected_index])
|
|
return {
|
|
"target_entity_type": str(selected.get("target_entity_type") or "unknown"),
|
|
"target_ref": selected.get("target_ref"),
|
|
"resolved_label": selected.get("resolved_label"),
|
|
"resolution_mode": "llm_rerank",
|
|
"confidence": round(max(0.0, min(1.0, float(parsed.get("confidence") or selected.get("score") or 0.0))), 4),
|
|
"candidate_summary": candidates[:4],
|
|
}
|
|
|
|
def _build_semantic_filter_target(
|
|
self,
|
|
*,
|
|
query_semantics: Optional[dict[str, Any]],
|
|
operation_contract: dict[str, Any],
|
|
) -> Optional[dict[str, Any]]:
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
subject_focus = str(semantics.get("subject_focus") or "unknown").strip().lower()
|
|
query_profile = semantics.get("query_profile") if isinstance(semantics.get("query_profile"), dict) else {}
|
|
mentions = list(operation_contract.get("object_mentions") or [])
|
|
resolved_label = mentions[0] if mentions else None
|
|
referenced_objects = {
|
|
str(item or "").strip().lower()
|
|
for item in list(query_profile.get("referenced_objects") or [])
|
|
if str(item or "").strip()
|
|
}
|
|
|
|
if {"model_routing", "system_config"}.intersection(referenced_objects):
|
|
target_ref = "config:model_routing" if "model_routing" in referenced_objects else "config:system"
|
|
return {
|
|
"target_entity_type": "config",
|
|
"target_ref": target_ref,
|
|
"resolved_label": resolved_label or ("模型配置" if target_ref.endswith("model_routing") else "系统配置"),
|
|
"resolution_mode": "deterministic",
|
|
"confidence": 0.84,
|
|
"candidate_summary": [],
|
|
}
|
|
if subject_focus == "project":
|
|
return {
|
|
"target_entity_type": "project",
|
|
"target_ref": None,
|
|
"resolved_label": resolved_label,
|
|
"resolution_mode": "semantic_filter",
|
|
"confidence": 0.78 if resolved_label else 0.64,
|
|
"candidate_summary": [],
|
|
}
|
|
if subject_focus == "feature" or "feature_catalog" in referenced_objects:
|
|
return {
|
|
"target_entity_type": "feature",
|
|
"target_ref": None,
|
|
"resolved_label": resolved_label,
|
|
"resolution_mode": "semantic_filter",
|
|
"confidence": 0.74 if resolved_label else 0.62,
|
|
"candidate_summary": [],
|
|
}
|
|
return None
|
|
|
|
async def _resolve_internal_target(
|
|
self,
|
|
*,
|
|
question: str,
|
|
project_id: Optional[int],
|
|
query_semantics: Optional[dict[str, Any]],
|
|
system_snapshot: Optional[dict[str, Any]],
|
|
operation_contract: Optional[dict[str, Any]],
|
|
) -> dict[str, Any]:
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
operation = operation_contract if isinstance(operation_contract, dict) else {}
|
|
source_policy = str(
|
|
semantics.get("effective_source_policy")
|
|
or semantics.get("source_policy")
|
|
or ""
|
|
).strip().lower()
|
|
if source_policy != "internal":
|
|
return {
|
|
"target_entity_type": "unknown",
|
|
"target_ref": None,
|
|
"resolved_label": None,
|
|
"resolution_mode": "none",
|
|
"confidence": 0.0,
|
|
"candidate_summary": [],
|
|
}
|
|
|
|
session_bound_target = self._build_session_followup_target(
|
|
query_semantics=semantics,
|
|
operation_contract=operation,
|
|
)
|
|
if session_bound_target is not None:
|
|
return session_bound_target
|
|
|
|
candidates = await self._collect_internal_target_candidates(
|
|
question=question,
|
|
project_id=project_id,
|
|
query_semantics=semantics,
|
|
system_snapshot=system_snapshot,
|
|
operation_contract=operation,
|
|
)
|
|
if not candidates:
|
|
fallback = self._build_semantic_filter_target(
|
|
query_semantics=semantics,
|
|
operation_contract=operation,
|
|
)
|
|
if fallback is not None:
|
|
return fallback
|
|
return {
|
|
"target_entity_type": "unknown",
|
|
"target_ref": None,
|
|
"resolved_label": None,
|
|
"resolution_mode": "clarify",
|
|
"confidence": 0.0,
|
|
"candidate_summary": [],
|
|
}
|
|
|
|
top = candidates[0]
|
|
second = candidates[1] if len(candidates) > 1 else None
|
|
margin = float(top.get("score") or 0.0) - float(second.get("score") or 0.0) if second else 1.0
|
|
if second and str(top.get("target_entity_type") or "") != str(second.get("target_entity_type") or "") and margin < 0.1:
|
|
reranked = await self._rerank_internal_target_candidates(
|
|
question=question,
|
|
operation_contract=operation,
|
|
candidates=candidates,
|
|
)
|
|
if reranked is not None:
|
|
return reranked
|
|
if float(top.get("score") or 0.0) >= 0.88 or margin >= 0.12 or second is None:
|
|
return {
|
|
"target_entity_type": str(top.get("target_entity_type") or "unknown"),
|
|
"target_ref": top.get("target_ref"),
|
|
"resolved_label": top.get("resolved_label"),
|
|
"resolution_mode": "deterministic",
|
|
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
|
|
"candidate_summary": candidates[:4],
|
|
}
|
|
if str(top.get("target_entity_type") or "") == str(second.get("target_entity_type") or ""):
|
|
return {
|
|
"target_entity_type": str(top.get("target_entity_type") or "unknown"),
|
|
"target_ref": top.get("target_ref"),
|
|
"resolved_label": top.get("resolved_label"),
|
|
"resolution_mode": "deterministic",
|
|
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
|
|
"candidate_summary": candidates[:4],
|
|
}
|
|
return {
|
|
"target_entity_type": "unknown",
|
|
"target_ref": None,
|
|
"resolved_label": None,
|
|
"resolution_mode": "clarify",
|
|
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
|
|
"candidate_summary": candidates[:4],
|
|
}
|
|
|
|
def _patch_scene_semantics_with_target_resolution(
|
|
self,
|
|
*,
|
|
scene_semantics: Optional[dict[str, Any]],
|
|
operation_contract: Optional[dict[str, Any]],
|
|
target_resolution: Optional[dict[str, Any]],
|
|
) -> dict[str, Any]:
|
|
normalized = dict(scene_semantics or {})
|
|
query_type = str((operation_contract or {}).get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower()
|
|
target_entity_type = str((target_resolution or {}).get("target_entity_type") or "unknown").strip().lower()
|
|
snapshot_focus = str(normalized.get("snapshot_focus") or "none").strip().lower()
|
|
if query_type == QUERY_TYPE_EXISTENCE and target_entity_type == "project":
|
|
normalized["snapshot_focus"] = "none"
|
|
return normalized
|
|
if target_entity_type not in {"feature", "config"} and snapshot_focus == "feature":
|
|
normalized["snapshot_focus"] = "none"
|
|
return normalized
|
|
return normalized
|
|
|
|
def _should_allow_snapshot_direct_answer(
|
|
self,
|
|
*,
|
|
scene_semantics: Optional[dict[str, Any]],
|
|
operation_contract: Optional[dict[str, Any]],
|
|
target_resolution: Optional[dict[str, Any]],
|
|
) -> bool:
|
|
snapshot_focus = str((scene_semantics or {}).get("snapshot_focus") or "none").strip().lower()
|
|
query_type = str((operation_contract or {}).get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower()
|
|
target_entity_type = str((target_resolution or {}).get("target_entity_type") or "unknown").strip().lower()
|
|
if snapshot_focus in {"identity", "architecture"}:
|
|
return query_type in {QUERY_TYPE_EXPLAIN, QUERY_TYPE_STATUS}
|
|
if snapshot_focus == "feature":
|
|
return query_type in {QUERY_TYPE_EXPLAIN, QUERY_TYPE_STATUS, QUERY_TYPE_LIST} and target_entity_type in {
|
|
"feature",
|
|
"config",
|
|
"unknown",
|
|
}
|
|
return False
|
|
|
|
@staticmethod
|
|
def _build_operation_contract_summary(operation_contract: Optional[dict[str, Any]]) -> dict[str, Any]:
|
|
payload = operation_contract if isinstance(operation_contract, dict) else {}
|
|
mentions = [
|
|
str(item or "").strip()
|
|
for item in list(payload.get("object_mentions") or [])
|
|
if str(item or "").strip()
|
|
]
|
|
return {
|
|
"query_type": str(payload.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN,
|
|
"answer_contract": str(payload.get("answer_contract") or "clarify_or_fail").strip() or "clarify_or_fail",
|
|
"object_mentions": mentions[:4],
|
|
"clarify_policy": str(payload.get("clarify_policy") or "").strip() or None,
|
|
}
|
|
|
|
@staticmethod
|
|
def _build_target_resolution_summary(target_resolution: Optional[dict[str, Any]]) -> dict[str, Any]:
|
|
payload = target_resolution if isinstance(target_resolution, dict) else {}
|
|
candidates: list[dict[str, Any]] = []
|
|
for item in list(payload.get("candidate_summary") or [])[:4]:
|
|
if not isinstance(item, dict):
|
|
continue
|
|
candidates.append(
|
|
{
|
|
"target_entity_type": str(item.get("target_entity_type") or "unknown").strip().lower() or "unknown",
|
|
"target_ref": str(item.get("target_ref") or "").strip() or None,
|
|
"resolved_label": str(item.get("resolved_label") or "").strip() or None,
|
|
"score": round(max(0.0, min(1.0, float(item.get("score") or 0.0))), 4),
|
|
"source_kind": str(item.get("source_kind") or "unknown").strip().lower() or "unknown",
|
|
}
|
|
)
|
|
return {
|
|
"target_entity_type": str(payload.get("target_entity_type") or "unknown").strip().lower() or "unknown",
|
|
"target_ref": str(payload.get("target_ref") or "").strip() or None,
|
|
"resolved_label": str(payload.get("resolved_label") or "").strip() or None,
|
|
"resolution_mode": str(payload.get("resolution_mode") or "none").strip().lower() or "none",
|
|
"confidence": round(max(0.0, min(1.0, float(payload.get("confidence") or 0.0))), 4),
|
|
"candidate_summary": candidates,
|
|
}
|
|
|
|
@staticmethod
|
|
def _build_query_plan_summary(query_plan: Optional[dict[str, Any]]) -> dict[str, Any]:
|
|
payload = query_plan if isinstance(query_plan, dict) else {}
|
|
filters: list[dict[str, Any]] = []
|
|
for item in list(payload.get("filters") or [])[:4]:
|
|
if not isinstance(item, dict):
|
|
continue
|
|
filters.append(
|
|
{
|
|
"field": str(item.get("field") or "").strip() or None,
|
|
"operator": str(item.get("operator") or "").strip() or None,
|
|
"value": item.get("value"),
|
|
"value_source": str(item.get("value_source") or "").strip() or None,
|
|
}
|
|
)
|
|
return {
|
|
"plan_version": str(payload.get("plan_version") or "unknown").strip() or "unknown",
|
|
"query_type": str(payload.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN,
|
|
"entity_type": str(payload.get("entity_type") or "unknown").strip().lower() or "unknown",
|
|
"target_ref": str(payload.get("target_ref") or "").strip() or None,
|
|
"answer_contract": str(payload.get("answer_contract") or "").strip() or None,
|
|
"filters": filters,
|
|
}
|
|
|
|
def _inject_internal_query_runtime_summaries(
|
|
self,
|
|
*,
|
|
retrieval_metrics: Optional[dict[str, Any]],
|
|
query_semantics: Optional[dict[str, Any]],
|
|
internal_context_packet: Any = None,
|
|
) -> dict[str, Any]:
|
|
metrics = dict(retrieval_metrics or {})
|
|
semantics = query_semantics if isinstance(query_semantics, dict) else {}
|
|
metrics["operation_contract_summary"] = self._build_operation_contract_summary(
|
|
internal_context_operation_contract(
|
|
packet=internal_context_packet,
|
|
fallback=semantics.get("operation_contract"),
|
|
)
|
|
)
|
|
metrics["target_resolution_summary"] = self._build_target_resolution_summary(
|
|
internal_context_target_resolution(
|
|
packet=internal_context_packet,
|
|
fallback=semantics.get("target_resolution"),
|
|
)
|
|
)
|
|
metrics["query_plan_summary"] = self._build_query_plan_summary(
|
|
internal_context_query_plan(
|
|
packet=internal_context_packet,
|
|
fallback=semantics.get("query_plan"),
|
|
)
|
|
)
|
|
return metrics
|
|
|
|
|
|
__all__ = ["AdaptiveRAGTargetResolutionMixin"]
|