按 docs/v2/05-implementation-plan.md §1 候选迁移表与 claude_use/T00 白名单, 从 55e2cdb 抽取 224 个文件(2.48 MB)到独立目录 F:/mini-OpenClaw-v2。 - 排除:凭据(.env/server-deploy.conf/旧DELIVERY.md)、运行数据(xingbang_data 等 4 个 SQLite)、依赖(venv/node_modules 符号链接)、归档(_backup*/test-evidence/ ori_file)、Office 临时文件 - 目标目录独立 git 仓库,初始恢复点;Gitea 远程与分支待 OQ-06 Q2 定案 - 未部署、未启动服务、未调用模型 Co-Authored-By: Claude Code <noreply@anthropic.com>
373 lines
13 KiB
Python
373 lines
13 KiB
Python
"""
|
||
兴邦金租智能体系统 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": "兴邦智能体系统"}
|