Files
mini-openclaw/backend/api/xingbang_v2.py
T

570 lines
19 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.
"""兴邦金租智能评审助手 API v2 — 全新设计,推翻旧五智能体框架
核心痛点:
1. 中诚信评级模板闲置(线下Excel手工录入)
2. 保单条款覆盖度盲区(只看形式不看内容)
3. 空窗期风险失控(17天立项到上会,期间风险恶化)
4. 评审逻辑因人而异(同一事项30%/60%/90%都可能)
5. 业务与评审对立(风险考量不一样)
新架构:4个专项助手
- 立项前置风控助手:业务经理立项前风险自查(只给风险点不给结论)
- 中诚信模板智能填报助手:激活闲置的中诚信模板,自动填充+评级
- 空窗期动态监控助手:每3天重扫征信/涉诉/舆情,上会前强制重扫
- 保单条款合规核验助手:OCR+条款解析,核验足额覆盖
"""
import asyncio
import json
from datetime import datetime
from pathlib import Path
from typing import AsyncGenerator
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
router = APIRouter(prefix="/api/xingbang/v2", tags=["兴邦智能评审助手V2"])
# ============ Pydantic 模型 ============
class ProjectData(BaseModel):
"""项目基础数据"""
project_name: str
client_name: str
industry: str
credit_amount: float # 授信金额(万元)
lease_object: str # 租赁物
lease_value: float # 租赁物价值(万元)
financial_data: dict # 财务数据
credit_report: dict # 征信报告
litigation_records: list # 涉诉记录
sentiment_info: dict # 舆情信息
class InsurancePolicy(BaseModel):
"""保单数据"""
policy_number: str
insurance_company: str
coverage_amount: float # 保额(万元)
policy_type: str # 险种
exclusions: list[str] # 除外责任
policy_pdf_path: str = ""
class RiskCheckRequest(BaseModel):
"""立项前置风控请求"""
project_data: ProjectData
class ZhongchengxinRequest(BaseModel):
"""中诚信模板填报请求"""
project_data: ProjectData
class MonitoringRequest(BaseModel):
"""空窗期监控请求"""
project_id: str
project_name: str
client_name: str
initial_data: ProjectData
class PolicyCheckRequest(BaseModel):
"""保单核验请求"""
project_name: str
client_name: str
credit_amount: float
lease_value: float
policy: InsurancePolicy
# ============ 辅助函数:创建专用 LLM 实例 ============
def _create_assistant_llm(system_prompt: str):
"""创建专用助手 LLM(复用核心 DeepSeek 配置)"""
import os
from langchain_deepseek import ChatDeepSeek
return ChatDeepSeek(
model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
api_base=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
temperature=0.3, # 降低温度,提高稳定性
streaming=True,
)
# ============ 助手1:立项前置风控助手 ============
SYSTEM_PROMPT_RISK_CHECK = """你是兴邦金租的"立项前置风控助手"。
**你的职责**:
帮助业务经理在立项前做风险自查,**只标识风险点,不给审批结论**,避免与评审员对立。
**工作流程**:
1. 读取项目基础数据(客户、财务、征信、涉诉、舆情)
2. 对照评审手册逐条自查
3. 输出风险自查清单,标记:
- 🔴 高风险:必须处理,否则评审大概率退回
- 🟡 中风险:建议补充材料或说明
- 🟢 低风险:符合要求,可直接提交
**评审手册核心要点**(简化版):
- 客户资质:注册资本≥1000万、成立时间≥3年、行业准入白名单
- 财务指标:资产负债率≤70%、流动比率≥1.2、净利润连续两年为正
- 征信记录:近2年无逾期、无被执行记录
- 租赁物:价值必须≥授信金额×120%、符合适格性要求
- 涉诉舆情:无重大诉讼、无负面舆情
**输出格式**(Markdown):
```markdown
# 立项前置风险自查清单
## 项目基本信息
- 项目名称:xxx
- 客户名称:xxx
- 授信金额:xxx万元
## 风险自查结果
### 🔴 高风险项(必须处理)
1. [风险点] 具体描述 + 建议措施
### 🟡 中风险项(建议处理)
1. [风险点] 具体描述 + 建议措施
### 🟢 低风险项(符合要求)
1. [检查项] 符合标准
## 整体评估
- 建议:[直接提交 / 补充材料后提交 / 重大调整后提交]
```
**重要**:不要给"通过/不通过"结论,只客观列出风险点,让业务经理自己决策。
"""
async def _stream_risk_check(project_data: ProjectData) -> AsyncGenerator[str, None]:
"""流式生成立项前置风控报告"""
llm = _create_assistant_llm(SYSTEM_PROMPT_RISK_CHECK)
# 构造用户消息
user_message = f"""请对以下项目进行立项前置风控自查:
**项目名称**:{project_data.project_name}
**客户名称**:{project_data.client_name}
**行业**:{project_data.industry}
**授信金额**:{project_data.credit_amount}万元
**租赁物**:{project_data.lease_object}
**租赁物价值**:{project_data.lease_value}万元
**财务数据**:
{json.dumps(project_data.financial_data, ensure_ascii=False, indent=2)}
**征信报告**:
{json.dumps(project_data.credit_report, ensure_ascii=False, indent=2)}
**涉诉记录**:
{json.dumps(project_data.litigation_records, ensure_ascii=False, indent=2)}
**舆情信息**:
{json.dumps(project_data.sentiment_info, ensure_ascii=False, indent=2)}
请生成风险自查清单。
"""
async for chunk in llm.astream(user_message):
if hasattr(chunk, 'content'):
yield chunk.content
@router.post("/risk-check/stream")
async def risk_check_stream(request: RiskCheckRequest):
"""立项前置风控自查(流式返回)"""
async def event_generator():
try:
yield f"data: {json.dumps({'type': 'start', 'message': '正在分析项目风险...'}, ensure_ascii=False)}\n\n"
async for content in _stream_risk_check(request.project_data):
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
yield f"data: {json.dumps({'type': 'done', 'message': '风险自查完成'}, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
# ============ 助手2:中诚信模板智能填报助手 ============
SYSTEM_PROMPT_ZHONGCHENGXIN = """你是兴邦金租的"中诚信模板智能填报助手"。
**背景**:
兴邦金租花了十几万咨询费购买中诚信评级模型(Excel模板),但因线下手工录入繁琐,一直闲置未用。
**你的职责**:
1. 从项目数据自动提取关键指标
2. 填充中诚信评级模板
3. 计算评级结果(3A/2A/A/BBB/BB/B/C)
4. 生成评级报告嵌入评审底稿
**中诚信评级模型简化版**(泛行业):
- 财务指标(40%):资产负债率、流动比率、净资产收益率、营收增长率
- 经营能力(30%):行业地位、市场份额、客户集中度、盈利稳定性
- 偿债能力(20%):速动比率、利息保障倍数、现金流覆盖率
- 发展前景(10%):行业景气度、技术创新能力、政策支持
**评级标准**:
- 3A(90-100分):信用极好,违约风险极低
- 2A(80-89分):信用优良,违约风险很低
- A(70-79分):信用良好,违约风险较低
- BBB(60-69分):信用一般,违约风险中等
- BB(50-59分):信用较差,违约风险较高
- B(40-49分):信用差,违约风险高
- C(<40分):信用极差,违约风险极高
**输出格式**(Markdown):
```markdown
# 中诚信评级报告
## 项目基本信息
- 项目名称:xxx
- 客户名称:xxx
- 行业:xxx
## 评级指标填充
### 一、财务指标(40%)
| 指标 | 数值 | 得分 |
|------|------|------|
| 资产负债率 | xx% | xx分 |
| ... | ... | ... |
**小计**:xx分
### 二、经营能力(30%)
...
### 三、偿债能力(20%)
...
### 四、发展前景(10%)
...
## 评级结果
- **综合得分**:xx分
- **评级等级**:2A
- **评级说明**:信用优良,违约风险很低
## 风险提示
1. [关键风险点1]
2. [关键风险点2]
```
"""
async def _stream_zhongchengxin_report(project_data: ProjectData) -> AsyncGenerator[str, None]:
"""流式生成中诚信评级报告"""
llm = _create_assistant_llm(SYSTEM_PROMPT_ZHONGCHENGXIN)
user_message = f"""请为以下项目生成中诚信评级报告:
**项目名称**:{project_data.project_name}
**客户名称**:{project_data.client_name}
**行业**:{project_data.industry}
**授信金额**:{project_data.credit_amount}万元
**财务数据**:
{json.dumps(project_data.financial_data, ensure_ascii=False, indent=2)}
**征信报告**:
{json.dumps(project_data.credit_report, ensure_ascii=False, indent=2)}
请填充中诚信评级模板并计算评级。
"""
async for chunk in llm.astream(user_message):
if hasattr(chunk, 'content'):
yield chunk.content
@router.post("/zhongchengxin/stream")
async def zhongchengxin_stream(request: ZhongchengxinRequest):
"""中诚信模板智能填报(流式返回)"""
async def event_generator():
try:
yield f"data: {json.dumps({'type': 'start', 'message': '正在填充中诚信评级模板...'}, ensure_ascii=False)}\n\n"
async for content in _stream_zhongchengxin_report(request.project_data):
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
yield f"data: {json.dumps({'type': 'done', 'message': '中诚信评级完成'}, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
# ============ 助手3:空窗期动态监控助手 ============
SYSTEM_PROMPT_MONITORING = """你是兴邦金租的"空窗期动态监控助手"。
**背景**:
立项到上会需要17个工作日,期间客户征信/涉诉/舆情可能恶化,但无人重扫,导致上会时信息已过时。
**你的职责**:
1. 对比初始数据与最新数据
2. 识别异常变化(新增被执行、新增诉讼、舆情负面、信用卡逾期等)
3. 评估风险等级(严重/一般/轻微)
4. 生成红色预警报告
**监控维度**:
- 征信变化:新增逾期、新增查询记录、信用卡额度变化
- 涉诉变化:新增诉讼、新增被执行、执行金额变化
- 舆情变化:负面新闻、行业风险、关联企业风险
- 消费行为:淘宝消费骤降、信用卡逾期
**风险等级**:
- 🔴 严重:新增被执行/重大诉讼/信用卡逾期,建议暂停审批
- 🟡 一般:征信查询次数增多/轻微负面舆情,建议重点关注
- 🟢 轻微:无明显异常
**输出格式**(Markdown):
```markdown
# 空窗期动态监控报告
## 项目基本信息
- 项目名称:xxx
- 监控时间:xxx
## 异常变化检测
### 🔴 严重异常
1. [异常项] 具体描述 + 建议措施
### 🟡 一般异常
1. [异常项] 具体描述
### 🟢 无异常
- 征信记录:无新增异常
- 涉诉记录:无新增异常
- 舆情信息:无新增负面
## 监控建议
- [暂停审批 / 重点关注 / 正常推进]
```
"""
async def _stream_monitoring_report(
initial_data: ProjectData,
latest_data: ProjectData
) -> AsyncGenerator[str, None]:
"""流式生成空窗期监控报告"""
llm = _create_assistant_llm(SYSTEM_PROMPT_MONITORING)
user_message = f"""请对比以下项目的初始数据与最新数据,生成空窗期动态监控报告:
**项目名称**:{initial_data.project_name}
**客户名称**:{initial_data.client_name}
**初始数据(立项时)**:
征信报告:{json.dumps(initial_data.credit_report, ensure_ascii=False, indent=2)}
涉诉记录:{json.dumps(initial_data.litigation_records, ensure_ascii=False, indent=2)}
舆情信息:{json.dumps(initial_data.sentiment_info, ensure_ascii=False, indent=2)}
**最新数据(当前)**:
征信报告:{json.dumps(latest_data.credit_report, ensure_ascii=False, indent=2)}
涉诉记录:{json.dumps(latest_data.litigation_records, ensure_ascii=False, indent=2)}
舆情信息:{json.dumps(latest_data.sentiment_info, ensure_ascii=False, indent=2)}
请识别异常变化并生成监控报告。
"""
async for chunk in llm.astream(user_message):
if hasattr(chunk, 'content'):
yield chunk.content
@router.post("/monitoring/stream")
async def monitoring_stream(request: MonitoringRequest):
"""空窗期动态监控(流式返回)
实际使用中,会定时调用此接口(每3天),传入最新数据
这里为演示方便,模拟一次对比
"""
# 模拟最新数据(实际应从外部数据源实时抓取)
latest_data = request.initial_data.model_copy()
latest_data.credit_report["overdue_count"] = latest_data.credit_report.get("overdue_count", 0) + 1
latest_data.litigation_records.append({"case_number": "2026执1234", "amount": 50, "status": "执行中"})
async def event_generator():
try:
yield f"data: {json.dumps({'type': 'start', 'message': '正在对比数据变化...'}, ensure_ascii=False)}\n\n"
async for content in _stream_monitoring_report(request.initial_data, latest_data):
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
yield f"data: {json.dumps({'type': 'done', 'message': '监控完成'}, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
# ============ 助手4:保单条款合规核验助手 ============
SYSTEM_PROMPT_POLICY_CHECK = """你是兴邦金租的"保单条款合规核验助手"。
**背景**:
兴邦金租要求客户为租赁物投保,保额需覆盖授信金额,但实际只检查"有没有保单",不细看条款,导致出险时发现保单回不了。
**你的职责**:
1. 解析保单关键信息(险种、保额、除外责任)
2. 核验是否符合兴邦要求:
- 保额 ≥ 授信金额(理想120%)
- 险种必须包含:财产一切险/车辆损失险/盗抢险(根据租赁物类型)
- 除外责任不能包含高频风险(如泡水、自然灾害等)
3. 标记风险点
4. 生成合规核验报告
**兴邦保单合规要求**:
- 设备类:财产一切险/工程一切险,保额≥授信×120%
- 车辆类:车损险+盗抢险+三者险,保额≥授信×120%
- 除外责任禁止:战争、核辐射、故意损坏 OK,但泡水、自然灾害、机械故障等高频风险不能除外
**输出格式**(Markdown):
```markdown
# 保单条款合规核验报告
## 保单基本信息
- 保单号:xxx
- 保险公司:xxx
- 险种:xxx
- 保额:xxx万元
## 合规核验结果
### ✅ 符合项
1. [核验项] 符合要求
### ❌ 不符合项
1. [核验项] 不符合要求 + 风险说明
## 核验结论
- **是否合规**:[合规 / 不合规]
- **风险等级**:[无风险 / 低风险 / 高风险]
- **处理建议**:[通过 / 要求客户补正保单 / 拒绝]
## 详细说明
[具体分析]
```
"""
async def _stream_policy_check(
project_name: str,
client_name: str,
credit_amount: float,
lease_value: float,
policy: InsurancePolicy
) -> AsyncGenerator[str, None]:
"""流式生成保单合规核验报告"""
llm = _create_assistant_llm(SYSTEM_PROMPT_POLICY_CHECK)
required_coverage = credit_amount * 1.2 # 要求120%覆盖
user_message = f"""请核验以下保单是否符合兴邦金租要求:
**项目信息**:
- 项目名称:{project_name}
- 客户名称:{client_name}
- 授信金额:{credit_amount}万元
- 租赁物价值:{lease_value}万元
- 要求保额:≥{required_coverage}万元(120%覆盖)
**保单信息**:
- 保单号:{policy.policy_number}
- 保险公司:{policy.insurance_company}
- 保额:{policy.coverage_amount}万元
- 险种:{policy.policy_type}
- 除外责任:{json.dumps(policy.exclusions, ensure_ascii=False)}
请生成合规核验报告。
"""
async for chunk in llm.astream(user_message):
if hasattr(chunk, 'content'):
yield chunk.content
@router.post("/policy-check/stream")
async def policy_check_stream(request: PolicyCheckRequest):
"""保单条款合规核验(流式返回)"""
async def event_generator():
try:
yield f"data: {json.dumps({'type': 'start', 'message': '正在核验保单条款...'}, ensure_ascii=False)}\n\n"
async for content in _stream_policy_check(
request.project_name,
request.client_name,
request.credit_amount,
request.lease_value,
request.policy
):
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
yield f"data: {json.dumps({'type': 'done', 'message': '保单核验完成'}, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
# ============ 综合工作台接口 ============
@router.get("/assistants/list")
async def list_assistants():
"""获取所有助手列表"""
return {
"assistants": [
{
"id": "risk-check",
"name": "立项前置风控助手",
"description": "业务经理立项前风险自查,只给风险点不给结论",
"icon": "🔍",
"pain_point": "业务与评审对立,项目被退回",
"value": "降低退回率,缩短评审周期"
},
{
"id": "zhongchengxin",
"name": "中诚信模板智能填报助手",
"description": "激活闲置的中诚信模板,自动填充+评级",
"icon": "📊",
"pain_point": "十几万的中诚信模板手工录入太繁琐",
"value": "每个项目省30-60分钟,激活投资"
},
{
"id": "monitoring",
"name": "空窗期动态监控助手",
"description": "每3天重扫征信/涉诉/舆情,上会前强制重扫",
"icon": "🚨",
"pain_point": "17天空窗期,上会时信息已过时",
"value": "17天风险不落地,避免决策失误"
},
{
"id": "policy-check",
"name": "保单条款合规核验助手",
"description": "OCR+条款解析,核验足额覆盖",
"icon": "🛡️",
"pain_point": "只看有没有保单,不看条款,出险回不了",
"value": "避免保单风险盲区"
}
]
}