feat: 完成三角色工作台及金租对公Skills全流程演示

This commit is contained in:
NathanAlex7
2026-09-15 10:32:24 +08:00
parent 5f0b1f9409
commit 47ec090345
58 changed files with 2534 additions and 609 deletions
+53 -2
View File
@@ -1,7 +1,7 @@
"""Snapshot-grounded report drafts, protected human text and immutable final copies."""
from copy import deepcopy
import io
from services.assistant_store import get, put, listing, event, uid, now, digest, Conflict
from services.assistant_store import get, put, listing, event, uid, now, digest, Conflict, time_event, finish_time_event
from services.component_registry import resolve
@@ -12,6 +12,19 @@ def section_content(section_id, snapshot, run):
return f"{snapshot['customer']},拟融资 {values.get('facility.amount', '未提供')} 万元;租赁物价值 {values.get('lease.total_value', '未提供')} 万元。\n期间:{snapshot.get('period', '待确认')};口径:{snapshot.get('scope', '待确认')}。\n价值覆盖率(比值):{run.get('summary', {}).get('coverage') or '未计算'}。"
if section_id == "findings":
return "\n".join(f"{i+1}. {f['title']}({f['result']}):{f['suggested_action']}\n依据:{'、'.join(f.get('evidence_ids', [])) or '缺少可验证证据'}" for i, f in enumerate(unresolved)) or "本次已执行的检查未发现待处理事项;不表示未执行的检查已通过。"
if section_id in ("completeness", "verification"):
missing = [f["title"] for f in unresolved if f.get("result") in ("unknown", "error")]
return ("待补充或无法完成核验:" + "、".join(missing)) if missing else "本次必做资料检查已执行,未发现明确缺件;仍需人工核对材料真实性和时效。"
if section_id == "financial":
assets = values.get("financial.total_assets", "未提供")
liabilities = values.get("financial.total_liabilities", "未提供")
cashflow = values.get("financial.operating_cashflow", "未提供")
return f"资产总额 {assets} 万元;负债总额 {liabilities} 万元;经营现金流 {cashflow} 万元。\n计算结果及口径以本次冻结数据卡和Skill执行记录为准。"
if section_id == "scenario":
return f"当前融资金额 {values.get('facility.amount', '未提供')} 万元,租赁物价值 {values.get('lease.total_value', '未提供')} 万元,价值覆盖率 {run.get('summary', {}).get('coverage') or '未计算'}。\n额度、期限、价格和增信条件由授权人员结合情景测算确认。"
if section_id == "manager_response":
notes = [f.get("human_note") for f in run.get("findings", []) if f.get("human_note")]
return "\n".join(f"{i+1}. {note}" for i, note in enumerate(notes)) or "尚无客户经理确认说明;风险回应及补件记录保留在项目问题清单。"
responses = [s.get("outputs", {}).get("requirement_response", "") for s in run["steps"] if s.get("outputs", {}).get("requirement_response")]
failed = [s["name"] for s in run["steps"] if s["status"] in ("failed", "partial", "skipped")]
return ("\n".join(responses) or "尚无经验证的模型核查意见,请人工补充判断。") + ("\n未完整执行:" + "、".join(failed) if failed else "") + "\n本底稿仅供核查,不构成审批结论。"
@@ -60,12 +73,31 @@ class DocumentsMixin:
stale = snapshot["data_revision"] != project["data_revision"]
sections = [{**deepcopy(s), "content": section_content(s["id"], snapshot, run), "locked": False, "confirmed": False,
"stale": stale, "source_snapshot_id": snapshot["id"], "candidate": None, "history": []} for s in template["sections"]]
if template_id in ("template.independent-review", "template.committee-brief", "template.review", "template.committee"):
decisions = listing(db, "review_decision", project_id)
judgment = next((section for section in sections if section["id"] == "judgment"), None)
if judgment and decisions:
current, seen = [], set()
for item in decisions:
key = item.get("node_id") or item.get("finding_id") or "overall"
if key in seen or (item.get("run_id") and item.get("run_id") != run_id):
continue
seen.add(key)
current.append(item)
judgment["content"] = "\n".join(
f"{item.get('node_title') or '项目整体'}|{item.get('decision')}:{item.get('opinion') or '评审员已确认'}\n"
f"数据来源:{'、'.join(item.get('evidence_ids', [])) or '该节点未取得可验证材料'};检查依据:{'、'.join(item.get('basis_ids', [])) or '人工专业判断'}"
for item in current
) + "\n\n以上意见由评审员确认;每项意见均关联冻结快照与证据索引,AI仅提供核查建议。"
report = {"id": uid("report"), "project_id": project_id, "run_id": run_id, "snapshot_id": snapshot["id"],
"template": deepcopy(template), "status": "stale" if stale else "draft", "sections": sections, "actor_id": actor,
"created_at": now(), "data_domain": project["data_domain"], "data_revision": snapshot["data_revision"],
"disclaimer": ("演示数据|" if project["data_domain"] == "demo" else "") + "核查底稿|不构成审批结论",
"evidence": deepcopy(run["evidence"])}
result = put(db, "report", report)
time_event(db, project_id, "report_drafting", "报告整理" if template_id == "template.ai-pre-review" else "评审报告编制",
"manager" if template_id == "template.ai-pre-review" else "reviewer", "active", actor, "report", report["id"],
detail={"template_id": template_id, "run_id": run_id})
if idempotency_key:
db.execute("INSERT INTO assistant_idempotency VALUES (?,?,?,?,?)", (actor, "report", idempotency_key, request_hash, result["id"]))
event(db, project_id, "report.created", actor, {"report_id": report["id"], "snapshot_id": snapshot["id"]})
@@ -127,9 +159,28 @@ class DocumentsMixin:
final["content_hash"] = digest(final)
put(db, "report_final", final, immutable=True)
report.update(status="finalized", final_id=final["id"], finalized_by=actor)
finish_time_event(db, "report", report_id)
elif action == "submit":
project = get(db, "project", project_id)
if report["data_revision"] != project["data_revision"] or any(s["stale"] for s in report["sections"]):
raise Conflict("报告来源已变化,请先重新预审并更新报告")
submissions = listing(db, "submission", project_id)
if any(item.get("status") == "submitted" for item in submissions):
raise Conflict("项目已在评审池中;如需更新,请先从评审池撤回再重新推送")
submission = {"id": uid("submission"), "project_id": project_id, "report_id": report_id, "run_id": report["run_id"],
"submitted_by": actor, "submitted_at": now(), "status": "submitted",
"sequence": 1 + max((item.get("sequence", 1) for item in submissions), default=0),
"content_hash": digest(report)}
put(db, "submission", submission, immutable=True)
report.update(status="submitted", submission_id=submission["id"], submitted_by=actor)
project.update(workflow_state="submitted", stage="待评审", submitted_at=submission["submitted_at"],
reviewer_id=None, review_claimed_at=None, review_started_at=None, review_decided_at=None)
put(db, "project", project)
finish_time_event(db, "report", report_id)
time_event(db, project_id, "review_queue", "评审排队", "reviewer", "queue", actor, "submission", submission["id"])
else:
raise ValueError("不支持的底稿动作")
if action != "finalize":
if action not in ("finalize", "submit"):
report["status"] = "stale" if any(s["stale"] for s in report["sections"]) else "draft"
result = put(db, "report", report, expected=expected)
event(db, project_id, "report." + action, actor, {"report_id": report_id, "section_id": payload.get("section_id")})