Files
mini-openclaw-v2/backend/data_layer/history_store.py
T

210 lines
6.5 KiB
Python
Raw Normal View History

"""历史评审案例向量库
功能:
1. 存储历史评审案例(项目特征+评审结果+评审员行为)
2. 相似案例检索(基于向量相似度)
3. 评审员行为学习(团队集体经验,非个人预测)
"""
import json
import os
from typing import List, Dict, Optional, Tuple
from datetime import datetime
from pathlib import Path
import logging
logger = logging.getLogger(__name__)
class HistoryStore:
"""历史评审案例存储(简化版,生产环境建议使用 Chroma/Qdrant)"""
def __init__(self, data_dir: str = "./xingbang_data/history"):
self.data_dir = Path(data_dir)
self.data_dir.mkdir(parents=True, exist_ok=True)
self.cases_file = self.data_dir / "review_cases.jsonl"
# 确保文件存在
if not self.cases_file.exists():
self.cases_file.touch()
def add_case(self, case_data: Dict) -> str:
"""添加一条历史评审案例"""
case_id = f"case_{datetime.now().strftime('%Y%m%d%H%M%S')}"
case_record = {
"case_id": case_id,
"timestamp": datetime.now().isoformat(),
**case_data
}
# 追加写入 JSONL
with open(self.cases_file, "a", encoding="utf-8") as f:
f.write(json.dumps(case_record, ensure_ascii=False) + "\n")
logger.info(f"案例已保存: {case_id}")
return case_id
def search_similar_cases(
self,
industry: str,
credit_amount: float,
risk_level: str = None,
limit: int = 5
) -> List[Dict]:
"""检索相似案例(简化版:基于行业+金额范围)"""
if not self.cases_file.exists():
return []
similar_cases = []
with open(self.cases_file, "r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
try:
case = json.loads(line)
# 行业匹配
industry_match = industry in case.get("industry", "") or case.get("industry", "") in industry
# 金额范围匹配(±30%)
case_amount = case.get("credit_amount", 0)
amount_match = 0.7 * credit_amount <= case_amount <= 1.3 * credit_amount
# 风险等级匹配(可选)
risk_match = risk_level is None or case.get("risk_level") == risk_level
if industry_match and amount_match and risk_match:
similar_cases.append(case)
except json.JSONDecodeError:
continue
# 按时间倒序返回最近的案例
similar_cases.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
return similar_cases[:limit]
def get_case_by_id(self, case_id: str) -> Optional[Dict]:
"""根据 ID 获取案例详情"""
if not self.cases_file.exists():
return None
with open(self.cases_file, "r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
try:
case = json.loads(line)
if case.get("case_id") == case_id:
return case
except json.JSONDecodeError:
continue
return None
def get_team_patterns(self, industry: str = None) -> Dict:
"""提取团队集体经验模式(高频风险点、常见处理方式)"""
if not self.cases_file.exists():
return {"high_frequency_risks": [], "common_solutions": []}
risk_counter = {}
solution_counter = {}
with open(self.cases_file, "r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
try:
case = json.loads(line)
# 行业过滤
if industry and industry not in case.get("industry", ""):
continue
# 统计高频风险点
for risk in case.get("identified_risks", []):
risk_key = risk.get("type", "unknown")
risk_counter[risk_key] = risk_counter.get(risk_key, 0) + 1
# 统计常见解决方案
solution = case.get("solution", "")
if solution:
solution_counter[solution] = solution_counter.get(solution, 0) + 1
except json.JSONDecodeError:
continue
# 排序
high_frequency_risks = sorted(
[{"risk": k, "count": v} for k, v in risk_counter.items()],
key=lambda x: x["count"],
reverse=True
)[:10]
common_solutions = sorted(
[{"solution": k, "count": v} for k, v in solution_counter.items()],
key=lambda x: x["count"],
reverse=True
)[:10]
return {
"high_frequency_risks": high_frequency_risks,
"common_solutions": common_solutions,
}
def get_statistics(self) -> Dict:
"""获取统计数据"""
if not self.cases_file.exists():
return {"total_cases": 0}
total_cases = 0
industry_dist = {}
approval_rate = {"approved": 0, "rejected": 0, "pending": 0}
with open(self.cases_file, "r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
try:
case = json.loads(line)
total_cases += 1
# 行业分布
industry = case.get("industry", "未知")
industry_dist[industry] = industry_dist.get(industry, 0) + 1
# 通过率
result = case.get("review_result", "pending")
if result in approval_rate:
approval_rate[result] += 1
except json.JSONDecodeError:
continue
return {
"total_cases": total_cases,
"industry_distribution": industry_dist,
"approval_rate": approval_rate,
}
# 全局单例
_history_store: Optional[HistoryStore] = None
def get_history_store() -> HistoryStore:
"""获取历史案例存储单例"""
global _history_store
if _history_store is None:
_history_store = HistoryStore()
return _history_store