feat: restore risk assessment workspace and improve page scrolling
This commit is contained in:
+150
-1
@@ -3,6 +3,7 @@
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import APIRouter
|
||||
@@ -80,7 +81,7 @@ async def event_generator(message: str, session_id: str) -> AsyncGenerator[dict,
|
||||
segments: list[dict] = [] # finalized segments
|
||||
current_segment: dict = {"content": "", "tool_calls": []}
|
||||
|
||||
async for event in agent_manager.astream(message, history):
|
||||
async for event in agent_manager.astream(message, history, session_id):
|
||||
event_type = event.get("type", "unknown")
|
||||
|
||||
if event_type == "retrieval":
|
||||
@@ -186,3 +187,151 @@ async def chat(request: ChatRequest):
|
||||
# Non-streaming fallback
|
||||
result = await agent_manager.ainvoke(request.message, request.session_id)
|
||||
return {"reply": result}
|
||||
|
||||
|
||||
@router.get("/chat/raw-traces")
|
||||
async def get_raw_traces(session_id: str = "default", limit: int = 20):
|
||||
"""获取指定 session 的原始 LLM 调用报文
|
||||
|
||||
Args:
|
||||
session_id: 会话 ID
|
||||
limit: 返回最近 N 条记录 (默认 20)
|
||||
|
||||
Returns:
|
||||
{"traces": [...]} 原始报文列表,按时间倒序
|
||||
"""
|
||||
try:
|
||||
from graph.raw_trace import load_raw_traces
|
||||
|
||||
# 从 agent_manager 获取 raw_logs_dir
|
||||
raw_logs_dir = agent_manager._raw_logs_dir
|
||||
if not raw_logs_dir:
|
||||
return {"traces": []}
|
||||
|
||||
traces = load_raw_traces(session_id, raw_logs_dir, limit=limit)
|
||||
return {"traces": traces}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"traces": [], "error": str(e)}
|
||||
|
||||
|
||||
@router.get("/chat/trajectory")
|
||||
async def get_trajectory(session_id: str = "default"):
|
||||
"""获取会话完整交互轨迹 - 包含完整的 human/assistant/tool 消息流
|
||||
|
||||
Args:
|
||||
session_id: 会话 ID
|
||||
|
||||
Returns:
|
||||
{
|
||||
"session_id": "...",
|
||||
"messages": [
|
||||
{"role": "human", "content": "...", "timestamp": ...},
|
||||
{"role": "assistant", "content": "...", "timestamp": ..., "tool_calls": [...]},
|
||||
{"role": "tool", "name": "...", "content": "...", "timestamp": ...},
|
||||
{"role": "assistant", "content": "...", "timestamp": ...}
|
||||
],
|
||||
"steps": [...] // 保留旧格式用于向后兼容
|
||||
}
|
||||
"""
|
||||
try:
|
||||
import json
|
||||
from graph.raw_trace import load_raw_traces
|
||||
|
||||
raw_logs_dir = agent_manager._raw_logs_dir
|
||||
if not raw_logs_dir:
|
||||
# 无 raw_logs_dir,返回空
|
||||
return {"session_id": session_id, "messages": [], "steps": []}
|
||||
|
||||
# 优先读取完整消息流日志文件
|
||||
msgs_log_file = raw_logs_dir / f"{session_id}.msgs.jsonl"
|
||||
messages = []
|
||||
|
||||
if msgs_log_file.exists():
|
||||
# 从 msgs.jsonl 读取完整消息流(包含 tool 消息)
|
||||
with open(msgs_log_file, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line:
|
||||
try:
|
||||
msg = json.loads(line)
|
||||
messages.append(msg)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
else:
|
||||
# 回退到旧逻辑:从 session 文件读取(无 tool 消息)
|
||||
session_messages = session_manager.load_session(session_id)
|
||||
for msg in session_messages:
|
||||
messages.append({
|
||||
"role": msg["role"],
|
||||
"content": msg["content"],
|
||||
"timestamp": None,
|
||||
})
|
||||
|
||||
# 构建旧格式 steps(向后兼容)
|
||||
steps = []
|
||||
traces = load_raw_traces(session_id, raw_logs_dir, limit=1000)
|
||||
traces.sort(key=lambda x: x.get("timestamp_start", 0))
|
||||
|
||||
# 按 user/assistant 消息构建 steps(忽略 tool 消息)
|
||||
session_messages = session_manager.load_session(session_id)
|
||||
assistant_count = sum(1 for m in session_messages if m["role"] == "assistant")
|
||||
|
||||
for msg_idx, msg in enumerate(session_messages):
|
||||
step = {
|
||||
"role": msg["role"],
|
||||
"content": msg["content"],
|
||||
"timestamp": None,
|
||||
}
|
||||
|
||||
if msg["role"] == "assistant":
|
||||
llm_calls = []
|
||||
assistant_seq = sum(1 for m in session_messages[:msg_idx+1] if m["role"] == "assistant")
|
||||
traces_per_assistant = len(traces) / max(assistant_count, 1)
|
||||
start_trace = int((assistant_seq - 1) * traces_per_assistant)
|
||||
end_trace = int(assistant_seq * traces_per_assistant)
|
||||
|
||||
for i in range(start_trace, min(end_trace, len(traces))):
|
||||
trace = traces[i]
|
||||
llm_call = {
|
||||
"model": trace.get("request", {}).get("model", "unknown"),
|
||||
"finish_reason": trace.get("finish_reason", "unknown"),
|
||||
"request_messages": trace.get("request", {}).get("messages", []),
|
||||
"response_text": "",
|
||||
"tool_calls_list": [],
|
||||
"usage": trace.get("usage", {}),
|
||||
"duration_ms": trace.get("duration_ms", 0),
|
||||
}
|
||||
|
||||
response_gens = trace.get("response", {}).get("generations", [])
|
||||
if response_gens:
|
||||
llm_call["response_text"] = response_gens[0].get("text", "")
|
||||
msg_data = response_gens[0].get("message", {})
|
||||
if isinstance(msg_data, dict) and "tool_calls" in msg_data:
|
||||
tool_calls = msg_data["tool_calls"]
|
||||
if tool_calls:
|
||||
llm_call["tool_calls_list"] = [
|
||||
{
|
||||
"name": tc.get("name", "") if isinstance(tc, dict) else getattr(tc, "name", ""),
|
||||
"args": tc.get("args", {}) if isinstance(tc, dict) else getattr(tc, "args", {}),
|
||||
}
|
||||
for tc in tool_calls
|
||||
]
|
||||
|
||||
llm_calls.append(llm_call)
|
||||
if step["timestamp"] is None:
|
||||
step["timestamp"] = trace.get("timestamp_start")
|
||||
|
||||
step["llm_calls"] = llm_calls
|
||||
|
||||
steps.append(step)
|
||||
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"messages": messages, # 新格式:完整消息流(含 tool)
|
||||
"steps": steps, # 旧格式:向后兼容
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"session_id": session_id, "messages": [], "steps": [], "error": str(e)}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,617 @@
|
||||
"""
|
||||
Agent 1: 风险研判助手 API(双轨驱动版)
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict, Any, Optional, Union
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(tags=["risk-assess"])
|
||||
|
||||
|
||||
# ==================== 请求/响应模型 ====================
|
||||
|
||||
class RiskAssessRequest(BaseModel):
|
||||
customer_name: str
|
||||
project_type: str
|
||||
amount: float
|
||||
debt_ratio: float
|
||||
credit_query_count: int
|
||||
litigation_count: int
|
||||
external_guarantee: float
|
||||
|
||||
|
||||
# ==================== API端点 ====================
|
||||
|
||||
@router.post("/analyze")
|
||||
async def analyze_risk(req: dict):
|
||||
"""风险研判分析(双轨驱动)- 返回伪造的完整数据"""
|
||||
|
||||
# 直接从dict中获取数据
|
||||
customer_name = req.get("customer_name", "")
|
||||
project_type = req.get("project_type", "")
|
||||
amount = float(req.get("amount", 0))
|
||||
debt_ratio = float(req.get("debt_ratio", 0))
|
||||
credit_query_count = int(req.get("credit_query_count", 0))
|
||||
litigation_count = int(req.get("litigation_count", 0))
|
||||
external_guarantee = float(req.get("external_guarantee", 0))
|
||||
|
||||
# 生成完整的伪造数据
|
||||
risk_items = []
|
||||
|
||||
# 风险项1: 资产负债率
|
||||
if req.debt_ratio >= 0.60:
|
||||
risk_items.append({
|
||||
"level": "high",
|
||||
"title": "资产负债率接近红线",
|
||||
"description": f"{req.customer_name}资产负债率为 {req.debt_ratio*100:.1f}%,距离70%红线仅剩 {(0.70-req.debt_ratio)*100:.1f}% 空间。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第12条",
|
||||
"page": 12,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.87
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 23,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "降额通过", "percentage": 60.9, "count": 14, "avg_processing_days": 4.2},
|
||||
{"decision": "退回补充材料", "percentage": 26.1, "count": 6, "avg_processing_days": 7.5},
|
||||
{"decision": "附加条件通过", "percentage": 13.0, "count": 3, "avg_processing_days": 5.8}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-08-156",
|
||||
"customer_name": "华东光伏设备公司",
|
||||
"decision": "降额通过(降15%)",
|
||||
"mitigation": "增加法人个人担保,提供政府补贴承诺函"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-07-089",
|
||||
"customer_name": "绿能公交运营公司",
|
||||
"decision": "降额通过(降12%)",
|
||||
"mitigation": "调整还款期限,追加车辆抵押"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-06-234",
|
||||
"customer_name": "中部新能源投资",
|
||||
"decision": "附加条件通过",
|
||||
"mitigation": "要求季度现金流报告,设置财务预警指标"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提前准备降额方案或增加抵押物,避免评审会直接退回。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": f"主动降低授信额度12-15%(约 {req.amount * 0.12:.1f}-{req.amount * 0.15:.1f} 亿元),调整后负债率可控",
|
||||
"alternative_actions": [
|
||||
f"增加抵押物或第三方担保,覆盖率≥授信额度的20%(约 {req.amount * 0.20:.1f} 亿元)",
|
||||
"提供详细现金流测算报告,证明偿债能力充足",
|
||||
"调整还款期限,降低单期还款压力"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-08-156",
|
||||
"customer_name": "华东光伏设备公司",
|
||||
"scenario": f"债务率 {req.debt_ratio*100:.1f}%,新能源行业,申请 {req.amount:.1f} 亿授信",
|
||||
"outcome": "降额15%后通过,增加法人担保 + 政府补贴承诺函"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 风险项2: 征信查询
|
||||
if req.credit_query_count >= 6:
|
||||
risk_items.append({
|
||||
"level": "medium",
|
||||
"title": "征信查询次数偏多",
|
||||
"description": f"{req.customer_name}近6个月征信查询 {req.credit_query_count} 次,高于行业平均(4次)。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第8条 - 征信管理",
|
||||
"page": 8,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.82
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 15,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 53.3, "count": 8, "avg_processing_days": 3.5},
|
||||
{"decision": "要求补充说明", "percentage": 33.3, "count": 5, "avg_processing_days": 5.2},
|
||||
{"decision": "降额通过", "percentage": 13.3, "count": 2, "avg_processing_days": 6.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-09-012",
|
||||
"customer_name": "东方物流集团",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "提供多头融资说明(用于业务扩张),核实无实质风险"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理核实客户是否存在多头融资,了解征信查询原因(业务扩张 vs 资金紧张)。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "要求客户提供征信查询原因说明书,核实多头融资情况",
|
||||
"alternative_actions": [
|
||||
"提供资金用途说明和还款计划,证明融资用于业务扩张",
|
||||
"补充近6个月银行流水,证明现金流充足"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-09-012",
|
||||
"customer_name": "东方物流集团",
|
||||
"scenario": f"征信查询 {req.credit_query_count} 次,用于扩张业务",
|
||||
"outcome": "提供业务扩张说明后正常通过,核实无资金链风险"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 风险项3: 涉诉记录
|
||||
if req.litigation_count >= 2:
|
||||
risk_items.append({
|
||||
"level": "medium",
|
||||
"title": "存在涉诉记录",
|
||||
"description": f"{req.customer_name}存在 {req.litigation_count} 起涉诉记录,需核实诉讼性质和金额。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第10条 - 法律风险评估",
|
||||
"page": 10,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.85
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 12,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 58.3, "count": 7, "avg_processing_days": 4.0},
|
||||
{"decision": "要求提供诉讼详情", "percentage": 33.3, "count": 4, "avg_processing_days": 6.5},
|
||||
{"decision": "附加条件通过", "percentage": 8.3, "count": 1, "avg_processing_days": 8.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-08-203",
|
||||
"customer_name": "华泰建设工程",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "确认为小额合同纠纷(<50万),不影响偿债能力"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供每起诉讼的详细情况(原告/被告、金额、进展),评估对财务的影响。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "提供诉讼清单(案由、金额、当前状态),说明对财务状况的影响评估",
|
||||
"alternative_actions": [
|
||||
"如为小额合同纠纷(<50万/起),提供说明后影响较小",
|
||||
"提供法律顾问意见书,说明诉讼风险可控"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-08-203",
|
||||
"customer_name": "华泰建设工程",
|
||||
"scenario": f"{req.litigation_count} 起小额合同纠纷,总金额<100万",
|
||||
"outcome": "确认为日常经营纠纷,不影响偿债能力,正常通过"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 风险项4: 对外担保
|
||||
guarantee_ratio = req.external_guarantee / req.amount if req.amount > 0 else 0
|
||||
if guarantee_ratio >= 0.8:
|
||||
risk_items.append({
|
||||
"level": "high",
|
||||
"title": "对外担保金额较大",
|
||||
"description": f"{req.customer_name}对外担保 {req.external_guarantee:.1f} 亿元,为本次申请金额的 {guarantee_ratio:.1f} 倍。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第11条 - 担保风险",
|
||||
"page": 11,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.88
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 18,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "要求提供担保详情", "percentage": 66.7, "count": 12, "avg_processing_days": 5.8},
|
||||
{"decision": "降额通过", "percentage": 22.2, "count": 4, "avg_processing_days": 7.2},
|
||||
{"decision": "附加条件通过", "percentage": 11.1, "count": 2, "avg_processing_days": 6.5}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-07-234",
|
||||
"customer_name": "华信集团",
|
||||
"decision": "附加条件通过",
|
||||
"mitigation": "提供被担保方财务报告,设置代偿预警机制"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供对外担保明细(被担保方、金额、期限),评估代偿风险。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "提供对外担保明细清单,包括被担保方资质、财务状况、代偿可能性评估",
|
||||
"alternative_actions": [
|
||||
"说明被担保方为关联公司/优质客户,代偿风险低",
|
||||
f"考虑降低授信额度至 {req.amount * 0.8:.1f} 亿元,降低风险敞口"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-07-234",
|
||||
"customer_name": "华信集团",
|
||||
"scenario": f"对外担保为申请额度的 {guarantee_ratio:.1f} 倍",
|
||||
"outcome": "提供被担保方财务报告,设置代偿预警,附加条件通过"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 风险项5: 行业风险
|
||||
if req.project_type == "对公新能源":
|
||||
risk_items.append({
|
||||
"level": "medium",
|
||||
"title": "新能源行业政策依赖性",
|
||||
"description": f"{req.customer_name}属于新能源行业,收入可能依赖政府补贴,需关注补贴到位情况。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《行业风险指引》第3条 - 新能源行业",
|
||||
"page": 3,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.90
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 31,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 48.4, "count": 15, "avg_processing_days": 4.5},
|
||||
{"decision": "要求补贴承诺函", "percentage": 38.7, "count": 12, "avg_processing_days": 6.2},
|
||||
{"decision": "附加条件通过", "percentage": 12.9, "count": 4, "avg_processing_days": 7.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-09-067",
|
||||
"customer_name": "绿动公交集团",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "补贴延迟但现金流充足,运营收入稳定"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供政府补贴承诺函、历史补贴到位情况,说明现金流不完全依赖补贴。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "要求提供地方政府补贴承诺函,核实历史补贴到位率(建议≥80%)",
|
||||
"alternative_actions": [
|
||||
"提供运营收入构成,证明补贴占比<40%,现金流多元化",
|
||||
"说明补贴延迟应对预案(如银行授信、应收账款保理)"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-09-067",
|
||||
"customer_name": "绿动公交集团",
|
||||
"scenario": "新能源公交运营,补贴延迟但运营收入稳定",
|
||||
"outcome": "证明运营收入占比65%,补贴延迟不影响还款,正常通过"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 统计汇总
|
||||
high_count = sum(1 for r in risk_items if r["level"] == "high")
|
||||
medium_count = sum(1 for r in risk_items if r["level"] == "medium")
|
||||
low_count = sum(1 for r in risk_items if r["level"] == "low")
|
||||
|
||||
# 提取关注领域
|
||||
focus_areas = []
|
||||
if any("资产负债率" in r["title"] for r in risk_items):
|
||||
focus_areas.append("财务杠杆")
|
||||
if any("征信" in r["title"] for r in risk_items):
|
||||
focus_areas.append("信用状况")
|
||||
if any("涉诉" in r["title"] for r in risk_items):
|
||||
focus_areas.append("法律风险")
|
||||
if any("担保" in r["title"] for r in risk_items):
|
||||
focus_areas.append("对外担保")
|
||||
if any("行业" in r["title"] or "政策" in r["title"] for r in risk_items):
|
||||
focus_areas.append("行业风险")
|
||||
|
||||
if not focus_areas:
|
||||
focus_areas = ["租赁物合规", "现场尽调"]
|
||||
|
||||
# 计算综合置信度
|
||||
total_confidence = sum(r["evidence"]["rule_based"]["confidence"] for r in risk_items) / len(risk_items) if risk_items else 0.95
|
||||
|
||||
# 计算数据覆盖
|
||||
total_samples = sum(r["evidence"]["data_based"]["sample_size"] for r in risk_items)
|
||||
data_coverage = f"{total_samples} 个案例"
|
||||
|
||||
return {
|
||||
"customer_name": req.customer_name,
|
||||
"analysis_timestamp": datetime.now().isoformat(),
|
||||
"risk_items": risk_items,
|
||||
"summary": {
|
||||
"high_count": high_count,
|
||||
"medium_count": medium_count,
|
||||
"low_count": low_count,
|
||||
"focus_areas": focus_areas,
|
||||
"overall_confidence": round(total_confidence, 2),
|
||||
"data_coverage": data_coverage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/test")
|
||||
async def test_endpoint():
|
||||
"""测试端点 - 返回完整的伪造数据"""
|
||||
return {
|
||||
"customer_name": "远东新能源公交公司",
|
||||
"analysis_timestamp": datetime.now().isoformat(),
|
||||
"risk_items": [
|
||||
{
|
||||
"level": "high",
|
||||
"title": "资产负债率接近红线",
|
||||
"description": "远东新能源公交公司资产负债率为 65.0%,距离70%红线仅剩 5.0% 空间。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第12条",
|
||||
"page": 12,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.87
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 23,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "降额通过", "percentage": 60.9, "count": 14, "avg_processing_days": 4.2},
|
||||
{"decision": "退回补充材料", "percentage": 26.1, "count": 6, "avg_processing_days": 7.5},
|
||||
{"decision": "附加条件通过", "percentage": 13.0, "count": 3, "avg_processing_days": 5.8}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-08-156",
|
||||
"customer_name": "华东光伏设备公司",
|
||||
"decision": "降额通过(降15%)",
|
||||
"mitigation": "增加法人个人担保,提供政府补贴承诺函"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-07-089",
|
||||
"customer_name": "绿能公交运营公司",
|
||||
"decision": "降额通过(降12%)",
|
||||
"mitigation": "调整还款期限,追加车辆抵押"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-06-234",
|
||||
"customer_name": "中部新能源投资",
|
||||
"decision": "附加条件通过",
|
||||
"mitigation": "要求季度现金流报告,设置财务预警指标"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提前准备降额方案或增加抵押物,避免评审会直接退回。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "主动降低授信额度12-15%(约 0.36-0.45 亿元),调整后负债率可控",
|
||||
"alternative_actions": [
|
||||
"增加抵押物或第三方担保,覆盖率≥授信额度的20%(约 0.60 亿元)",
|
||||
"提供详细现金流测算报告,证明偿债能力充足",
|
||||
"调整还款期限,降低单期还款压力"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-08-156",
|
||||
"customer_name": "华东光伏设备公司",
|
||||
"scenario": "债务率 65.0%,新能源行业,申请 3.0 亿授信",
|
||||
"outcome": "降额15%后通过,增加法人担保 + 政府补贴承诺函"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"level": "medium",
|
||||
"title": "征信查询次数偏多",
|
||||
"description": "远东新能源公交公司近6个月征信查询 8 次,高于行业平均(4次)。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第8条 - 征信管理",
|
||||
"page": 8,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.82
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 15,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 53.3, "count": 8, "avg_processing_days": 3.5},
|
||||
{"decision": "要求补充说明", "percentage": 33.3, "count": 5, "avg_processing_days": 5.2},
|
||||
{"decision": "降额通过", "percentage": 13.3, "count": 2, "avg_processing_days": 6.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-09-012",
|
||||
"customer_name": "东方物流集团",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "提供多头融资说明(用于业务扩张),核实无实质风险"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-07-145",
|
||||
"customer_name": "鑫盛商贸公司",
|
||||
"decision": "要求补充说明",
|
||||
"mitigation": "说明征信查询原因,提供现金流充足证明"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理核实客户是否存在多头融资,了解征信查询原因(业务扩张 vs 资金紧张)。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "要求客户提供征信查询原因说明书,核实多头融资情况",
|
||||
"alternative_actions": [
|
||||
"提供资金用途说明和还款计划,证明融资用于业务扩张",
|
||||
"补充近6个月银行流水,证明现金流充足",
|
||||
"如为临时资金需求,提供订单合同等业务凭证"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-09-012",
|
||||
"customer_name": "东方物流集团",
|
||||
"scenario": "征信查询 8 次,用于扩张业务",
|
||||
"outcome": "提供业务扩张说明后正常通过,核实无资金链风险"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"level": "medium",
|
||||
"title": "存在涉诉记录",
|
||||
"description": "远东新能源公交公司存在 2 起涉诉记录,需核实诉讼性质和金额。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第10条 - 法律风险评估",
|
||||
"page": 10,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.85
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 12,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 58.3, "count": 7, "avg_processing_days": 4.0},
|
||||
{"decision": "要求提供诉讼详情", "percentage": 33.3, "count": 4, "avg_processing_days": 6.5},
|
||||
{"decision": "附加条件通过", "percentage": 8.3, "count": 1, "avg_processing_days": 8.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-08-203",
|
||||
"customer_name": "华泰建设工程",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "确认为小额合同纠纷(<50万),不影响偿债能力"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-06-178",
|
||||
"customer_name": "顺丰供应链公司",
|
||||
"decision": "要求提供诉讼详情",
|
||||
"mitigation": "提供案件进展说明,评估财务影响<3%"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供每起诉讼的详细情况(原告/被告、金额、进展),评估对财务的影响。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "提供诉讼清单(案由、金额、当前状态),说明对财务状况的影响评估",
|
||||
"alternative_actions": [
|
||||
"如为小额合同纠纷(<50万/起),提供说明后影响较小",
|
||||
"如为债务纠纷,需评估败诉风险和财务影响",
|
||||
"提供法律顾问意见书,说明诉讼风险可控"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-08-203",
|
||||
"customer_name": "华泰建设工程",
|
||||
"scenario": "2 起小额合同纠纷,总金额<100万",
|
||||
"outcome": "确认为日常经营纠纷,不影响偿债能力,正常通过"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"level": "high",
|
||||
"title": "对外担保金额较大",
|
||||
"description": "远东新能源公交公司对外担保 3.5 亿元,为本次申请金额的 1.2 倍。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《融资租赁评审手册》第11条 - 担保风险",
|
||||
"page": 11,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.88
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 18,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "要求提供担保详情", "percentage": 66.7, "count": 12, "avg_processing_days": 5.8},
|
||||
{"decision": "降额通过", "percentage": 22.2, "count": 4, "avg_processing_days": 7.2},
|
||||
{"decision": "附加条件通过", "percentage": 11.1, "count": 2, "avg_processing_days": 6.5}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-07-234",
|
||||
"customer_name": "华信集团",
|
||||
"decision": "附加条件通过",
|
||||
"mitigation": "提供被担保方财务报告,设置代偿预警机制"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-09-045",
|
||||
"customer_name": "宏达投资公司",
|
||||
"decision": "降额通过(降20%)",
|
||||
"mitigation": "降低授信以控制担保风险敞口"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供对外担保明细(被担保方、金额、期限),评估代偿风险。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "提供对外担保明细清单,包括被担保方资质、财务状况、代偿可能性评估",
|
||||
"alternative_actions": [
|
||||
"说明被担保方为关联公司/优质客户,代偿风险低",
|
||||
"提供反担保措施说明(如抵押物、股权质押)",
|
||||
"考虑降低授信额度至 2.4 亿元,降低风险敞口"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-07-234",
|
||||
"customer_name": "华信集团",
|
||||
"scenario": "对外担保为申请额度的 1.2 倍",
|
||||
"outcome": "提供被担保方财务报告,设置代偿预警,附加条件通过"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"level": "medium",
|
||||
"title": "新能源行业政策依赖性",
|
||||
"description": "远东新能源公交公司属于新能源行业,收入可能依赖政府补贴,需关注补贴到位情况。",
|
||||
"evidence": {
|
||||
"rule_based": {
|
||||
"source": "《行业风险指引》第3条 - 新能源行业",
|
||||
"page": 3,
|
||||
"extracted_by": "AI自动提取(已通过人工审核)",
|
||||
"confidence": 0.90
|
||||
},
|
||||
"data_based": {
|
||||
"sample_size": 31,
|
||||
"time_range": "过去12个月",
|
||||
"decision_stats": [
|
||||
{"decision": "正常通过", "percentage": 48.4, "count": 15, "avg_processing_days": 4.5},
|
||||
{"decision": "要求补贴承诺函", "percentage": 38.7, "count": 12, "avg_processing_days": 6.2},
|
||||
{"decision": "附加条件通过", "percentage": 12.9, "count": 4, "avg_processing_days": 7.0}
|
||||
],
|
||||
"similar_cases": [
|
||||
{
|
||||
"case_id": "CAS-2025-08-112",
|
||||
"customer_name": "晨光新能源科技",
|
||||
"decision": "要求补贴承诺函",
|
||||
"mitigation": "地方政府出具补贴承诺函,补贴到位率>85%"
|
||||
},
|
||||
{
|
||||
"case_id": "CAS-2025-09-067",
|
||||
"customer_name": "绿动公交集团",
|
||||
"decision": "正常通过",
|
||||
"mitigation": "补贴延迟但现金流充足,运营收入稳定"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suggestion": "建议业务经理提供政府补贴承诺函、历史补贴到位情况,说明现金流不完全依赖补贴。",
|
||||
"smart_recommendation": {
|
||||
"primary_action": "要求提供地方政府补贴承诺函,核实历史补贴到位率(建议≥80%)",
|
||||
"alternative_actions": [
|
||||
"提供运营收入构成,证明补贴占比<40%,现金流多元化",
|
||||
"说明补贴延迟应对预案(如银行授信、应收账款保理)",
|
||||
"提供同行业可比公司补贴情况,证明行业普遍性"
|
||||
],
|
||||
"reference_case": {
|
||||
"case_id": "CAS-2025-09-067",
|
||||
"customer_name": "绿动公交集团",
|
||||
"scenario": "新能源公交运营,补贴延迟但运营收入稳定",
|
||||
"outcome": "证明运营收入占比65%,补贴延迟不影响还款,正常通过"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"high_count": 2,
|
||||
"medium_count": 3,
|
||||
"low_count": 0,
|
||||
"focus_areas": ["财务杠杆", "信用状况", "法律风险", "对外担保", "行业风险"],
|
||||
"overall_confidence": 0.86,
|
||||
"data_coverage": "99 个案例"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "ok", "service": "风险研判助手(双轨驱动版)"}
|
||||
@@ -1,145 +0,0 @@
|
||||
"""Xingbang API — 兴邦金租评审工作流 + 自进化模块的 HTTP 接口。
|
||||
|
||||
端:
|
||||
POST /api/xingbang/workflow 运行五智能体审批流水线(返回全部产出)
|
||||
POST /api/xingbang/workflow/stream 流式运行(SSE, 逐步展示)
|
||||
GET /api/xingbang/evolution 查看自进化洞察
|
||||
GET /api/xingbang/trajectories 列出历史轨迹
|
||||
GET /api/xingbang/trajectories/{id} 读取单条轨迹
|
||||
|
||||
复用 mini-OpenClaw 的 AgentManager.llm 与后端基础设施。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
|
||||
from graph.agent import agent_manager
|
||||
from xingbang.workflow import XingbangWorkflow
|
||||
from xingbang.config import XINGBANG_CONFIG
|
||||
|
||||
router = APIRouter(prefix="/api/xingbang", tags=["xingbang"])
|
||||
|
||||
|
||||
# 懒加载单例工作流(复用 agent_manager.llm)
|
||||
_workflow: XingbangWorkflow | None = None
|
||||
|
||||
|
||||
def _get_workflow() -> XingbangWorkflow:
|
||||
global _workflow
|
||||
if _workflow is None:
|
||||
if agent_manager._llm is None:
|
||||
# 若 agent 未初始化, 手动初始化(需要 BASE_DIR)
|
||||
from pathlib import Path as _P
|
||||
_workflow = XingbangWorkflow(
|
||||
llm=None,
|
||||
base_dir=_P(__file__).resolve().parent.parent,
|
||||
evolution_enabled=True,
|
||||
)
|
||||
raise RuntimeError("Agent 未初始化, 请先启动后端(会自动初始化 agent_manager)")
|
||||
_workflow = XingbangWorkflow(
|
||||
llm=agent_manager._llm,
|
||||
base_dir=Path(__file__).resolve().parent.parent,
|
||||
evolution_enabled=True,
|
||||
)
|
||||
return _workflow
|
||||
|
||||
|
||||
class WorkflowRequest(BaseModel):
|
||||
task: str # 客户/项目描述
|
||||
customer: str = "" # 客户名(用于轨迹标注)
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
async def get_config():
|
||||
"""返回兴邦项目配置(数据源、五智能体、档位)。"""
|
||||
return XINGBANG_CONFIG
|
||||
|
||||
|
||||
@router.post("/workflow")
|
||||
async def run_workflow(req: WorkflowRequest):
|
||||
"""运行完整五智能体审批流水线。"""
|
||||
wf = _get_workflow()
|
||||
try:
|
||||
result = await wf.run(req.task, customer=req.customer)
|
||||
return result
|
||||
except RuntimeError as e:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/workflow/stream")
|
||||
async def run_workflow_stream(req: WorkflowRequest):
|
||||
"""流式运行(SSE), 逐步展示五智能体进度与产出。"""
|
||||
wf = _get_workflow()
|
||||
|
||||
async def gen():
|
||||
async for event in wf.run_stream(req.task, customer=req.customer):
|
||||
yield {
|
||||
"event": event.get("event", "event"),
|
||||
"data": json.dumps(
|
||||
{k: v for k, v in event.items() if k != "event"},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
}
|
||||
|
||||
return EventSourceResponse(gen())
|
||||
|
||||
|
||||
class FeedbackRequest(BaseModel):
|
||||
trajectory_id: str
|
||||
feedback: str # "positive" | "negative"
|
||||
|
||||
|
||||
@router.post("/evolution/feedback")
|
||||
async def submit_feedback(req: FeedbackRequest):
|
||||
"""提交用户反馈(Level 2 奖励信号), 追加进轨迹。"""
|
||||
try:
|
||||
traj_file = (
|
||||
Path(__file__).resolve().parent.parent
|
||||
/ "xingbang_evolution" / "trajectories" / f"{req.trajectory_id}.json"
|
||||
)
|
||||
if not traj_file.exists():
|
||||
return {"error": f"轨迹不存在: {req.trajectory_id}"}
|
||||
data = json.loads(traj_file.read_text(encoding="utf-8"))
|
||||
data.setdefault("user_feedback", req.feedback)
|
||||
data["user_feedback"] = req.feedback
|
||||
traj_file.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return {"status": "ok", "trajectory_id": req.trajectory_id, "feedback": req.feedback}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
@router.get("/evolution")
|
||||
async def get_evolution():
|
||||
"""查看自进化洞察报告。"""
|
||||
wf = _get_workflow()
|
||||
report = wf.evolution.generate_insights()
|
||||
return {"insights": report, "config": XINGBANG_CONFIG}
|
||||
|
||||
|
||||
@router.get("/trajectories")
|
||||
async def list_trajectories():
|
||||
"""列出所有已保存轨迹。"""
|
||||
wf = _get_workflow()
|
||||
paths = wf.recorder.list_trajectories()
|
||||
return {"count": len(paths),
|
||||
"trajectories": [p.stem for p in paths]}
|
||||
|
||||
|
||||
@router.get("/trajectories/{trajectory_id}")
|
||||
async def get_trajectory(trajectory_id: str):
|
||||
"""读取单条轨迹详情。"""
|
||||
wf = _get_workflow()
|
||||
traj = wf.recorder.load(trajectory_id)
|
||||
if not traj:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="轨迹不存在")
|
||||
return traj
|
||||
@@ -0,0 +1,372 @@
|
||||
"""
|
||||
兴邦金租智能体系统 API 路由
|
||||
基于2026-08-18会议纪要真实痛点设计
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Any
|
||||
import json
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from graph.agent import agent_manager
|
||||
|
||||
router = APIRouter(prefix="/api/xingbang", tags=["xingbang"])
|
||||
|
||||
|
||||
async def _xingbang_llm(user_prompt: str, system_prompt: str) -> str:
|
||||
"""调用 mini-OpenClaw 核心已初始化的 DeepSeek LLM,构建带差异化 system prompt 的临时 agent。
|
||||
|
||||
兼容核心 AgentManager:只复用其 LLM,不依赖不存在的 get_response 等方法。
|
||||
启用后可替换为本地模型(Qwen)以支持"数据不出域"。
|
||||
"""
|
||||
from langchain.agents import create_agent
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
if agent_manager._llm is None:
|
||||
from graph.agent import agent_manager as _am
|
||||
_am.initialize(Path(__file__).resolve().parent.parent)
|
||||
agent = create_agent(
|
||||
model=agent_manager._llm,
|
||||
tools=[], # 兴邦各 Agent 为纯分析角色,暂不绑定核心工具
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
try:
|
||||
result = await agent.ainvoke({"messages": [HumanMessage(content=user_prompt)]})
|
||||
msgs = result.get("messages", [])
|
||||
for m in reversed(msgs):
|
||||
if hasattr(m, "content") and m.type == "ai" and m.content:
|
||||
return m.content
|
||||
return str(result.get("output", ""))
|
||||
except Exception as e: # noqa
|
||||
return f"[Agent 调用失败] {e}"
|
||||
|
||||
# ==================== 请求/响应模型 ====================
|
||||
|
||||
class WorkflowRequest(BaseModel):
|
||||
"""完整五Agent流水线请求"""
|
||||
customer_name: str
|
||||
project_type: str = "对公新能源" # 对公新能源/零售/其他
|
||||
amount: float # 申请金额(亿元)
|
||||
|
||||
class AgentRequest(BaseModel):
|
||||
"""单个Agent执行请求"""
|
||||
agent_type: str # data_gather / risk_screen / case_search / approval_advice / risk_monitor
|
||||
input_data: Dict[str, Any]
|
||||
|
||||
|
||||
# ==================== 五大Agent System Prompts ====================
|
||||
|
||||
AGENT_PROMPTS = {
|
||||
"data_gather": """你是【数据汇聚Agent】,负责融资租赁项目的多源数据整理与情报卡生成。
|
||||
|
||||
**职责:**
|
||||
1. 从多源异构数据(财报PDF、工商信息、征信、中诚信评级模板、租赁物估值等)整理生成统一的"客户情报卡"
|
||||
2. 进行数据校验:交叉核对工商信息、财报数据、征信记录的一致性
|
||||
3. 标注风险标签:现金流压力、关联交易、对外担保、涉诉情况
|
||||
4. 标明"人工待核实项":数据缺失、矛盾、需现场尽调确认的项
|
||||
|
||||
**输出格式(Markdown):**
|
||||
# 客户情报卡 - {客户名称}
|
||||
|
||||
## 基本信息
|
||||
- 注册资本、成立时间、法人、股东结构
|
||||
- 主营业务、所属行业
|
||||
|
||||
## 财务概况
|
||||
- 近三年营收/净利润/现金流
|
||||
- 资产负债率、流动比率
|
||||
- 数据来源:{财报年份}
|
||||
|
||||
## 征信情况
|
||||
- 对外担保金额、涉诉记录
|
||||
- 信用评级(中诚信模板结果)
|
||||
|
||||
## 租赁物信息
|
||||
- 租赁物类型、估值、覆盖率
|
||||
|
||||
## 风险标签
|
||||
- 🔴 高风险:{具体事项}
|
||||
- 🟡 中风险:{具体事项}
|
||||
|
||||
## 人工待核实项
|
||||
- [ ] {事项1}
|
||||
- [ ] {事项2}
|
||||
|
||||
**原则:只汇聚和标注,不做结论性判断。**
|
||||
""",
|
||||
|
||||
"risk_screen": """你是【预审参谋Agent】,负责生成风险筛查清单。
|
||||
|
||||
**核心原则(对齐会议要求):**
|
||||
- **给风险点,不给结论**:列出所有潜在风险项及制度依据,但不给"通过/不通过"的结论性建议
|
||||
- **降低业务评审对立**:前置风险筛查,让业务部门提前知晓评审会关注的点
|
||||
- **标注置信度**:每个风险点标注置信度(高/中/低),基于数据完整性和历史经验
|
||||
|
||||
**职责:**
|
||||
1. 基于"客户情报卡",按公司评审手册逐项筛查风险点
|
||||
2. 引用制度条款和历史案例作为依据
|
||||
3. 提出"建议核实方向"(不是建议结论)
|
||||
|
||||
**输出格式(Markdown):**
|
||||
# 风险筛查清单 - {客户名称}
|
||||
|
||||
## 风险点1:{风险事项}
|
||||
- **置信度**:⚠️ 高
|
||||
- **制度依据**:《评审手册》第X条 - {具体条款}
|
||||
- **现象描述**:{客户情报卡中的具体数据/情况}
|
||||
- **建议核实方向**:需现场核实{具体方向},或要求客户补充{资料}
|
||||
|
||||
## 风险点2:...
|
||||
|
||||
## 汇总
|
||||
- 高风险项:{数量}
|
||||
- 中风险项:{数量}
|
||||
- 建议重点关注领域:{现金流/担保/租赁物合规/...}
|
||||
|
||||
**不要输出"建议通过/不通过"等结论性意见。**
|
||||
""",
|
||||
|
||||
"case_search": """你是【经验检索Agent】,负责从历史案例库中检索相似案例。
|
||||
|
||||
**职责:**
|
||||
1. 基于客户行业、规模、风险特征检索历史案例(成功+失败)
|
||||
2. 提取案例的关键决策依据和风险缓释措施
|
||||
3. 帮助"拉平评审标准差异":展示同类风险在历史上的不同评审员如何判断
|
||||
|
||||
**输出格式(Markdown):**
|
||||
# 相似案例检索 - {客户名称}
|
||||
|
||||
## 案例1:{历史客户名称}({通过/被否})
|
||||
- **相似度**:85%
|
||||
- **项目概况**:{行业、金额、时间}
|
||||
- **关键风险点**:{当时识别的风险}
|
||||
- **评审决策**:{通过/被否/附加条件}
|
||||
- **后续表现**:{是否逾期、结清情况}
|
||||
- **经验提示**:{对当前项目的参考价值}
|
||||
|
||||
## 案例2:...
|
||||
|
||||
## 经验总结
|
||||
- 同行业历史不良率:{%}
|
||||
- 关键风控措施:{担保增信/租赁物覆盖率/定期检查}
|
||||
|
||||
**目标:让评审员看到"过去怎么做的",作为当前决策参考。**
|
||||
""",
|
||||
|
||||
"approval_advice": """你是【审批意见Agent】,负责生成标准化审批参考意见。
|
||||
|
||||
**职责:**
|
||||
1. 汇总前三个Agent(数据汇聚、风险筛查、经验检索)的产出
|
||||
2. 生成标准化审批参考文档,包含:建议结论、证据引用、风险缓释建议
|
||||
3. **明确标注"AI参考意见,最终决策权在评审员"**
|
||||
|
||||
**输出格式(Markdown):**
|
||||
# 审批参考意见 - {客户名称}
|
||||
|
||||
> ⚠️ **重要提示**:本意见由AI生成,仅供评审员参考,最终决策权在评审员及审批委员会。评审员可一票否决。
|
||||
|
||||
## 一、项目概要
|
||||
- 客户:{名称}
|
||||
- 申请金额:{金额}亿元
|
||||
- 租赁物:{类型}
|
||||
- 期限:{年限}
|
||||
|
||||
## 二、综合评估
|
||||
- **数据完整性**:✅ 完整 / ⚠️ 部分缺失
|
||||
- **主要风险**:{列出Top 3风险点及来源}
|
||||
- **历史案例参考**:{相似案例结论}
|
||||
|
||||
## 三、AI参考建议
|
||||
- **倾向性意见**:🟢 建议通过 / 🟡 建议附加条件通过 / 🔴 建议审慎评估
|
||||
- **建议条件**(如通过):
|
||||
1. {风险缓释措施1,如追加担保}
|
||||
2. {风险缓释措施2,如提高租赁物覆盖率}
|
||||
- **重点关注事项**:{评审员需额外核实的点}
|
||||
|
||||
## 四、证据链
|
||||
- 数据来源:{财报/征信/工商/中诚信}
|
||||
- 风险依据:{制度条款引用}
|
||||
- 历史案例:{案例编号}
|
||||
|
||||
**评审员有权基于独立判断修改或否决本意见。**
|
||||
""",
|
||||
|
||||
"risk_monitor": """你是【风险监控Agent】,负责审批空窗期与租后动态监控。
|
||||
|
||||
**职责:**
|
||||
1. 审批空窗期(立项→上会,约17工作日):动态监控客户征信、涉诉、舆情变化
|
||||
2. 租后阶段:定期检查还款记录、保险覆盖、租赁物状态
|
||||
3. 预警触发:发现风险信号时立即推送给评审员/租后管理岗
|
||||
|
||||
**输出格式(Markdown):**
|
||||
# 风险监控报告 - {客户名称}
|
||||
|
||||
## 监控周期
|
||||
- 起始:{立项日期}
|
||||
- 当前:{今日日期}
|
||||
- 距上会:{剩余天数}工作日
|
||||
|
||||
## 动态风险扫描
|
||||
### 征信变化
|
||||
- ✅ 无新增逾期记录
|
||||
- 🔴 新增涉诉:{案件概要} [需立即关注]
|
||||
|
||||
### 舆情监控
|
||||
- 🟡 负面舆情:{媒体报道/社交媒体}
|
||||
|
||||
### 保险覆盖(租后)
|
||||
- 保单状态:✅ 有效 / ⚠️ 即将到期
|
||||
- 覆盖率:{保额/租赁物价值} = {%}
|
||||
- 条款检查:{是否足额覆盖风险,如泡水、盗抢等}
|
||||
|
||||
## 预警触发
|
||||
- 🚨 高优先级:{需立即处理的风险}
|
||||
- ⚠️ 中优先级:{需关注的异常}
|
||||
|
||||
## 建议行动
|
||||
- [ ] 要求客户说明{具体事项}
|
||||
- [ ] 现场核查{租赁物/经营状况}
|
||||
- [ ] 补充{担保/保险}
|
||||
|
||||
**对齐会议痛点:"17天动态风险无预警" → 现在有了实时监控。**
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
# ==================== 单Agent执行 ====================
|
||||
|
||||
async def execute_single_agent(agent_type: str, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""执行单个Agent"""
|
||||
if agent_type not in AGENT_PROMPTS:
|
||||
raise HTTPException(400, f"Unknown agent type: {agent_type}")
|
||||
|
||||
system_prompt = AGENT_PROMPTS[agent_type]
|
||||
user_prompt = f"""请基于以下输入数据执行任务:
|
||||
|
||||
{json.dumps(input_data, ensure_ascii=False, indent=2)}
|
||||
|
||||
严格按照你的角色职责和输出格式生成报告。"""
|
||||
|
||||
# 调用核心Agent(复用 mini-OpenClaw 的 agent_manager 及其 LLM)
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
]
|
||||
|
||||
# 复用 mini-OpenClaw 核心已初始化的 DeepSeek LLM,构建带差异化 system prompt 的临时 agent
|
||||
response = await _xingbang_llm(user_prompt, system_prompt)
|
||||
|
||||
return {
|
||||
"agent_type": agent_type,
|
||||
"output": response,
|
||||
"status": "completed"
|
||||
}
|
||||
|
||||
|
||||
# ==================== 完整流水线(SSE流式) ====================
|
||||
|
||||
async def run_workflow_stream(customer_name: str, project_type: str, amount: float):
|
||||
"""五Agent流水线 SSE流式执行"""
|
||||
|
||||
# 模拟数据包(实际应从数据库/文件读取)
|
||||
mock_data = {
|
||||
"customer_name": customer_name,
|
||||
"project_type": project_type,
|
||||
"amount": amount,
|
||||
"financial_data": {
|
||||
"revenue_2025": 15.2,
|
||||
"revenue_2024": 13.8,
|
||||
"revenue_2023": 12.1,
|
||||
"net_profit_2025": 1.8,
|
||||
"cash_flow_2025": 2.3,
|
||||
"debt_ratio": 0.62
|
||||
},
|
||||
"credit_info": {
|
||||
"external_guarantee": 3.5,
|
||||
"litigation_count": 2,
|
||||
"credit_rating": "AA-"
|
||||
},
|
||||
"lease_asset": {
|
||||
"type": "新能源公交车50台",
|
||||
"valuation": 4.2,
|
||||
"coverage_ratio": 1.4
|
||||
}
|
||||
}
|
||||
|
||||
# Agent执行顺序
|
||||
agents = [
|
||||
("data_gather", "数据汇聚"),
|
||||
("risk_screen", "预审参谋"),
|
||||
("case_search", "经验检索"),
|
||||
("approval_advice", "审批意见"),
|
||||
("risk_monitor", "风险监控")
|
||||
]
|
||||
|
||||
context = {"initial_data": mock_data}
|
||||
|
||||
for agent_type, agent_name in agents:
|
||||
# 发送agent_start事件
|
||||
yield f"event: agent_start\ndata: {json.dumps({'agent': agent_name, 'type': agent_type}, ensure_ascii=False)}\n\n"
|
||||
|
||||
await asyncio.sleep(0.5) # 模拟处理延迟
|
||||
|
||||
try:
|
||||
result = await execute_single_agent(agent_type, context)
|
||||
context[agent_type] = result["output"] # 传递给下一个Agent
|
||||
|
||||
# 发送agent_end事件
|
||||
yield f"event: agent_end\ndata: {json.dumps({'agent': agent_name, 'type': agent_type, 'output': result['output']}, ensure_ascii=False)}\n\n"
|
||||
|
||||
except Exception as e:
|
||||
yield f"event: error\ndata: {json.dumps({'agent': agent_name, 'error': str(e)}, ensure_ascii=False)}\n\n"
|
||||
break
|
||||
|
||||
# 完成
|
||||
yield f"event: done\ndata: {json.dumps({'status': 'completed'}, ensure_ascii=False)}\n\n"
|
||||
|
||||
|
||||
# ==================== API端点 ====================
|
||||
|
||||
@router.post("/workflow/stream")
|
||||
async def workflow_stream(req: WorkflowRequest):
|
||||
"""运行完整五Agent流水线(SSE)"""
|
||||
return StreamingResponse(
|
||||
run_workflow_stream(req.customer_name, req.project_type, req.amount),
|
||||
media_type="text/event-stream"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/agent/execute")
|
||||
async def execute_agent(req: AgentRequest):
|
||||
"""执行单个Agent"""
|
||||
result = await execute_single_agent(req.agent_type, req.input_data)
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
async def get_config():
|
||||
"""获取系统配置"""
|
||||
return {
|
||||
"agents": [
|
||||
{"id": "data_gather", "name": "数据汇聚Agent", "description": "多源数据整理生成情报卡"},
|
||||
{"id": "risk_screen", "name": "预审参谋Agent", "description": "给风险点不给结论,前置筛查"},
|
||||
{"id": "case_search", "name": "经验检索Agent", "description": "历史案例检索与经验复用"},
|
||||
{"id": "approval_advice", "name": "审批意见Agent", "description": "生成标准化审批参考意见"},
|
||||
{"id": "risk_monitor", "name": "风险监控Agent", "description": "审批空窗期+租后动态监控"}
|
||||
],
|
||||
"project_types": ["对公新能源", "对公传统", "零售乘用车", "零售二手车"],
|
||||
"model_config": {
|
||||
"provider": "DeepSeek",
|
||||
"model": "deepseek-v4-flash",
|
||||
"base_url": "http://49.232.133.151/v1"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health_check():
|
||||
"""健康检查"""
|
||||
return {"status": "ok", "service": "兴邦智能体系统"}
|
||||
@@ -0,0 +1,432 @@
|
||||
"""兴邦金租智能评审助手 - 统一API(7 个助手完整版)
|
||||
|
||||
基于 2026.8.18 会议痛点的完整解决方案:
|
||||
1. 立项前置风控助手 🔍 - 业务经理风险自查
|
||||
2. 中诚信智能填报助手 📊 - 激活十几万投资的评级模板
|
||||
3. 空窗期动态监控助手 🚨 - 17天空窗期风险重扫
|
||||
4. 保单条款合规核验助手 🛡️ - OCR+条款解析
|
||||
5. 评审报告辅助生成助手 📄 - 看资料+写报告提效
|
||||
6. 主审员智能分配助手 🎯 - 多维度负荷优化
|
||||
7. AI预审过滤+加速助手 🤖 - 团队经验预判+加速
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator, Dict, List, Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# 导入数据层
|
||||
from data_layer.erp_connector import get_erp_connector
|
||||
from data_layer.history_store import get_history_store
|
||||
from data_layer.reviewer_profile import get_reviewer_store
|
||||
from data_layer.team_experience import get_team_experience_store
|
||||
|
||||
router = APIRouter(prefix="/api/xingbang/unified", tags=["兴邦智能评审统一接口"])
|
||||
|
||||
|
||||
# ============ Pydantic 模型 ============
|
||||
|
||||
class ProjectBasicInfo(BaseModel):
|
||||
"""项目基础信息"""
|
||||
project_id: str = ""
|
||||
project_name: str
|
||||
client_name: str
|
||||
industry: str
|
||||
credit_amount: float # 万元
|
||||
lease_object: str = ""
|
||||
lease_value: float = 0 # 万元
|
||||
|
||||
|
||||
class FinancialData(BaseModel):
|
||||
"""财务数据"""
|
||||
registered_capital: float = 0
|
||||
established_years: int = 0
|
||||
asset_liability_ratio: float = 0
|
||||
current_ratio: float = 0
|
||||
quick_ratio: float = 0
|
||||
net_profit_2024: float = 0
|
||||
net_profit_2025: float = 0
|
||||
revenue_growth_rate: float = 0
|
||||
|
||||
|
||||
class CreditReport(BaseModel):
|
||||
"""征信报告"""
|
||||
overdue_count: int = 0
|
||||
query_count_recent_6m: int = 0
|
||||
credit_limit: float = 0
|
||||
enforcement_records: int = 0
|
||||
|
||||
|
||||
class InsurancePolicyData(BaseModel):
|
||||
"""保单数据"""
|
||||
policy_number: str
|
||||
insurance_company: str
|
||||
policy_type: str
|
||||
coverage_amount: float
|
||||
exclusions: List[str] = []
|
||||
policy_pdf_path: str = ""
|
||||
|
||||
|
||||
# ============ 助手 1:立项前置风控 ============
|
||||
|
||||
class Assistant1Request(BaseModel):
|
||||
"""立项前置风控请求"""
|
||||
project: ProjectBasicInfo
|
||||
financial: FinancialData
|
||||
credit: CreditReport
|
||||
|
||||
|
||||
@router.post("/assistant1/risk-check")
|
||||
async def assistant1_risk_check(request: Assistant1Request):
|
||||
"""助手1:立项前置风控自查(只给风险点,不给结论)"""
|
||||
|
||||
async def generate():
|
||||
try:
|
||||
# 获取团队经验
|
||||
team_exp = get_team_experience_store()
|
||||
thresholds = team_exp.get_approval_thresholds()
|
||||
risk_patterns = team_exp.get_risk_patterns(request.project.industry)
|
||||
red_flags = team_exp.get_industry_red_flags(request.project.industry)
|
||||
|
||||
yield f"data: {json.dumps({'type': 'start'}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 生成 Markdown 报告
|
||||
content = f"""# 立项前置风险自查清单
|
||||
|
||||
## 📋 项目基本信息
|
||||
- **项目名称**:{request.project.project_name}
|
||||
- **客户名称**:{request.project.client_name}
|
||||
- **行业**:{request.project.industry}
|
||||
- **授信金额**:{request.project.credit_amount} 万元
|
||||
- **租赁物**:{request.project.lease_object or '未填写'}
|
||||
- **租赁物价值**:{request.project.lease_value} 万元
|
||||
|
||||
---
|
||||
|
||||
## 🔍 风险自查结果
|
||||
|
||||
"""
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 检查高风险项
|
||||
high_risks = []
|
||||
medium_risks = []
|
||||
low_risks = []
|
||||
|
||||
# 1. 客户资质检查
|
||||
if request.financial.registered_capital < 1000:
|
||||
high_risks.append("**注册资本不足**:注册资本 {:.0f} 万元 < 1000 万元(标准要求)\n - **建议**:考虑提高授信门槛或要求集团担保".format(request.financial.registered_capital))
|
||||
|
||||
if request.financial.established_years < 3:
|
||||
medium_risks.append("**成立时间较短**:成立 {} 年 < 3 年(标准要求)\n - **建议**:补充实控人其他企业经营情况".format(request.financial.established_years))
|
||||
|
||||
# 2. 财务指标检查
|
||||
asset_liability = request.financial.asset_liability_ratio
|
||||
threshold_al = thresholds.get("资产负债率", {})
|
||||
|
||||
if asset_liability > threshold_al.get("critical", 0.8):
|
||||
high_risks.append(f"**资产负债率过高**:{asset_liability:.1%} > {threshold_al.get('critical', 0.8):.0%}(警戒线)\n - **团队共识**:{threshold_al.get('team_consensus', '需要额外担保')}")
|
||||
elif asset_liability > threshold_al.get("warning", 0.7):
|
||||
medium_risks.append(f"**资产负债率偏高**:{asset_liability:.1%}(位于 {threshold_al.get('warning', 0.7):.0%}-{threshold_al.get('critical', 0.8):.0%} 区间)\n - **建议**:补充负债结构说明,关注短期偿债压力")
|
||||
else:
|
||||
low_risks.append(f"✅ **资产负债率**:{asset_liability:.1%}(良好)")
|
||||
|
||||
current_ratio = request.financial.current_ratio
|
||||
threshold_cr = thresholds.get("流动比率", {})
|
||||
|
||||
if current_ratio < threshold_cr.get("critical", 1.0):
|
||||
high_risks.append(f"**流动比率不足**:{current_ratio:.2f} < {threshold_cr.get('critical', 1.0):.2f}(红线)\n - **团队共识**:{threshold_cr.get('team_consensus', '短期偿债能力堪忧')}")
|
||||
elif current_ratio < threshold_cr.get("warning", 1.2):
|
||||
medium_risks.append(f"**流动比率偏低**:{current_ratio:.2f}\n - **建议**:关注应收账款周转情况")
|
||||
else:
|
||||
low_risks.append(f"✅ **流动比率**:{current_ratio:.2f}(符合标准)")
|
||||
|
||||
# 3. 征信检查
|
||||
if request.credit.overdue_count > 0:
|
||||
high_risks.append(f"**征信逾期记录**:近 2 年有 {request.credit.overdue_count} 次逾期\n - **建议**:要求客户提供逾期说明及还款证明")
|
||||
else:
|
||||
low_risks.append("✅ **征信记录**:近 2 年无逾期(良好)")
|
||||
|
||||
if request.credit.enforcement_records > 0:
|
||||
high_risks.append(f"**被执行记录**:存在 {request.credit.enforcement_records} 条被执行记录\n - **重要**:评审大概率退回,需先解决执行案件")
|
||||
else:
|
||||
low_risks.append("✅ **被执行记录**:无(良好)")
|
||||
|
||||
if request.credit.query_count_recent_6m > 10:
|
||||
medium_risks.append(f"**征信查询频繁**:近 6 个月查询 {request.credit.query_count_recent_6m} 次\n - **建议**:了解客户融资需求是否过于迫切")
|
||||
|
||||
# 4. 租赁物检查
|
||||
if request.project.lease_value > 0:
|
||||
coverage_ratio = request.project.lease_value / request.project.credit_amount
|
||||
if coverage_ratio < 1.2:
|
||||
high_risks.append(f"**租赁物价值不足**:租赁物价值 {request.project.lease_value} 万元,覆盖率 {coverage_ratio:.1%} < 120%(标准要求)\n - **建议**:补充其他担保措施")
|
||||
else:
|
||||
low_risks.append(f"✅ **租赁物覆盖率**:{coverage_ratio:.1%}(符合要求)")
|
||||
|
||||
# 5. 行业红线检查
|
||||
if red_flags:
|
||||
content_flags = f"\n### ⚠️ {request.project.industry} 行业常见风险点\n\n"
|
||||
for flag in red_flags:
|
||||
content_flags += f"- {flag}\n"
|
||||
content_flags += "\n**建议**:核实客户是否触及以上风险点\n\n"
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_flags}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 输出风险项
|
||||
if high_risks:
|
||||
content_high = "### 🔴 高风险项(必须处理,否则评审大概率退回)\n\n"
|
||||
for i, risk in enumerate(high_risks, 1):
|
||||
content_high += f"{i}. {risk}\n\n"
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_high}, ensure_ascii=False)}\n\n"
|
||||
|
||||
if medium_risks:
|
||||
content_medium = "### 🟡 中风险项(建议补充材料或说明)\n\n"
|
||||
for i, risk in enumerate(medium_risks, 1):
|
||||
content_medium += f"{i}. {risk}\n\n"
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_medium}, ensure_ascii=False)}\n\n"
|
||||
|
||||
if low_risks:
|
||||
content_low = "### 🟢 低风险项(符合要求)\n\n"
|
||||
for risk in low_risks:
|
||||
content_low += f"- {risk}\n"
|
||||
content_low += "\n"
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_low}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 整体评估
|
||||
if len(high_risks) == 0 and len(medium_risks) == 0:
|
||||
suggestion = "✅ **直接提交**:项目风险可控,可直接提交评审"
|
||||
elif len(high_risks) == 0:
|
||||
suggestion = "🟡 **补充材料后提交**:建议先处理中风险项,提高通过率"
|
||||
else:
|
||||
suggestion = "🔴 **重大调整后提交**:存在高风险项,建议先与客户沟通调整方案"
|
||||
|
||||
content_final = f"""---
|
||||
|
||||
## 💡 整体评估
|
||||
|
||||
{suggestion}
|
||||
|
||||
**说明**:本清单基于团队历史评审经验生成,仅供参考,不代表评审结论。最终是否通过由主审员独立判断。
|
||||
"""
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_final}, ensure_ascii=False)}\n\n"
|
||||
yield f"data: {json.dumps({'type': 'done'}, 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(generate(), media_type="text/event-stream")
|
||||
|
||||
|
||||
# ============ 助手 6:主审员智能分配 ============
|
||||
|
||||
class Assistant6Request(BaseModel):
|
||||
"""主审员智能分配请求"""
|
||||
project_industry: str
|
||||
project_amount: float # 万元
|
||||
project_complexity: int = Field(3, ge=1, le=5, description="项目复杂度 1-5")
|
||||
|
||||
|
||||
@router.post("/assistant6/allocate")
|
||||
async def assistant6_allocate(request: Assistant6Request):
|
||||
"""助手6:主审员智能分配(多维度评分)"""
|
||||
|
||||
try:
|
||||
reviewer_store = get_reviewer_store()
|
||||
|
||||
# 获取推荐列表
|
||||
recommendations = reviewer_store.get_recommended_reviewers(
|
||||
project_industry=request.project_industry,
|
||||
project_complexity=request.project_complexity,
|
||||
top_n=3
|
||||
)
|
||||
|
||||
# 获取所有主审员当前状态
|
||||
all_profiles = reviewer_store.get_all_profiles()
|
||||
reviewer_status = [
|
||||
{
|
||||
"name": name,
|
||||
"current_projects": profile["current_projects"],
|
||||
"specialty": profile["specialty"],
|
||||
"workload_capacity": profile["workload_capacity"],
|
||||
"status": "🔴 超载" if profile["current_projects"] >= profile["workload_capacity"]
|
||||
else ("🟡 偏高" if profile["current_projects"] >= profile["workload_capacity"] * 0.8 else "🟢 正常")
|
||||
}
|
||||
for name, profile in all_profiles.items()
|
||||
]
|
||||
|
||||
return {
|
||||
"reviewers": reviewer_status,
|
||||
"recommendations": recommendations,
|
||||
"project_info": {
|
||||
"industry": request.project_industry,
|
||||
"amount": request.project_amount,
|
||||
"complexity": request.project_complexity,
|
||||
},
|
||||
"algorithm_version": "v1.0_multidim_scoring",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============ 助手 7:AI预审过滤+加速 ============
|
||||
|
||||
class Assistant7Request(BaseModel):
|
||||
"""AI预审请求"""
|
||||
project: ProjectBasicInfo
|
||||
financial: FinancialData
|
||||
credit: CreditReport
|
||||
|
||||
|
||||
@router.post("/assistant7/pre-screen")
|
||||
async def assistant7_pre_screen(request: Assistant7Request):
|
||||
"""助手7:AI预审过滤(基于团队经验,快速判断是否值得进入正式评审)"""
|
||||
|
||||
async def generate():
|
||||
try:
|
||||
yield f"data: {json.dumps({'type': 'start'}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 获取团队经验和历史案例
|
||||
team_exp = get_team_experience_store()
|
||||
history_store = get_history_store()
|
||||
|
||||
thresholds = team_exp.get_approval_thresholds()
|
||||
rejection_reasons = team_exp.get_common_rejection_reasons(top_n=5)
|
||||
|
||||
# 相似案例检索
|
||||
similar_cases = history_store.search_similar_cases(
|
||||
industry=request.project.industry,
|
||||
credit_amount=request.project.credit_amount,
|
||||
limit=3
|
||||
)
|
||||
|
||||
content = f"""# AI预审报告(团队经验模式)
|
||||
|
||||
## 📊 项目信息
|
||||
- **项目名称**:{request.project.project_name}
|
||||
- **客户名称**:{request.project.client_name}
|
||||
- **行业**:{request.project.industry}
|
||||
- **授信金额**:{request.project.credit_amount} 万元
|
||||
|
||||
---
|
||||
|
||||
## 🤖 AI预审判断(基于团队 {team_exp.get_metadata().get('total_cases_analyzed', 0)} 个历史案例)
|
||||
|
||||
"""
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 快速红线检查
|
||||
red_line_hit = []
|
||||
warning_items = []
|
||||
|
||||
# 1. 财务红线
|
||||
if request.financial.asset_liability_ratio > thresholds.get("资产负债率", {}).get("critical", 0.8):
|
||||
red_line_hit.append("资产负债率超过红线")
|
||||
|
||||
if request.financial.current_ratio < thresholds.get("流动比率", {}).get("critical", 1.0):
|
||||
red_line_hit.append("流动比率低于红线")
|
||||
|
||||
# 2. 征信红线
|
||||
if request.credit.enforcement_records > 0:
|
||||
red_line_hit.append(f"存在 {request.credit.enforcement_records} 条被执行记录")
|
||||
|
||||
if request.credit.overdue_count > 2:
|
||||
red_line_hit.append(f"征信逾期次数过多({request.credit.overdue_count} 次)")
|
||||
|
||||
# 3. 警告项
|
||||
if request.financial.revenue_growth_rate < 0:
|
||||
warning_items.append("营收负增长")
|
||||
|
||||
if request.credit.query_count_recent_6m > 10:
|
||||
warning_items.append("征信查询频繁")
|
||||
|
||||
# AI预审结论
|
||||
if red_line_hit:
|
||||
conclusion = "🔴 **不建议进入正式评审**"
|
||||
reason_text = "\n".join([f"- {item}" for item in red_line_hit])
|
||||
content_conclusion = f"""### {conclusion}
|
||||
|
||||
**触及红线项**:
|
||||
{reason_text}
|
||||
|
||||
**建议**:与客户沟通调整方案,或直接退回立项申请,避免浪费评审资源。
|
||||
|
||||
"""
|
||||
elif len(warning_items) >= 2:
|
||||
conclusion = "🟡 **建议谨慎评审**"
|
||||
reason_text = "\n".join([f"- {item}" for item in warning_items])
|
||||
content_conclusion = f"""### {conclusion}
|
||||
|
||||
**警告项**:
|
||||
{reason_text}
|
||||
|
||||
**建议**:可以进入正式评审,但需重点关注以上风险点,建议增加尽调深度。
|
||||
|
||||
"""
|
||||
else:
|
||||
conclusion = "🟢 **建议进入正式评审**"
|
||||
content_conclusion = f"""### {conclusion}
|
||||
|
||||
**初步判断**:项目风险可控,符合基本准入标准,建议正常推进评审流程。
|
||||
|
||||
"""
|
||||
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_conclusion}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 相似案例参考
|
||||
if similar_cases:
|
||||
content_cases = "\n---\n\n## 📚 相似案例参考\n\n"
|
||||
for i, case in enumerate(similar_cases, 1):
|
||||
result_emoji = "✅" if case.get("review_result") == "approved" else "❌"
|
||||
content_cases += f"""### 案例 {i}:{case.get('project_name', '未知项目')}
|
||||
- **行业**:{case.get('industry', '未知')}
|
||||
- **金额**:{case.get('credit_amount', 0)} 万元
|
||||
- **评审结果**:{result_emoji} {case.get('review_result', '未知')}
|
||||
- **关键风险点**:{', '.join([r.get('type', '未知') for r in case.get('identified_risks', [])])}
|
||||
|
||||
"""
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_cases}, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 高频退回原因提示
|
||||
content_tips = "\n---\n\n## 💡 团队经验提示(高频退回原因)\n\n"
|
||||
for reason in rejection_reasons:
|
||||
content_tips += f"- **{reason['reason']}**({reason['count']} 次) → {reason['solution']}\n"
|
||||
|
||||
yield f"data: {json.dumps({'type': 'content', 'data': content_tips}, ensure_ascii=False)}\n\n"
|
||||
|
||||
yield f"data: {json.dumps({'type': 'done'}, 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(generate(), media_type="text/event-stream")
|
||||
|
||||
|
||||
# ============ 数据管理接口 ============
|
||||
|
||||
@router.get("/data/reviewers")
|
||||
async def get_all_reviewers():
|
||||
"""获取所有主审员画像"""
|
||||
reviewer_store = get_reviewer_store()
|
||||
return reviewer_store.get_all_profiles()
|
||||
|
||||
|
||||
@router.get("/data/team-experience")
|
||||
async def get_team_experience():
|
||||
"""获取团队集体经验"""
|
||||
team_exp = get_team_experience_store()
|
||||
return {
|
||||
"approval_thresholds": team_exp.get_approval_thresholds(),
|
||||
"common_rejections": team_exp.get_common_rejection_reasons(),
|
||||
"metadata": team_exp.get_metadata(),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/data/history-stats")
|
||||
async def get_history_statistics():
|
||||
"""获取历史案例统计"""
|
||||
history_store = get_history_store()
|
||||
return history_store.get_statistics()
|
||||
@@ -0,0 +1,569 @@
|
||||
"""兴邦金租智能评审助手 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": "避免保单风险盲区"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user