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