@hupan56/wlkj 2.5.0 → 2.7.0

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 (127) hide show
  1. package/bin/cli.js +289 -12
  2. package/package.json +1 -1
  3. package/templates/qoder/agents/insight-planning.md +67 -0
  4. package/templates/qoder/agents/insight-research.md +61 -0
  5. package/templates/qoder/agents/prd-quick.md +1 -0
  6. package/templates/qoder/agents/prd-reference.md +10 -2
  7. package/templates/qoder/commands/optional/wl-insight.md +275 -0
  8. package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
  9. package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
  10. package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
  11. package/templates/qoder/commands/wl-code.md +10 -2
  12. package/templates/qoder/commands/wl-commit.md +1 -1
  13. package/templates/qoder/commands/wl-design-draw.md +78 -0
  14. package/templates/qoder/commands/wl-design-scan.md +108 -0
  15. package/templates/qoder/commands/wl-design-spec.md +154 -0
  16. package/templates/qoder/commands/wl-design.md +32 -0
  17. package/templates/qoder/commands/wl-init.md +24 -3
  18. package/templates/qoder/commands/wl-prd-full.md +226 -0
  19. package/templates/qoder/commands/wl-prd-quick.md +134 -0
  20. package/templates/qoder/commands/wl-prd-review.md +104 -0
  21. package/templates/qoder/commands/wl-prd.md +17 -288
  22. package/templates/qoder/commands/wl-search.md +66 -30
  23. package/templates/qoder/commands/wl-task.md +290 -59
  24. package/templates/qoder/commands/wl-test.md +92 -24
  25. package/templates/qoder/config.yaml +59 -15
  26. package/templates/qoder/hooks/inject-workflow-state.py +35 -9
  27. package/templates/qoder/hooks/session-start.py +144 -62
  28. package/templates/qoder/rules/wl-pipeline.md +216 -105
  29. package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
  30. package/templates/qoder/scripts/archive_prd.py +377 -0
  31. package/templates/qoder/scripts/autotest.py +1715 -0
  32. package/templates/qoder/scripts/autotest_batch.py +224 -0
  33. package/templates/qoder/scripts/autotest_run.py +297 -0
  34. package/templates/qoder/scripts/benchmark.py +210 -209
  35. package/templates/qoder/scripts/build_style_index.py +444 -4
  36. package/templates/qoder/scripts/check_carriers.py +238 -0
  37. package/templates/qoder/scripts/check_mcp.py +298 -0
  38. package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
  39. package/templates/qoder/scripts/common/developer.py +26 -19
  40. package/templates/qoder/scripts/common/events.py +46 -0
  41. package/templates/qoder/scripts/common/extract.py +419 -0
  42. package/templates/qoder/scripts/common/graph_traverse.py +533 -0
  43. package/templates/qoder/scripts/common/identity.py +6 -1
  44. package/templates/qoder/scripts/common/paths.py +89 -0
  45. package/templates/qoder/scripts/common/pip_install.py +144 -0
  46. package/templates/qoder/scripts/common/platform_guard.py +61 -0
  47. package/templates/qoder/scripts/common/search_engine.py +205 -205
  48. package/templates/qoder/scripts/common/terms.py +57 -0
  49. package/templates/qoder/scripts/common/ts_extract.py +536 -0
  50. package/templates/qoder/scripts/context_pack.py +73 -13
  51. package/templates/qoder/scripts/enrich_prompt.py +226 -0
  52. package/templates/qoder/scripts/eval_prd.py +318 -225
  53. package/templates/qoder/scripts/export.py +487 -487
  54. package/templates/qoder/scripts/extract_api_params.py +246 -0
  55. package/templates/qoder/scripts/extract_routes.py +54 -0
  56. package/templates/qoder/scripts/extract_routes_tree.py +78 -0
  57. package/templates/qoder/scripts/fill_prototype.py +707 -0
  58. package/templates/qoder/scripts/gen_design_doc.py +394 -0
  59. package/templates/qoder/scripts/git_sync.py +27 -15
  60. package/templates/qoder/scripts/init_doctor.py +306 -41
  61. package/templates/qoder/scripts/install_qoderwork.py +366 -9
  62. package/templates/qoder/scripts/kg.py +708 -0
  63. package/templates/qoder/scripts/kg_auto_login.py +196 -0
  64. package/templates/qoder/scripts/kg_build.py +612 -0
  65. package/templates/qoder/scripts/kg_build_db.py +327 -0
  66. package/templates/qoder/scripts/kg_duckdb.py +549 -0
  67. package/templates/qoder/scripts/kg_incremental.py +393 -0
  68. package/templates/qoder/scripts/kg_link_db.py +224 -0
  69. package/templates/qoder/scripts/kg_mcp_server.py +801 -0
  70. package/templates/qoder/scripts/kg_semantic.py +150 -0
  71. package/templates/qoder/scripts/kg_test_runner.py +241 -0
  72. package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
  73. package/templates/qoder/scripts/learn.py +118 -39
  74. package/templates/qoder/scripts/learn_aggregate.py +201 -0
  75. package/templates/qoder/scripts/mcp_launcher.py +359 -0
  76. package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
  77. package/templates/qoder/scripts/repo_root.py +106 -0
  78. package/templates/qoder/scripts/role.py +12 -0
  79. package/templates/qoder/scripts/run_weekly_update.bat +5 -0
  80. package/templates/qoder/scripts/run_weekly_update.sh +5 -0
  81. package/templates/qoder/scripts/search_index.py +307 -60
  82. package/templates/qoder/scripts/secure-ls.js +5640 -0
  83. package/templates/qoder/scripts/setup.py +706 -453
  84. package/templates/qoder/scripts/setup_lanhu.py +963 -0
  85. package/templates/qoder/scripts/status.py +250 -11
  86. package/templates/qoder/scripts/sync_carriers.py +259 -0
  87. package/templates/qoder/scripts/syncgate.py +5 -4
  88. package/templates/qoder/scripts/task.py +75 -0
  89. package/templates/qoder/scripts/team_sync.py +60 -4
  90. package/templates/qoder/scripts/workspace_init.py +1 -1
  91. package/templates/qoder/skills/design-import/SKILL.md +226 -0
  92. package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
  93. package/templates/qoder/skills/design-review/SKILL.md +82 -25
  94. package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
  95. package/templates/qoder/skills/prd-review/SKILL.md +18 -1
  96. package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
  97. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
  98. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  99. package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
  100. package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
  101. package/templates/qoder/skills/test-generator/SKILL.md +15 -2
  102. package/templates/qoder/skills/wl-code/SKILL.md +55 -36
  103. package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
  104. package/templates/qoder/skills/wl-design/SKILL.md +55 -0
  105. package/templates/qoder/skills/wl-init/SKILL.md +76 -67
  106. package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
  107. package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
  108. package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
  109. package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
  110. package/templates/qoder/skills/wl-report/SKILL.md +131 -107
  111. package/templates/qoder/skills/wl-search/SKILL.md +141 -75
  112. package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
  113. package/templates/qoder/skills/wl-status/SKILL.md +83 -61
  114. package/templates/qoder/skills/wl-task/SKILL.md +132 -58
  115. package/templates/qoder/skills/wl-test/SKILL.md +406 -40
  116. package/templates/qoder/templates/prd-full-template.md +2 -0
  117. package/templates/qoder/templates/prd-quick-template.md +1 -0
  118. package/templates/qoder/templates/prototype-app.html +13 -8
  119. package/templates/qoder/templates/prototype-web.html +376 -93
  120. package/templates/root/AGENTS.md +89 -34
  121. package/templates/root/requirements.txt +21 -0
  122. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
  123. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
  124. package/templates/qoder/agents/prd-planning.md +0 -56
  125. package/templates/qoder/agents/prd-research.md +0 -33
  126. package/templates/qoder/commands/wl-insight.md +0 -51
  127. package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
