""" 兴邦金租智能体系统 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": "兴邦智能体系统"}