feat(flywheel): few-shot 在线检索注入打通风险规则编译链路

- 新增 FewShotStore:独立 Qdrant collection few_shot_samples,向量 upsert/search/delete,
  全程失败降级不阻塞主链路
- 新增 FewShotIngestionService:RiskObservation confirmed/false_positive → FewShotSample +
  向量,带 sample_key 幂等去重
- 新增 FewShotRetriever:按 case 特征检索相似历史样本,去重 + token 预算 + 单条字符上限裁剪
- risk_observations.create_feedback commit 后挂 hook 自动入库,带 feature flag 和 try/except 兜底
- risk_rule_generation_prompt 新增 few_shot_samples 可选参数,合并进 examples 并标
  source=historical_confirmed;risk_rule_generation 构造 prompt 前调 retriever,失败降级为空
This commit is contained in:
caoxiaozhu
2026-07-03 13:55:52 +08:00
parent 765cfb40f3
commit 3a9d154783
6 changed files with 584 additions and 1 deletions

View File

@@ -234,6 +234,10 @@ class RiskRuleGenerationService:
}
for item in fields
]
few_shot_samples = self._retrieve_few_shot_samples(
domain=domain,
natural_language=natural_language,
)
messages = build_risk_rule_compiler_messages(
domain=domain,
domain_label=BUSINESS_DOMAIN_LABELS[domain],
@@ -243,6 +247,7 @@ class RiskRuleGenerationService:
expense_category_label=expense_category_label,
natural_language=natural_language,
available_fields=field_payload,
few_shot_samples=few_shot_samples,
)
answer = self.runtime_chat_service.complete(
messages,
@@ -263,6 +268,29 @@ class RiskRuleGenerationService:
payload = unwrap_semantic_plan_payload(payload)
return self._sanitize_model_draft(payload, fields=fields)
def _retrieve_few_shot_samples(
self,
*,
domain: str,
natural_language: str,
) -> list[dict[str, Any]]:
"""检索已确认历史样本,失败降级为空列表。"""
import os
if os.environ.get("FEW_SHOT_INJECTION_ENABLED", "true").strip().lower() in {"0", "false", "no"}:
return []
try:
from app.services.few_shot_retrieval import FewShotRetriever
retriever = FewShotRetriever.from_session(self.db)
return retriever.retrieve_for_risk_rule_generation(
domain=domain,
natural_language=natural_language,
)
except Exception:
return []
def _sanitize_model_draft(
self,
payload: dict[str, Any],