Files
Agent-System/scripts/build_neuropulse_demo_seed.py

362 lines
12 KiB
Python

#!/usr/bin/env python3
"""从实测产物构建前端展示种子:NeuroPulse (project 5201)。"""
from __future__ import annotations
import csv
import json
import os
import re
import subprocess
from datetime import datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "web" / "src" / "data" / "seeds" / "neuropulseSeed.json"
PROJECT_ID = 5201
PROJECT_NAME = "神经外科双频超声吸引系统 (CUSA-NeuroPulse)"
DEVICE_TYPE = "超声软组织手术吸引设备"
DOC_MAP = {
"intake": {
"work_dir": "data/dev/workspaces/bizdev/intake_1788920542147409000",
"title": "用户初始需求收集表",
"stage": "V0.1",
"state": "approved",
"summary": "CUSA-NeuroPulse 立项需求摸底与临床预期用途基线(真实 MiniMax 产出)。",
},
"research": {
"work_dir": "data/dev/workspaces/bizdev/research_1788921024562992000",
"title": "调研报告(竞品、市场与法规初筛)",
"stage": "V0.5",
"state": "approved",
"summary": "竞品/市场/法规初筛调研报告(真实 MiniMax 产出)。",
},
"feasibility_dev": {
"work_dir": "data/dev/workspaces/bizdev/feasibility_dev_1788922272891355000",
"title": "设计开发可行性评估报告",
"stage": "V0.5",
"state": "approved",
"summary": "设计开发可行性评估(真实 MiniMax 产出)。",
},
"feasibility_reg": {
"work_dir": "data/dev/workspaces/bizdev/feasibility_reg_1788922655324377000",
"title": "注册路径可行性评估报告",
"stage": "V0.5",
"state": "approved",
"summary": "NMPA 三类创新通道注册路径可行性(已终审批准)。",
},
"user_requirements": {
"work_dir": "data/dev/workspaces/bizdev/user_requirements_1788923266008791000",
"title": "用户需求表",
"stage": "V0.5",
"state": "reviewing",
"summary": "用户需求表 UR(真实 MiniMax 产出,审核中)。",
},
"prelim_design": {
"work_dir": "data/dev/workspaces/bizdev/prelim_design_1788923504095801000",
"title": "系统初步设计方案",
"stage": "V0.5",
"state": "approving",
"summary": "系统初步设计方案(真实 MiniMax 产出,待批准)。",
},
"cdo_quote": {
"work_dir": "data/dev/workspaces/bizdev/cdo_quote_1788924027615053000",
"title": "CDO商务报价单",
"stage": "V1.0",
"state": "drafting",
"summary": "CDO 商务报价与工时拆解(真实 MiniMax 产出)。",
},
}
def sid(uid: int) -> str:
return f"sess-neuropulse-u{uid}"
def read_md(rel: str) -> str:
p = ROOT / rel / "output.md"
if not p.exists():
return f"# 缺失产物\n\n路径不存在: `{rel}/output.md`"
return p.read_text(encoding="utf-8")
def load_db_messages():
cmd = [
"docker",
"exec",
"shuimu-context-pg-dev",
"psql",
"-U",
"app",
"-d",
"agent_system_dev",
"-c",
r"\copy (SELECT session_id, role, content, COALESCE(intent,''), created_at FROM chat_messages WHERE session_id LIKE 'sess_full_p5201%' ORDER BY id) TO STDOUT WITH CSV",
]
raw = subprocess.check_output(cmd, text=True)
rows = list(csv.reader(raw.splitlines()))
# map backend session -> UI session
mapped = []
for sess, role, content, intent, created in rows:
uid = 1
if sess.endswith("_u2"):
uid = 2
elif sess.endswith("_u3"):
uid = 3
mapped.append(
{
"session_id": sid(uid),
"role": role,
"content": content,
"intent": intent,
"created_at": created,
}
)
return mapped
def load_early_dialogues_from_results():
path = Path("/tmp/full_lifecycle_p5201_results.json")
if not path.exists():
return []
data = json.loads(path.read_text(encoding="utf-8"))
out = []
for d in data.get("dialogues") or []:
uid = d.get("user_id") or 1
name = d.get("user") or "张工"
# skip empty timeout
user_msg = d.get("input") or ""
reply = d.get("reply") or ""
intent = d.get("intent") or ""
if not user_msg:
continue
out.append({"uid": uid, "name": name, "role": "user", "content": user_msg, "intent": ""})
if reply:
out.append(
{
"uid": uid,
"name": "水木东方协同参谋",
"role": "assistant",
"content": reply,
"intent": intent,
}
)
elif "生成《用户初始需求收集表》" in user_msg:
out.append(
{
"uid": uid,
"name": "水木东方协同参谋",
"role": "assistant",
"content": (
"已为您启动《用户初始需求收集表》编制任务。智能体小队已完成立项画像整理,"
"正式稿已写入交付物大盘(doc-neuropulse-intake)。您可在右侧工作流文档中打开查阅,"
"并按编制人流程认领后送审。"
),
"intent": "TRIGGER_TASK",
}
)
# reminders (full from DB preferred; keep order markers)
for key in (
"author_after_reject",
"reviewer_pending",
"approver_pending",
"reviewer_after_resubmit",
"approver_after_pass",
):
item = (data.get("reminders") or {}).get(key)
if not isinstance(item, dict) or not item.get("input"):
continue
# DB has fuller replies for these; skip adding truncated duplicates here
return out
def enrich_assistant_from_reply_files(messages):
"""用 dialogue/reply.md 全文替换截断的同类答复。"""
replies = []
for p in (ROOT / "data/dev/workspaces/dialogue").glob("*/reply.md"):
text = p.read_text(encoding="utf-8").strip()
if text:
replies.append(text)
def better(old: str) -> str:
if not old:
return old
head = old[:80].replace("\n", " ")
for r in replies:
if r.startswith(old[:40]) or old[:40] in r[:120] or head[:30] in r[:200]:
if len(r) > len(old):
return r
return old
for m in messages:
if m.get("role") == "assistant":
m["content"] = better(m["content"])
return messages
def build_messages():
early = load_early_dialogues_from_results()
db = load_db_messages()
# 先装 early(张工主线),再装 DB(避免重复时以 DB 全文覆盖同类)
by_session: dict[str, list] = {sid(1): [], sid(2): [], sid(3): []}
idx = 0
def push(uid, role, content, intent="", author=None, ts=None):
nonlocal idx
idx += 1
session = sid(uid)
hhmm = ts or f"10:{10 + idx:02d}"
if "T" in str(hhmm):
# 2026-09-09 11:28:52.xxx+08
try:
hhmm = str(hhmm)[11:16]
except Exception:
hhmm = f"11:{idx:02d}"
msg = {
"id": f"m-np-{idx}",
"sessionId": session,
"role": role,
"content": content,
"timestamp": hhmm,
}
if role == "user":
msg["authorName"] = {1: "张工", 2: "李主管", 3: "王总监"}.get(uid, "用户")
else:
msg["authorName"] = author or "水木东方协同参谋"
if intent:
msg["intentTag"] = intent
by_session[session].append(msg)
# system banner for each
for uid, uname in [(1, "张工"), (2, "李主管"), (3, "王总监")]:
push(
uid,
"system",
f"水木东方协同参谋小队已就位。当前项目【{PROJECT_NAME}】进入【V0.5 方案成型与法规对齐】。"
f"您正以「{uname}」身份与智能体私聊,其他成员对话不可见。",
ts="09:50",
)
for e in early:
push(e["uid"], e["role"], e["content"], e.get("intent") or "", e.get("name"), ts=None)
# DB messages (reminders / later turns) — append if not duplicate of last user text
existing_user_texts = set()
for sess_msgs in by_session.values():
for m in sess_msgs:
if m["role"] == "user":
existing_user_texts.add(m["content"][:40])
for row in db:
uid = 1
if row["session_id"].endswith("u2"):
uid = 2
elif row["session_id"].endswith("u3"):
uid = 3
if row["role"] == "user" and row["content"][:40] in existing_user_texts:
continue
push(uid, row["role"], row["content"], row.get("intent") or "", ts=row.get("created_at"))
# flatten + enrich
all_msgs = []
for s in (sid(1), sid(2), sid(3)):
all_msgs.extend(by_session[s])
all_msgs = enrich_assistant_from_reply_files(all_msgs)
return all_msgs, {s: len(by_session[s]) for s in by_session}
def main():
messages, counts = build_messages()
documents = {}
deliverables = []
for key, meta in DOC_MAP.items():
doc_id = f"doc-neuropulse-{key}"
md = read_md(meta["work_dir"])
documents[doc_id] = {
"docId": doc_id,
"projectId": PROJECT_ID,
"taskKey": key,
"title": meta["title"],
"stage": meta["stage"],
"category": "workflow",
"creatorId": 1,
"creatorName": "张工",
"version": "V1.0" if meta["state"] == "approved" else "V0.9",
"lifecycleState": meta["state"],
"isTemplateOnly": False,
"isClaimed": True,
"author": "张工",
"reviewer": "李主管",
"approver": "王总监",
"updatedAt": "2026-09-09 11:30",
"summary": meta["summary"],
"markdown": md,
}
deliverables.append(
{
"docId": doc_id,
"projectId": PROJECT_ID,
"taskKey": key,
"title": meta["title"],
"stage": meta["stage"],
"category": "workflow",
"creatorId": 1,
"creatorName": "张工",
"isFixedWorkflow": True,
"isTemplateOnly": False,
"lifecycleState": meta["state"],
"isClaimed": True,
"author": "张工",
"reviewer": "李主管",
"approver": "王总监",
"updatedAt": "2026-09-09 11:30",
"summary": meta["summary"],
}
)
sessions = []
for uid, uname in [(1, "张工"), (2, "李主管"), (3, "王总监")]:
sessions.append(
{
"sessionId": sid(uid),
"userId": uid,
"projectId": PROJECT_ID,
"title": PROJECT_NAME,
"deviceType": DEVICE_TYPE,
"stage": "V0.5",
"stageLabel": "V0.5 方案成型与法规对齐",
"messageCount": counts.get(sid(uid), 0),
"updatedAt": "刚刚",
}
)
seed = {
"generatedAt": datetime.now().isoformat(timespec="seconds"),
"projectId": PROJECT_ID,
"projectName": PROJECT_NAME,
"activeSessionId": sid(1),
"sessions": sessions,
"messages": messages,
"deliverables": deliverables,
"documents": documents,
"permissions": {
"1": {d["docId"]: "author" for d in deliverables},
"2": {d["docId"]: "reviewer" for d in deliverables},
"3": {d["docId"]: "approver" for d in deliverables},
},
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(seed, ensure_ascii=False), encoding="utf-8")
print(f"Wrote {OUT}")
print(f"sessions={len(sessions)} messages={len(messages)} docs={len(documents)}")
for k, v in documents.items():
print(f" {k}: markdown={len(v['markdown'])} state={v['lifecycleState']}")
if __name__ == "__main__":
main()