Files
group_xinghuo_jinrong/app/service/metric_ambiguity.py
T
zhanghongyu_0626 601ed258f3 feat(analyst): Enhance asset management and ambiguity detection features
- Added new endpoints to the analyst API for managing assets, including `GET /assets` to list assets and `POST /assets/{kind}/{asset_id}/publish` to publish assets.
- Introduced `DictAmbiguityCheckRequest` schema for checking metric ambiguities, enhancing the analyst's ability to clarify definitions and aliases.
- Implemented `detect_dict_ambiguity` function to analyze potential ambiguities in metrics, providing structured feedback for users.
- Updated `AnalystAgent` to support the new asset management functionalities and ambiguity detection logic, improving overall user experience.
- Enhanced existing schemas and services to accommodate new features, ensuring robust data handling and validation.

This update significantly improves the analyst API's capabilities, allowing for better asset management and clarity in metric definitions.
2026-09-12 15:14:01 +08:00

136 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""口径字典歧义检测(D-11 · 沉淀前 / 问数 clarify 共用结构)。"""
from __future__ import annotations
import json
from typing import Any
from app.service.dict_service import Ambiguity, Metric, MetricRegistry, merge_registries, registry_with_published
from app.service.llm import DeepSeekLLM
DICT_CHECK_SYSTEM = (
"你是金融数据口径审核助手。根据已有口径与新指标草稿,判断用户说法是否会混淆。"
"只输出 JSON:{\"summary\":\"一句话建议\",\"actions\":[\"建议1\",\"建议2\"]}"
"不要 markdown。"
)
def _parse_aliases(raw: Any) -> list[str]:
if raw is None:
return []
if isinstance(raw, list):
return [str(x).strip() for x in raw if str(x).strip()]
s = str(raw).strip()
if not s:
return []
if s.startswith("["):
try:
arr = json.loads(s)
if isinstance(arr, list):
return [str(x).strip() for x in arr if str(x).strip()]
except json.JSONDecodeError:
pass
return [p.strip() for p in s.replace(",", ",").split(",") if p.strip()]
def _issue_from_ambiguity(term: str, amb: Ambiguity) -> dict[str, Any]:
return {
"term": term,
"options": [
{
"metric_key": c.key,
"metric_name": c.name,
"definition": c.definition,
}
for c in amb.candidates
],
}
def detect_dict_ambiguity(
*,
metric_key: str,
metric_name: str,
definition: str,
aliases: Any,
repo: Any | None = None,
base_registry: MetricRegistry | None = None,
llm: DeepSeekLLM | None = None,
) -> dict[str, Any]:
"""合并草稿进 registry 后扫描别名/名称是否触发消歧。"""
if base_registry is not None:
reg = base_registry
else:
from app.service.analytics_repo import AnalyticsRepo
reg = registry_with_published(repo or AnalyticsRepo())
alias_list = _parse_aliases(aliases)
draft = Metric(
key=(metric_key or "").strip(),
name=(metric_name or "").strip(),
aliases=alias_list,
definition=(definition or "").strip(),
)
merged = merge_registries(reg, [draft]) if draft.key and draft.name else reg
terms: list[str] = []
if draft.name:
terms.append(draft.name)
terms.extend(alias_list)
issues: list[dict[str, Any]] = []
seen: set[str] = set()
for term in terms:
if not term or term in seen:
continue
seen.add(term)
resolved = merged.resolve(term)
if isinstance(resolved, Ambiguity):
issues.append(_issue_from_ambiguity(term, resolved))
llm_summary = ""
llm_actions: list[str] = []
if issues and llm is not None:
try:
payload = {
"existing": [m.to_dict() for m in reg.all()],
"draft": draft.to_dict(),
"conflicts": issues,
}
text, _ = llm.complete(
[
{"role": "system", "content": DICT_CHECK_SYSTEM},
{
"role": "user",
"content": f"数据:{json.dumps(payload, ensure_ascii=False)[:6000]}\n请输出 JSON:",
},
],
temperature=0.1,
max_tokens=400,
)
data = json.loads(text.strip().strip("`").replace("json", "", 1).strip())
llm_summary = str(data.get("summary") or "")
raw_actions = data.get("actions") or []
if isinstance(raw_actions, list):
llm_actions = [str(a) for a in raw_actions[:5]]
except Exception:
llm_summary = "存在与现有口径重叠的说法,请收窄别名或改用更具体的指标名。"
return {
"needs_choice": bool(issues),
"issues": issues,
"llm_summary": llm_summary,
"llm_actions": llm_actions,
}
def clarify_payload_from_ambiguity(amb: Ambiguity, *, llm_hint: str | None = None) -> dict[str, Any]:
return {
"term": amb.term,
"options": [
{"metric_key": c.key, "metric_name": c.name, "definition": c.definition}
for c in amb.candidates
],
"llm_hint": llm_hint,
}