Files

196 lines
7.5 KiB
Python

"""团队集体经验库
功能:
1. 学习团队历史评审行为(聚合,非个人)
2. 提取高频风险模式
3. 为预审助手提供经验支持
"""
import json
from typing import Dict, List, Optional
from pathlib import Path
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class TeamExperienceStore:
"""团队集体经验存储"""
def __init__(self, data_dir: str = "./xingbang_data/team_experience"):
self.data_dir = Path(data_dir)
self.data_dir.mkdir(parents=True, exist_ok=True)
self.experience_file = self.data_dir / "team_patterns.json"
# 初始化默认经验模式
if not self.experience_file.exists():
self._init_default_patterns()
def _init_default_patterns(self):
"""初始化默认团队经验模式"""
default_patterns = {
"risk_patterns": {
"制造业": [
{
"risk_type": "应收账款集中度过高",
"frequency": 23,
"typical_threshold": "单一客户占比 > 30%",
"team_action": "要求补充第二担保人或增加保证金",
},
{
"risk_type": "原材料价格波动风险",
"frequency": 18,
"typical_threshold": "大宗商品占成本 > 50%",
"team_action": "要求提供价格锁定合同或套期保值证明",
},
],
"新能源": [
{
"risk_type": "政策依赖度高",
"frequency": 34,
"typical_threshold": "补贴收入 > 营收 20%",
"team_action": "降低授信额度或缩短租赁期限",
},
{
"risk_type": "技术迭代淘汰风险",
"frequency": 21,
"typical_threshold": "设备技术落后 2 代以上",
"team_action": "要求租赁物残值担保或回购承诺",
},
],
"交通运输": [
{
"risk_type": "油价波动影响",
"frequency": 15,
"typical_threshold": "燃油成本 > 营收 40%",
"team_action": "要求提供运价调整机制证明",
},
],
},
"approval_thresholds": {
"资产负债率": {
"safe": 0.6,
"warning": 0.7,
"critical": 0.8,
"team_consensus": "≤60% 低风险,60-70% 中风险需补充说明,>70% 高风险需额外担保",
},
"流动比率": {
"safe": 1.5,
"warning": 1.2,
"critical": 1.0,
"team_consensus": "≥1.5 优秀,1.2-1.5 合格,<1.2 需关注短期偿债能力",
},
"净利润增长率": {
"safe": 0.15,
"warning": 0.05,
"critical": 0.0,
"team_consensus": "持续两年负增长通常不予通过",
},
},
"industry_red_flags": {
"光伏": ["产能严重过剩", "欧美双反调查", "地方政府欠补"],
"房地产": ["三道红线踩线", "土地储备不足", "回款周期超18个月"],
"餐饮": ["单店模型未跑通", "加盟商纠纷", "食品安全事故"],
},
"common_rejection_reasons": [
{"reason": "担保方资产不足", "count": 45, "solution": "要求补充第二担保人"},
{"reason": "征信查询次数过多", "count": 32, "solution": "要求客户说明查询原因"},
{"reason": "涉诉金额过大", "count": 28, "solution": "等待诉讼结果或要求提供和解协议"},
{"reason": "租赁物适格性存疑", "count": 25, "solution": "要求第三方评估报告"},
],
"metadata": {
"last_updated": datetime.now().isoformat(),
"total_cases_analyzed": 156,
"data_source": "2024-2025 年历史评审案例",
},
}
with open(self.experience_file, "w", encoding="utf-8") as f:
json.dump(default_patterns, f, ensure_ascii=False, indent=2)
logger.info("团队经验模式初始化完成")
def get_risk_patterns(self, industry: str = None) -> List[Dict]:
"""获取风险模式(可按行业过滤)"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
risk_patterns = data.get("risk_patterns", {})
if industry:
# 精确匹配或模糊匹配
for key in risk_patterns:
if industry in key or key in industry:
return risk_patterns[key]
return []
else:
# 返回所有模式
all_patterns = []
for patterns in risk_patterns.values():
all_patterns.extend(patterns)
return all_patterns
def get_approval_thresholds(self) -> Dict:
"""获取团队共识的审批阈值"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("approval_thresholds", {})
def get_industry_red_flags(self, industry: str) -> List[str]:
"""获取行业红线(团队总结的高危信号)"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
industry_flags = data.get("industry_red_flags", {})
# 模糊匹配
for key in industry_flags:
if industry in key or key in industry:
return industry_flags[key]
return []
def get_common_rejection_reasons(self, top_n: int = 10) -> List[Dict]:
"""获取高频退回原因及解决方案"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
reasons = data.get("common_rejection_reasons", [])
return reasons[:top_n]
def update_pattern(self, pattern_type: str, pattern_data: Dict):
"""更新经验模式(用于持续学习)"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
# 更新指定类型的模式
if pattern_type in data:
data[pattern_type].update(pattern_data)
data["metadata"]["last_updated"] = datetime.now().isoformat()
with open(self.experience_file, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
logger.info(f"团队经验模式已更新: {pattern_type}")
def get_metadata(self) -> Dict:
"""获取元数据"""
with open(self.experience_file, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("metadata", {})
# 全局单例
_team_experience_store: Optional[TeamExperienceStore] = None
def get_team_experience_store() -> TeamExperienceStore:
"""获取团队经验存储单例"""
global _team_experience_store
if _team_experience_store is None:
_team_experience_store = TeamExperienceStore()
return _team_experience_store