@hupan56/wlkj 3.3.7 → 3.3.9

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 (120) hide show
  1. package/package.json +1 -1
  2. package/templates/qoder/commands/optional/wl-spec.md +12 -2
  3. package/templates/qoder/commands/optional/wl-status.md +8 -0
  4. package/templates/qoder/commands/wl-code.md +8 -0
  5. package/templates/qoder/commands/wl-commit.md +11 -1
  6. package/templates/qoder/commands/wl-data.md +46 -0
  7. package/templates/qoder/commands/wl-fix.md +47 -0
  8. package/templates/qoder/commands/wl-init.md +2 -2
  9. package/templates/qoder/commands/wl-knowledge.md +41 -0
  10. package/templates/qoder/commands/wl-prd.md +23 -22
  11. package/templates/qoder/commands/wl-review.md +49 -0
  12. package/templates/qoder/commands/wl-search.md +23 -27
  13. package/templates/qoder/commands/wl-task.md +11 -1
  14. package/templates/qoder/commands/wl-test.md +8 -0
  15. package/templates/qoder/contracts/insight.md +55 -0
  16. package/templates/qoder/hooks/__pycache__/pre-tool-use-commit.cpython-39.pyc +0 -0
  17. package/templates/qoder/hooks/__pycache__/pre-tool-use.cpython-39.pyc +0 -0
  18. package/templates/qoder/hooks/__pycache__/session-start.cpython-39.pyc +0 -0
  19. package/templates/qoder/hooks/__pycache__/stop-eval.cpython-39.pyc +0 -0
  20. package/templates/qoder/hooks/pre-tool-use-commit.py +124 -0
  21. package/templates/qoder/hooks/session-start.py +20 -1
  22. package/templates/qoder/hooks/stop-eval.py +129 -0
  23. package/templates/qoder/scripts/capability/adapters/mcp.py +1 -0
  24. package/templates/qoder/scripts/capability/registry.py +0 -3
  25. package/templates/qoder/scripts/domain/integration/return_to_platform.py +1 -0
  26. package/templates/qoder/scripts/domain/integration/spec_upload.py +3 -3
  27. package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +12 -12
  28. package/templates/qoder/scripts/domain/kg/build/build_relations.py +12 -12
  29. package/templates/qoder/scripts/foundation/__pycache__/__init__.cpython-39.pyc +0 -0
  30. package/templates/qoder/scripts/foundation/io/__pycache__/__init__.cpython-39.pyc +0 -0
  31. package/templates/qoder/scripts/foundation/io/__pycache__/context_cache.cpython-39.pyc +0 -0
  32. package/templates/qoder/scripts/foundation/io/context_cache.py +94 -0
  33. package/templates/qoder/scripts/orchestration/__pycache__/wlkj.cpython-39.pyc +0 -0
  34. package/templates/qoder/scripts/orchestration/wlkj.py +95 -0
  35. package/templates/qoder/scripts/tool_guide.md +70 -0
  36. package/templates/qoder/scripts/validation/eval/__pycache__/alignment_matrix.cpython-39.pyc +0 -0
  37. package/templates/qoder/scripts/validation/eval/__pycache__/bf2_content_fidelity.cpython-39.pyc +0 -0
  38. package/templates/qoder/scripts/validation/eval/__pycache__/bf2_llmjudge.cpython-39.pyc +0 -0
  39. package/templates/qoder/scripts/validation/eval/__pycache__/bf_score.cpython-39.pyc +0 -0
  40. package/templates/qoder/scripts/validation/eval/__pycache__/code_flywheel.cpython-39.pyc +0 -0
  41. package/templates/qoder/scripts/validation/eval/__pycache__/dispatcher_ab.cpython-39.pyc +0 -0
  42. package/templates/qoder/scripts/validation/eval/__pycache__/feature_fidelity_flywheel.cpython-39.pyc +0 -0
  43. package/templates/qoder/scripts/validation/eval/__pycache__/gradient_matrix.cpython-39.pyc +0 -0
  44. package/templates/qoder/scripts/validation/eval/__pycache__/metrics_dashboard.cpython-39.pyc +0 -0
  45. package/templates/qoder/scripts/validation/eval/__pycache__/multi_turn_flywheel.cpython-39.pyc +0 -0
  46. package/templates/qoder/scripts/validation/eval/__pycache__/prd_fidelity_flywheel.cpython-39.pyc +0 -0
  47. package/templates/qoder/scripts/validation/eval/__pycache__/prd_flywheel.cpython-39.pyc +0 -0
  48. package/templates/qoder/scripts/validation/eval/__pycache__/prototype_fidelity_flywheel.cpython-39.pyc +0 -0
  49. package/templates/qoder/scripts/validation/eval/__pycache__/recall_flywheel.cpython-39.pyc +0 -0
  50. package/templates/qoder/scripts/validation/eval/__pycache__/robustness_flywheel.cpython-39.pyc +0 -0
  51. package/templates/qoder/scripts/validation/eval/__pycache__/speed_accuracy_flywheel.cpython-39.pyc +0 -0
  52. package/templates/qoder/scripts/validation/eval/__pycache__/task_flywheel.cpython-39.pyc +0 -0
  53. package/templates/qoder/scripts/validation/eval/__pycache__/token_flywheel.cpython-39.pyc +0 -0
  54. package/templates/qoder/scripts/validation/eval/alignment_matrix.py +176 -0
  55. package/templates/qoder/scripts/validation/eval/bf2_content_fidelity.py +110 -0
  56. package/templates/qoder/scripts/validation/eval/bf2_llmjudge.py +104 -0
  57. package/templates/qoder/scripts/validation/eval/bf_score.py +218 -0
  58. package/templates/qoder/scripts/validation/eval/code_flywheel.py +150 -0
  59. package/templates/qoder/scripts/validation/eval/dispatcher_ab.py +156 -0
  60. package/templates/qoder/scripts/validation/eval/dispatcher_ab_2026-07-21.json +23 -0
  61. package/templates/qoder/scripts/validation/eval/feature_fidelity_flywheel.py +143 -0
  62. package/templates/qoder/scripts/validation/eval/gradient_matrix.py +261 -0
  63. package/templates/qoder/scripts/validation/eval/gradient_matrix_baseline_2026-07-21.json +33 -0
  64. package/templates/qoder/scripts/validation/eval/metrics_dashboard.py +105 -0
  65. package/templates/qoder/scripts/validation/eval/multi_turn_flywheel.py +118 -0
  66. package/templates/qoder/scripts/validation/eval/prd_fidelity_flywheel.py +128 -0
  67. package/templates/qoder/scripts/validation/eval/prd_flywheel.py +148 -0
  68. package/templates/qoder/scripts/validation/eval/prototype_fidelity_flywheel.py +166 -0
  69. package/templates/qoder/scripts/validation/eval/recall_flywheel.py +148 -0
  70. package/templates/qoder/scripts/validation/eval/robustness_flywheel.py +139 -0
  71. package/templates/qoder/scripts/validation/eval/speed_accuracy_flywheel.py +188 -0
  72. package/templates/qoder/scripts/validation/eval/task_flywheel.py +124 -0
  73. package/templates/qoder/scripts/validation/eval/token_flywheel.py +88 -0
  74. package/templates/qoder/scripts/validation/metrics/__pycache__/lint_cases.cpython-39.pyc +0 -0
  75. package/templates/qoder/scripts/validation/metrics/eval_code_ac.py +177 -0
  76. package/templates/qoder/scripts/validation/metrics/lint_cases.py +170 -0
  77. package/templates/qoder/scripts/validation/test/__pycache__/test_context_cache.cpython-39.pyc +0 -0
  78. package/templates/qoder/scripts/validation/test/test_context_cache.py +78 -0
  79. package/templates/qoder/scripts/validation/test/test_lint_cases.py +60 -0
  80. package/templates/qoder/scripts/validation/test/test_pre_tool_use_commit.py +70 -0
  81. package/templates/qoder/settings.json +8 -0
  82. package/templates/qoder/skills/wl-spec/SKILL.md +1 -1
  83. package/templates/workspace/specs/REQ-TEST-CTX/.context-cache.json +1 -0
  84. package/templates/qoder/agents/design-agent.md +0 -20
  85. package/templates/qoder/agents/spec-generator.md +0 -21
  86. package/templates/qoder/scripts/capability/smoke_test_report.json.new +0 -94
  87. package/templates/qoder/scripts/domain/kg/extract/asset/__init__.py +0 -10
  88. package/templates/qoder/scripts/domain/kg/extract/asset/asset_tree.py +0 -57
  89. package/templates/qoder/scripts/domain/kg/extract/asset/discussion_importer.py +0 -62
  90. package/templates/qoder/scripts/domain/kg/extract/asset/prd_importer.py +0 -146
  91. package/templates/qoder/scripts/domain/kg/extract/asset/prototype_importer.py +0 -64
  92. package/templates/qoder/scripts/domain/kg/extract/asset/returns_importer.py +0 -52
  93. package/templates/qoder/scripts/domain/kg/extract/build_goal3.py +0 -104
  94. package/templates/qoder/scripts/domain/kg/extract/build_goal4.py +0 -55
  95. package/templates/qoder/scripts/domain/kg/extract/build_goal5.py +0 -95
  96. package/templates/qoder/scripts/domain/kg/extract/db/__init__.py +0 -8
  97. package/templates/qoder/scripts/domain/kg/extract/db/data_profile.py +0 -22
  98. package/templates/qoder/scripts/domain/kg/extract/db/fk_extractor.py +0 -55
  99. package/templates/qoder/scripts/domain/kg/extract/db/schema_extractor.py +0 -90
  100. package/templates/qoder/scripts/domain/kg/extract/inference/__init__.py +0 -9
  101. package/templates/qoder/scripts/domain/kg/extract/inference/community_summarizer.py +0 -206
  102. package/templates/qoder/scripts/domain/kg/extract/inference/embed_builder.py +0 -132
  103. package/templates/qoder/scripts/domain/kg/extract/inference/naming_matcher.py +0 -80
  104. package/templates/qoder/scripts/domain/kg/extract/inference/promote.py +0 -59
  105. package/templates/qoder/scripts/domain/kg/extract/inference/recompute.py +0 -93
  106. package/templates/qoder/scripts/domain/kg/extract/inference/weak_link.py +0 -421
  107. package/templates/qoder/scripts/domain/kg/extract/mybatis/__init__.py +0 -9
  108. package/templates/qoder/scripts/domain/kg/extract/mybatis/all.py +0 -79
  109. package/templates/qoder/scripts/domain/kg/extract/mybatis/mapper_parser.py +0 -99
  110. package/templates/qoder/scripts/domain/kg/extract/mybatis/relation_builder.py +0 -69
  111. package/templates/qoder/scripts/domain/kg/extract/mybatis/sql_extractor.py +0 -78
  112. package/templates/qoder/scripts/domain/kg/extract/prd/__init__.py +0 -8
  113. package/templates/qoder/scripts/domain/kg/extract/prd/prd_chunk_embed.py +0 -105
  114. package/templates/qoder/scripts/domain/kg/extract/prd/prd_llm_extract.py +0 -153
  115. package/templates/qoder/scripts/domain/kg/extract/prd/req_anchor.py +0 -120
  116. package/templates/qoder/scripts/domain/kg/graph/kg_link_db.py +0 -235
  117. package/templates/qoder/scripts/domain/kg/search/enrich_prompt.py +0 -238
  118. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +0 -197
  119. package/templates/qoder/scripts/domain/kg/sync_repowiki.py +0 -109
  120. package/templates/qoder/scripts/engine/poller.py +0 -219
