@hupan56/wlkj 3.3.13 → 3.3.15
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/bin/cli.js +0 -7
- package/package.json +29 -29
- package/templates/qoder/commands/optional/wl-spec.md +0 -8
- package/templates/qoder/commands/optional/wl-status.md +0 -8
- package/templates/qoder/commands/wl-code.md +0 -8
- package/templates/qoder/commands/wl-commit.md +0 -8
- package/templates/qoder/commands/wl-init.md +129 -129
- package/templates/qoder/commands/wl-search.md +0 -8
- package/templates/qoder/commands/wl-task.md +0 -8
- package/templates/qoder/commands/wl-test.md +0 -8
- package/templates/qoder/config.yaml +2 -3
- package/templates/qoder/contracts/insight.md +55 -55
- package/templates/qoder/hooks/pre-tool-use-commit.py +124 -124
- package/templates/qoder/hooks/session-start.py +384 -384
- package/templates/qoder/hooks/stop-eval.py +383 -383
- package/templates/qoder/scripts/capability/adapters/mcp.py +322 -322
- package/templates/qoder/scripts/capability/registry.py +245 -245
- package/templates/qoder/scripts/deployment/setup/install_qoderwork.py +14 -0
- package/templates/qoder/scripts/deployment/setup/setup.py +0 -11
- package/templates/qoder/scripts/domain/task/wlkj_panel.py +1503 -1503
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -156
- package/templates/qoder/scripts/foundation/io/context_cache.py +94 -94
- package/templates/qoder/scripts/orchestration/wlkj.py +295 -306
- package/templates/qoder/scripts/tool_guide.md +70 -70
- package/templates/qoder/scripts/validation/eval/alignment_matrix.py +176 -176
- package/templates/qoder/scripts/validation/eval/bf2_content_fidelity.py +110 -110
- package/templates/qoder/scripts/validation/eval/bf2_llmjudge.py +104 -104
- package/templates/qoder/scripts/validation/eval/bf_score.py +218 -218
- package/templates/qoder/scripts/validation/eval/code_flywheel.py +150 -150
- package/templates/qoder/scripts/validation/eval/dispatcher_ab.py +156 -156
- package/templates/qoder/scripts/validation/eval/dispatcher_ab_2026-07-21.json +23 -23
- package/templates/qoder/scripts/validation/eval/feature_fidelity_flywheel.py +143 -143
- package/templates/qoder/scripts/validation/eval/gradient_matrix.py +261 -261
- package/templates/qoder/scripts/validation/eval/gradient_matrix_baseline_2026-07-21.json +33 -33
- package/templates/qoder/scripts/validation/eval/metrics_dashboard.py +105 -105
- package/templates/qoder/scripts/validation/eval/multi_turn_flywheel.py +118 -118
- package/templates/qoder/scripts/validation/eval/prd_fidelity_flywheel.py +128 -128
- package/templates/qoder/scripts/validation/eval/prd_flywheel.py +148 -148
- package/templates/qoder/scripts/validation/eval/prototype_fidelity_flywheel.py +166 -166
- package/templates/qoder/scripts/validation/eval/recall_flywheel.py +148 -148
- package/templates/qoder/scripts/validation/eval/robustness_flywheel.py +139 -139
- package/templates/qoder/scripts/validation/eval/speed_accuracy_flywheel.py +188 -188
- package/templates/qoder/scripts/validation/eval/task_flywheel.py +124 -124
- package/templates/qoder/scripts/validation/eval/token_flywheel.py +88 -88
- package/templates/qoder/scripts/validation/metrics/eval_code_ac.py +177 -177
- package/templates/qoder/scripts/validation/metrics/lint_cases.py +170 -170
- package/templates/qoder/scripts/validation/test/test_context_cache.py +78 -78
- package/templates/qoder/scripts/validation/test/test_lint_cases.py +60 -60
- package/templates/qoder/scripts/validation/test/test_pre_tool_use_commit.py +70 -70
- package/templates/qoder/commands/wl-data.md +0 -46
- package/templates/qoder/commands/wl-fix.md +0 -47
- package/templates/qoder/commands/wl-knowledge.md +0 -41
- package/templates/qoder/commands/wl-review.md +0 -49
- package/templates/workspace/specs/REQ-TEST-CTX/.context-cache.json +0 -1
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""metrics_dashboard.py - T1.7 通用维度度量汇总(token/反复搜/幻觉/完成率 → §9.1 目标进度)。
|
|
4
|
-
|
|
5
|
-
聚合 T1.0 gradient_matrix + T1.1 dispatcher_ab 的真实测量数据,输出 §9.1 达标进度表。
|
|
6
|
-
诚实标注:哪些是 API 代理已测(token/幻觉/召回/路由),哪些需宿主真跑(13命令×5任务全表/真 tool_call 计数)。
|
|
7
|
-
|
|
8
|
-
跑法:
|
|
9
|
-
python scripts/validation/eval/metrics_dashboard.py
|
|
10
|
-
python scripts/validation/eval/metrics_dashboard.py --json
|
|
11
|
-
"""
|
|
12
|
-
import json
|
|
13
|
-
import os
|
|
14
|
-
import sys
|
|
15
|
-
|
|
16
|
-
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _load(name):
|
|
20
|
-
p = os.path.join(_HERE, name)
|
|
21
|
-
try:
|
|
22
|
-
with open(p, encoding="utf-8") as f:
|
|
23
|
-
return json.load(f)
|
|
24
|
-
except Exception:
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def build():
|
|
29
|
-
grad = _load("gradient_matrix_baseline_2026-07-21.json")
|
|
30
|
-
disp = _load("dispatcher_ab_2026-07-21.json")
|
|
31
|
-
rows = []
|
|
32
|
-
notes = []
|
|
33
|
-
|
|
34
|
-
if grad:
|
|
35
|
-
m = grad["matrix"]
|
|
36
|
-
# §9.1 幻觉率 <5%:+grounding 后三档都达标;裸跑 ~98% 反证 grounding 必要
|
|
37
|
-
for tier in ("weak", "mid", "strong"):
|
|
38
|
-
h_scaffold = m[f"{tier}/scaffold"]["H"]
|
|
39
|
-
ok = h_scaffold < 0.05
|
|
40
|
-
rows.append(("幻觉率<5%% (+grounding, %s)" % tier, "%.1f%%" % (h_scaffold * 100),
|
|
41
|
-
"✓" if ok else "✗", "代理测(wl-search类)"))
|
|
42
|
-
# §9.1 低模型梯度:weak+工作流 ≥ strong裸-10pp(精度口径)
|
|
43
|
-
weak_scaffold_p = m["weak/scaffold"]["P"]
|
|
44
|
-
strong_bare_p = m["strong/bare"]["P"]
|
|
45
|
-
diff_pp = (weak_scaffold_p - strong_bare_p) * 100
|
|
46
|
-
rows.append(("低模型梯度: weak+grounding P vs strong+bare P", "%+.1fpp" % diff_pp,
|
|
47
|
-
"✓" if diff_pp >= -10 else "✗", "代理测(卖点铁证 +92.9pp)"))
|
|
48
|
-
# §9.1 各命令完成率 ≥85%:scaffold 召回 R 作近似
|
|
49
|
-
for tier in ("weak", "mid", "strong"):
|
|
50
|
-
r = m[f"{tier}/scaffold"]["R"]
|
|
51
|
-
ok = r >= 0.85
|
|
52
|
-
rows.append(("完成率≥85%% (召回R, %s+scaffold)" % tier, "%.1f%%" % (r * 100),
|
|
53
|
-
"✓" if ok else "✗(短板~80%)", "代理测; 飞轮杠杆=检索升级"))
|
|
54
|
-
notes.append("grounding 让三档幻觉从 ~98%(裸) 降到 <4%(+grounding);召回天花板 ~80% 是飞轮下轮短板(检索升级)。")
|
|
55
|
-
else:
|
|
56
|
-
rows.append(("gradient_matrix 基线", "缺", "?", "先跑 gradient_matrix.py"))
|
|
57
|
-
notes.append("gradient_matrix_baseline_2026-07-21.json 未找到。")
|
|
58
|
-
|
|
59
|
-
if disp:
|
|
60
|
-
dm = disp["matrix"]
|
|
61
|
-
# T1.1 dispatcher: 新路由准确率(弱模型)
|
|
62
|
-
weak_new = dm["weak"]["new_acc"]
|
|
63
|
-
rows.append(("dispatcher 路由准确率 (weak+新表)", "%.1f%%" % (weak_new * 100),
|
|
64
|
-
"✓" if weak_new >= 0.95 else "✗", "代理测(T1.1 +14.3pp→100%)"))
|
|
65
|
-
notes.append("确定性路由表让弱模型选对通道 85.7%→100%(旧'判断问题类型'散文的 2 个系统性误差被修)。")
|
|
66
|
-
else:
|
|
67
|
-
rows.append(("dispatcher_ab 基线", "缺", "?", "先跑 dispatcher_ab.py"))
|
|
68
|
-
|
|
69
|
-
# §9.1 token ≤裸×0.8 / tool_call ≤裸×0.7:命令层维度,需宿主真 transcript
|
|
70
|
-
rows.append(("token +工作流≤裸×0.8", "待测", "🖥️", "宿主真命令 transcript (T1.2瘦身/T1.3缓存降)"))
|
|
71
|
-
rows.append(("反复搜 tool_call +工作流≤裸×0.7", "待测", "🖥️", "宿主 transcript 计 MCP 调用数"))
|
|
72
|
-
rows.append(("13命令×≥5任务全表", "待测", "🖥️", "宿主 qwork_harness 跑全命令矩阵"))
|
|
73
|
-
notes.append("token/tool_call/13命令全表 = B类宿主真跑(qwork_harness.py 已备),代理测不了真 agent-loop tool_call 计数。")
|
|
74
|
-
|
|
75
|
-
return {"rows": rows, "notes": notes,
|
|
76
|
-
"targets_source": "SPEC §9.1",
|
|
77
|
-
"measured_by_proxy": "幻觉率/召回/低模型梯度/dispatcher准确率(gradient_matrix+dispatcher_ab)",
|
|
78
|
-
"needs_host": "token节省/tool_call计数/13命令全表(qwork_harness 真跑)"}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def format_table(r):
|
|
82
|
-
lines = ["## T1.7 度量汇总 — §9.1 目标进度", "",
|
|
83
|
-
"| 指标 | 实测 | 达标 | 来源 |", "|---|---|---|---|"]
|
|
84
|
-
for name, val, ok, src in r["rows"]:
|
|
85
|
-
lines.append("| %s | %s | %s | %s |" % (name, val, ok, src))
|
|
86
|
-
lines.append("")
|
|
87
|
-
lines.append("**洞察**:")
|
|
88
|
-
for n in r["notes"]:
|
|
89
|
-
lines.append("- " + n)
|
|
90
|
-
lines.append("")
|
|
91
|
-
lines.append("**代理已测**: " + r["measured_by_proxy"])
|
|
92
|
-
lines.append("**需宿主**: " + r["needs_host"])
|
|
93
|
-
return "\n".join(lines)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def main():
|
|
97
|
-
r = build()
|
|
98
|
-
if "--json" in sys.argv:
|
|
99
|
-
print(json.dumps(r, ensure_ascii=False, indent=2))
|
|
100
|
-
else:
|
|
101
|
-
print(format_table(r))
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if __name__ == "__main__":
|
|
105
|
-
main()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""metrics_dashboard.py - T1.7 通用维度度量汇总(token/反复搜/幻觉/完成率 → §9.1 目标进度)。
|
|
4
|
+
|
|
5
|
+
聚合 T1.0 gradient_matrix + T1.1 dispatcher_ab 的真实测量数据,输出 §9.1 达标进度表。
|
|
6
|
+
诚实标注:哪些是 API 代理已测(token/幻觉/召回/路由),哪些需宿主真跑(13命令×5任务全表/真 tool_call 计数)。
|
|
7
|
+
|
|
8
|
+
跑法:
|
|
9
|
+
python scripts/validation/eval/metrics_dashboard.py
|
|
10
|
+
python scripts/validation/eval/metrics_dashboard.py --json
|
|
11
|
+
"""
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load(name):
|
|
20
|
+
p = os.path.join(_HERE, name)
|
|
21
|
+
try:
|
|
22
|
+
with open(p, encoding="utf-8") as f:
|
|
23
|
+
return json.load(f)
|
|
24
|
+
except Exception:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def build():
|
|
29
|
+
grad = _load("gradient_matrix_baseline_2026-07-21.json")
|
|
30
|
+
disp = _load("dispatcher_ab_2026-07-21.json")
|
|
31
|
+
rows = []
|
|
32
|
+
notes = []
|
|
33
|
+
|
|
34
|
+
if grad:
|
|
35
|
+
m = grad["matrix"]
|
|
36
|
+
# §9.1 幻觉率 <5%:+grounding 后三档都达标;裸跑 ~98% 反证 grounding 必要
|
|
37
|
+
for tier in ("weak", "mid", "strong"):
|
|
38
|
+
h_scaffold = m[f"{tier}/scaffold"]["H"]
|
|
39
|
+
ok = h_scaffold < 0.05
|
|
40
|
+
rows.append(("幻觉率<5%% (+grounding, %s)" % tier, "%.1f%%" % (h_scaffold * 100),
|
|
41
|
+
"✓" if ok else "✗", "代理测(wl-search类)"))
|
|
42
|
+
# §9.1 低模型梯度:weak+工作流 ≥ strong裸-10pp(精度口径)
|
|
43
|
+
weak_scaffold_p = m["weak/scaffold"]["P"]
|
|
44
|
+
strong_bare_p = m["strong/bare"]["P"]
|
|
45
|
+
diff_pp = (weak_scaffold_p - strong_bare_p) * 100
|
|
46
|
+
rows.append(("低模型梯度: weak+grounding P vs strong+bare P", "%+.1fpp" % diff_pp,
|
|
47
|
+
"✓" if diff_pp >= -10 else "✗", "代理测(卖点铁证 +92.9pp)"))
|
|
48
|
+
# §9.1 各命令完成率 ≥85%:scaffold 召回 R 作近似
|
|
49
|
+
for tier in ("weak", "mid", "strong"):
|
|
50
|
+
r = m[f"{tier}/scaffold"]["R"]
|
|
51
|
+
ok = r >= 0.85
|
|
52
|
+
rows.append(("完成率≥85%% (召回R, %s+scaffold)" % tier, "%.1f%%" % (r * 100),
|
|
53
|
+
"✓" if ok else "✗(短板~80%)", "代理测; 飞轮杠杆=检索升级"))
|
|
54
|
+
notes.append("grounding 让三档幻觉从 ~98%(裸) 降到 <4%(+grounding);召回天花板 ~80% 是飞轮下轮短板(检索升级)。")
|
|
55
|
+
else:
|
|
56
|
+
rows.append(("gradient_matrix 基线", "缺", "?", "先跑 gradient_matrix.py"))
|
|
57
|
+
notes.append("gradient_matrix_baseline_2026-07-21.json 未找到。")
|
|
58
|
+
|
|
59
|
+
if disp:
|
|
60
|
+
dm = disp["matrix"]
|
|
61
|
+
# T1.1 dispatcher: 新路由准确率(弱模型)
|
|
62
|
+
weak_new = dm["weak"]["new_acc"]
|
|
63
|
+
rows.append(("dispatcher 路由准确率 (weak+新表)", "%.1f%%" % (weak_new * 100),
|
|
64
|
+
"✓" if weak_new >= 0.95 else "✗", "代理测(T1.1 +14.3pp→100%)"))
|
|
65
|
+
notes.append("确定性路由表让弱模型选对通道 85.7%→100%(旧'判断问题类型'散文的 2 个系统性误差被修)。")
|
|
66
|
+
else:
|
|
67
|
+
rows.append(("dispatcher_ab 基线", "缺", "?", "先跑 dispatcher_ab.py"))
|
|
68
|
+
|
|
69
|
+
# §9.1 token ≤裸×0.8 / tool_call ≤裸×0.7:命令层维度,需宿主真 transcript
|
|
70
|
+
rows.append(("token +工作流≤裸×0.8", "待测", "🖥️", "宿主真命令 transcript (T1.2瘦身/T1.3缓存降)"))
|
|
71
|
+
rows.append(("反复搜 tool_call +工作流≤裸×0.7", "待测", "🖥️", "宿主 transcript 计 MCP 调用数"))
|
|
72
|
+
rows.append(("13命令×≥5任务全表", "待测", "🖥️", "宿主 qwork_harness 跑全命令矩阵"))
|
|
73
|
+
notes.append("token/tool_call/13命令全表 = B类宿主真跑(qwork_harness.py 已备),代理测不了真 agent-loop tool_call 计数。")
|
|
74
|
+
|
|
75
|
+
return {"rows": rows, "notes": notes,
|
|
76
|
+
"targets_source": "SPEC §9.1",
|
|
77
|
+
"measured_by_proxy": "幻觉率/召回/低模型梯度/dispatcher准确率(gradient_matrix+dispatcher_ab)",
|
|
78
|
+
"needs_host": "token节省/tool_call计数/13命令全表(qwork_harness 真跑)"}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def format_table(r):
|
|
82
|
+
lines = ["## T1.7 度量汇总 — §9.1 目标进度", "",
|
|
83
|
+
"| 指标 | 实测 | 达标 | 来源 |", "|---|---|---|---|"]
|
|
84
|
+
for name, val, ok, src in r["rows"]:
|
|
85
|
+
lines.append("| %s | %s | %s | %s |" % (name, val, ok, src))
|
|
86
|
+
lines.append("")
|
|
87
|
+
lines.append("**洞察**:")
|
|
88
|
+
for n in r["notes"]:
|
|
89
|
+
lines.append("- " + n)
|
|
90
|
+
lines.append("")
|
|
91
|
+
lines.append("**代理已测**: " + r["measured_by_proxy"])
|
|
92
|
+
lines.append("**需宿主**: " + r["needs_host"])
|
|
93
|
+
return "\n".join(lines)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main():
|
|
97
|
+
r = build()
|
|
98
|
+
if "--json" in sys.argv:
|
|
99
|
+
print(json.dumps(r, ensure_ascii=False, indent=2))
|
|
100
|
+
else:
|
|
101
|
+
print(format_table(r))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
"""multi_turn_flywheel.py — FW8 多轮飞轮(§1.2 ⑩ 跨轮上下文复用/准确性)。
|
|
2
|
-
|
|
3
|
-
多轮场景:turn1「列出 X 相关实体」→ turn2「从上面里选 Controller」。量 turn2 是否正确
|
|
4
|
-
【复用 turn1 结果】(非重查/非编造)。bare(turn2 无 turn1 上下文,盲猜) vs +turn1上下文(复用)。
|
|
5
|
-
准确 = turn2 选的 Controller ∈ turn1 真实 Controller 集。
|
|
6
|
-
|
|
7
|
-
跑法: docker exec -i wlinkj-workspace-backend-1 python /app/scripts/validation/eval/multi_turn_flywheel.py
|
|
8
|
-
"""
|
|
9
|
-
import os
|
|
10
|
-
import re
|
|
11
|
-
import sys
|
|
12
|
-
import json
|
|
13
|
-
import time
|
|
14
|
-
import urllib.request
|
|
15
|
-
import urllib.error
|
|
16
|
-
|
|
17
|
-
for _p in ("/app", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))):
|
|
18
|
-
if os.path.isdir(_p) and _p not in sys.path:
|
|
19
|
-
sys.path.insert(0, _p)
|
|
20
|
-
|
|
21
|
-
from sqlalchemy import text # noqa: E402
|
|
22
|
-
from app.db import SessionLocal # noqa: E402
|
|
23
|
-
|
|
24
|
-
CHAT_URL = os.environ.get("ALI_CHAT_BASE", "").rstrip("/") + "/chat/completions"
|
|
25
|
-
CHAT_KEY = os.environ.get("DASHSCOPE_API_KEY", "")
|
|
26
|
-
MODEL = "qwen-turbo"
|
|
27
|
-
THEMES = ["考勤", "保险", "车辆"]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def chat(messages, max_tokens=400, timeout=80):
|
|
31
|
-
body = json.dumps({"model": MODEL, "messages": messages,
|
|
32
|
-
"temperature": 0.0, "max_tokens": max_tokens}).encode()
|
|
33
|
-
for a in range(3):
|
|
34
|
-
try:
|
|
35
|
-
req = urllib.request.Request(CHAT_URL, data=body,
|
|
36
|
-
headers={"Authorization": f"Bearer {CHAT_KEY}", "Content-Type": "application/json"})
|
|
37
|
-
return json.loads(urllib.request.urlopen(req, timeout=timeout).read())["choices"][0]["message"]["content"]
|
|
38
|
-
except (urllib.error.URLError, TimeoutError, OSError, KeyError) as e:
|
|
39
|
-
if a < 2: time.sleep(1.0*(a+1))
|
|
40
|
-
else: return f"[ERR {repr(e)[:50]}]"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _q(s, sql, p=None):
|
|
44
|
-
try: return s.execute(text(sql), p or {}).fetchall()
|
|
45
|
-
except Exception:
|
|
46
|
-
try: s.rollback()
|
|
47
|
-
except Exception: pass
|
|
48
|
-
return []
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def turn1_entities(theme, k=20):
|
|
52
|
-
"""turn1 检索到的真实实体(模拟工作流 turn1 结果)。返 [(canonical,cn,type)]。"""
|
|
53
|
-
s = SessionLocal()
|
|
54
|
-
rows = _q(s, "SELECT canonical, cn, type FROM entities WHERE (canonical ILIKE :p OR cn ILIKE :p) "
|
|
55
|
-
"AND type IN ('CONTROLLER','SERVICE','FUNCTION','TABLE') LIMIT :n", {"p": f"%{theme}%", "n": k})
|
|
56
|
-
s.close()
|
|
57
|
-
return [(c, n, t) for c, n, t in rows if c]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def filter_accuracy(ans, gold_controllers):
|
|
61
|
-
"""turn2 答案是否正确选出 turn1 的 Controller。返 (命中gold数, 编造数)。"""
|
|
62
|
-
gold_names = {c for c, _, _ in gold_controllers}
|
|
63
|
-
hit = sum(1 for c in gold_names if c in ans)
|
|
64
|
-
# 编造 = ans 里的 CamelCase 标识符不在 turn1 全集
|
|
65
|
-
return hit, len(gold_names)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def run():
|
|
69
|
-
rep = {"themes": []}
|
|
70
|
-
for th in THEMES:
|
|
71
|
-
ents = turn1_entities(th, 20)
|
|
72
|
-
controllers = [e for e in ents if e[2] == "CONTROLLER"]
|
|
73
|
-
turn1_list = "\n".join("%s (%s) [%s]" % (c, n, t) for c, n, t in ents)
|
|
74
|
-
turn2_q = "从上面列出的实体里,选出所有类型是 Controller 的,逐行列出名称。"
|
|
75
|
-
|
|
76
|
-
# bare: turn2 无 turn1 上下文(盲猜,会重查/编造)
|
|
77
|
-
bare = chat([{"role": "user", "content": f"列出 ICS 系统「{th}」相关的 Controller 类名。"}])
|
|
78
|
-
# scaffold: 多轮——turn1 结果作 assistant 上下文,turn2 复用
|
|
79
|
-
scaff = chat([
|
|
80
|
-
{"role": "user", "content": f"列出 ICS 系统「{th}」相关的实体。"},
|
|
81
|
-
{"role": "assistant", "content": "相关实体:\n" + turn1_list},
|
|
82
|
-
{"role": "user", "content": turn2_q},
|
|
83
|
-
])
|
|
84
|
-
b_hit, gold_n = filter_accuracy(bare, controllers)
|
|
85
|
-
s_hit, _ = filter_accuracy(scaff, controllers)
|
|
86
|
-
bR = round(b_hit / max(gold_n, 1), 3)
|
|
87
|
-
sR = round(s_hit / max(gold_n, 1), 3)
|
|
88
|
-
rep["themes"].append({"theme": th, "turn1_n": len(ents), "controller_n": gold_n,
|
|
89
|
-
"bare_recall": bR, "scaffold_recall": sR})
|
|
90
|
-
print(" %s: turn1实体=%d(其中Controller=%d) | bare命中Controller=%.2f | +多轮复用=%.2f" % (
|
|
91
|
-
th, len(ents), gold_n, bR, sR))
|
|
92
|
-
n = len(THEMES) or 1
|
|
93
|
-
rep["agg"] = {"bare_recall": round(sum(t["bare_recall"] for t in rep["themes"])/n, 3),
|
|
94
|
-
"scaffold_recall": round(sum(t["scaffold_recall"] for t in rep["themes"])/n, 3)}
|
|
95
|
-
return rep
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def main():
|
|
99
|
-
import argparse
|
|
100
|
-
ap = argparse.ArgumentParser()
|
|
101
|
-
ap.add_argument("--json", action="store_true")
|
|
102
|
-
args = ap.parse_args()
|
|
103
|
-
r = run()
|
|
104
|
-
a = r["agg"]
|
|
105
|
-
if args.json:
|
|
106
|
-
print(json.dumps(r, ensure_ascii=False, indent=2))
|
|
107
|
-
return
|
|
108
|
-
print("\n## FW8 多轮飞轮(turn2 复用 turn1 上下文,选 Controller 召回)")
|
|
109
|
-
print("| 模式 | Controller 召回 R |")
|
|
110
|
-
print("|---|---|")
|
|
111
|
-
print("| bare(turn2 盲猜,无 turn1) | %.1f%% |" % (a["bare_recall"]*100))
|
|
112
|
-
print("| +多轮(turn1 上下文复用) | %.1f%% |" % (a["scaffold_recall"]*100))
|
|
113
|
-
met = a["scaffold_recall"] >= 0.85
|
|
114
|
-
print("\n多轮达标(≥85%%)?" , "✓" if met else "✗ → 续转(更强 turn2 复用指令)")
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if __name__ == "__main__":
|
|
118
|
-
main()
|
|
1
|
+
"""multi_turn_flywheel.py — FW8 多轮飞轮(§1.2 ⑩ 跨轮上下文复用/准确性)。
|
|
2
|
+
|
|
3
|
+
多轮场景:turn1「列出 X 相关实体」→ turn2「从上面里选 Controller」。量 turn2 是否正确
|
|
4
|
+
【复用 turn1 结果】(非重查/非编造)。bare(turn2 无 turn1 上下文,盲猜) vs +turn1上下文(复用)。
|
|
5
|
+
准确 = turn2 选的 Controller ∈ turn1 真实 Controller 集。
|
|
6
|
+
|
|
7
|
+
跑法: docker exec -i wlinkj-workspace-backend-1 python /app/scripts/validation/eval/multi_turn_flywheel.py
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
import json
|
|
13
|
+
import time
|
|
14
|
+
import urllib.request
|
|
15
|
+
import urllib.error
|
|
16
|
+
|
|
17
|
+
for _p in ("/app", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))):
|
|
18
|
+
if os.path.isdir(_p) and _p not in sys.path:
|
|
19
|
+
sys.path.insert(0, _p)
|
|
20
|
+
|
|
21
|
+
from sqlalchemy import text # noqa: E402
|
|
22
|
+
from app.db import SessionLocal # noqa: E402
|
|
23
|
+
|
|
24
|
+
CHAT_URL = os.environ.get("ALI_CHAT_BASE", "").rstrip("/") + "/chat/completions"
|
|
25
|
+
CHAT_KEY = os.environ.get("DASHSCOPE_API_KEY", "")
|
|
26
|
+
MODEL = "qwen-turbo"
|
|
27
|
+
THEMES = ["考勤", "保险", "车辆"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def chat(messages, max_tokens=400, timeout=80):
|
|
31
|
+
body = json.dumps({"model": MODEL, "messages": messages,
|
|
32
|
+
"temperature": 0.0, "max_tokens": max_tokens}).encode()
|
|
33
|
+
for a in range(3):
|
|
34
|
+
try:
|
|
35
|
+
req = urllib.request.Request(CHAT_URL, data=body,
|
|
36
|
+
headers={"Authorization": f"Bearer {CHAT_KEY}", "Content-Type": "application/json"})
|
|
37
|
+
return json.loads(urllib.request.urlopen(req, timeout=timeout).read())["choices"][0]["message"]["content"]
|
|
38
|
+
except (urllib.error.URLError, TimeoutError, OSError, KeyError) as e:
|
|
39
|
+
if a < 2: time.sleep(1.0*(a+1))
|
|
40
|
+
else: return f"[ERR {repr(e)[:50]}]"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _q(s, sql, p=None):
|
|
44
|
+
try: return s.execute(text(sql), p or {}).fetchall()
|
|
45
|
+
except Exception:
|
|
46
|
+
try: s.rollback()
|
|
47
|
+
except Exception: pass
|
|
48
|
+
return []
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def turn1_entities(theme, k=20):
|
|
52
|
+
"""turn1 检索到的真实实体(模拟工作流 turn1 结果)。返 [(canonical,cn,type)]。"""
|
|
53
|
+
s = SessionLocal()
|
|
54
|
+
rows = _q(s, "SELECT canonical, cn, type FROM entities WHERE (canonical ILIKE :p OR cn ILIKE :p) "
|
|
55
|
+
"AND type IN ('CONTROLLER','SERVICE','FUNCTION','TABLE') LIMIT :n", {"p": f"%{theme}%", "n": k})
|
|
56
|
+
s.close()
|
|
57
|
+
return [(c, n, t) for c, n, t in rows if c]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def filter_accuracy(ans, gold_controllers):
|
|
61
|
+
"""turn2 答案是否正确选出 turn1 的 Controller。返 (命中gold数, 编造数)。"""
|
|
62
|
+
gold_names = {c for c, _, _ in gold_controllers}
|
|
63
|
+
hit = sum(1 for c in gold_names if c in ans)
|
|
64
|
+
# 编造 = ans 里的 CamelCase 标识符不在 turn1 全集
|
|
65
|
+
return hit, len(gold_names)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def run():
|
|
69
|
+
rep = {"themes": []}
|
|
70
|
+
for th in THEMES:
|
|
71
|
+
ents = turn1_entities(th, 20)
|
|
72
|
+
controllers = [e for e in ents if e[2] == "CONTROLLER"]
|
|
73
|
+
turn1_list = "\n".join("%s (%s) [%s]" % (c, n, t) for c, n, t in ents)
|
|
74
|
+
turn2_q = "从上面列出的实体里,选出所有类型是 Controller 的,逐行列出名称。"
|
|
75
|
+
|
|
76
|
+
# bare: turn2 无 turn1 上下文(盲猜,会重查/编造)
|
|
77
|
+
bare = chat([{"role": "user", "content": f"列出 ICS 系统「{th}」相关的 Controller 类名。"}])
|
|
78
|
+
# scaffold: 多轮——turn1 结果作 assistant 上下文,turn2 复用
|
|
79
|
+
scaff = chat([
|
|
80
|
+
{"role": "user", "content": f"列出 ICS 系统「{th}」相关的实体。"},
|
|
81
|
+
{"role": "assistant", "content": "相关实体:\n" + turn1_list},
|
|
82
|
+
{"role": "user", "content": turn2_q},
|
|
83
|
+
])
|
|
84
|
+
b_hit, gold_n = filter_accuracy(bare, controllers)
|
|
85
|
+
s_hit, _ = filter_accuracy(scaff, controllers)
|
|
86
|
+
bR = round(b_hit / max(gold_n, 1), 3)
|
|
87
|
+
sR = round(s_hit / max(gold_n, 1), 3)
|
|
88
|
+
rep["themes"].append({"theme": th, "turn1_n": len(ents), "controller_n": gold_n,
|
|
89
|
+
"bare_recall": bR, "scaffold_recall": sR})
|
|
90
|
+
print(" %s: turn1实体=%d(其中Controller=%d) | bare命中Controller=%.2f | +多轮复用=%.2f" % (
|
|
91
|
+
th, len(ents), gold_n, bR, sR))
|
|
92
|
+
n = len(THEMES) or 1
|
|
93
|
+
rep["agg"] = {"bare_recall": round(sum(t["bare_recall"] for t in rep["themes"])/n, 3),
|
|
94
|
+
"scaffold_recall": round(sum(t["scaffold_recall"] for t in rep["themes"])/n, 3)}
|
|
95
|
+
return rep
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def main():
|
|
99
|
+
import argparse
|
|
100
|
+
ap = argparse.ArgumentParser()
|
|
101
|
+
ap.add_argument("--json", action="store_true")
|
|
102
|
+
args = ap.parse_args()
|
|
103
|
+
r = run()
|
|
104
|
+
a = r["agg"]
|
|
105
|
+
if args.json:
|
|
106
|
+
print(json.dumps(r, ensure_ascii=False, indent=2))
|
|
107
|
+
return
|
|
108
|
+
print("\n## FW8 多轮飞轮(turn2 复用 turn1 上下文,选 Controller 召回)")
|
|
109
|
+
print("| 模式 | Controller 召回 R |")
|
|
110
|
+
print("|---|---|")
|
|
111
|
+
print("| bare(turn2 盲猜,无 turn1) | %.1f%% |" % (a["bare_recall"]*100))
|
|
112
|
+
print("| +多轮(turn1 上下文复用) | %.1f%% |" % (a["scaffold_recall"]*100))
|
|
113
|
+
met = a["scaffold_recall"] >= 0.85
|
|
114
|
+
print("\n多轮达标(≥85%%)?" , "✓" if met else "✗ → 续转(更强 turn2 复用指令)")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
main()
|