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, 不含隐私 遵循兴邦三档分级审批制度, 数据源指向兴邦实验数据包
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"""Trajectory Recorder — 采集五智能体每次调用的完整轨迹。
|
||||
|
||||
轨迹格式 (trajectory):
|
||||
{
|
||||
"trajectory_id": "traj_<timestamp>",
|
||||
"customer": "客户名",
|
||||
"workflow_id": "...",
|
||||
"steps": [
|
||||
{
|
||||
"agent": "data_curator",
|
||||
"input": "任务描述",
|
||||
"prompt_snippet": "...",
|
||||
"tools": ["read_xingbang_data", ...],
|
||||
"output_file": "...",
|
||||
"output_preview": "...",
|
||||
"success": true/false,
|
||||
"time_ms": 1234
|
||||
}, ...
|
||||
],
|
||||
"final_result": {...},
|
||||
"cumulative_reward": 0.0
|
||||
}
|
||||
|
||||
轨迹保存在 <base>/xingbang_evolution/trajectories/*.json。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
class TrajectoryRecorder:
|
||||
"""记录并持久化一次评审流水线的完整执行轨迹。"""
|
||||
|
||||
def __init__(self, base_dir: Path, enabled: bool = True):
|
||||
self._base_dir = Path(base_dir)
|
||||
self._dir = self._base_dir / "xingbang_evolution" / "trajectories"
|
||||
self._dir.mkdir(parents=True, exist_ok=True)
|
||||
self.enabled = enabled
|
||||
# 当前正在记录的轨迹
|
||||
self._current: dict[str, Any] | None = None
|
||||
# metadata (customer 等)
|
||||
self._meta: dict = {}
|
||||
self._start_time: float = 0.0
|
||||
|
||||
def begin(self, workflow_id: str | None = None, customer: str = "") -> str:
|
||||
"""开始记录一条新轨迹,返回 trajectory_id。"""
|
||||
self._start_time = time.time()
|
||||
self._current = {
|
||||
"trajectory_id": f"traj_{int(time.time())}_{uuid.uuid4().hex[:6]}",
|
||||
"workflow_id": workflow_id or f"wf_{int(time.time())}",
|
||||
"customer": customer or "",
|
||||
"created_at": self._start_time,
|
||||
"steps": [],
|
||||
"final_result": {},
|
||||
"cumulative_reward": 0.0,
|
||||
}
|
||||
return self._current["trajectory_id"]
|
||||
|
||||
def add_step(self, step: dict[str, Any]) -> None:
|
||||
"""追加一步智能体执行记录。"""
|
||||
if self._current is not None:
|
||||
self._current["steps"].append(step)
|
||||
|
||||
def finalize(self, success: bool = True, summary: str = "") -> dict[str, Any]:
|
||||
"""结束记录,保存轨迹到磁盘,返回轨迹。"""
|
||||
if self._current is None:
|
||||
return {}
|
||||
self._current["final_result"] = {
|
||||
"success": success,
|
||||
"summary": summary,
|
||||
"duration_ms": int((time.time() - self._start_time) * 1000),
|
||||
"total_steps": len(self._current["steps"]),
|
||||
}
|
||||
# 记录成功/失败
|
||||
trajectory = self._current
|
||||
self._save(trajectory)
|
||||
self._current = None
|
||||
return trajectory
|
||||
|
||||
def _save(self, trajectory: dict[str, Any]) -> Path:
|
||||
path = self._dir / f"{trajectory['trajectory_id']}.json"
|
||||
path.write_text(
|
||||
json.dumps(trajectory, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return path
|
||||
|
||||
# ── 从调用侧采集 ────────────────────────────────────
|
||||
def on_evidence(self, kind: str, data: dict) -> None:
|
||||
"""订阅 agents 的 evidence 回调,累积成本步记录。"""
|
||||
if not self.enabled or self._current is None:
|
||||
return
|
||||
if kind == "agent_start":
|
||||
buffered = []
|
||||
self._current["_buffered"] = buffered
|
||||
# 这一步先不展开, 由 workflow 显式调用 add_step 更为清晰
|
||||
|
||||
def list_trajectories(self) -> list[Path]:
|
||||
return sorted(self._dir.glob("*.json"))
|
||||
|
||||
def load(self, trajectory_id: str) -> dict[str, Any]:
|
||||
path = self._dir / f"{trajectory_id}.json"
|
||||
if path.exists():
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
return {}
|
||||
Reference in New Issue
Block a user