@@ -1,197 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
3
- import os as _o, sys as _s
4
- _f = _o.path.abspath(__file__)
5
- for _ in range(10):
6
- _f = _o.path.dirname(_f)
7
- _cp = _o.path.join(_f, 'foundation')
8
- if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
9
- break
10
- if _cp not in _s.path: _s.path.insert(0, _cp)
11
- from bootstrap import setup; setup()
12
- from foundation.core.paths import get_repo_root
13
-
14
- """
15
- perf_bench.py — 知识图谱关键路径性能基准
16
-
17
- 诊断"用了工作流反而更慢"的根因, 并验证常驻化(daemon)加速层的效果。
18
-
19
- 两种测量模式:
20
- 1. 冷启动 (subprocess 跑 kg.py) — 模拟真实用户体验: 起进程→import→连库→跑
21
- 2. daemon 热路径 (kgd_client) — daemon 常驻时的 IPC 调用, 模型/连接已就绪
22
-
23
- 用法:
24
- python perf_bench.py # 默认: 测冷启动 (--no-daemon)
25
- python perf_bench.py --with-daemon # 同时测 daemon 热路径 (需先 kgd.py start)
26
- python perf_bench.py --quick # 跳过 semantic (26s), 只测轻量路径
27
- """
28
- import io
29
- import os
30
- import subprocess
31
- import sys
32
- import time
33
-
34
- # UTF-8 输出 (Windows 终端默认 GBK 会乱码)
35
- try:
36
- sys.stdout.reconfigure(encoding='utf-8', errors='replace')
37
- except Exception:
38
- pass
39
-
40
- KG_PY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kg.py')
41
- REPO_ROOT = get_repo_root()
42
-
43
- # ── 测试用例 (覆盖四层) ──────────────────────────────────────────────
44
- # (名称, kg子命令, 参数列表, 预期层)
45
- CASES = [
46
- ('search-考勤(web)', 'search', ['考勤', '--platform', 'web'], '结构层'),
47
- ('search-车辆(web)', 'search', ['车辆', '--platform', 'web'], '结构层'),
48
- ('search-报销(app)', 'search', ['报销', '--platform', 'app'], '结构层'),
49
- ('api-asset', 'api', ['asset'], '结构层'),
50
- ('context_pack-车辆', 'context', ['车辆', '--platform', 'web'], '①③+设计'),
51
- ('impact-/asset', 'impact', ['/asset'], '图谱层'),
52
- ('feature-资产管理', 'feature', ['资产管理'], '图谱层'),
53
- ('semantic-报销', 'semantic', ['报销'], '向量层'), # 26s 慢王
54
- ]
55
-
56
-
57
- def _run_kg(args, use_daemon=True, timeout=120):
58
- """跑一次 kg.py 子命令, 返回 (耗时秒, 返回码)。
59
-
60
- use_daemon=True : 走默认 (kg.py 内部会优先 daemon)
61
- use_daemon=False : 加 --no-daemon 强制冷路径
62
- """
63
- cmd = [sys.executable, KG_PY] + args
64
- if not use_daemon:
65
- cmd.append('--no-daemon')
66
- env = dict(os.environ)
67
- env['PYTHONIOENCODING'] = 'utf-8'
68
- t0 = time.time()
69
- try:
70
- # stdout 重定向到 DEVNULL, 只测耗时, 不看内容
71
- r = subprocess.run(cmd, stdout=subprocess.DEVNULL,
72
- stderr=subprocess.DEVNULL, cwd=REPO_ROOT,
73
- env=env, timeout=timeout)
74
- return time.time() - t0, r.returncode
75
- except subprocess.TimeoutExpired:
76
- return float(timeout), -1
77
-
78
-
79
- def _median(samples):
80
- """取中位数 (抗抖动)。"""
81
- s = sorted(samples)
82
- n = len(s)
83
- return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2.0
84
-
85
-
86
- def bench_case(name, cmd, args, cold=True, daemon=False, repeats=3):
87
- """跑单个用例, 返回 (中位数耗时, 状态)。
88
-
89
- cold=True : 测冷启动 (--no-daemon)
90
- daemon=True: 测 daemon 热路径 (不加 --no-daemon, 但需 daemon 在线)
91
- """
92
- if cold and daemon:
93
- raise ValueError('cold 和 daemon 互斥')
94
- # 重量级命令 (semantic 冷启动 ~27s) 只跑 1 次, 避免基准脚本本身过慢
95
- if cmd == 'semantic' and cold:
96
- repeats = 1
97
- use_daemon = daemon
98
- samples = []
99
- status = 'ok'
100
- for _ in range(repeats):
101
- elapsed, rc = _run_kg([cmd] + args, use_daemon=use_daemon)
102
- samples.append(elapsed)
103
- if rc != 0:
104
- status = 'rc=%d' % rc
105
- return _median(samples), status
106
-
107
-
108
- def daemon_online():
109
- """探测 daemon 是否在线 (通过 kg.py status, 1s 超时)。"""
110
- try:
111
- from kgd import kgd_client
112
- return kgd_client.ping(timeout=1.0)
113
- except Exception:
114
- return False
115
-
116
-
117
- def fmt_speedup(cold_s, hot_s):
118
- """格式化加速比。"""
119
- if hot_s <= 0.001:
120
- return '-'
121
- return '%.0fx' % (cold_s / hot_s)
122
-
123
-
124
- def main():
125
- args = sys.argv[1:]
126
- quick = '--quick' in args
127
- want_daemon = '--with-daemon' in args
128
-
129
- print('=' * 72)
130
- print('知识图谱性能基准 (repo: %s)' % os.path.basename(REPO_ROOT))
131
- print('=' * 72)
132
-
133
- if quick:
134
- print('[quick 模式] 跳过 semantic (向量层 26s), 只测轻量路径\n')
135
- cases = [c for c in CASES if c[1] != 'semantic']
136
- else:
137
- cases = CASES
138
-
139
- cold_results = [] # [(name, secs, status, layer)]
140
- hot_results = {} # name -> (secs, status)
141
-
142
- # ── 1. 冷启动基线 (真实用户体验: 每次起一个进程) ──
143
- print('[1/2] 冷启动基线 (--no-daemon, 模拟现状) ...')
144
- for name, cmd, cargs, layer in cases:
145
- secs, status = bench_case(name, cmd, cargs, cold=True)
146
- cold_results.append((name, secs, status, layer))
147
- marker = ' ' if status == 'ok' else '! '
148
- print(' %s%-22s %6.2fs [%s] %s' % (marker, name, secs, layer, status))
149
- print()
150
-
151
- # ── 2. daemon 热路径 (常驻加速层) ──
152
- if want_daemon:
153
- online = daemon_online()
154
- if not online:
155
- print('[2/2] daemon 未在线 — 跳过热路径测试。')
156
- print(' (先启动: python .qoder/scripts/domain/kg/server/kgd.py start)')
157
- else:
158
- print('[2/2] daemon 在线, 测热路径 ...')
159
- for name, cmd, cargs, layer in cases:
160
- secs, status = bench_case(name, cmd, cargs, cold=False, daemon=True)
161
- hot_results[name] = (secs, status)
162
- marker = ' ' if status == 'ok' else '! '
163
- print(' %s%-22s %6.2fs [%s] %s' % (marker, name, secs, layer, status))
164
- print()
165
-
166
- # ── 汇总对比表 ──
167
- print('-' * 72)
168
- print('%-22s %10s %10s %10s' % ('用例', '冷启动', 'daemon', '加速'))
169
- print('%-22s %10s %10s %10s' % ('─' * 18, '─' * 8, '─' * 8, '─' * 6))
170
- for name, cold_s, status, layer in cold_results:
171
- hot_s = hot_results.get(name, (None, None))[0]
172
- hot_str = '%.2fs' % hot_s if hot_s else '-'
173
- speed = fmt_speedup(cold_s, hot_s) if hot_s else '-'
174
- print('%-22s %8.2fs %10s %10s' % (name, cold_s, hot_str, speed))
175
- print('-' * 72)
176
-
177
- # ── 诊断结论 ──
178
- print('\n诊断:')
179
- semantic_s = next((s for n, s, _, _ in cold_results if 'semantic' in n), None)
180
- if semantic_s and semantic_s > 10:
181
- print(' 🔴 semantic %.1fs — 模型冷加载占 99%%, 是头号慢源' % semantic_s)
182
- print(' 解法: kgd.py start 常驻模型 → <0.5s')
183
- elif semantic_s:
184
- print(' 🟢 semantic %.1fs — 在可接受范围' % semantic_s)
185
-
186
- total_cold = sum(s for _, s, _, _ in cold_results)
187
- print(' 冷启动累积: %.1fs (一次 PRD 流程的隐性等待)' % total_cold)
188
- if hot_results:
189
- total_hot = sum(hot_results[n][0] for n in hot_results)
190
- print(' daemon 累积: %.1fs → 整体提速 %.0fx' %
191
- (total_hot, total_cold / max(total_hot, 0.01)))
192
-
193
- return 0
194
-
195
-
196
- if __name__ == '__main__':
197
- sys.exit(main())
@@ -1,109 +0,0 @@
1
- #!/usr/bin/env python
2
- """同步 Qoder Repo Wiki → 平台 PG wiki 表。
3
-
4
- Qoder 自动为代码仓库生成 Repo Wiki(750+篇业务文档),存在:
5
- data/code/{repo}/.qoder/repowiki/knowledge/zh/**/*.md
6
-
7
- 本脚本读这些 md → UPSERT 到 PG wiki 表(repo + keyword + title + description)。
8
- 这样知识层的 wiki 搜索(graphrag_ask/rag_search)能命中 Qoder 自动生成的高质量文档。
9
-
10
- 用法:
11
- python sync_repowiki.py # 同步全部仓库
12
- python sync_repowiki.py --repo fywl-ics # 只同步指定仓库
13
- python sync_repowiki.py --dry-run # 只统计不写
14
- """
15
- import os
16
- import sys
17
- import glob
18
-
19
- _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
20
- _up1 = os.path.dirname(_THIS_DIR)
21
- _up2 = os.path.dirname(_up1)
22
- SCRIPTS_DIR = os.path.dirname(_up2)
23
- REPO_ROOT = os.path.dirname(SCRIPTS_DIR)
24
- CODE_ROOT = os.path.join(REPO_ROOT, "data", "code")
25
-
26
- PG = dict(host="127.0.0.1", port=5432, user="wlinkj", password="changeme", dbname="wlinkj")
27
-
28
-
29
- def scan_repowiki(repo):
30
- """扫描某仓库的 Repo Wiki,返回 [{path, title, content, dir_path}]。"""
31
- wiki_base = os.path.join(CODE_ROOT, repo, ".qoder", "repowiki")
32
- if not os.path.isdir(wiki_base):
33
- return []
34
- results = []
35
- for md_path in glob.glob(os.path.join(wiki_base, "**", "*.md"), recursive=True):
36
- try:
37
- with open(md_path, encoding="utf-8") as f:
38
- content = f.read()
39
- # 标题:md 文件名或第一行 #
40
- title = os.path.splitext(os.path.basename(md_path))[0]
41
- for line in content.split("\n")[:5]:
42
- if line.strip().startswith("#"):
43
- title = line.strip().lstrip("#").strip()
44
- break
45
- # keyword:从目录路径提取业务模块名
46
- rel = os.path.relpath(md_path, wiki_base)
47
- parts = rel.replace("\\", "/").split("/")
48
- keyword = parts[-2] if len(parts) >= 2 else title
49
- results.append({
50
- "path": md_path,
51
- "rel_path": rel,
52
- "title": title,
53
- "keyword": keyword,
54
- "content": content[:2000], # 取前2000字
55
- })
56
- except Exception:
57
- continue
58
- return results
59
-
60
-
61
- def sync_to_pg(wiki_items, repo):
62
- """UPSERT 到 PG wiki 表。"""
63
- import psycopg2
64
- from psycopg2.extras import execute_values
65
-
66
- conn = psycopg2.connect(**PG)
67
- conn.autocommit = True
68
- cur = conn.cursor()
69
-
70
- # ★ 不删旧数据(原 wiki 表有业务文档),只删 repowiki 来源的记录
71
- # 用 md_path 前缀 'repowiki:' 标识(避免路径分隔符差异)
72
- cur.execute("DELETE FROM wiki WHERE project = %s AND md_path LIKE 'repowiki:%%'", (repo,))
73
-
74
- rows = [(w["keyword"], repo, w["title"], "repowiki:" + w["rel_path"], w["content"][:500])
75
- for w in wiki_items]
76
- if rows:
77
- execute_values(
78
- cur,
79
- "INSERT INTO wiki(keyword, project, title, md_path, description) VALUES %s",
80
- rows,
81
- )
82
-
83
- print(f"[{repo}] 同步 {len(rows)} 篇 Wiki 到 PG")
84
- conn.close()
85
-
86
-
87
- def main():
88
- import argparse
89
- parser = argparse.ArgumentParser(description="同步 Qoder Repo Wiki → PG")
90
- parser.add_argument("--repo", help="指定仓库名")
91
- parser.add_argument("--dry-run", action="store_true", help="只统计不写")
92
- args = parser.parse_args()
93
-
94
- repos = [args.repo] if args.repo else ["fywl-ui", "fywl-ics", "Carmg-H5"]
95
- total = 0
96
- for repo in repos:
97
- items = scan_repowiki(repo)
98
- print(f" {repo}: 扫到 {len(items)} 篇 Repo Wiki")
99
- if items:
100
- for it in items[:3]:
101
- print(f" [{it['keyword']}] {it['title']}")
102
- if not args.dry_run:
103
- sync_to_pg(items, repo)
104
- total += len(items)
105
- print(f"\n总计: {total} 篇 Wiki{'(dry-run 未写入)' if args.dry_run else ' 已同步到 PG'}")
106
-
107
-
108
- if __name__ == "__main__":
109
- main()
@@ -1,219 +0,0 @@
1
- """引擎轮询器:后台轮询平台 pending 触发 → 认领 → 执行 → 回流 → 回填结果。
2
-
3
- 这是"平台→引擎→平台"真闭环的引擎侧执行器:
4
- 1. 轮询 GET /trigger/pending(带项目令牌)
5
- 2. 认领 POST /trigger/{tid}/claim(原子锁)
6
- 3. 执行:根据 command 调 MCP 取知识 + 生成产出(简化版:rag_search + 模板拼接)
7
- 4. 回流:create_prd/submit_return
8
- 5. 回填:PATCH /trigger/{tid}(mcp_tools_called/knowledge_used)
9
-
10
- 用法:
11
- python poller.py --config ../mcp_config.json --interval 10
12
-
13
- 注:完整版需要接 LLM(qwen-plus)生成 PRD/原型。当前是简化版(rag_search + 模板),
14
- 验证闭环链路。接 LLM 后把 _execute 里的模板拼接换成 cap.mcp.call("ask_corpus",...) 生成。
15
- """
16
- import json
17
- import time
18
- import urllib.request
19
- import urllib.error
20
- import argparse
21
- import os
22
- import sys
23
- from pathlib import Path
24
-
25
-
26
- def _load_config(config_path: str) -> dict:
27
- with open(config_path, encoding="utf-8") as f:
28
- return json.load(f)
29
-
30
-
31
- def _get_token(cfg: dict) -> str:
32
- for srv in (cfg.get("mcpServers") or {}).values():
33
- return srv.get("token", "")
34
- return ""
35
-
36
-
37
- def _get_pid(cfg: dict) -> str:
38
- for srv in (cfg.get("mcpServers") or {}).values():
39
- env = srv.get("env") or {}
40
- return env.get("WLKJ_PROJECT_ID", "")
41
- return ""
42
-
43
-
44
- def _get_base(cfg: dict) -> str:
45
- ep = cfg.get("return_endpoint") or {}
46
- url = ep.get("url", "")
47
- # 从 return_endpoint.url 提取 base(/api/projects 前面那段)
48
- if "/api/" in url:
49
- return url.split("/api/")[0]
50
- for srv in (cfg.get("mcpServers") or {}).values():
51
- u = srv.get("url", "")
52
- if "/mcp" in u:
53
- return u.split("/mcp")[0]
54
- return os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120")
55
-
56
-
57
- def _api(base, method, path, token, body=None):
58
- url = base + path
59
- data = json.dumps(body).encode() if body else None
60
- req = urllib.request.Request(url, data=data, method=method)
61
- req.add_header("Content-Type", "application/json")
62
- if token:
63
- req.add_header("X-Engine-Token", token)
64
- try:
65
- with urllib.request.urlopen(req, timeout=30) as resp:
66
- return resp.status, json.loads(resp.read().decode())
67
- except urllib.error.HTTPError as e:
68
- return e.code, json.loads(e.read().decode() or "{}")
69
- except Exception as e:
70
- return 0, {"error": str(e)}
71
-
72
-
73
- def _mcp_call(base, token, tool, args):
74
- """通过 MCP JSON-RPC 调工具。"""
75
- body = {"jsonrpc": "2.0", "id": 1, "method": "tools/call",
76
- "params": {"name": tool, "arguments": args}}
77
- data = json.dumps(body).encode()
78
- req = urllib.request.Request(base + "/mcp", data=data, method="POST")
79
- req.add_header("Content-Type", "application/json")
80
- req.add_header("X-MCP-Token", token)
81
- try:
82
- with urllib.request.urlopen(req, timeout=60) as resp:
83
- r = json.loads(resp.read().decode())
84
- text = r.get("result", {}).get("content", [{}])[0].get("text", "")
85
- return json.loads(text) if text else {}
86
- except Exception as e:
87
- return {"error": str(e)}
88
-
89
-
90
- def _execute(base, token, pid, command, args):
91
- """执行触发的工作流(简化版:rag_search 取知识 + 模板拼 PRD + learn 写回)。
92
- 完整版应接 LLM(ask_corpus)生成自然语言 PRD。"""
93
- query = (args or {}).get("query", "")
94
- tools_called = []
95
- knowledge_used = []
96
-
97
- # 1) rag_search 取知识
98
- if query:
99
- r = _mcp_call(base, token, "rag_search", {"query": query, "top_k": 8})
100
- tools_called.append("rag_search")
101
- knowledge_used = [it.get("entity_id", "") for it in r.get("items", [])[:5]]
102
- knowledge_text = "\n".join(
103
- "- %s (%s)" % (it.get("text", "")[:60], it.get("entity_id", "")[:30])
104
- for it in r.get("items", [])[:8]
105
- )
106
- # 1b) get_learnings 查之前学到的(反哺)
107
- lr = _mcp_call(base, token, "get_learnings", {"project_id": pid, "limit": 5})
108
- tools_called.append("get_learnings")
109
- learnings_text = "\n".join(
110
- "- [%s] %s" % (l.get("category",""), (l.get("pattern","")[:60]))
111
- for l in (lr.get("items") or [])[:5]
112
- ) or "(暂无学习记录)"
113
- else:
114
- knowledge_text = "(无关键词)"
115
- learnings_text = ""
116
-
117
- # 2) 根据 command 生成产出
118
- if "prd" in command.lower():
119
- prd_md = f"# {query} PRD(引擎自动生成)\n\n## 概述\n基于知识层自动生成的「{query}」需求文档。\n\n## 相关知识\n{knowledge_text}\n\n## 历史学习\n{learnings_text}\n\n## 功能\n(根据上述知识补充)"
120
- # 回流
121
- r = _mcp_call(base, token, "create_prd", {
122
- "project_id": pid, "title": f"{query}PRD(引擎触发)", "content_md": prd_md, "status": "planning"
123
- })
124
- tools_called.append("create_prd")
125
- # 学习写回:把这次的关键认知沉淀
126
- _mcp_call(base, token, "learn", {"project_id": pid, "category": "rule",
127
- "pattern": f"{query}: PRD已生成,涉及知识{len(knowledge_used)}条", "source": "wlkj-poller"})
128
- tools_called.append("learn")
129
- output = f"生成PRD: {query}PRD(引擎触发) + 学习沉淀"
130
- elif "design" in command.lower():
131
- html = f"<html><body><h1>{query}</h1><p>引擎自动生成原型</p></body></html>"
132
- r = _mcp_call(base, token, "create_prototype", {
133
- "project_id": pid, "feature": f"{query}(引擎触发)", "platform": "web", "html": html
134
- })
135
- tools_called.append("create_prototype")
136
- output = f"生成原型: {query}(引擎触发)"
137
- else:
138
- # learn 写回
139
- _mcp_call(base, token, "learn", {
140
- "project_id": pid, "category": "rule", "pattern": f"{query}相关规则(引擎触发)",
141
- "source": "wlkj-poller"
142
- })
143
- tools_called.append("learn")
144
- output = f"执行命令: {command}"
145
-
146
- return {"output": output, "tools": tools_called, "knowledge": knowledge_used}
147
-
148
-
149
- def poll_once(cfg):
150
- """轮询一次:取 pending → 认领 → 执行 → 回填。"""
151
- token = _get_token(cfg)
152
- pid = _get_pid(cfg)
153
- base = _get_base(cfg)
154
- if not token or not pid:
155
- print("[poller] 配置缺 token/project_id,跳过")
156
- return
157
-
158
- # 1) 查 pending
159
- _, pending = _api(base, "GET", f"/api/projects/{pid}/trigger/pending", token)
160
- items = pending.get("pending", [])
161
- if not items:
162
- return # 无待执行
163
-
164
- for item in items[:1]: # 一次只处理 1 条
165
- tid = item.get("triggerId")
166
- cmd = item.get("command", "")
167
- args = item.get("args", {})
168
- print(f"[poller] 认领: {tid} {cmd} {args}")
169
-
170
- # 2) claim
171
- code, resp = _api(base, "POST", f"/api/projects/{pid}/trigger/workflow/{tid}/claim",
172
- token, {"engine_id": "poller-001"})
173
- if code != 200:
174
- print(f"[poller] 认领失败({code}): {resp}")
175
- continue
176
-
177
- # 3) 执行
178
- t0 = time.time()
179
- try:
180
- result = _execute(base, token, pid, cmd, args)
181
- status = "success"
182
- except Exception as e:
183
- result = {"output": str(e), "tools": [], "knowledge": []}
184
- status = "failed"
185
-
186
- # 4) patch 回填
187
- _api(base, "PATCH", f"/api/projects/{pid}/trigger/workflow/{tid}", token, {
188
- "status": status,
189
- "output_summary": result["output"],
190
- "knowledge_used": result["knowledge"],
191
- "mcp_tools_called": result["tools"],
192
- "duration_sec": int(time.time() - t0),
193
- })
194
- print(f"[poller] 完成: {status} → {result['output']} (tools={result['tools']})")
195
-
196
-
197
- def main():
198
- parser = argparse.ArgumentParser(description="wlkj 引擎轮询器")
199
- parser.add_argument("--config", default=str(Path(__file__).parent.parent.parent / "mcp_config.json"))
200
- parser.add_argument("--interval", type=int, default=10, help="轮询间隔(秒)")
201
- parser.add_argument("--once", action="store_true", help="只跑一次")
202
- args = parser.parse_args()
203
-
204
- cfg = _load_config(args.config)
205
- print(f"[poller] 启动: project={_get_pid(cfg)} interval={args.interval}s")
206
- print(f"[poller] 按 Ctrl+C 停止")
207
-
208
- while True:
209
- try:
210
- poll_once(cfg)
211
- except Exception as e:
212
- print(f"[poller] 异常: {e}")
213
- if args.once:
214
- break
215
- time.sleep(args.interval)
216
-
217
-
218
- if __name__ == "__main__":
219
- main()