Files
mini-openclaw/backend/api/xingbang_api.py
T
NathanAlex7 c4ac5aa82e feat(xingbang): 兴邦金租 AI 评审平台模块(构建于 mini-OpenClaw)
新增兴邦金租评审项目模块, 作为 mini-OpenClaw 之上的完整交付:
- 五智能体流水线(data_curator/risk_scout/case_finder/approval_advisor/risk_monitor)
- evolution 自进化子系统(轨迹采集+奖励计算+模式归纳prompt增强)
- /api/xingbang/* 端点(config/workflow/stream/evolution/trajectories)
- 运行时产物(客户产出情报卡 + 进化轨迹数据)已 gitignore, 不含隐私

遵循兴邦三档分级审批制度, 数据源指向兴邦实验数据包
2026-09-01 16:58:44 +08:00

146 lines
4.7 KiB
Python

"""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