@shulex/cxclawctl 0.1.2 → 0.1.3

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 (208) hide show
  1. package/README.md +25 -22
  2. package/dist/cli.js +2202 -127
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +19 -6
  5. package/capabilities.lock.json +0 -898
  6. package/requirements-cli.txt +0 -2
  7. package/skills/__lib__/__init__.py +0 -0
  8. package/skills/__lib__/skill-runtime/bootstrap.py +0 -134
  9. package/skills/__lib__/solvea-api/__init__.py +0 -0
  10. package/skills/__lib__/solvea-api/client.py +0 -1944
  11. package/skills/__lib__/solvea-api/dashboard.py +0 -100
  12. package/skills/__lib__/solvea-api/solvea_api.py +0 -1341
  13. package/skills/agent-analytics/SKILL.md +0 -74
  14. package/skills/agent-analytics/references/chat-report-fields.md +0 -36
  15. package/skills/agent-analytics/references/report-fields.md +0 -130
  16. package/skills/agent-analytics/references/transcript.md +0 -110
  17. package/skills/agent-builder/SKILL.md +0 -42
  18. package/skills/agent-builder/interfaces/fallback-chatflow.md +0 -47
  19. package/skills/agent-builder/references/chatflow/graph-spec.md +0 -259
  20. package/skills/agent-builder/references/chatflow/node-selection.md +0 -89
  21. package/skills/agent-builder/references/chatflow/nodes/agent.md +0 -350
  22. package/skills/agent-builder/references/chatflow/nodes/business-track.md +0 -43
  23. package/skills/agent-builder/references/chatflow/nodes/code.md +0 -157
  24. package/skills/agent-builder/references/chatflow/nodes/collect-order.md +0 -47
  25. package/skills/agent-builder/references/chatflow/nodes/finish.md +0 -34
  26. package/skills/agent-builder/references/chatflow/nodes/human-review.md +0 -60
  27. package/skills/agent-builder/references/chatflow/nodes/if-else.md +0 -100
  28. package/skills/agent-builder/references/chatflow/nodes/intent.md +0 -79
  29. package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +0 -43
  30. package/skills/agent-builder/references/chatflow/nodes/llm.md +0 -117
  31. package/skills/agent-builder/references/chatflow/nodes/note.md +0 -38
  32. package/skills/agent-builder/references/chatflow/nodes/question.md +0 -97
  33. package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +0 -68
  34. package/skills/agent-builder/references/chatflow/nodes/reply.md +0 -97
  35. package/skills/agent-builder/references/chatflow/nodes/start.md +0 -35
  36. package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +0 -48
  37. package/skills/agent-builder/references/chatflow/nodes/tool.md +0 -138
  38. package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +0 -63
  39. package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +0 -52
  40. package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +0 -50
  41. package/skills/agent-builder/references/chatflow/nodes/variable-process.md +0 -70
  42. package/skills/agent-builder/references/chatflow/overview.md +0 -53
  43. package/skills/agent-builder/references/chatflow/visual-translation.md +0 -64
  44. package/skills/agent-builder/references/chatflow/workflow.md +0 -134
  45. package/skills/agent-builder/references/multi-agent/create-update.md +0 -185
  46. package/skills/agent-builder/references/multi-agent/graph-spec.md +0 -246
  47. package/skills/agent-builder/references/multi-agent/graph-structure.md +0 -12
  48. package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +0 -13
  49. package/skills/agent-builder/references/multi-agent/migration/large.md +0 -554
  50. package/skills/agent-builder/references/multi-agent/migration/principles.md +0 -446
  51. package/skills/agent-builder/references/multi-agent/migration/small.md +0 -225
  52. package/skills/agent-builder/references/multi-agent/orchestration.md +0 -11
  53. package/skills/agent-builder/references/multi-agent/overview.md +0 -48
  54. package/skills/agent-builder/references/multi-agent/update.md +0 -128
  55. package/skills/agent-builder/references/multi-agent/workflow.md +0 -87
  56. package/skills/agent-builder/references/single-agent/overview.md +0 -48
  57. package/skills/agent-builder/references/single-agent/prompt.md +0 -16
  58. package/skills/agent-builder/references/single-agent/tools.md +0 -36
  59. package/skills/agent-builder/references/single-agent/voice.md +0 -46
  60. package/skills/agent-builder/references/tools/http-tool.md +0 -315
  61. package/skills/agent-builder/references/tools/mock-tool.md +0 -279
  62. package/skills/agent-builder/references/tools/overview.md +0 -46
  63. package/skills/agent-builder/references/tools/tool-config.md +0 -43
  64. package/skills/agent-builder/references/tools/x-token.md +0 -34
  65. package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +0 -3458
  66. package/skills/agent-builder/scripts/chatflow/validate_graph.py +0 -1697
  67. package/skills/agent-builder/scripts/copy_marketplace_agent.py +0 -61
  68. package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +0 -5974
  69. package/skills/agent-builder/scripts/multi-agent/replay_compare.py +0 -679
  70. package/skills/agent-builder/scripts/patch_agent_tools.py +0 -135
  71. package/skills/agent-builder/scripts/tools/validate_tool_config.py +0 -384
  72. package/skills/agent-tester/SKILL.md +0 -35
  73. package/skills/agent-tester/batch-test/overview.md +0 -86
  74. package/skills/agent-tester/scripts/batch_test_sheet.py +0 -213
  75. package/skills/agent-tester/single-test/badcase-retest.md +0 -85
  76. package/skills/agent-tester/single-test/direct-test.md +0 -24
  77. package/skills/agent-tester/single-test/overview.md +0 -142
  78. package/skills/delivery/SKILL.md +0 -112
  79. package/skills/delivery/interfaces/classify.md +0 -38
  80. package/skills/delivery/interfaces/create-agents.md +0 -89
  81. package/skills/delivery/interfaces/delivery-test.md +0 -67
  82. package/skills/delivery/references/flow.md +0 -40
  83. package/skills/delivery/references/validation.md +0 -15
  84. package/skills/delivery/scripts/batch_test.py +0 -704
  85. package/skills/delivery/scripts/classify.py +0 -22
  86. package/skills/delivery/scripts/create_agents.py +0 -695
  87. package/skills/delivery/scripts/gen_delivery_report.py +0 -335
  88. package/skills/delivery/scripts/gen_excel.py +0 -234
  89. package/skills/delivery/scripts/gen_report.py +0 -208
  90. package/skills/delivery/scripts/plan.py +0 -163
  91. package/skills/delivery/scripts/render.py +0 -713
  92. package/skills/delivery/scripts/render_summary.py +0 -38
  93. package/skills/delivery/scripts/sample_history_cases.py +0 -109
  94. package/skills/kb-builder/SKILL.md +0 -80
  95. package/skills/kb-builder/interfaces/qa-upload.md +0 -55
  96. package/skills/kb-builder/interfaces/web-kb.md +0 -84
  97. package/skills/kb-builder/references/directory.md +0 -373
  98. package/skills/kb-builder/references/export.md +0 -282
  99. package/skills/kb-builder/references/isolation-keywords.md +0 -113
  100. package/skills/kb-builder/references/knowledge.md +0 -153
  101. package/skills/kb-builder/references/product.md +0 -52
  102. package/skills/kb-builder/references/qa-from-help-center.md +0 -58
  103. package/skills/kb-builder/references/qa-from-pdf.md +0 -94
  104. package/skills/kb-builder/references/qa-from-tickets.md +0 -168
  105. package/skills/kb-builder/references/qa-from-web.md +0 -98
  106. package/skills/kb-builder/references/qa-quality.md +0 -73
  107. package/skills/kb-builder/references/reply-template.md +0 -237
  108. package/skills/kb-builder/references/tag-isolation-migration.md +0 -61
  109. package/skills/kb-builder/references/tags.md +0 -45
  110. package/skills/kb-builder/references/upload.md +0 -130
  111. package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +0 -333
  112. package/skills/kb-builder/scripts/build_directory_plan.py +0 -144
  113. package/skills/kb-builder/scripts/crawl_site.py +0 -433
  114. package/skills/kb-builder/scripts/create_dirs.py +0 -146
  115. package/skills/kb-builder/scripts/export_knowledge.py +0 -454
  116. package/skills/kb-builder/scripts/extract_ticket_qa.py +0 -441
  117. package/skills/kb-builder/scripts/extract_web_qa.py +0 -118
  118. package/skills/kb-builder/scripts/import_qa.py +0 -219
  119. package/skills/kb-builder/scripts/import_source_documents.py +0 -143
  120. package/skills/kb-builder/scripts/import_templates.py +0 -409
  121. package/skills/poc/SKILL.md +0 -125
  122. package/skills/poc/assets/default_manual.css +0 -279
  123. package/skills/poc/assets/notice_escalate.png +0 -0
  124. package/skills/poc/assets/notice_escalate2.png +0 -0
  125. package/skills/poc/assets/notice_new_chat.png +0 -0
  126. package/skills/poc/assets/poc_test_template.html +0 -645
  127. package/skills/poc/assets/poc_test_template.md +0 -104
  128. package/skills/poc/assets/step3_preview.png +0 -0
  129. package/skills/poc/assets/step4_order_note.png +0 -0
  130. package/skills/poc/interfaces/classify.md +0 -38
  131. package/skills/poc/interfaces/create-agents.md +0 -108
  132. package/skills/poc/interfaces/poc-test.md +0 -94
  133. package/skills/poc/references/flow.md +0 -47
  134. package/skills/poc/references/kb-directory.md +0 -22
  135. package/skills/poc/references/validation.md +0 -17
  136. package/skills/poc/scripts/classify.py +0 -22
  137. package/skills/poc/scripts/create_agents.py +0 -818
  138. package/skills/poc/scripts/gen_excel.py +0 -234
  139. package/skills/poc/scripts/gen_poc_manual.py +0 -249
  140. package/skills/poc/scripts/gen_report.py +0 -208
  141. package/skills/poc/scripts/inspect_chatflow.py +0 -209
  142. package/skills/poc/scripts/plan.py +0 -178
  143. package/skills/poc/scripts/poc_test.py +0 -495
  144. package/skills/poc/scripts/render.py +0 -713
  145. package/skills/poc/scripts/render_summary.py +0 -41
  146. package/skills/runtime-inspector/SKILL.md +0 -51
  147. package/skills/runtime-inspector/references/frontend-rendering.md +0 -126
  148. package/skills/runtime-inspector/references/model.md +0 -59
  149. package/skills/runtime-inspector/references/sandbox.md +0 -131
  150. package/skills/runtime-inspector/scripts/model_inspect.py +0 -68
  151. package/skills/runtime-inspector/scripts/prepare.py +0 -98
  152. package/skills/runtime-inspector/scripts/process.py +0 -84
  153. package/skills/runtime-inspector/scripts/read_state.py +0 -32
  154. package/skills/runtime-inspector/scripts/sandbox_inspect.py +0 -463
  155. package/skills/runtime-inspector/scripts/test_claude_proxy.py +0 -49
  156. package/skills/runtime-inspector/scripts/test_openai_proxy.py +0 -41
  157. package/skills/runtime-inspector/scripts/verify_persistence.py +0 -101
  158. package/skills/runtime-inspector/scripts/write_test.py +0 -41
  159. package/skills/setup/SKILL.md +0 -17
  160. package/skills/shulex-support/SKILL.md +0 -39
  161. package/skills/shulex-support/references/badcase-analysis/overview.md +0 -34
  162. package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +0 -105
  163. package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +0 -89
  164. package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +0 -36
  165. package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +0 -53
  166. package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +0 -259
  167. package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +0 -148
  168. package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +0 -57
  169. package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +0 -120
  170. package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +0 -126
  171. package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +0 -121
  172. package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +0 -208
  173. package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +0 -115
  174. package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +0 -72
  175. package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +0 -92
  176. package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +0 -147
  177. package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +0 -283
  178. package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +0 -77
  179. package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +0 -19
  180. package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +0 -23
  181. package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +0 -31
  182. package/skills/shulex-support/references/product-docs.md +0 -70
  183. package/skills/shulex-support/references/tickets.md +0 -184
  184. package/skills/shulex-support/scripts/support.py +0 -427
  185. package/skills/solvea-desk/SKILL.md +0 -39
  186. package/skills/solvea-desk/references/report/fields.md +0 -82
  187. package/skills/solvea-desk/references/report/report-design.md +0 -200
  188. package/skills/solvea-desk/references/report/report-template.html +0 -44
  189. package/skills/solvea-desk/references/report/report.css +0 -517
  190. package/skills/solvea-desk/references/report/workflow.md +0 -464
  191. package/skills/solvea-desk/references/tickets/query.md +0 -103
  192. package/skills/solvea-desk/references/tickets/tags.md +0 -83
  193. package/skills/solvea-desk/scripts/report/fetch_sources.py +0 -580
  194. package/skills/solvea-desk/scripts/report/render_report.py +0 -763
  195. package/skills/solvea-desk/scripts/tickets/desk_api.py +0 -73
  196. package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +0 -403
  197. package/skills/solvea-desk/scripts/tickets/ticket_tags.py +0 -305
  198. package/skills/solvea-kit/SKILL.md +0 -84
  199. package/skills/solvea-kit/references/agent.md +0 -145
  200. package/skills/solvea-kit/references/chat.md +0 -244
  201. package/skills/solvea-kit/references/intent.md +0 -69
  202. package/skills/solvea-kit/references/plugin.md +0 -53
  203. package/skills/solvea-kit/references/quality-inspection.md +0 -75
  204. package/skills/solvea-kit/references/strategy.md +0 -62
  205. package/skills/solvea-kit/references/tagging.md +0 -231
  206. package/skills/solvea-kit/references/variables.md +0 -53
  207. package/skills/solvea-kit/scripts/generate_label_import_file.py +0 -151
  208. package/skills/solvea-kit/scripts/intent_classify.py +0 -843
