@shulex/cxclawctl 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +228 -2
  2. package/bin/cxclawctl.mjs +11 -0
  3. package/capabilities.lock.json +898 -0
  4. package/dist/cli.js +363 -0
  5. package/dist/cli.js.map +1 -0
  6. package/package.json +33 -4
  7. package/requirements-cli.txt +2 -0
  8. package/skills/__lib__/__init__.py +0 -0
  9. package/skills/__lib__/skill-runtime/bootstrap.py +134 -0
  10. package/skills/__lib__/solvea-api/__init__.py +0 -0
  11. package/skills/__lib__/solvea-api/client.py +1944 -0
  12. package/skills/__lib__/solvea-api/dashboard.py +100 -0
  13. package/skills/__lib__/solvea-api/solvea_api.py +1341 -0
  14. package/skills/agent-analytics/SKILL.md +74 -0
  15. package/skills/agent-analytics/references/chat-report-fields.md +36 -0
  16. package/skills/agent-analytics/references/report-fields.md +130 -0
  17. package/skills/agent-analytics/references/transcript.md +110 -0
  18. package/skills/agent-builder/SKILL.md +42 -0
  19. package/skills/agent-builder/interfaces/fallback-chatflow.md +47 -0
  20. package/skills/agent-builder/references/chatflow/graph-spec.md +259 -0
  21. package/skills/agent-builder/references/chatflow/node-selection.md +89 -0
  22. package/skills/agent-builder/references/chatflow/nodes/agent.md +350 -0
  23. package/skills/agent-builder/references/chatflow/nodes/business-track.md +43 -0
  24. package/skills/agent-builder/references/chatflow/nodes/code.md +157 -0
  25. package/skills/agent-builder/references/chatflow/nodes/collect-order.md +47 -0
  26. package/skills/agent-builder/references/chatflow/nodes/finish.md +34 -0
  27. package/skills/agent-builder/references/chatflow/nodes/human-review.md +60 -0
  28. package/skills/agent-builder/references/chatflow/nodes/if-else.md +100 -0
  29. package/skills/agent-builder/references/chatflow/nodes/intent.md +79 -0
  30. package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +43 -0
  31. package/skills/agent-builder/references/chatflow/nodes/llm.md +117 -0
  32. package/skills/agent-builder/references/chatflow/nodes/note.md +38 -0
  33. package/skills/agent-builder/references/chatflow/nodes/question.md +97 -0
  34. package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +68 -0
  35. package/skills/agent-builder/references/chatflow/nodes/reply.md +97 -0
  36. package/skills/agent-builder/references/chatflow/nodes/start.md +35 -0
  37. package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +48 -0
  38. package/skills/agent-builder/references/chatflow/nodes/tool.md +138 -0
  39. package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +63 -0
  40. package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +52 -0
  41. package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +50 -0
  42. package/skills/agent-builder/references/chatflow/nodes/variable-process.md +70 -0
  43. package/skills/agent-builder/references/chatflow/overview.md +53 -0
  44. package/skills/agent-builder/references/chatflow/visual-translation.md +64 -0
  45. package/skills/agent-builder/references/chatflow/workflow.md +134 -0
  46. package/skills/agent-builder/references/multi-agent/create-update.md +185 -0
  47. package/skills/agent-builder/references/multi-agent/graph-spec.md +246 -0
  48. package/skills/agent-builder/references/multi-agent/graph-structure.md +12 -0
  49. package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +13 -0
  50. package/skills/agent-builder/references/multi-agent/migration/large.md +554 -0
  51. package/skills/agent-builder/references/multi-agent/migration/principles.md +446 -0
  52. package/skills/agent-builder/references/multi-agent/migration/small.md +225 -0
  53. package/skills/agent-builder/references/multi-agent/orchestration.md +11 -0
  54. package/skills/agent-builder/references/multi-agent/overview.md +48 -0
  55. package/skills/agent-builder/references/multi-agent/update.md +128 -0
  56. package/skills/agent-builder/references/multi-agent/workflow.md +87 -0
  57. package/skills/agent-builder/references/single-agent/overview.md +48 -0
  58. package/skills/agent-builder/references/single-agent/prompt.md +16 -0
  59. package/skills/agent-builder/references/single-agent/tools.md +36 -0
  60. package/skills/agent-builder/references/single-agent/voice.md +46 -0
  61. package/skills/agent-builder/references/tools/http-tool.md +315 -0
  62. package/skills/agent-builder/references/tools/mock-tool.md +279 -0
  63. package/skills/agent-builder/references/tools/overview.md +46 -0
  64. package/skills/agent-builder/references/tools/tool-config.md +43 -0
  65. package/skills/agent-builder/references/tools/x-token.md +34 -0
  66. package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +3458 -0
  67. package/skills/agent-builder/scripts/chatflow/validate_graph.py +1697 -0
  68. package/skills/agent-builder/scripts/copy_marketplace_agent.py +61 -0
  69. package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +5974 -0
  70. package/skills/agent-builder/scripts/multi-agent/replay_compare.py +679 -0
  71. package/skills/agent-builder/scripts/patch_agent_tools.py +135 -0
  72. package/skills/agent-builder/scripts/tools/validate_tool_config.py +384 -0
  73. package/skills/agent-tester/SKILL.md +35 -0
  74. package/skills/agent-tester/batch-test/overview.md +86 -0
  75. package/skills/agent-tester/scripts/batch_test_sheet.py +213 -0
  76. package/skills/agent-tester/single-test/badcase-retest.md +85 -0
  77. package/skills/agent-tester/single-test/direct-test.md +24 -0
  78. package/skills/agent-tester/single-test/overview.md +142 -0
  79. package/skills/delivery/SKILL.md +112 -0
  80. package/skills/delivery/interfaces/classify.md +38 -0
  81. package/skills/delivery/interfaces/create-agents.md +89 -0
  82. package/skills/delivery/interfaces/delivery-test.md +67 -0
  83. package/skills/delivery/references/flow.md +40 -0
  84. package/skills/delivery/references/validation.md +15 -0
  85. package/skills/delivery/scripts/batch_test.py +704 -0
  86. package/skills/delivery/scripts/classify.py +22 -0
  87. package/skills/delivery/scripts/create_agents.py +695 -0
  88. package/skills/delivery/scripts/gen_delivery_report.py +335 -0
  89. package/skills/delivery/scripts/gen_excel.py +234 -0
  90. package/skills/delivery/scripts/gen_report.py +208 -0
  91. package/skills/delivery/scripts/plan.py +163 -0
  92. package/skills/delivery/scripts/render.py +713 -0
  93. package/skills/delivery/scripts/render_summary.py +38 -0
  94. package/skills/delivery/scripts/sample_history_cases.py +109 -0
  95. package/skills/kb-builder/SKILL.md +80 -0
  96. package/skills/kb-builder/interfaces/qa-upload.md +55 -0
  97. package/skills/kb-builder/interfaces/web-kb.md +84 -0
  98. package/skills/kb-builder/references/directory.md +373 -0
  99. package/skills/kb-builder/references/export.md +282 -0
  100. package/skills/kb-builder/references/isolation-keywords.md +113 -0
  101. package/skills/kb-builder/references/knowledge.md +153 -0
  102. package/skills/kb-builder/references/product.md +52 -0
  103. package/skills/kb-builder/references/qa-from-help-center.md +58 -0
  104. package/skills/kb-builder/references/qa-from-pdf.md +94 -0
  105. package/skills/kb-builder/references/qa-from-tickets.md +168 -0
  106. package/skills/kb-builder/references/qa-from-web.md +98 -0
  107. package/skills/kb-builder/references/qa-quality.md +73 -0
  108. package/skills/kb-builder/references/reply-template.md +237 -0
  109. package/skills/kb-builder/references/tag-isolation-migration.md +61 -0
  110. package/skills/kb-builder/references/tags.md +45 -0
  111. package/skills/kb-builder/references/upload.md +130 -0
  112. package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +333 -0
  113. package/skills/kb-builder/scripts/build_directory_plan.py +144 -0
  114. package/skills/kb-builder/scripts/crawl_site.py +433 -0
  115. package/skills/kb-builder/scripts/create_dirs.py +146 -0
  116. package/skills/kb-builder/scripts/export_knowledge.py +454 -0
  117. package/skills/kb-builder/scripts/extract_ticket_qa.py +441 -0
  118. package/skills/kb-builder/scripts/extract_web_qa.py +118 -0
  119. package/skills/kb-builder/scripts/import_qa.py +219 -0
  120. package/skills/kb-builder/scripts/import_source_documents.py +143 -0
  121. package/skills/kb-builder/scripts/import_templates.py +409 -0
  122. package/skills/poc/SKILL.md +125 -0
  123. package/skills/poc/assets/default_manual.css +279 -0
  124. package/skills/poc/assets/notice_escalate.png +0 -0
  125. package/skills/poc/assets/notice_escalate2.png +0 -0
  126. package/skills/poc/assets/notice_new_chat.png +0 -0
  127. package/skills/poc/assets/poc_test_template.html +645 -0
  128. package/skills/poc/assets/poc_test_template.md +104 -0
  129. package/skills/poc/assets/step3_preview.png +0 -0
  130. package/skills/poc/assets/step4_order_note.png +0 -0
  131. package/skills/poc/interfaces/classify.md +38 -0
  132. package/skills/poc/interfaces/create-agents.md +108 -0
  133. package/skills/poc/interfaces/poc-test.md +94 -0
  134. package/skills/poc/references/flow.md +47 -0
  135. package/skills/poc/references/kb-directory.md +22 -0
  136. package/skills/poc/references/validation.md +17 -0
  137. package/skills/poc/scripts/classify.py +22 -0
  138. package/skills/poc/scripts/create_agents.py +818 -0
  139. package/skills/poc/scripts/gen_excel.py +234 -0
  140. package/skills/poc/scripts/gen_poc_manual.py +249 -0
  141. package/skills/poc/scripts/gen_report.py +208 -0
  142. package/skills/poc/scripts/inspect_chatflow.py +209 -0
  143. package/skills/poc/scripts/plan.py +178 -0
  144. package/skills/poc/scripts/poc_test.py +495 -0
  145. package/skills/poc/scripts/render.py +713 -0
  146. package/skills/poc/scripts/render_summary.py +41 -0
  147. package/skills/runtime-inspector/SKILL.md +51 -0
  148. package/skills/runtime-inspector/references/frontend-rendering.md +126 -0
  149. package/skills/runtime-inspector/references/model.md +59 -0
  150. package/skills/runtime-inspector/references/sandbox.md +131 -0
  151. package/skills/runtime-inspector/scripts/model_inspect.py +68 -0
  152. package/skills/runtime-inspector/scripts/prepare.py +98 -0
  153. package/skills/runtime-inspector/scripts/process.py +84 -0
  154. package/skills/runtime-inspector/scripts/read_state.py +32 -0
  155. package/skills/runtime-inspector/scripts/sandbox_inspect.py +463 -0
  156. package/skills/runtime-inspector/scripts/test_claude_proxy.py +49 -0
  157. package/skills/runtime-inspector/scripts/test_openai_proxy.py +41 -0
  158. package/skills/runtime-inspector/scripts/verify_persistence.py +101 -0
  159. package/skills/runtime-inspector/scripts/write_test.py +41 -0
  160. package/skills/setup/SKILL.md +17 -0
  161. package/skills/shulex-support/SKILL.md +39 -0
  162. package/skills/shulex-support/references/badcase-analysis/overview.md +34 -0
  163. package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +105 -0
  164. package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +89 -0
  165. package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +36 -0
  166. package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +53 -0
  167. package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +259 -0
  168. package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +148 -0
  169. package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +57 -0
  170. package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +120 -0
  171. package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +126 -0
  172. package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +121 -0
  173. package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +208 -0
  174. package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +115 -0
  175. package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +72 -0
  176. package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +92 -0
  177. package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +147 -0
  178. package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +283 -0
  179. package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +77 -0
  180. package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +19 -0
  181. package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +23 -0
  182. package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +31 -0
  183. package/skills/shulex-support/references/product-docs.md +70 -0
  184. package/skills/shulex-support/references/tickets.md +184 -0
  185. package/skills/shulex-support/scripts/support.py +427 -0
  186. package/skills/solvea-desk/SKILL.md +39 -0
  187. package/skills/solvea-desk/references/report/fields.md +82 -0
  188. package/skills/solvea-desk/references/report/report-design.md +200 -0
  189. package/skills/solvea-desk/references/report/report-template.html +44 -0
  190. package/skills/solvea-desk/references/report/report.css +517 -0
  191. package/skills/solvea-desk/references/report/workflow.md +464 -0
  192. package/skills/solvea-desk/references/tickets/query.md +103 -0
  193. package/skills/solvea-desk/references/tickets/tags.md +83 -0
  194. package/skills/solvea-desk/scripts/report/fetch_sources.py +580 -0
  195. package/skills/solvea-desk/scripts/report/render_report.py +763 -0
  196. package/skills/solvea-desk/scripts/tickets/desk_api.py +73 -0
  197. package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +403 -0
  198. package/skills/solvea-desk/scripts/tickets/ticket_tags.py +305 -0
  199. package/skills/solvea-kit/SKILL.md +84 -0
  200. package/skills/solvea-kit/references/agent.md +145 -0
  201. package/skills/solvea-kit/references/chat.md +244 -0
  202. package/skills/solvea-kit/references/intent.md +69 -0
  203. package/skills/solvea-kit/references/plugin.md +53 -0
  204. package/skills/solvea-kit/references/quality-inspection.md +75 -0
  205. package/skills/solvea-kit/references/strategy.md +62 -0
  206. package/skills/solvea-kit/references/tagging.md +231 -0
  207. package/skills/solvea-kit/references/variables.md +53 -0
  208. package/skills/solvea-kit/scripts/generate_label_import_file.py +151 -0
  209. package/skills/solvea-kit/scripts/intent_classify.py +843 -0
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env python3
2
+ """为 POC 案例设计精简 chatflow 图视图。
3
+
4
+ 把 raw graph(完整 nodes/edges + UUID + 渲染坐标)压成 agent 看一眼就能设计 case 的形态:
5
+ - 节点按 category 提取**业务关键字段**(NOTE 的 noteText、AGENT/REPLY 的 prompt、
6
+ INTENT 的 intents 列表、TOOL 的 inputParams 等),不仅是字段名
7
+ - edge label 中的 intent uuid / next_step uuid **自动解码**为人类可读的名字
8
+ - 去掉 position 坐标、UUID 中段(保留前 8 位)等纯渲染元素
9
+
10
+ 为什么这段不放 solvea-api:solvea-api 是纯封装层(API + CLI 透传),不掺业务视图加工。
11
+ 这种 POC 设计 case 的特定 view 属于 poc skill 自己的职责。
12
+
13
+ 用法:
14
+ python3 inspect_chatflow.py --agent-id 38099 --pretty
15
+ python3 inspect_chatflow.py --agent-id 38099 --agent-id 38100 --agent-id 38101 --pretty
16
+ """
17
+ import argparse
18
+ import json
19
+ import os
20
+ import sys
21
+
22
+ sys.path.insert(0, os.path.join(os.environ.get("SOLVEA_LIB_DIR", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "__lib__"))), "skill-runtime"))
23
+ from bootstrap import * # noqa: F401,F403
24
+ from client import SolveaClient
25
+
26
+
27
+ # 长 prompt 的截断阈值;prompt / desc 超过这个长度时尾部加 …<truncated>
28
+ _PROMPT_TRUNCATE = 1500
29
+
30
+
31
+ def _truncate(text: str, limit: int = _PROMPT_TRUNCATE) -> str:
32
+ if not isinstance(text, str):
33
+ return text
34
+ if len(text) <= limit:
35
+ return text
36
+ return text[:limit] + f"\n…<truncated, total {len(text)} chars>"
37
+
38
+
39
+ def _short_id(uuid: str) -> str:
40
+ return (uuid or "")[:8]
41
+
42
+
43
+ def _summarize_node(n: dict) -> dict:
44
+ """按 category 提取关键业务字段。未识别的 category 退回到 raw config。"""
45
+ data = n.get("data") or {}
46
+ config = data.get("config") or {}
47
+ category = data.get("category") or ""
48
+ title = data.get("title") or "(start)"
49
+
50
+ out: dict = {
51
+ "id": _short_id(n.get("id")),
52
+ "category": category,
53
+ "title": title,
54
+ }
55
+ desc = data.get("desc")
56
+ if desc:
57
+ out["desc"] = desc
58
+
59
+ if category == "START":
60
+ return out
61
+
62
+ if category == "NOTE":
63
+ out["note_text"] = config.get("noteText", "")
64
+ return out
65
+
66
+ if category in ("AGENT", "KNOWLEDGE_REPLY"):
67
+ agent_data = config.get("agentData") or {}
68
+ prompt = agent_data.get("prompt") or (config.get("knowledgeReply") or {}).get("prompt")
69
+ if prompt:
70
+ out["prompt"] = _truncate(prompt)
71
+ next_steps = config.get("nextStep") or []
72
+ if next_steps:
73
+ out["next_step"] = [
74
+ {k: v for k, v in {"id": ns.get("id"), "title": ns.get("title"), "prompt": _truncate(ns.get("prompt") or "", 300)}.items() if v}
75
+ for ns in next_steps
76
+ ]
77
+ if config.get("maxRounds"):
78
+ out["max_rounds"] = config["maxRounds"]
79
+ return out
80
+
81
+ if category == "REPLY":
82
+ rc = config.get("replyContent") or {}
83
+ if rc.get("prompt"):
84
+ out["reply_prompt"] = _truncate(rc["prompt"])
85
+ if rc.get("type") and rc["type"] != "PROMPT":
86
+ out["reply_type"] = rc["type"]
87
+ return out
88
+
89
+ if category == "INTENT":
90
+ out["intents"] = [
91
+ {k: v for k, v in {"id": it.get("id"), "name": it.get("name"), "desc": _truncate(it.get("desc") or "", 400)}.items() if v}
92
+ for it in (config.get("intents") or [])
93
+ ]
94
+ return out
95
+
96
+ if category == "TOOL":
97
+ tool = config.get("tool") or {}
98
+ out["tool_id"] = tool.get("id")
99
+ out["tool_type"] = tool.get("type")
100
+ params = tool.get("inputParams") or []
101
+ if params:
102
+ out["input_params"] = [
103
+ {"name": p.get("name"), "var_type": p.get("varType"), "from": p.get("variableSelector")}
104
+ for p in params
105
+ ]
106
+ return out
107
+
108
+ if category == "VARIABLE_EXTRACT":
109
+ ve = config.get("variableExtract") or {}
110
+ out["extracts"] = [
111
+ {k: v for k, v in {"name": e.get("name"), "describe": e.get("describe"), "type": e.get("type")}.items() if v}
112
+ for e in (ve.get("extracts") or [])
113
+ ]
114
+ return out
115
+
116
+ if category == "VARIABLE_ASSIGNMENT":
117
+ out["assignments"] = [
118
+ {"source": a.get("source"), "target": a.get("target"), "operation": a.get("operation")}
119
+ for a in (config.get("variableAssignment") or [])
120
+ ]
121
+ return out
122
+
123
+ if category == "TRANSFER_TO_HUMAN":
124
+ h = config.get("transferToHuman") or {}
125
+ if h.get("prompt"):
126
+ out["handoff_prompt"] = _truncate(h["prompt"])
127
+ return out
128
+
129
+ # 未知 category:保留原始 config(截断长字符串)便于后续扩展
130
+ if config:
131
+ out["config"] = config
132
+ return out
133
+
134
+
135
+ def _build_handle_index(raw_nodes: list) -> dict:
136
+ """构造 source_handle uuid → 可读 label 的反查表。
137
+
138
+ INTENT 节点的 intents[].id 和 AGENT/KNOWLEDGE_REPLY 节点的 nextStep[].id 都是 uuid,
139
+ 会出现在 edge.sourceHandle 上。把它们映射成 `[Intent] xxx` / `[NextStep] xxx`,
140
+ edge label 立刻可读,不再是裸 uuid。
141
+ """
142
+ idx: dict[str, str] = {}
143
+ for n in raw_nodes:
144
+ data = n.get("data") or {}
145
+ config = data.get("config") or {}
146
+ category = data.get("category") or ""
147
+ if category == "INTENT":
148
+ for it in (config.get("intents") or []):
149
+ if it.get("id") and it.get("name"):
150
+ idx[it["id"]] = f"[Intent] {it['name']}"
151
+ elif category in ("AGENT", "KNOWLEDGE_REPLY"):
152
+ for ns in (config.get("nextStep") or []):
153
+ if ns.get("id") and ns.get("title"):
154
+ idx[ns["id"]] = f"[NextStep] {ns['title']}"
155
+ return idx
156
+
157
+
158
+ def build_summary(client: SolveaClient, agent_id: int) -> dict:
159
+ g = client.get_chatflow_graph(agent_id)
160
+ graph = g.get("graph") or {}
161
+ raw_nodes = graph.get("nodes") or []
162
+ raw_edges = graph.get("edges") or []
163
+ by_id = {n.get("id"): n for n in raw_nodes}
164
+ handle_idx = _build_handle_index(raw_nodes)
165
+
166
+ def _title(nid):
167
+ return ((by_id.get(nid) or {}).get("data") or {}).get("title") or "(start)"
168
+
169
+ nodes = [_summarize_node(n) for n in raw_nodes]
170
+
171
+ edges = []
172
+ for e in raw_edges:
173
+ handle = e.get("sourceHandle") or ""
174
+ # 优先解码 intent / next_step uuid,否则回退到 handle 原值
175
+ label = handle_idx.get(handle) or handle or e.get("label") or ""
176
+ edges.append({
177
+ "from": f"{_short_id(e.get('source'))} ({_title(e.get('source'))})",
178
+ "to": f"{_short_id(e.get('target'))} ({_title(e.get('target'))})",
179
+ "label": label,
180
+ })
181
+
182
+ return {
183
+ "agent_id": g.get("agentId"),
184
+ "version": g.get("chatFlowVersion") or g.get("version"),
185
+ "stats": {"nodes": len(raw_nodes), "edges": len(raw_edges)},
186
+ "nodes": nodes,
187
+ "edges": edges,
188
+ }
189
+
190
+
191
+ def main():
192
+ p = argparse.ArgumentParser(description="Inspect chatflow graph for POC case design")
193
+ p.add_argument("--agent-id", type=int, action="append", required=True,
194
+ help="Agent ID (可重复传多个,例如 --agent-id 38099 --agent-id 38100)")
195
+ p.add_argument("--pretty", action="store_true", help="Pretty print JSON")
196
+ args = p.parse_args()
197
+
198
+ client = SolveaClient()
199
+ result = [build_summary(client, aid) for aid in args.agent_id]
200
+ out = result[0] if len(result) == 1 else result
201
+
202
+ if args.pretty:
203
+ print(json.dumps(out, indent=2, ensure_ascii=False))
204
+ else:
205
+ print(json.dumps(out, ensure_ascii=False))
206
+
207
+
208
+ if __name__ == "__main__":
209
+ main()
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env python3
2
+ """POC 顶层状态规划器。
3
+
4
+ 只维护 `/workspace/state/poc/progress.json`,不执行 intent / agent / KB / batch
5
+ 等底层能力。其他 skill 的状态只通过路径引用记录在本文件中,避免职责泄漏。
6
+ """
7
+ import os, sys
8
+ sys.path.insert(0, os.path.join(os.environ.get("SOLVEA_LIB_DIR", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "__lib__"))), "skill-runtime"))
9
+ from bootstrap import * # noqa: F401,F403
10
+
11
+ import argparse
12
+ import json
13
+ from datetime import datetime, timezone
14
+ from typing import Any
15
+
16
+
17
+ STATE_FILE = STATE_DIR / "progress.json"
18
+
19
+ STATUS_ORDER = [
20
+ "empty",
21
+ "classified",
22
+ "business_agents_created",
23
+ "fallback_agent_created",
24
+ "fallback_strategy_linked",
25
+ "kb_done",
26
+ "cases_ready",
27
+ "test_done",
28
+ "done",
29
+ ]
30
+
31
+
32
+ def default_state() -> dict[str, Any]:
33
+ return {
34
+ "status": "empty",
35
+ "tenant": SOLVEA_TENANT_ID or "",
36
+ "brand_name": "",
37
+ "brand_website": "",
38
+ "intent": {
39
+ "progress_path": "/workspace/state/poc/progress.json",
40
+ "created_log_path": "",
41
+ "classification_report_path": "",
42
+ "classification_excel_path": "",
43
+ },
44
+ "fallback_agent": {
45
+ "agent_id": "",
46
+ "agent_name": "",
47
+ "created": False,
48
+ },
49
+ "kb": {
50
+ "enabled": False,
51
+ "source_url": "",
52
+ "directory_plan_path": "",
53
+ "qa_plan_path": "",
54
+ "upload_result_path": "",
55
+ },
56
+ "test": {
57
+ "agents": [],
58
+ "cases_json_path": "",
59
+ "results_path": "",
60
+ "manual_path": "",
61
+ },
62
+ "updated_at": "",
63
+ }
64
+
65
+
66
+ def read_state() -> dict[str, Any]:
67
+ if not STATE_FILE.exists():
68
+ return default_state()
69
+ try:
70
+ loaded = json.loads(STATE_FILE.read_text(encoding="utf-8"))
71
+ except Exception:
72
+ return default_state()
73
+ state = default_state()
74
+ merge_dict(state, loaded)
75
+ return state
76
+
77
+
78
+ def merge_dict(base: dict[str, Any], patch: dict[str, Any]) -> None:
79
+ for key, value in patch.items():
80
+ if isinstance(value, dict) and isinstance(base.get(key), dict):
81
+ merge_dict(base[key], value)
82
+ else:
83
+ base[key] = value
84
+
85
+
86
+ def write_state(state: dict[str, Any]) -> None:
87
+ state["updated_at"] = datetime.now(timezone.utc).isoformat()
88
+ STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
89
+ STATE_FILE.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")
90
+
91
+
92
+ def set_path(state: dict[str, Any], dotted_key: str, raw_value: str) -> None:
93
+ parts = [p for p in dotted_key.split(".") if p]
94
+ if not parts:
95
+ raise ValueError("empty key")
96
+ cur = state
97
+ for part in parts[:-1]:
98
+ nxt = cur.get(part)
99
+ if not isinstance(nxt, dict):
100
+ nxt = {}
101
+ cur[part] = nxt
102
+ cur = nxt
103
+ value: Any = raw_value
104
+ if raw_value.lower() == "true":
105
+ value = True
106
+ elif raw_value.lower() == "false":
107
+ value = False
108
+ elif raw_value.startswith("[") or raw_value.startswith("{"):
109
+ value = json.loads(raw_value)
110
+ cur[parts[-1]] = value
111
+
112
+
113
+ def recommendation(status: str) -> str:
114
+ return {
115
+ "empty": "下一步:读取 poc/interfaces/classify.md 和 poc/interfaces/create-agents.md,确认从 xlsx 分类还是固定 7 意图开始。",
116
+ "classified": "下一步:展示分类结果并等待用户确认;确认后调用 /skills/poc/scripts/create_agents.py --mode poc。",
117
+ "business_agents_created": "下一步:通过 agent-builder/interfaces/fallback-chatflow.md 创建兜底 CHAT_FLOW agent。",
118
+ "fallback_agent_created": "下一步:调用 /skills/poc/scripts/create_agents.py --mode poc --poc-fallback-agent-id <agent_id> 绑定兜底策略。",
119
+ "fallback_strategy_linked": "下一步:询问是否进入可选 KB 阶段;跳过则进入 POC 批测。",
120
+ "kb_done": "下一步:读取 poc/interfaces/poc-test.md,设计并确认测试 cases。",
121
+ "cases_ready": "下一步:执行 poc_test.py,然后生成 POC 测试手册。",
122
+ "test_done": "下一步:汇总 POC 测试手册和原始结果,确认交付完成。",
123
+ "done": "当前 POC 已完成;如需重跑,请明确要重跑的阶段。",
124
+ }.get(status, "状态未知:请检查 progress.json。")
125
+
126
+
127
+ def main() -> None:
128
+ parser = argparse.ArgumentParser(description="POC top-level progress planner")
129
+ parser.add_argument("--init", action="store_true", help="Create progress.json if missing")
130
+ parser.add_argument("--status", choices=STATUS_ORDER, help="Update top-level status")
131
+ parser.add_argument("--tenant", default=None)
132
+ parser.add_argument("--brand-name", default=None)
133
+ parser.add_argument("--brand-website", default=None)
134
+ parser.add_argument("--set", action="append", default=[], metavar="KEY=VALUE",
135
+ help="Set a dotted state path, e.g. intent.created_log_path=/workspace/...")
136
+ parser.add_argument("--json", action="store_true", help="Print JSON only")
137
+ args = parser.parse_args()
138
+
139
+ existed = STATE_FILE.exists()
140
+ state = read_state()
141
+ changed = False
142
+
143
+ if args.init and not existed:
144
+ changed = True
145
+ if args.status:
146
+ state["status"] = args.status
147
+ changed = True
148
+ if args.tenant is not None:
149
+ state["tenant"] = args.tenant
150
+ changed = True
151
+ if args.brand_name is not None:
152
+ state["brand_name"] = args.brand_name
153
+ changed = True
154
+ if args.brand_website is not None:
155
+ state["brand_website"] = args.brand_website
156
+ changed = True
157
+ for item in args.set:
158
+ if "=" not in item:
159
+ raise SystemExit(f"--set expects KEY=VALUE, got: {item}")
160
+ key, value = item.split("=", 1)
161
+ set_path(state, key, value)
162
+ changed = True
163
+
164
+ if changed:
165
+ write_state(state)
166
+
167
+ if args.json:
168
+ print(json.dumps(state, ensure_ascii=False, indent=2))
169
+ return
170
+
171
+ print(f"POC progress: {STATE_FILE}")
172
+ print(f"status={state.get('status')} tenant={state.get('tenant') or '-'} brand={state.get('brand_name') or '-'}")
173
+ print(recommendation(str(state.get("status") or "")))
174
+ print(json.dumps(state, ensure_ascii=False, indent=2))
175
+
176
+
177
+ if __name__ == "__main__":
178
+ main()