@hupan56/wlkj 3.0.0 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1435 -1373
- package/package.json +1 -1
- package/templates/qoder/commands/optional/wl-insight.md +6 -4
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +5 -5
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +3 -3
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +10 -10
- package/templates/qoder/config.yaml +35 -13
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +82 -1
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +70 -8
- package/templates/qoder/scripts/README.md +78 -115
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +55 -23
- package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
- package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
- package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +35 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
- package/templates/qoder/scripts/domain/__init__.py +0 -0
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
- package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
- package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
- package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
- package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
- package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
- package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
- package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
- package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
- package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
- package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
- package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
- package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
- package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/task/__pycache__/syncgate.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
- package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
- package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
- package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
- package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
- package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
- package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/__pycache__/bootstrap.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/core/__pycache__/paths.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/identity/__pycache__/identity.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
- package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
- package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/io/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/io/__pycache__/atomicio.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/io/__pycache__/filelock.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
- package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
- package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
- package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
- package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
- package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
- package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +30 -0
- package/templates/qoder/skills/design-import/SKILL.md +22 -13
- package/templates/qoder/skills/design-review/SKILL.md +19 -10
- package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
- package/templates/qoder/skills/prd-review/SKILL.md +6 -4
- package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
- package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
- package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
- package/templates/qoder/skills/test-generator/SKILL.md +7 -5
- package/templates/qoder/skills/wl-code/SKILL.md +23 -11
- package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
- package/templates/qoder/skills/wl-design/SKILL.md +5 -3
- package/templates/qoder/skills/wl-init/SKILL.md +6 -6
- package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
- package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
- package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
- package/templates/qoder/skills/wl-report/SKILL.md +25 -23
- package/templates/qoder/skills/wl-search/SKILL.md +149 -78
- package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
- package/templates/qoder/skills/wl-status/SKILL.md +48 -16
- package/templates/qoder/skills/wl-task/SKILL.md +92 -10
- package/templates/qoder/skills/wl-test/SKILL.md +75 -29
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -236
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
- package/templates/.qoder/learning/eval-history.jsonl +0 -28
- package/templates/data/index/wiki-index.json +0 -8
- package/templates/qoder/nul +0 -4
- package/templates/qoder/scripts/kg/extract.py +0 -398
- package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
- package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
- package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
- package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
- package/templates/qoder/scripts/task/__init__.py +0 -11
- package/templates/qoder/scripts/test/__init__.py +0 -11
- package/templates/qoder/scripts/wlkj.py +0 -116
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{test → validation/test}/secure-ls.js +0 -0
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
kg_capabilities.py - 知识图谱共享查询能力 (Strategy 模式, 消除 CLI↔MCP 重复)
|
|
4
|
-
|
|
5
|
-
v3.0 f3: kg.py (CLI) 和 kg_mcp_server.py (MCP) 有近字重复的查询逻辑
|
|
6
|
-
(workflow/hop/wiki/design_system 等)。本模块提取共享查询, 两边都调它,
|
|
7
|
-
消除重复, 且保证 CLI 和 MCP 行为完全一致。
|
|
8
|
-
|
|
9
|
-
核心 API (都接收已打开的 duckdb con, 由调用方管理连接):
|
|
10
|
-
workflow_query(con, q) -> str 业务流程链查询
|
|
11
|
-
# hop_query / wiki_query / design_system_query 后续添加
|
|
12
|
-
|
|
13
|
-
设计: 查询逻辑 (数据清洗/SQL/格式化) 在此单一真源;
|
|
14
|
-
kg.py 和 kg_mcp_server 只负责"开连接 + 调本模块 + 返回"。
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
__all__ = ["workflow_query", "hop_query", "wiki_query"]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def workflow_query(con, q):
|
|
21
|
-
"""workflows 表查询 + 脏数据清洗 + 中文兜底。
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
con: 已打开的 duckdb 连接 (read_only)
|
|
25
|
-
q: 模块名 (英文 assets/cost/hr 或中文触发兜底)
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
格式化的业务流程链文本
|
|
29
|
-
"""
|
|
30
|
-
q = (q or '').strip()
|
|
31
|
-
# 1) 精确 + 模糊匹配 workflows.module(英文)
|
|
32
|
-
hits = con.execute(
|
|
33
|
-
"SELECT DISTINCT module FROM workflows WHERE module = ? OR module LIKE '%' || ? || '%' ORDER BY module",
|
|
34
|
-
[q.lower(), q.lower()]
|
|
35
|
-
).fetchall()
|
|
36
|
-
if not hits:
|
|
37
|
-
# 2) 中文兜底: 查 entities.cn 返回英文候选
|
|
38
|
-
cn_hits = con.execute(
|
|
39
|
-
"SELECT canonical, cn FROM entities WHERE type IN ('FEATURE','MODULE') AND cn <> '' AND (cn LIKE '%' || ? OR cn LIKE '%' || ? || '%')",
|
|
40
|
-
[q, q]
|
|
41
|
-
).fetchall()
|
|
42
|
-
if cn_hits:
|
|
43
|
-
cands = ', '.join('%s(%s)' % (c[0], c[1]) for c in cn_hits[:8])
|
|
44
|
-
return "未在 workflows 表找到模块 '%s'。但 entities 表有相近中文名,可换这些英文模块名重试:\n %s" % (q, cands)
|
|
45
|
-
all_mods = [r[0] for r in con.execute("SELECT DISTINCT module FROM workflows ORDER BY module").fetchall()]
|
|
46
|
-
return "未找到模块 '%s'。现有模块: %s" % (q, ', '.join(all_mods))
|
|
47
|
-
|
|
48
|
-
out = []
|
|
49
|
-
for (mod,) in hits:
|
|
50
|
-
# 清洗: 过滤空端点和模板泄漏按钮
|
|
51
|
-
rows = con.execute("""
|
|
52
|
-
SELECT state, seq, button, endpoint FROM workflows
|
|
53
|
-
WHERE module = ?
|
|
54
|
-
AND endpoint <> 'None' AND endpoint <> ''
|
|
55
|
-
AND button NOT LIKE '{{%'
|
|
56
|
-
ORDER BY seq, state
|
|
57
|
-
""", [mod]).fetchall()
|
|
58
|
-
if not rows:
|
|
59
|
-
continue
|
|
60
|
-
out.append('=== %s (%d 步, 已清洗脏数据) ===' % (mod, len(rows)))
|
|
61
|
-
cur_seq = -1
|
|
62
|
-
for st, sq, btn, ep in rows:
|
|
63
|
-
if sq != cur_seq:
|
|
64
|
-
out.append(' [%d] %s' % (sq, st))
|
|
65
|
-
cur_seq = sq
|
|
66
|
-
out.append(' %-16s -> %s' % (btn, ep))
|
|
67
|
-
out.append('')
|
|
68
|
-
return '\n'.join(out).strip() or "模块 '%s' 清洗后无有效操作链" % q
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def hop_query(con, symbol, depth, multi_hop_fn=None):
|
|
72
|
-
"""多跳遍历: 别名解析(三级兜底) + 递归CTE + 按depth分组展示。
|
|
73
|
-
|
|
74
|
-
Args:
|
|
75
|
-
con: duckdb 连接
|
|
76
|
-
symbol: 起始符号 (中文/端点/函数名, 自动别名解析)
|
|
77
|
-
depth: 最大跳数 1-5
|
|
78
|
-
multi_hop_fn: kg_duckdb.multi_hop 函数 (避免硬依赖)
|
|
79
|
-
Returns:
|
|
80
|
-
格式化的多跳遍历文本
|
|
81
|
-
"""
|
|
82
|
-
# 1) 别名解析(三级兜底)
|
|
83
|
-
start_id = symbol
|
|
84
|
-
resolved = con.execute(
|
|
85
|
-
"SELECT entity_id FROM aliases WHERE alias = ?", [symbol.lower()]
|
|
86
|
-
).fetchone()
|
|
87
|
-
if resolved:
|
|
88
|
-
start_id = resolved[0]
|
|
89
|
-
else:
|
|
90
|
-
fuzzy = con.execute(
|
|
91
|
-
"SELECT id FROM entities WHERE id = ? OR id LIKE '%' || ? || '%' LIMIT 1",
|
|
92
|
-
[symbol, symbol]
|
|
93
|
-
).fetchone()
|
|
94
|
-
if fuzzy:
|
|
95
|
-
start_id = fuzzy[0]
|
|
96
|
-
else:
|
|
97
|
-
edge_hit = con.execute(
|
|
98
|
-
"SELECT from_id FROM edges WHERE from_id LIKE '%' || ? || '%' LIMIT 1",
|
|
99
|
-
[symbol]
|
|
100
|
-
).fetchone()
|
|
101
|
-
if edge_hit:
|
|
102
|
-
start_id = edge_hit[0]
|
|
103
|
-
|
|
104
|
-
# 2) 递归 CTE 遍历
|
|
105
|
-
if multi_hop_fn is None:
|
|
106
|
-
import kg_duckdb as K
|
|
107
|
-
multi_hop_fn = K.multi_hop
|
|
108
|
-
try:
|
|
109
|
-
hops = multi_hop_fn(con, start_id, max_depth=depth)
|
|
110
|
-
except Exception as e:
|
|
111
|
-
return "遍历出错 (起点=%s): %s" % (start_id, str(e))
|
|
112
|
-
|
|
113
|
-
if not hops or (len(hops) == 1 and hops[0][1] == 0):
|
|
114
|
-
return "从 '%s' (解析为 %s) 出发无可达节点。可能该符号不在图谱中,或无出边。" % (symbol, start_id)
|
|
115
|
-
|
|
116
|
-
# 3) 按 depth 分组展示
|
|
117
|
-
from collections import defaultdict
|
|
118
|
-
by_depth = defaultdict(list)
|
|
119
|
-
for eid, d in hops:
|
|
120
|
-
by_depth[d].append(eid)
|
|
121
|
-
|
|
122
|
-
out = ['=== 多跳遍历: %s (解析为 %s) ===' % (symbol, start_id)]
|
|
123
|
-
out.append('可达 %d 个实体, 最大 %d 跳:' % (len(hops), max(by_depth.keys())))
|
|
124
|
-
out.append('')
|
|
125
|
-
for d in sorted(by_depth.keys()):
|
|
126
|
-
eids = by_depth[d]
|
|
127
|
-
out.append('[第 %d 跳] %d 个实体:' % (d, len(eids)))
|
|
128
|
-
shown = 0
|
|
129
|
-
for eid in eids:
|
|
130
|
-
if shown >= 12:
|
|
131
|
-
out.append(' ... 还有 %d 个' % (len(eids) - 12))
|
|
132
|
-
break
|
|
133
|
-
info = con.execute(
|
|
134
|
-
"SELECT type, canonical, cn FROM entities WHERE id = ?", [eid]
|
|
135
|
-
).fetchone()
|
|
136
|
-
if info:
|
|
137
|
-
t, c, cn = info
|
|
138
|
-
label = '%s / %s' % (c, cn) if cn and cn != c else c
|
|
139
|
-
out.append(' %-14s %-12s %s' % ('[%s]' % t, eid[:40], label))
|
|
140
|
-
else:
|
|
141
|
-
out.append(' %-14s %s' % ('[edge]', eid[:60]))
|
|
142
|
-
shown += 1
|
|
143
|
-
out.append('')
|
|
144
|
-
return '\n'.join(out).strip()
|
|
145
|
-
|
|
146
|
-
def wiki_query(kw):
|
|
147
|
-
"""Repo Wiki 搜索 + 格式化 (CLI/MCP 共用)。
|
|
148
|
-
|
|
149
|
-
Args:
|
|
150
|
-
kw: 搜索关键词
|
|
151
|
-
Returns:
|
|
152
|
-
格式化的 wiki 匹配结果文本
|
|
153
|
-
"""
|
|
154
|
-
import os
|
|
155
|
-
from
|
|
156
|
-
wiki_path = os.path.join(str(_idx), 'wiki-index.json')
|
|
157
|
-
if not os.path.isfile(wiki_path):
|
|
158
|
-
return ("wiki-index.json 不存在。请手动构建(一次性):\n"
|
|
159
|
-
" python .qoder/scripts/search_index.py wiki")
|
|
160
|
-
try:
|
|
161
|
-
from
|
|
162
|
-
from
|
|
163
|
-
except ImportError as e:
|
|
164
|
-
return "Wiki 搜索模块缺失: %s" % str(e)
|
|
165
|
-
code_dir = os.path.join(_base, 'data', 'code')
|
|
166
|
-
try:
|
|
167
|
-
cn_map = get_cn_map_with_auto()
|
|
168
|
-
results = search_wiki(kw, str(_idx), code_dir, cn_map)
|
|
169
|
-
except Exception as e:
|
|
170
|
-
return "Wiki 搜索出错: %s" % str(e)
|
|
171
|
-
if not results:
|
|
172
|
-
return "No wiki pages match: %s" % kw
|
|
173
|
-
out = ['Wiki pages matching: %s (%d found)' % (kw, len(results)), '']
|
|
174
|
-
for i, r in enumerate(results, 1):
|
|
175
|
-
out.append(' %d. [%s] %s' % (i, r.get('project', '?'), r.get('title', '')))
|
|
176
|
-
deps = r.get('dependent_files') or []
|
|
177
|
-
if deps:
|
|
178
|
-
out.append(' 依赖: %s%s' % (', '.join(deps[:2]),
|
|
179
|
-
(' (共 %d 个文件)' % len(deps)) if len(deps) > 2 else ''))
|
|
180
|
-
if r.get('md_path'):
|
|
181
|
-
out.append(' 文档: %s' % r['md_path'])
|
|
182
|
-
return '\n'.join(out)
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
kg_capabilities.py - 知识图谱共享查询能力 (Strategy 模式, 消除 CLI↔MCP 重复)
|
|
4
|
+
|
|
5
|
+
v3.0 f3: kg.py (CLI) 和 kg_mcp_server.py (MCP) 有近字重复的查询逻辑
|
|
6
|
+
(workflow/hop/wiki/design_system 等)。本模块提取共享查询, 两边都调它,
|
|
7
|
+
消除重复, 且保证 CLI 和 MCP 行为完全一致。
|
|
8
|
+
|
|
9
|
+
核心 API (都接收已打开的 duckdb con, 由调用方管理连接):
|
|
10
|
+
workflow_query(con, q) -> str 业务流程链查询
|
|
11
|
+
# hop_query / wiki_query / design_system_query 后续添加
|
|
12
|
+
|
|
13
|
+
设计: 查询逻辑 (数据清洗/SQL/格式化) 在此单一真源;
|
|
14
|
+
kg.py 和 kg_mcp_server 只负责"开连接 + 调本模块 + 返回"。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__all__ = ["workflow_query", "hop_query", "wiki_query"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def workflow_query(con, q):
|
|
21
|
+
"""workflows 表查询 + 脏数据清洗 + 中文兜底。
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
con: 已打开的 duckdb 连接 (read_only)
|
|
25
|
+
q: 模块名 (英文 assets/cost/hr 或中文触发兜底)
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
格式化的业务流程链文本
|
|
29
|
+
"""
|
|
30
|
+
q = (q or '').strip()
|
|
31
|
+
# 1) 精确 + 模糊匹配 workflows.module(英文)
|
|
32
|
+
hits = con.execute(
|
|
33
|
+
"SELECT DISTINCT module FROM workflows WHERE module = ? OR module LIKE '%' || ? || '%' ORDER BY module",
|
|
34
|
+
[q.lower(), q.lower()]
|
|
35
|
+
).fetchall()
|
|
36
|
+
if not hits:
|
|
37
|
+
# 2) 中文兜底: 查 entities.cn 返回英文候选
|
|
38
|
+
cn_hits = con.execute(
|
|
39
|
+
"SELECT canonical, cn FROM entities WHERE type IN ('FEATURE','MODULE') AND cn <> '' AND (cn LIKE '%' || ? OR cn LIKE '%' || ? || '%')",
|
|
40
|
+
[q, q]
|
|
41
|
+
).fetchall()
|
|
42
|
+
if cn_hits:
|
|
43
|
+
cands = ', '.join('%s(%s)' % (c[0], c[1]) for c in cn_hits[:8])
|
|
44
|
+
return "未在 workflows 表找到模块 '%s'。但 entities 表有相近中文名,可换这些英文模块名重试:\n %s" % (q, cands)
|
|
45
|
+
all_mods = [r[0] for r in con.execute("SELECT DISTINCT module FROM workflows ORDER BY module").fetchall()]
|
|
46
|
+
return "未找到模块 '%s'。现有模块: %s" % (q, ', '.join(all_mods))
|
|
47
|
+
|
|
48
|
+
out = []
|
|
49
|
+
for (mod,) in hits:
|
|
50
|
+
# 清洗: 过滤空端点和模板泄漏按钮
|
|
51
|
+
rows = con.execute("""
|
|
52
|
+
SELECT state, seq, button, endpoint FROM workflows
|
|
53
|
+
WHERE module = ?
|
|
54
|
+
AND endpoint <> 'None' AND endpoint <> ''
|
|
55
|
+
AND button NOT LIKE '{{%'
|
|
56
|
+
ORDER BY seq, state
|
|
57
|
+
""", [mod]).fetchall()
|
|
58
|
+
if not rows:
|
|
59
|
+
continue
|
|
60
|
+
out.append('=== %s (%d 步, 已清洗脏数据) ===' % (mod, len(rows)))
|
|
61
|
+
cur_seq = -1
|
|
62
|
+
for st, sq, btn, ep in rows:
|
|
63
|
+
if sq != cur_seq:
|
|
64
|
+
out.append(' [%d] %s' % (sq, st))
|
|
65
|
+
cur_seq = sq
|
|
66
|
+
out.append(' %-16s -> %s' % (btn, ep))
|
|
67
|
+
out.append('')
|
|
68
|
+
return '\n'.join(out).strip() or "模块 '%s' 清洗后无有效操作链" % q
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def hop_query(con, symbol, depth, multi_hop_fn=None):
|
|
72
|
+
"""多跳遍历: 别名解析(三级兜底) + 递归CTE + 按depth分组展示。
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
con: duckdb 连接
|
|
76
|
+
symbol: 起始符号 (中文/端点/函数名, 自动别名解析)
|
|
77
|
+
depth: 最大跳数 1-5
|
|
78
|
+
multi_hop_fn: kg_duckdb.multi_hop 函数 (避免硬依赖)
|
|
79
|
+
Returns:
|
|
80
|
+
格式化的多跳遍历文本
|
|
81
|
+
"""
|
|
82
|
+
# 1) 别名解析(三级兜底)
|
|
83
|
+
start_id = symbol
|
|
84
|
+
resolved = con.execute(
|
|
85
|
+
"SELECT entity_id FROM aliases WHERE alias = ?", [symbol.lower()]
|
|
86
|
+
).fetchone()
|
|
87
|
+
if resolved:
|
|
88
|
+
start_id = resolved[0]
|
|
89
|
+
else:
|
|
90
|
+
fuzzy = con.execute(
|
|
91
|
+
"SELECT id FROM entities WHERE id = ? OR id LIKE '%' || ? || '%' LIMIT 1",
|
|
92
|
+
[symbol, symbol]
|
|
93
|
+
).fetchone()
|
|
94
|
+
if fuzzy:
|
|
95
|
+
start_id = fuzzy[0]
|
|
96
|
+
else:
|
|
97
|
+
edge_hit = con.execute(
|
|
98
|
+
"SELECT from_id FROM edges WHERE from_id LIKE '%' || ? || '%' LIMIT 1",
|
|
99
|
+
[symbol]
|
|
100
|
+
).fetchone()
|
|
101
|
+
if edge_hit:
|
|
102
|
+
start_id = edge_hit[0]
|
|
103
|
+
|
|
104
|
+
# 2) 递归 CTE 遍历
|
|
105
|
+
if multi_hop_fn is None:
|
|
106
|
+
from domain.kg.storage import kg_duckdb as K
|
|
107
|
+
multi_hop_fn = K.multi_hop
|
|
108
|
+
try:
|
|
109
|
+
hops = multi_hop_fn(con, start_id, max_depth=depth)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
return "遍历出错 (起点=%s): %s" % (start_id, str(e))
|
|
112
|
+
|
|
113
|
+
if not hops or (len(hops) == 1 and hops[0][1] == 0):
|
|
114
|
+
return "从 '%s' (解析为 %s) 出发无可达节点。可能该符号不在图谱中,或无出边。" % (symbol, start_id)
|
|
115
|
+
|
|
116
|
+
# 3) 按 depth 分组展示
|
|
117
|
+
from collections import defaultdict
|
|
118
|
+
by_depth = defaultdict(list)
|
|
119
|
+
for eid, d in hops:
|
|
120
|
+
by_depth[d].append(eid)
|
|
121
|
+
|
|
122
|
+
out = ['=== 多跳遍历: %s (解析为 %s) ===' % (symbol, start_id)]
|
|
123
|
+
out.append('可达 %d 个实体, 最大 %d 跳:' % (len(hops), max(by_depth.keys())))
|
|
124
|
+
out.append('')
|
|
125
|
+
for d in sorted(by_depth.keys()):
|
|
126
|
+
eids = by_depth[d]
|
|
127
|
+
out.append('[第 %d 跳] %d 个实体:' % (d, len(eids)))
|
|
128
|
+
shown = 0
|
|
129
|
+
for eid in eids:
|
|
130
|
+
if shown >= 12:
|
|
131
|
+
out.append(' ... 还有 %d 个' % (len(eids) - 12))
|
|
132
|
+
break
|
|
133
|
+
info = con.execute(
|
|
134
|
+
"SELECT type, canonical, cn FROM entities WHERE id = ?", [eid]
|
|
135
|
+
).fetchone()
|
|
136
|
+
if info:
|
|
137
|
+
t, c, cn = info
|
|
138
|
+
label = '%s / %s' % (c, cn) if cn and cn != c else c
|
|
139
|
+
out.append(' %-14s %-12s %s' % ('[%s]' % t, eid[:40], label))
|
|
140
|
+
else:
|
|
141
|
+
out.append(' %-14s %s' % ('[edge]', eid[:60]))
|
|
142
|
+
shown += 1
|
|
143
|
+
out.append('')
|
|
144
|
+
return '\n'.join(out).strip()
|
|
145
|
+
|
|
146
|
+
def wiki_query(kw):
|
|
147
|
+
"""Repo Wiki 搜索 + 格式化 (CLI/MCP 共用)。
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
kw: 搜索关键词
|
|
151
|
+
Returns:
|
|
152
|
+
格式化的 wiki 匹配结果文本
|
|
153
|
+
"""
|
|
154
|
+
import os
|
|
155
|
+
from foundation.core.paths import DATA_INDEX_DIR as _idx, PROJECT_ROOT as _base
|
|
156
|
+
wiki_path = os.path.join(str(_idx), 'wiki-index.json')
|
|
157
|
+
if not os.path.isfile(wiki_path):
|
|
158
|
+
return ("wiki-index.json 不存在。请手动构建(一次性):\n"
|
|
159
|
+
" python .qoder/scripts/domain/kg/search/search_index.py wiki")
|
|
160
|
+
try:
|
|
161
|
+
from repowiki import search_wiki
|
|
162
|
+
from foundation.integrations.terms import get_cn_map_with_auto
|
|
163
|
+
except ImportError as e:
|
|
164
|
+
return "Wiki 搜索模块缺失: %s" % str(e)
|
|
165
|
+
code_dir = os.path.join(_base, 'data', 'code')
|
|
166
|
+
try:
|
|
167
|
+
cn_map = get_cn_map_with_auto()
|
|
168
|
+
results = search_wiki(kw, str(_idx), code_dir, cn_map)
|
|
169
|
+
except Exception as e:
|
|
170
|
+
return "Wiki 搜索出错: %s" % str(e)
|
|
171
|
+
if not results:
|
|
172
|
+
return "No wiki pages match: %s" % kw
|
|
173
|
+
out = ['Wiki pages matching: %s (%d found)' % (kw, len(results)), '']
|
|
174
|
+
for i, r in enumerate(results, 1):
|
|
175
|
+
out.append(' %d. [%s] %s' % (i, r.get('project', '?'), r.get('title', '')))
|
|
176
|
+
deps = r.get('dependent_files') or []
|
|
177
|
+
if deps:
|
|
178
|
+
out.append(' 依赖: %s%s' % (', '.join(deps[:2]),
|
|
179
|
+
(' (共 %d 个文件)' % len(deps)) if len(deps) > 2 else ''))
|
|
180
|
+
if r.get('md_path'):
|
|
181
|
+
out.append(' 文档: %s' % r['md_path'])
|
|
182
|
+
return '\n'.join(out)
|
|
File without changes
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
4
|
import os as _o, sys as _s
|
|
5
|
-
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
6
11
|
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
12
|
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
8
14
|
|
|
9
15
|
"""
|
|
10
16
|
QODER Context Pack - 一次调用返回写 PRD/原型所需的全部上下文
|
|
@@ -25,7 +31,7 @@ Usage:
|
|
|
25
31
|
"""
|
|
26
32
|
|
|
27
33
|
import os
|
|
28
|
-
from
|
|
34
|
+
from foundation.core.paths import BUILD_CACHE_DIR
|
|
29
35
|
import sys
|
|
30
36
|
import json
|
|
31
37
|
import time
|
|
@@ -40,9 +46,9 @@ except (AttributeError, TypeError, OSError, IOError):
|
|
|
40
46
|
pass
|
|
41
47
|
|
|
42
48
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
43
|
-
from
|
|
49
|
+
from foundation.integrations.terms import CN_MAP, BUSINESS_PATH_MAP, CN_TO_EN, get_platform_map, expand_chinese_query
|
|
44
50
|
|
|
45
|
-
BASE =
|
|
51
|
+
BASE = get_repo_root()
|
|
46
52
|
INDEX_DIR = os.path.join(BASE, 'data', 'index')
|
|
47
53
|
PLATFORM_MAP = get_platform_map()
|
|
48
54
|
MIN_FUZZY_LEN = 4
|
|
@@ -51,7 +57,7 @@ MIN_FUZZY_LEN = 4
|
|
|
51
57
|
def load(filename):
|
|
52
58
|
"""Load index from DuckDB (preferred) or JSON fallback."""
|
|
53
59
|
try:
|
|
54
|
-
from search_index import _duckdb_load
|
|
60
|
+
from domain.kg.search.search_index import _duckdb_load
|
|
55
61
|
result = _duckdb_load(filename)
|
|
56
62
|
if result is not None:
|
|
57
63
|
return result
|
|
@@ -141,7 +147,7 @@ def _save_cache(key, content):
|
|
|
141
147
|
os.replace(tmp, cache_path)
|
|
142
148
|
# LRU 清理: 保留最新 30 个, 删更老的
|
|
143
149
|
try:
|
|
144
|
-
from search_index import _cleanup_cache_dir
|
|
150
|
+
from domain.kg.search.search_index import _cleanup_cache_dir
|
|
145
151
|
_cleanup_cache_dir(cache_dir, 'ctx-cache-', max_files=30)
|
|
146
152
|
except Exception:
|
|
147
153
|
pass
|
|
@@ -221,6 +227,21 @@ def _compute_and_print(query, platform, page_type):
|
|
|
221
227
|
print('- {} (命中: {}) -> {}'.format(f, ','.join(sorted(kws)[:3]), full))
|
|
222
228
|
if not hits:
|
|
223
229
|
print('- (无命中 — 换业务词重试, 或该功能为全新)')
|
|
230
|
+
# 语义补充: 关键词命中稀疏时, 用 bge-small-zh 向量检索补一轮 (中→英跨语言)。
|
|
231
|
+
# 模型未装/无 embedding → 静默跳过, 不报错。
|
|
232
|
+
if len(hits) <= 2:
|
|
233
|
+
try:
|
|
234
|
+
from domain.kg.graph.kg_semantic import semantic_search_files, filter_semantic_by_platform
|
|
235
|
+
sem = semantic_search_files(query, top_k=8)
|
|
236
|
+
if sem:
|
|
237
|
+
filtered = filter_semantic_by_platform(sem, platform, BASE)
|
|
238
|
+
if filtered:
|
|
239
|
+
print('- [语义补充] (关键词命中少, 用向量检索跨语言补充):')
|
|
240
|
+
for fp, cn, score in filtered[:6]:
|
|
241
|
+
full = 'data/code/' + fp
|
|
242
|
+
print(' - %.2f %s -> %s' % (score, cn[:20], full))
|
|
243
|
+
except Exception:
|
|
244
|
+
pass
|
|
224
245
|
|
|
225
246
|
# ---- 2. 同类型页面真实示例 ----
|
|
226
247
|
si = load('ui-style.json') or {}
|
|
@@ -288,8 +309,8 @@ def _compute_and_print(query, platform, page_type):
|
|
|
288
309
|
code_dir = os.path.join(BASE, 'data', 'code')
|
|
289
310
|
wiki_results = []
|
|
290
311
|
try:
|
|
291
|
-
from
|
|
292
|
-
from
|
|
312
|
+
from repowiki import search_wiki, build_wiki_index
|
|
313
|
+
from foundation.integrations.terms import get_cn_map_with_auto
|
|
293
314
|
wiki_idx_path = os.path.join(BUILD_CACHE_DIR, 'wiki-index.json')
|
|
294
315
|
if not os.path.isfile(wiki_idx_path):
|
|
295
316
|
build_wiki_index(code_dir, wiki_idx_path)
|
|
@@ -324,7 +345,7 @@ def _compute_and_print(query, platform, page_type):
|
|
|
324
345
|
|
|
325
346
|
# ── 8. 数据库表结构 (从 kg_db.duckdb, 合并了原来要单独调 MCP 的查询) ──
|
|
326
347
|
try:
|
|
327
|
-
from search_index import _get_kg_db
|
|
348
|
+
from domain.kg.search.search_index import _get_kg_db
|
|
328
349
|
con = _get_kg_db() # 惰性缓存的只读连接, 不在此 close (供后续调用复用)
|
|
329
350
|
if con is not None:
|
|
330
351
|
# 关键词匹配表名 (英文词或snake_case, camelCase自动转)
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
4
|
import os as _o, sys as _s
|
|
5
|
-
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
6
11
|
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
12
|
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
8
14
|
|
|
9
15
|
"""enrich_prompt.py - 极速提示词润色 (用户提问后第 0 步)
|
|
10
16
|
|
|
@@ -38,7 +44,7 @@ if sys.platform == 'win32':
|
|
|
38
44
|
except Exception:
|
|
39
45
|
pass
|
|
40
46
|
|
|
41
|
-
BASE =
|
|
47
|
+
BASE = get_repo_root()
|
|
42
48
|
sys.path.insert(0, os.path.join(BASE, '.qoder', 'scripts'))
|
|
43
49
|
|
|
44
50
|
IDX_DIR = os.path.join(BASE, 'data', 'index')
|
|
@@ -51,7 +57,7 @@ def _load(name):
|
|
|
51
57
|
if name in _CACHE:
|
|
52
58
|
return _CACHE[name]
|
|
53
59
|
try:
|
|
54
|
-
from search_index import load_index
|
|
60
|
+
from domain.kg.search.search_index import load_index
|
|
55
61
|
d = load_index(name) or {}
|
|
56
62
|
except ImportError:
|
|
57
63
|
# search_index 不可用时, 直接读 JSON (老逻辑)
|
|
@@ -136,7 +142,7 @@ def enrich(query):
|
|
|
136
142
|
}
|
|
137
143
|
"""
|
|
138
144
|
t0 = time.perf_counter()
|
|
139
|
-
from
|
|
145
|
+
from foundation.integrations.terms import expand_chinese_query
|
|
140
146
|
|
|
141
147
|
# 1. 中文分词 → 英文搜索词
|
|
142
148
|
cn_words = list(dict.fromkeys(expand_chinese_query(query))) # 去重保序
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
4
|
import os as _o, sys as _s
|
|
5
|
-
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
6
11
|
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
12
|
from bootstrap import setup; setup()
|
|
8
13
|
|
|
@@ -40,11 +45,11 @@ except (AttributeError, TypeError, OSError, IOError):
|
|
|
40
45
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
41
46
|
|
|
42
47
|
# 复用 context_pack 的底层 (单一数据源, 不重复造轮子)
|
|
43
|
-
from context_pack import (
|
|
48
|
+
from domain.kg.search.context_pack import (
|
|
44
49
|
load, match_kw, plat_filter, PLATFORM_MAP, MIN_FUZZY_LEN,
|
|
45
50
|
BASE, INDEX_DIR, BUILD_CACHE_DIR,
|
|
46
51
|
)
|
|
47
|
-
from
|
|
52
|
+
from foundation.integrations.terms import expand_chinese_query, get_cn_map_with_auto, BUSINESS_PATH_MAP
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
# ── 词预测 ──────────────────────────────────────────────────────────
|
|
@@ -111,11 +116,12 @@ def predict_words(sentence):
|
|
|
111
116
|
|
|
112
117
|
# ── 批量合并取上下文 ──────────────────────────────────────────────────
|
|
113
118
|
|
|
114
|
-
def _collect(targets, en_words):
|
|
119
|
+
def _collect(targets, en_words, query=None):
|
|
115
120
|
"""对预测出的所有词, 批量收集 1/3/4/5 段命中, 合并去重。
|
|
116
121
|
|
|
117
122
|
返回 dict: {段名: 合并去重 + 排序后的列表}。
|
|
118
123
|
排序: 多词命中的项排前面 (被越多词命中越相关)。
|
|
124
|
+
query: 原始需求句 (用于语义补充检索, 中文句比拆词后的英文更准)。
|
|
119
125
|
"""
|
|
120
126
|
ki = load('code-keyword.json') or {}
|
|
121
127
|
si = load('ui-style.json') or {}
|
|
@@ -156,6 +162,25 @@ def _collect(targets, en_words):
|
|
|
156
162
|
# 还原成 fywl-ui/... 形式 (views 里是相对 src/views 的路径)
|
|
157
163
|
f = 'fywl-ui/apps/ics/src/views/' + vp
|
|
158
164
|
file_hits.setdefault(f, set()).add(word)
|
|
165
|
+
|
|
166
|
+
# 1c. 语义补充: 若关键词命中稀疏 (<=2 文件), 用 bge-small-zh 向量检索补一轮。
|
|
167
|
+
# 解决"中文业务词→英文代码"的跨语言漏召 (查"报销"命中 reimburse/expense)。
|
|
168
|
+
# 把语义命中的文件按低权重并入 file_hits, 不喧宾夺主 (关键词命中优先)。
|
|
169
|
+
if len(file_hits) <= 2:
|
|
170
|
+
try:
|
|
171
|
+
from domain.kg.graph.kg_semantic import semantic_search_files
|
|
172
|
+
sem = semantic_search_files(query, top_k=8)
|
|
173
|
+
if sem:
|
|
174
|
+
for fp, eid, cn, score in sem:
|
|
175
|
+
if targets:
|
|
176
|
+
matched = any(t.lower() in fp.lower() for t in targets)
|
|
177
|
+
if not matched:
|
|
178
|
+
continue
|
|
179
|
+
# 语义命中标 'sem' 词, 不计入 en_words 命中数 (排序靠后)
|
|
180
|
+
file_hits.setdefault(fp, set()).add('sem')
|
|
181
|
+
except Exception:
|
|
182
|
+
pass # 模型未装/无 embedding → 静默跳过
|
|
183
|
+
|
|
159
184
|
# 按命中词数排序, 取前 12
|
|
160
185
|
files_ranked = sorted(file_hits.items(), key=lambda x: len(x[1]), reverse=True)[:12]
|
|
161
186
|
|
|
@@ -271,7 +296,7 @@ def _print_merged(query, platform, primary_cn, en_words, collected):
|
|
|
271
296
|
code_dir = os.path.join(BASE, 'data', 'code')
|
|
272
297
|
wiki_results = []
|
|
273
298
|
try:
|
|
274
|
-
from
|
|
299
|
+
from repowiki import search_wiki, build_wiki_index
|
|
275
300
|
wiki_idx_path = os.path.join(BUILD_CACHE_DIR, 'wiki-index.json')
|
|
276
301
|
if not os.path.isfile(wiki_idx_path):
|
|
277
302
|
build_wiki_index(code_dir, wiki_idx_path)
|
|
@@ -293,7 +318,7 @@ def _print_merged(query, platform, primary_cn, en_words, collected):
|
|
|
293
318
|
|
|
294
319
|
# 8. 数据库表结构 (主词查一次即可)
|
|
295
320
|
try:
|
|
296
|
-
from search_index import _get_kg_db
|
|
321
|
+
from domain.kg.search.search_index import _get_kg_db
|
|
297
322
|
con = _get_kg_db()
|
|
298
323
|
if con is not None:
|
|
299
324
|
tbl_q = primary_cn[0] if primary_cn else query
|
|
@@ -338,7 +363,7 @@ def main():
|
|
|
338
363
|
|
|
339
364
|
# 降级: 预测不出任何英文词 → 回退单词 context_pack (保持兼容)
|
|
340
365
|
if not en_words:
|
|
341
|
-
import context_pack as _cp
|
|
366
|
+
from domain.kg.search import context_pack as _cp
|
|
342
367
|
# 直接调 context_pack 的计算逻辑 (它会自己 print)
|
|
343
368
|
_cp._compute_and_print(query, platform, None)
|
|
344
369
|
return 0
|
|
@@ -350,7 +375,7 @@ def main():
|
|
|
350
375
|
targets = sorted(set(PLATFORM_MAP.values()))
|
|
351
376
|
|
|
352
377
|
# 批量合并取上下文
|
|
353
|
-
collected = _collect(targets, en_words)
|
|
378
|
+
collected = _collect(targets, en_words, query)
|
|
354
379
|
_print_merged(query, platform, primary_cn, en_words, collected)
|
|
355
380
|
return 0
|
|
356
381
|
|