@@ -1,209 +1,210 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- benchmark.py - QODER 流水线性能基准 (性能优化 A7)
5
-
6
- 测量关键操作的延迟和内存, 供优化前后对比。
7
-
8
- Usage:
9
- python benchmark.py # 跑全部基准
10
- python benchmark.py --search # 只测搜索
11
- python benchmark.py --build # 只测构建 (耗时)
12
- python benchmark.py --json # 输出 JSON (供对比)
13
-
14
- 指标:
15
- - search_cold: 冷启动搜索 (Python 启动 + JSON 加载 + 扫描)
16
- - search_warm: 热搜索 (倒排索引缓存命中)
17
- - index_load: keyword-index.json 加载时间
18
- - inverted_build: 倒排索引构建时间
19
- - inverted_cache_load: 缓存加载时间
20
- - cnmap_lookup: CN_MAP 查询 (含 auto-expand)
21
- """
22
-
23
- import argparse
24
- import json
25
- import os
26
- import sys
27
- import time
28
- from datetime import datetime
29
-
30
- try:
31
- sys.stdout.reconfigure(encoding='utf-8', errors='replace')
32
- except (AttributeError, TypeError, OSError):
33
- pass
34
-
35
- THIS_DIR = os.path.dirname(os.path.abspath(__file__)) # .qoder/scripts
36
- sys.path.insert(0, THIS_DIR)
37
- # BASE = repo root = scripts 的上两级 (.qoder/scripts -> .qoder -> repo)
38
- BASE = os.path.dirname(os.path.dirname(THIS_DIR))
39
-
40
- INDEX_DIR = os.path.join(BASE, 'data', 'index')
41
-
42
-
43
- def measure(fn, repeat=1):
44
- """测函数执行时间, 返回 (avg_ms, min_ms, max_ms)。"""
45
- times = []
46
- for _ in range(repeat):
47
- t0 = time.perf_counter()
48
- fn()
49
- times.append((time.perf_counter() - t0) * 1000)
50
- return sum(times) / len(times), min(times), max(times)
51
-
52
-
53
- def bench_search():
54
- """搜索基准: 冷启动 + 热搜索对比。"""
55
- results = {}
56
-
57
- # 1. JSON 加载 (冷)
58
- ki_path = os.path.join(INDEX_DIR, 'keyword-index.json')
59
- def load_ki():
60
- with open(ki_path, encoding='utf-8') as f:
61
- return json.load(f)
62
- results['index_load_ms'] = measure(load_ki)[0]
63
- ki = load_ki()
64
- results['keyword_count'] = len(ki)
65
-
66
- # 2. 全扫描 (旧逻辑)
67
- from search_index import _match_keyword
68
- queries = ['insurance', 'attendance', 'salary', 'vehicle', 'user']
69
- def full_scan():
70
- for q in queries:
71
- for kw in ki:
72
- if _match_keyword(q, kw):
73
- pass
74
- results['full_scan_5q_ms'] = measure(full_scan, repeat=5)[0] / 5
75
-
76
- # 3. 倒排索引构建 (首次)
77
- from common.search_engine import get_inverted_index, search_keywords_fast, build_inverted_index
78
- # 清缓存测构建
79
- cache_path = os.path.join(INDEX_DIR, '.inverted-cache.json')
80
- if os.path.exists(cache_path):
81
- os.remove(cache_path)
82
- results['inverted_build_ms'] = measure(lambda: get_inverted_index(BASE))[0]
83
-
84
- # 4. 缓存加载 (热)
85
- results['inverted_cache_load_ms'] = measure(lambda: get_inverted_index(BASE), repeat=5)[0]
86
-
87
- # 5. 倒排搜索 (热)
88
- inverted = get_inverted_index(BASE)
89
- def inverted_search():
90
- for q in queries:
91
- search_keywords_fast([q], inverted, ki)
92
- results['inverted_scan_5q_ms'] = measure(inverted_search, repeat=5)[0] / 5
93
-
94
- # 6. 提速比
95
- if results['inverted_scan_5q_ms'] > 0:
96
- results['speedup'] = round(results['full_scan_5q_ms'] / results['inverted_scan_5q_ms'], 1)
97
- else:
98
- results['speedup'] = float('inf')
99
-
100
- return results
101
-
102
-
103
- def bench_cnmap():
104
- """CN_MAP 查询基准。"""
105
- from common.terms import CN_MAP, BUSINESS_PATH_MAP, get_cn_map_with_auto
106
- results = {}
107
- results['cn_map_static_count'] = len(CN_MAP)
108
- results['business_path_map_count'] = len(BUSINESS_PATH_MAP)
109
-
110
- # auto-expand (含 PRD 扫描)
111
- def auto_expand():
112
- return get_cn_map_with_auto()
113
- results['auto_expand_ms'] = measure(auto_expand)[0]
114
- results['cn_map_total_count'] = len(get_cn_map_with_auto())
115
-
116
- # 查询扩展
117
- test_terms = ['考勤', '保险', '库存', '采购', '发票']
118
- def expand_query():
119
- out = []
120
- for t in test_terms:
121
- if t in CN_MAP:
122
- out.extend(CN_MAP[t].split())
123
- return out
124
- results['query_expand_5q_ms'] = measure(expand_query, repeat=10)[0] / 10
125
-
126
- return results
127
-
128
-
129
- def bench_index_sizes():
130
- """索引文件大小。"""
131
- results = {}
132
- for name in ['keyword-index.json', 'api-index.json', 'style-index.json',
133
- 'prd-index.json', 'module-map.json', 'prd-code-map.json']:
134
- p = os.path.join(INDEX_DIR, name)
135
- if os.path.isfile(p):
136
- results[name] = os.path.getsize(p)
137
- # 缓存文件
138
- for name in ['.inverted-cache.json', '.file-keys.json']:
139
- p = os.path.join(INDEX_DIR, name)
140
- if os.path.isfile(p):
141
- results[name] = os.path.getsize(p)
142
- return results
143
-
144
-
145
- def main():
146
- parser = argparse.ArgumentParser(description='QODER 性能基准')
147
- parser.add_argument('--search', action='store_true', help='只测搜索')
148
- parser.add_argument('--build', action='store_true', help='测构建 (耗时)')
149
- parser.add_argument('--json', action='store_true', help='JSON 输出')
150
- args = parser.parse_args()
151
-
152
- report = {
153
- 'timestamp': datetime.now().isoformat(timespec='seconds'),
154
- 'python': sys.version.split()[0],
155
- 'platform': sys.platform,
156
- 'base': os.path.basename(BASE),
157
- }
158
-
159
- if not args.build:
160
- print('=' * 50)
161
- print('QODER 性能基准')
162
- print('=' * 50)
163
-
164
- if not args.build or args.search:
165
- if not args.json:
166
- print('\n--- 搜索性能 ---')
167
- sr = bench_search()
168
- report['search'] = sr
169
- if not args.json:
170
- print(' keyword-index 加载: %.0f ms (%d keys)' % (sr['index_load_ms'], sr['keyword_count']))
171
- print(' 全扫描 (5 query): %.2f ms' % sr['full_scan_5q_ms'])
172
- print(' 倒排索引构建: %.0f ms (首次)' % sr['inverted_build_ms'])
173
- print(' 缓存加载: %.2f ms ()' % sr['inverted_cache_load_ms'])
174
- print(' 倒排搜索 (5 query): %.2f ms' % sr['inverted_scan_5q_ms'])
175
- print(' 提速: %.1fx' % sr['speedup'])
176
-
177
- if not args.json:
178
- print('\n--- CN_MAP ---')
179
- cr = bench_cnmap()
180
- report['cnmap'] = cr
181
- if not args.json:
182
- print(' 静态词: %d, 路径映射: %d' % (cr['cn_map_static_count'], cr['business_path_map_count']))
183
- print(' auto-expand: %.0f ms, 总计 %d' % (cr['auto_expand_ms'], cr['cn_map_total_count']))
184
-
185
- if not args.build:
186
- if not args.json:
187
- print('\n--- 索引大小 ---')
188
- sz = bench_index_sizes()
189
- report['index_sizes'] = sz
190
- if not args.json:
191
- for name, size in sorted(sz.items(), key=lambda x: -x[1]):
192
- print(' %-30s %s' % (name, format_size(size)))
193
-
194
- if args.json:
195
- print(json.dumps(report, indent=2, ensure_ascii=False))
196
- else:
197
- print('\n' + '=' * 50)
198
-
199
-
200
- def format_size(n):
201
- for unit in ['B', 'KB', 'MB']:
202
- if n < 1024:
203
- return '%.1f %s' % (n, unit)
204
- n /= 1024
205
- return '%.1f GB' % n
206
-
207
-
208
- if __name__ == '__main__':
209
- main()
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ benchmark.py - QODER 流水线性能基准 (性能优化 A7)
5
+
6
+ 测量关键操作的延迟和内存, 供优化前后对比。
7
+
8
+ Usage:
9
+ python benchmark.py # 跑全部基准
10
+ python benchmark.py --search # 只测搜索
11
+ python benchmark.py --build # 只测构建 (耗时)
12
+ python benchmark.py --json # 输出 JSON (供对比)
13
+
14
+ 指标:
15
+ - search_cold: 冷启动搜索 (Python 启动 + JSON 加载 + 扫描)
16
+ - search_warm: 热搜索 (倒排索引缓存命中)
17
+ - index_load: code-keyword.json 加载时间
18
+ - inverted_build: 倒排索引构建时间
19
+ - inverted_cache_load: 缓存加载时间
20
+ - cnmap_lookup: CN_MAP 查询 (含 auto-expand)
21
+ """
22
+
23
+ import argparse
24
+ from common.paths import BUILD_CACHE_DIR
25
+ import json
26
+ import os
27
+ import sys
28
+ import time
29
+ from datetime import datetime
30
+
31
+ try:
32
+ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
33
+ except (AttributeError, TypeError, OSError):
34
+ pass
35
+
36
+ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) # .qoder/scripts
37
+ sys.path.insert(0, THIS_DIR)
38
+ # BASE = repo root = scripts 的上两级 (.qoder/scripts -> .qoder -> repo)
39
+ BASE = os.path.dirname(os.path.dirname(THIS_DIR))
40
+
41
+ INDEX_DIR = os.path.join(BASE, 'data', 'index')
42
+
43
+
44
+ def measure(fn, repeat=1):
45
+ """测函数执行时间, 返回 (avg_ms, min_ms, max_ms)。"""
46
+ times = []
47
+ for _ in range(repeat):
48
+ t0 = time.perf_counter()
49
+ fn()
50
+ times.append((time.perf_counter() - t0) * 1000)
51
+ return sum(times) / len(times), min(times), max(times)
52
+
53
+
54
+ def bench_search():
55
+ """搜索基准: 冷启动 + 热搜索对比。"""
56
+ results = {}
57
+
58
+ # 1. JSON 加载 ()
59
+ ki_path = os.path.join(BUILD_CACHE_DIR, 'code-keyword.json')
60
+ def load_ki():
61
+ with open(ki_path, encoding='utf-8') as f:
62
+ return json.load(f)
63
+ results['index_load_ms'] = measure(load_ki)[0]
64
+ ki = load_ki()
65
+ results['keyword_count'] = len(ki)
66
+
67
+ # 2. 全扫描 (旧逻辑)
68
+ from search_index import _match_keyword
69
+ queries = ['insurance', 'attendance', 'salary', 'vehicle', 'user']
70
+ def full_scan():
71
+ for q in queries:
72
+ for kw in ki:
73
+ if _match_keyword(q, kw):
74
+ pass
75
+ results['full_scan_5q_ms'] = measure(full_scan, repeat=5)[0] / 5
76
+
77
+ # 3. 倒排索引构建 (首次)
78
+ from common.search_engine import get_inverted_index, search_keywords_fast, build_inverted_index
79
+ # 清缓存测构建
80
+ cache_path = os.path.join(INDEX_DIR, '.inverted-cache.json')
81
+ if os.path.exists(cache_path):
82
+ os.remove(cache_path)
83
+ results['inverted_build_ms'] = measure(lambda: get_inverted_index(BASE))[0]
84
+
85
+ # 4. 缓存加载 ()
86
+ results['inverted_cache_load_ms'] = measure(lambda: get_inverted_index(BASE), repeat=5)[0]
87
+
88
+ # 5. 倒排搜索 ()
89
+ inverted = get_inverted_index(BASE)
90
+ def inverted_search():
91
+ for q in queries:
92
+ search_keywords_fast([q], inverted, ki)
93
+ results['inverted_scan_5q_ms'] = measure(inverted_search, repeat=5)[0] / 5
94
+
95
+ # 6. 提速比
96
+ if results['inverted_scan_5q_ms'] > 0:
97
+ results['speedup'] = round(results['full_scan_5q_ms'] / results['inverted_scan_5q_ms'], 1)
98
+ else:
99
+ results['speedup'] = float('inf')
100
+
101
+ return results
102
+
103
+
104
+ def bench_cnmap():
105
+ """CN_MAP 查询基准。"""
106
+ from common.terms import CN_MAP, BUSINESS_PATH_MAP, get_cn_map_with_auto
107
+ results = {}
108
+ results['cn_map_static_count'] = len(CN_MAP)
109
+ results['business_path_map_count'] = len(BUSINESS_PATH_MAP)
110
+
111
+ # auto-expand (含 PRD 扫描)
112
+ def auto_expand():
113
+ return get_cn_map_with_auto()
114
+ results['auto_expand_ms'] = measure(auto_expand)[0]
115
+ results['cn_map_total_count'] = len(get_cn_map_with_auto())
116
+
117
+ # 查询扩展
118
+ test_terms = ['考勤', '保险', '库存', '采购', '发票']
119
+ def expand_query():
120
+ out = []
121
+ for t in test_terms:
122
+ if t in CN_MAP:
123
+ out.extend(CN_MAP[t].split())
124
+ return out
125
+ results['query_expand_5q_ms'] = measure(expand_query, repeat=10)[0] / 10
126
+
127
+ return results
128
+
129
+
130
+ def bench_index_sizes():
131
+ """索引文件大小。"""
132
+ results = {}
133
+ for name in ['code-keyword.json', 'code-api.json', 'ui-style.json',
134
+ 'prd-index.json', 'code-stats.json', 'prd-code-map.json']:
135
+ p = os.path.join(INDEX_DIR, name)
136
+ if os.path.isfile(p):
137
+ results[name] = os.path.getsize(p)
138
+ # 缓存文件
139
+ for name in ['.inverted-cache.json', '.file-keys.json']:
140
+ p = os.path.join(INDEX_DIR, name)
141
+ if os.path.isfile(p):
142
+ results[name] = os.path.getsize(p)
143
+ return results
144
+
145
+
146
+ def main():
147
+ parser = argparse.ArgumentParser(description='QODER 性能基准')
148
+ parser.add_argument('--search', action='store_true', help='只测搜索')
149
+ parser.add_argument('--build', action='store_true', help='测构建 (耗时)')
150
+ parser.add_argument('--json', action='store_true', help='JSON 输出')
151
+ args = parser.parse_args()
152
+
153
+ report = {
154
+ 'timestamp': datetime.now().isoformat(timespec='seconds'),
155
+ 'python': sys.version.split()[0],
156
+ 'platform': sys.platform,
157
+ 'base': os.path.basename(BASE),
158
+ }
159
+
160
+ if not args.build:
161
+ print('=' * 50)
162
+ print('QODER 性能基准')
163
+ print('=' * 50)
164
+
165
+ if not args.build or args.search:
166
+ if not args.json:
167
+ print('\n--- 搜索性能 ---')
168
+ sr = bench_search()
169
+ report['search'] = sr
170
+ if not args.json:
171
+ print(' keyword-index 加载: %.0f ms (%d keys)' % (sr['index_load_ms'], sr['keyword_count']))
172
+ print(' 全扫描 (5 query): %.2f ms' % sr['full_scan_5q_ms'])
173
+ print(' 倒排索引构建: %.0f ms (首次)' % sr['inverted_build_ms'])
174
+ print(' 缓存加载: %.2f ms (热)' % sr['inverted_cache_load_ms'])
175
+ print(' 倒排搜索 (5 query): %.2f ms' % sr['inverted_scan_5q_ms'])
176
+ print(' 提速: %.1fx' % sr['speedup'])
177
+
178
+ if not args.json:
179
+ print('\n--- CN_MAP ---')
180
+ cr = bench_cnmap()
181
+ report['cnmap'] = cr
182
+ if not args.json:
183
+ print(' 静态词: %d, 路径映射: %d' % (cr['cn_map_static_count'], cr['business_path_map_count']))
184
+ print(' auto-expand: %.0f ms, 总计 %d 词' % (cr['auto_expand_ms'], cr['cn_map_total_count']))
185
+
186
+ if not args.build:
187
+ if not args.json:
188
+ print('\n--- 索引大小 ---')
189
+ sz = bench_index_sizes()
190
+ report['index_sizes'] = sz
191
+ if not args.json:
192
+ for name, size in sorted(sz.items(), key=lambda x: -x[1]):
193
+ print(' %-30s %s' % (name, format_size(size)))
194
+
195
+ if args.json:
196
+ print(json.dumps(report, indent=2, ensure_ascii=False))
197
+ else:
198
+ print('\n' + '=' * 50)
199
+
200
+
201
+ def format_size(n):
202
+ for unit in ['B', 'KB', 'MB']:
203
+ if n < 1024:
204
+ return '%.1f %s' % (n, unit)
205
+ n /= 1024
206
+ return '%.1f GB' % n
207
+
208
+
209
+ if __name__ == '__main__':
210
+ main()