@@ -1,695 +0,0 @@
1
- """
2
- Delivery intent / agent / strategy creation pipeline.
3
-
4
- Creates all standard delivery intents, copies fixed marketplace agents by
5
- squareAgent_id when available, binds strategy routing, and patches X-Token in
6
- order tools by default. Same-name intents are reused. Missing preset agents are
7
- recorded as recoverable gaps instead of crashing the run.
8
-
9
- Override the X-Token behaviour explicitly via --update-x-token / --no-update-x-token.
10
-
11
- Usage:
12
- # delivery: create all 7 intents + update X-Token
13
- python3 scripts/create_agents.py
14
-
15
- # disable SYSTEM intents after a creation run
16
- python3 scripts/create_agents.py --disable-system
17
-
18
- # dry-run preview (no API calls beyond marketplace listing in non-disable mode)
19
- python3 scripts/create_agents.py --dry-run
20
- """
21
-
22
- import os, sys
23
- 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"))
24
- from bootstrap import *
25
-
26
- import json, argparse
27
- from pathlib import Path
28
-
29
- from client import SolveaClient
30
-
31
-
32
- def _read_state() -> dict:
33
- _sf = STATE_DIR / "progress.json"
34
- if _sf.exists():
35
- try:
36
- return json.loads(_sf.read_text(encoding="utf-8"))
37
- except Exception:
38
- pass
39
- return {}
40
-
41
-
42
- def _write_state(data: dict) -> None:
43
- _sf = STATE_DIR / "progress.json"
44
- _sf.parent.mkdir(parents=True, exist_ok=True)
45
- _sf.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
46
-
47
-
48
- # ── Fixed mapping table ────────────────────────────────────────────────────────
49
-
50
- # Delivery 固定绑定:每个意图 → 一个广场 agent(按 squareAgent_id 直接克隆,
51
- # 不再走"查广场列表 + 按名字匹配"链路,避免名字漂移。
52
- # 不在此 map 中的意图(数据中新发现的 L1)→ 跳过 agent / 策略,标 SKIPPED。
53
- # 固定 squareAgent_id 是首选来源,不是硬阻塞;复制失败时记录可恢复缺口。
54
- DELIVERY_AGENT_BINDINGS = {
55
- "Pre-Sales Inquiry": 37625,
56
- "Invoices": 37627,
57
- "Order Issues": 37631,
58
- "Logistics Issues": 37633,
59
- "Package issues": 37623,
60
- "Product issues": 38114,
61
- "Return and exchange": 37626,
62
- }
63
-
64
- # ── Data loading ───────────────────────────────────────────────────────────────
65
-
66
- def load_results(state: dict) -> dict:
67
- """Load classification results pointed to by state.results_path.
68
-
69
- eager 模型下 workspace/outputs/ 跨轮路径稳定,state 里存的绝对路径下轮直接可用。
70
- """
71
- path = state.get("results_path")
72
- if not path:
73
- raise FileNotFoundError("state.results_path missing. Run classify.py first.")
74
- if not os.path.exists(path):
75
- raise FileNotFoundError(f"Results file missing: {path}")
76
- with open(path) as f:
77
- return json.load(f)
78
-
79
-
80
- # ── Feature pre-flight checks ─────────────────────────────────────────────────
81
-
82
- def check_and_enable_intent_feature(solvea: SolveaClient) -> None:
83
- """创建意图前确保意图功能已开启(configurationWizard.intent=true)。
84
-
85
- 查询:GET /api_v2/gpt/project/{projectId}/config → configurationWizard.intent
86
- 开启:PUT /api_v2/gpt/project/{projectId} → body: {configurationWizard: {intent: true}}
87
- """
88
- resp = solvea._curl("GET", f"/api_v2/gpt/project/{solvea.project_id}/config")
89
- payload = resp.get("data", resp) or {}
90
- wizard = payload.get("configurationWizard") or {}
91
- if wizard.get("intent"):
92
- return
93
- solvea._curl("PUT", f"/api_v2/gpt/project/{solvea.project_id}", {
94
- "configurationWizard": {"intent": True}
95
- })
96
- print("[NOTICE] 该租户意图功能(Intent)原本未开启,已自动启用")
97
-
98
-
99
- def check_and_enable_variable_feature(solvea: SolveaClient) -> None:
100
- """复制 agent 前确保变量功能已初始化(isInitAiField=true)。
101
-
102
- 查询:POST /api_v2/tars/api/field/manage/guide/record → isInitAiField
103
- 开启:GET /api_v2/tars/api/field/manage/pre/install
104
- """
105
- resp = solvea._curl("POST", "/api_v2/tars/api/field/manage/guide/record")
106
- payload = resp.get("data", resp) or {}
107
- if payload.get("isInitAiField"):
108
- return
109
- solvea._curl("GET", "/api_v2/tars/api/field/manage/pre/install")
110
- print("[NOTICE] 该租户变量功能(AI Field)原本未开启,已自动初始化预装字段")
111
-
112
-
113
- # ── Marketplace agent lookup ───────────────────────────────────────────────────
114
-
115
- def copy_marketplace_agent_by_id(solvea: SolveaClient, square_agent_id: int) -> str:
116
- """直接按广场 agent ID 克隆到当前租户,返回新 agent_id。
117
-
118
- Delivery 按固定 squareAgent_id 复制广场 agent,取代"查广场→按名字匹配"。
119
- 链路,避免广场 agent 名字漂移。
120
- """
121
- result = solvea._curl("POST", "/api_v2/gpt/ap/copyAgent", {
122
- "projectId": int(solvea.project_id),
123
- "agentId": int(square_agent_id),
124
- })
125
- new_id = str(
126
- result.get("id") or
127
- result.get("agentId") or
128
- result.get("data", {}).get("id", "")
129
- )
130
- if not new_id:
131
- raise RuntimeError(f"copyAgent(square_agent_id={square_agent_id}) returned no id: {result}")
132
- print(f"[copy_agent] square_id={square_agent_id} → new id={new_id}")
133
- return new_id
134
-
135
-
136
- # ── L1 intent creation ─────────────────────────────────────────────────────────
137
-
138
- def _intent_items(result) -> list:
139
- if isinstance(result, list):
140
- return result
141
- if isinstance(result, dict):
142
- for key in ("data", "list", "records"):
143
- if isinstance(result.get(key), list):
144
- return result[key]
145
- return []
146
-
147
-
148
- def _find_existing_intent_by_name(solvea: SolveaClient, name: str) -> dict | None:
149
- result = solvea.list_intents()
150
- for item in _intent_items(result):
151
- if isinstance(item, dict) and item.get("name") == name:
152
- intent_id = item.get("id") or item.get("intentId")
153
- if intent_id:
154
- return item
155
- return None
156
-
157
-
158
- def create_l1_intent(
159
- solvea: SolveaClient,
160
- intent: dict,
161
- dry_run: bool = False,
162
- existing_by_name: dict | None = None,
163
- ) -> dict | None:
164
- name = intent["name"]
165
- definition = intent.get("definition") or f"Customer messages related to {name}."
166
- data = {
167
- "name": name,
168
- "description": definition,
169
- "example": definition,
170
- "status": "ENABLED",
171
- }
172
- if dry_run:
173
- print(f"[dry-run] create_intent: {name}")
174
- return None
175
-
176
- existing = (existing_by_name or {}).get(name)
177
- if existing:
178
- intent_id = existing.get("id") or existing.get("intentId")
179
- print(f"[create_intent] existing same-name intent found, reusing id={intent_id} name={name}")
180
- return {"id": intent_id, **existing}
181
-
182
- print(f"[create_intent] {name} ...")
183
- try:
184
- result = solvea.create_intent(data)
185
- except RuntimeError as exc:
186
- if "already exists" not in str(exc):
187
- raise
188
- existing = _find_existing_intent_by_name(solvea, name)
189
- if not existing:
190
- raise
191
- intent_id = existing.get("id") or existing.get("intentId")
192
- print(f"[create_intent] already exists, reusing id={intent_id} name={name}")
193
- return {"id": intent_id, **existing}
194
- intent_id = result.get("id") or result.get("intentId")
195
- if not intent_id:
196
- if "already exists" in str(result.get("errmsg", "")):
197
- existing = _find_existing_intent_by_name(solvea, name)
198
- if existing:
199
- intent_id = existing.get("id") or existing.get("intentId")
200
- print(f"[create_intent] already exists, reusing id={intent_id} name={name}")
201
- result["id"] = intent_id
202
- if not intent_id:
203
- raise RuntimeError(f"create_intent returned no id for '{name}': {result}")
204
- else:
205
- print(f"[create_intent] OK → id={intent_id}")
206
- return result
207
-
208
-
209
- # ── Strategy creation ──────────────────────────────────────────────────────────
210
-
211
- def create_strategy_for_intent(
212
- solvea: SolveaClient,
213
- intent_id: str,
214
- agent_id: str,
215
- intent_name: str,
216
- dry_run: bool = False,
217
- ) -> dict | None:
218
- if dry_run:
219
- print(f"[dry-run] create_strategy: intent={intent_id} → agent={agent_id}")
220
- return None
221
- data = {
222
- "name": f"Route_{intent_name}",
223
- "status": "ENABLED",
224
- "priority": 0,
225
- "trigger": "MESSAGE",
226
- "condition": {
227
- "and": [{
228
- "rules": [{
229
- "apiName": "__intent_ids",
230
- "apiType": "TEXT",
231
- "relation": "IN",
232
- "type": "GENERIC",
233
- "values": [int(intent_id)],
234
- }]
235
- }]
236
- },
237
- "action": {
238
- "type": "AGENT",
239
- "agentId": int(agent_id),
240
- },
241
- }
242
- print(f"[create_strategy] Route_{intent_name} ...")
243
- result = solvea.create_strategy(data)
244
- strategy_id = result.get("id") or result.get("strategyId")
245
- if not strategy_id:
246
- raise RuntimeError(
247
- f"create_strategy returned no id for '{intent_name}' "
248
- f"(intent={intent_id}, agent={agent_id}): {result}"
249
- )
250
- print(f"[create_strategy] OK → id={strategy_id}")
251
- return result
252
-
253
-
254
- def create_strategy_for_intents(
255
- solvea: SolveaClient,
256
- intent_ids: list,
257
- agent_id: str,
258
- name: str,
259
- priority: int = 0,
260
- dry_run: bool = False,
261
- ) -> dict | None:
262
- """N 个意图 → 1 个 agent 的策略(rules.values 多值)。"""
263
- if not intent_ids:
264
- raise ValueError(f"create_strategy_for_intents '{name}': intent_ids is empty")
265
- if dry_run:
266
- print(f"[dry-run] create_strategy: intents={intent_ids} → agent={agent_id} (priority={priority})")
267
- return None
268
- data = {
269
- "name": f"Route_{name}",
270
- "status": "ENABLED",
271
- "priority": priority,
272
- "trigger": "MESSAGE",
273
- "condition": {
274
- "and": [{
275
- "rules": [{
276
- "apiName": "__intent_ids",
277
- "apiType": "TEXT",
278
- "relation": "IN",
279
- "type": "GENERIC",
280
- "values": [int(i) for i in intent_ids],
281
- }]
282
- }]
283
- },
284
- "action": {
285
- "type": "AGENT",
286
- "agentId": int(agent_id),
287
- },
288
- }
289
- print(f"[create_strategy] Route_{name} (intents={intent_ids}, priority={priority}) ...")
290
- result = solvea.create_strategy(data)
291
- strategy_id = result.get("id") or result.get("strategyId")
292
- if not strategy_id:
293
- raise RuntimeError(
294
- f"create_strategy returned no id for '{name}' "
295
- f"(intents={intent_ids}, agent={agent_id}): {result}"
296
- )
297
- print(f"[create_strategy] OK → id={strategy_id}")
298
- return result
299
-
300
-
301
- # ── Update order tool X-Token ────────────────────────────────────────────────
302
-
303
- def update_order_tool_x_token(solvea: SolveaClient, creation_log: list, dry_run: bool = False) -> None:
304
- """After all agent copies, patch X-Token header in any order information tools found."""
305
- TARGET_TOOLS = {
306
- "new_Order_information_product",
307
- "new_Order_information_FBA",
308
- "new_Order_information_ship",
309
- }
310
- copied_agent_ids = [
311
- item["agent_id"] for item in creation_log
312
- if not item.get("skipped") and item.get("agent_id")
313
- ]
314
- if not copied_agent_ids:
315
- return
316
-
317
- raw = solvea.get_api_key()
318
- if isinstance(raw, dict):
319
- x_token = raw.get("apiKey") or raw.get("key") or raw.get("value") or raw.get("data") or str(raw)
320
- else:
321
- x_token = str(raw)
322
-
323
- updated = 0
324
- for agent_id in copied_agent_ids:
325
- tools_resp = solvea._curl("GET", f"/api_v2/gpt/ap/agent/{agent_id}/tools", {})
326
- for tool in (tools_resp if isinstance(tools_resp, list) else []):
327
- if tool.get("name") not in TARGET_TOOLS:
328
- continue
329
- tool_id = tool["id"]
330
- if dry_run:
331
- print(f"[dry-run] would update X-Token for tool '{tool['name']}' (id={tool_id})")
332
- continue
333
- config = tool.get("config") or {}
334
- headers = config.get("headers") or {}
335
- headers["X-Token"] = x_token
336
- config["headers"] = headers
337
- solvea._curl("PUT", f"/api_v2/gpt/ap/tools/{tool_id}", {"config": config})
338
- print(f"[x_token] updated '{tool['name']}' (tool_id={tool_id})")
339
- updated += 1
340
-
341
- if updated:
342
- print(f"[x_token] {updated} tool(s) X-Token updated.")
343
- elif not dry_run:
344
- print("[x_token] No order information tools found — skipped.")
345
-
346
-
347
- # ── Disable SYSTEM intents ────────────────────────────────────────────────────
348
-
349
- def disable_system_intents(solvea: SolveaClient, keep_names: set, dry_run: bool = False) -> int:
350
- """Disable SYSTEM-source intents whose name is not in keep_names."""
351
- result = solvea.list_intents()
352
- intents = result if isinstance(result, list) else result.get("data", result.get("list", []))
353
- to_disable = [
354
- i for i in intents
355
- if i.get("source") == "SYSTEM"
356
- and i.get("name") not in keep_names
357
- and i.get("status") != "DISABLED"
358
- ]
359
- print(f"[disable_system] {len(to_disable)} SYSTEM intents to disable "
360
- f"(keeping {len(keep_names)} by name match)")
361
- for i in to_disable:
362
- if dry_run:
363
- print(f"[dry-run] disable intent: {i['name']} id={i['id']}")
364
- else:
365
- solvea.toggle_intent(i["id"], enabled=False)
366
- print(f"[disable_system] disabled: {i['name']} id={i['id']}")
367
- return len(to_disable)
368
-
369
-
370
- def _load_agent_map(path: str) -> dict:
371
- if not path or not os.path.exists(path):
372
- raise FileNotFoundError(f"custom agent map not found: {path}")
373
- with open(path, encoding="utf-8") as f:
374
- data = json.load(f)
375
- if isinstance(data, dict) and isinstance(data.get("intents"), dict):
376
- data = data["intents"]
377
- if not isinstance(data, dict):
378
- raise ValueError("custom agent map must be a JSON object or {'intents': {...}}")
379
- return {str(k): str(v) for k, v in data.items() if v not in (None, "")}
380
-
381
-
382
- def _run_delivery_custom_agent_link(
383
- solvea: SolveaClient,
384
- log_path: str,
385
- map_path: str,
386
- dry_run: bool = False,
387
- ) -> None:
388
- """把用户确认的新建/替代 Agent 按 Delivery 意图写入策略。"""
389
- if not log_path or not os.path.exists(log_path):
390
- raise FileNotFoundError(
391
- f"created_log not found (state.created_log_path={log_path}). "
392
- "Run delivery creation first."
393
- )
394
- with open(log_path, encoding="utf-8") as f:
395
- log_data = json.load(f)
396
- if log_data.get("mode") != "delivery":
397
- raise RuntimeError(f"created_log mode is {log_data.get('mode')!r}, expected 'delivery'.")
398
-
399
- agent_map = _load_agent_map(map_path)
400
- linked = []
401
- for item in log_data.get("intents", []):
402
- intent_name = item.get("intent_name")
403
- if not intent_name or intent_name not in agent_map:
404
- continue
405
- intent_id = item.get("intent_id")
406
- agent_id = agent_map[intent_name]
407
- if not intent_id:
408
- print(f"[delivery.custom] WARNING intent '{intent_name}' has no intent_id; skipped")
409
- continue
410
- print(f"[delivery.custom] Binding {intent_name}: intent={intent_id} → agent {agent_id}")
411
- strategy_result = create_strategy_for_intent(
412
- solvea,
413
- str(intent_id),
414
- str(agent_id),
415
- intent_name,
416
- dry_run=dry_run,
417
- )
418
- strategy_id = str(strategy_result.get("id")) if strategy_result else f"dry_strategy_{intent_name}"
419
- item["agent_id"] = str(agent_id)
420
- item["strategy_id"] = strategy_id
421
- item["skipped"] = False
422
- item["skip_reason"] = ""
423
- item["recoverable"] = False
424
- item["custom_agent"] = True
425
- linked.append({"intent_name": intent_name, "agent_id": str(agent_id), "intent_id": intent_id, "strategy_id": strategy_id})
426
-
427
- if not linked:
428
- raise ValueError("custom agent map did not match any created Delivery intent name")
429
-
430
- log_data["custom_agent_strategies"] = [*(log_data.get("custom_agent_strategies") or []), *linked]
431
- log_data["next_action"] = "Continue knowledge-base setup and delivery regression test."
432
- with open(log_path, "w", encoding="utf-8") as f:
433
- json.dump(log_data, f, ensure_ascii=False, indent=2)
434
-
435
- state = _read_state()
436
- state.update({
437
- "status": "created",
438
- "mode": "delivery",
439
- "created_log_path": log_path,
440
- "updated_at": timestamp(),
441
- })
442
- _write_state(state)
443
- print(f"\n[delivery.custom] Done. Linked {len(linked)} intent(s); log updated → {log_path}")
444
-
445
-
446
- # ── Main ───────────────────────────────────────────────────────────────────────
447
-
448
- def main():
449
- parser = argparse.ArgumentParser(
450
- description="Create intents + copy marketplace agents + strategy routing"
451
- )
452
- parser.add_argument("--mode", choices=["delivery"], default="delivery",
453
- help="Delivery only: create all intents + update X-Token.")
454
- parser.add_argument("--select", default=None,
455
- help="DEPRECATED — ignored in all modes (all intents are now created).")
456
- parser.add_argument("--dry-run", action="store_true")
457
- parser.add_argument("--disable-system", action="store_true",
458
- help="Disable active SYSTEM intents not matching created intent names")
459
- parser.add_argument("--custom-agent-map", default=None,
460
- help="Delivery recovery stage: JSON map of intent name to custom/existing agent id, "
461
- "for example {'Pre-Sales Inquiry': 2642, 'Product issues': 2643}.")
462
- x_token_grp = parser.add_mutually_exclusive_group()
463
- x_token_grp.add_argument("--update-x-token", dest="update_x_token", action="store_true",
464
- help="Force X-Token patch after copy (default for --mode delivery)")
465
- x_token_grp.add_argument("--no-update-x-token", dest="update_x_token", action="store_false",
466
- help="Skip X-Token patch")
467
- parser.set_defaults(update_x_token=None)
468
- args = parser.parse_args()
469
-
470
- # ── Early args sanity ──────────────────────────────────────────────────────
471
- if args.select:
472
- print(f"[main] WARNING: --select is deprecated and ignored (mode={args.mode}). "
473
- "All intents will be created.")
474
-
475
- # Resolve update_x_token default by mode when caller did not set it explicitly
476
- if args.update_x_token is None:
477
- update_x_token = (args.mode == "delivery")
478
- else:
479
- update_x_token = args.update_x_token
480
-
481
- state = _read_state()
482
- if not state:
483
- raise FileNotFoundError(
484
- "No state found. Run classify.py first (state should contain run_id and results_path)."
485
- )
486
-
487
- run_id = state.get("run_id")
488
- if not run_id:
489
- raise ValueError("state.run_id missing. Re-run classify.py.")
490
-
491
- # ── Disable-only mode ──────────────────────────────────────────────────────
492
- if args.disable_system:
493
- log_path = state.get("created_log_path")
494
- if not log_path or not os.path.exists(log_path):
495
- raise FileNotFoundError(
496
- f"No creation log found (state.created_log_path={log_path}). "
497
- "Run without --disable-system first."
498
- )
499
- with open(log_path) as f:
500
- existing_log = json.load(f)
501
- our_names = {item["intent_name"] for item in existing_log.get("intents", [])}
502
- solvea = SolveaClient()
503
- print(f"[main] Disable-only mode. project_id={solvea.project_id}")
504
- disable_system_intents(solvea, keep_names=our_names, dry_run=args.dry_run)
505
- return
506
-
507
- if args.custom_agent_map:
508
- solvea = SolveaClient()
509
- if not args.dry_run:
510
- print(f"[main] project_id={solvea.project_id}")
511
- _run_delivery_custom_agent_link(
512
- solvea,
513
- log_path=state.get("created_log_path"),
514
- map_path=args.custom_agent_map,
515
- dry_run=args.dry_run,
516
- )
517
- return
518
-
519
- results = load_results(state)
520
- all_intents = results.get("intents", []) + results.get("new_intents", [])
521
- print(f"[main] {len(all_intents)} intents loaded, run_id={run_id} (mode={args.mode})")
522
-
523
- if not all_intents:
524
- print("[main] No intents found. Run classify.py first.")
525
- return
526
-
527
- solvea = SolveaClient()
528
- if not args.dry_run:
529
- print(f"[main] project_id={solvea.project_id}")
530
-
531
- # ── 补全 Delivery 固定 7 个意图 ─────────────────────────────────────────────
532
- # 客户数据里如果没有某个固定意图的样本,classify.py 不会产生它;
533
- # Delivery 要建全部 7 个标准意图,这里强制补入缺失项。
534
- found_names = {i["name"] for i in all_intents}
535
- for fixed_name in DELIVERY_AGENT_BINDINGS:
536
- if fixed_name not in found_names:
537
- print(f"[main] '{fixed_name}' missing from classification results → added from fixed taxonomy")
538
- all_intents.append({"name": fixed_name, "definition": ""})
539
-
540
- # ── Feature pre-flight ─────────────────────────────────────────────────────
541
- if not args.dry_run:
542
- check_and_enable_intent_feature(solvea) # 建意图前
543
- check_and_enable_variable_feature(solvea) # copyAgent 前
544
-
545
- # ── Delivery ───────────────────────────────────────────────────────────────
546
- # 走固定 squareAgent_id 直接 copyAgent,不查广场 / 不按名字匹配。
547
- # 不在 DELIVERY_AGENT_BINDINGS 中的意图(数据中新发现的 L1)→ 跳过 agent/策略。
548
-
549
- creation_log = []
550
- preset_agent_errors = []
551
- existing_by_name = {}
552
- if not args.dry_run:
553
- existing_by_name = {
554
- item.get("name"): item
555
- for item in _intent_items(solvea.list_intents())
556
- if isinstance(item, dict) and item.get("name") and (item.get("id") or item.get("intentId"))
557
- }
558
-
559
- for intent in all_intents:
560
- name = intent["name"]
561
- print(f"\n{'='*60}")
562
- print(f"[main] Processing: {name}")
563
-
564
- # 1. Create L1 intent
565
- intent_result = create_l1_intent(
566
- solvea,
567
- intent,
568
- dry_run=args.dry_run,
569
- existing_by_name=existing_by_name,
570
- )
571
- intent_id = str(intent_result.get("id")) if intent_result else f"dry_{name}"
572
-
573
- # 2. Resolve square_agent_id from fixed binding
574
- square_id = DELIVERY_AGENT_BINDINGS.get(name)
575
- if not square_id:
576
- print(f"[main] No mapping for '{name}' in DELIVERY_AGENT_BINDINGS — skipping")
577
- creation_log.append({
578
- "intent_name": name, "intent_id": intent_id,
579
- "agent_name": None, "agent_id": None, "strategy_id": None,
580
- "skipped": True, "skip_reason": "no mapping in DELIVERY_AGENT_BINDINGS",
581
- })
582
- continue
583
-
584
- # 3. Copy by squareAgent_id
585
- agent_name = f"{name}【Delivery Version】"
586
- if args.dry_run:
587
- print(f"[dry-run] copy square_agent_id={square_id} ('{agent_name}')")
588
- agent_id = f"dry_agent_{name}"
589
- else:
590
- try:
591
- agent_id = copy_marketplace_agent_by_id(solvea, square_id)
592
- except Exception as exc:
593
- preset_agent_errors.append({
594
- "intent_name": name,
595
- "intent_id": intent_id,
596
- "square_agent_id": square_id,
597
- "agent_name": agent_name,
598
- "error": str(exc),
599
- })
600
- print(
601
- f"[main] PRESET_AGENT_MISSING intent={name} "
602
- f"square_agent_id={square_id}: {exc}"
603
- )
604
- creation_log.append({
605
- "intent_name": name, "intent_id": intent_id,
606
- "agent_name": agent_name, "agent_id": None, "strategy_id": None,
607
- "skipped": True, "skip_reason": "preset agent unavailable",
608
- "recoverable": True,
609
- "square_agent_id": square_id,
610
- })
611
- continue
612
-
613
- # 4. Create strategy routing
614
- strategy_result = create_strategy_for_intent(
615
- solvea, intent_id, agent_id, name, dry_run=args.dry_run
616
- )
617
- strategy_id = str(strategy_result.get("id")) if strategy_result else f"dry_strategy_{name}"
618
-
619
- creation_log.append({
620
- "intent_name": name, "intent_id": intent_id,
621
- "agent_name": agent_name, "agent_id": agent_id,
622
- "strategy_id": strategy_id, "skipped": False,
623
- })
624
-
625
- # 4. Patch X-Token in order information tools (delivery default; togglable)
626
- if update_x_token:
627
- update_order_tool_x_token(solvea, creation_log, dry_run=args.dry_run)
628
- else:
629
- print("[x_token] skipped (--no-update-x-token)")
630
-
631
- OUTPUTS_DIR.mkdir(parents=True, exist_ok=True)
632
- log_path = str(OUTPUTS_DIR / f"created_{timestamp()}.json")
633
- with open(log_path, "w", encoding="utf-8") as f:
634
- json.dump({
635
- "intents": creation_log,
636
- "mode": args.mode,
637
- "run_id": run_id,
638
- "preset_agent_errors": preset_agent_errors,
639
- "next_action": (
640
- "For skipped preset-agent intents, ask the user whether to build new "
641
- "CHAT_FLOW agents from scratch or use substitute existing agent ids."
642
- if preset_agent_errors else ""
643
- ),
644
- }, f, ensure_ascii=False, indent=2)
645
-
646
- status = "preset_agent_missing" if preset_agent_errors else "created"
647
- state = _read_state()
648
- state.update({
649
- "status": status,
650
- "mode": args.mode,
651
- "created_log_path": log_path,
652
- "preset_agent_errors": preset_agent_errors,
653
- "updated_at": timestamp(),
654
- })
655
- _write_state(state)
656
-
657
- # Report SYSTEM intents that could be disabled (user confirms separately)
658
- if not args.dry_run:
659
- our_names = {item["intent_name"] for item in creation_log}
660
- result2 = solvea.list_intents()
661
- intents2 = result2 if isinstance(result2, list) else result2.get("data", result2.get("list", []))
662
- pending_disable = [
663
- i for i in intents2
664
- if i.get("source") == "SYSTEM"
665
- and i.get("name") not in our_names
666
- and i.get("status") != "DISABLED"
667
- ]
668
- if pending_disable:
669
- print(f"\n[system_intents] {len(pending_disable)} SYSTEM intents can be disabled:")
670
- for i in pending_disable:
671
- print(f" id={i['id']} name={i['name']}")
672
- print("[system_intents] Run with --disable-system to disable them.")
673
- else:
674
- print("\n[system_intents] No active SYSTEM intents to disable.")
675
-
676
- created = [x for x in creation_log if not x.get("skipped")]
677
- skipped = [x for x in creation_log if x.get("skipped")]
678
- print(f"\n[main] Done. {len(created)} created, {len(skipped)} skipped.")
679
- print(f"[main] Log → {log_path}")
680
- if preset_agent_errors:
681
- print("[main] Recoverable preset-agent gap detected.")
682
- print("[main] Ask the user to choose one option for each skipped standard intent:")
683
- print(" 1. Build a new CHAT_FLOW agent from scratch.")
684
- print(" 2. Use an existing substitute agent id.")
685
- print(f"\n[main] Summary:")
686
- for item in creation_log:
687
- if item.get("skipped"):
688
- print(f" {item['intent_name']}: SKIPPED ({item.get('skip_reason', '')})")
689
- else:
690
- print(f" {item['intent_name']}: intent={item['intent_id']} "
691
- f"agent={item['agent_id']} strategy={item['strategy_id']}")
692
-
693
-
694
- if __name__ == "__main__":
695
- main()