# 项目知识图谱(代码结构认知层)
{PROJECT_ROOT}(必填):项目代码根,图谱按工作区独立存储于 {PROJECT_ROOT}/.ai-memory/knowledge-graph/,与会话生命周期无关、可随时重建。file(相对路径)或 symbol(类名/函数名,经 symbols.json 解析)。graph.md(文本邻接表,agent 不读)。task-decomposition / software-project / refactoring 里的"依赖图"是任务执行依赖(临时、本次任务内)。前者称"代码结构图谱",后者称"任务依赖",命名严格拉开。project-memory——结构层 vs 决策层,互引不重叠;③ 不进记忆蒸馏、不参与 Git 团队共享。| 场景 | 行为 |
| - | - |
| 简单任务 / 单文件(L0) | 不触发,图谱是噪声 |
| 跨模块改动 / 重构 / 审计 / 接手陌生项目(≥3 文件) | 主动查图谱(先走新鲜度校验) |
| 显式 @project-knowledge-graph 或"画依赖图/模块关系" | 构建 / 查看 / 查询 |
| 文件变更后下次查询 | 哈希比对自动判定过期 → 提示或增量重建 |
调用 scripts/build_graph.py(纯脚本写盘,不进 LLM 上下文):
# 全量构建
python scripts/build_graph.py --root {PROJECT_ROOT} --rebuild
# 查时若图不存在 → 冷启动:先全量构建再查(见第三步)
# 内置 E2E 自检(删文件无悬挂边 + 改文件过期判定,不改交付物)
python scripts/build_graph.py --root {PROJECT_ROOT} --selftest
detect_lsp 记 lsp_available),抽取仍走正则(语义增强为可选留口,当前未实现)。结果写 meta.json.lsp_available。.tmp 再 rename 覆盖,避免半文件。meta.json.dangling_edges(含源:行号 + 解析值 + 所用 base)。base 解析规则:源文件目录优先;无 .//../ 前缀且非绝对 → 回退 include_path + 工作区根;两路都解析不到才判悬空。symbols.json 找不到定义 → 标 orphan 提示,不自动删节点/边(symbols 自身可能漏抽导致误标);真正剔除须查后动作 grep 复核确认。meta.json.accuracy_report(edges_total / edges_valid / edges_dangling / symbols_* / static_coverage / lsp_available);构建日志输出 [GRAPH-ACCURACY] 有效边 X / 悬空 Y(已剔除)/ 覆盖率 Z% / LSP: php。python scripts/build_graph.py --root {PROJECT_ROOT} --query <file|symbol> [--direction up|down|both] [--depth N]
--query:file 直接定位;symbol 经 symbols.json 解析为文件:行号再查(无索引则打印"符号索引缺失"并返回空子图,不扫描全图);多个入口用逗号分隔(如 --query a.php,b.php),返回合并子图;同名符号歧义 → 返回候选列表(每个含文件:行号 + 所属 module),由模型/用户选定其一。--direction:上游依赖(谁依赖它)/ 下游(它依赖谁)/ 双向。--depth N:默认 2 跳——防止一次查询把半个项目读进上下文,守住"省 token"承诺。cycle:true 不再展开,杜绝 A→B→A 膨胀/死循环;输出附 [GRAPH-CYCLE] 检测到 N 个环。MAX_NODES=80 即截断(防枢纽模块 2 跳爆炸,如 connect.php)。{nodes, edges, cycle} 紧凑 JSON 片段(预计 2K–10K token)进入上下文。唯一判定机制:查时比对哈希(不依赖 PostToolUse 脏标记,避免双套逻辑分裂):
meta.json 文件清单 vs 当前工作区:先用 mtime 粗筛(mtime 未变 → 直接判未过期),仅变更文件重算内容哈希精筛。[GRAPH-STALE] 图谱可能过期(N 个文件变更),正在增量重建… 并触发重建(v1 增量=全量重抽,统一覆盖增/删/改/重命名四类);或直接提示用户重建。meta.json/产物缺失(首次使用或图被误删)→ 输出 [GRAPH-STALE] 图谱可能过期(N 个文件变更/产物缺失),正在增量重建… 并当场全量构建;构建完成再查。若自主度低或构建耗时过长 → 降级为"临时 grep 局部依赖"并在交付标注"图谱未构建,已用临时分析替代",不阻断主任务。模型拿到子图后必须显式转化为执行动作,而非"看了眼":
自校验保证"不准的边不进图"(第二步),但漏抽(动态依赖)自校验发现不了,须靠门禁兜底——以下任一条不满足,不得交付,且 SELF-AUDIT 直接判不通过:
grep -rn "class X" 命中 N 处 @ file:line),SELF-AUDIT 核对证据存在且覆盖清单全部文件;无证据即判不通过,卡在交付前(与"无验证证据=未完成"红线同源)。agent 不能靠自评绕过谎报。| 作用点 | 触发条件 | 钉到 Step | 查什么 | 产出落哪 |
| - | - | - | - | - |
| P1 规划前置依赖分析 | 复杂任务(≥3 文件 / 跨模块)进方案阶段 | Step 2 规划(PLAN-GATE 前) | 改动文件集闭包(--query <改动文件1,改动文件2,…> --direction both --depth 3),非项目全量 | 喂 _plan.md 依赖矩阵,替代临时 grep |
| P2 执行前局部影响面 | Step 3 要改具体模块 | Step 3 执行(改码前,先新鲜度校验) | 该模块 2 跳上游(--query <模块> --direction up --depth 2) | 喂查后动作规范(受影响文件一并改+验证) |
| P3 显式请求 | 用户 @project-knowledge-graph 或"画依赖图/模块关系" | 任意 Step,用户直接要 | 按用户入口 | 构建/查看/查询 |
| 不触发 | 简单任务 / 单文件(L0) | — | — | 图谱是噪声,跳过 |
复杂任务 → Step 2 前 P1 查全量依赖 → 写 plan 依赖矩阵 → Step 3 改某模块前 P2 查上游 → 查后动作规范 → 执行。{PROJECT_ROOT}/.ai-memory/knowledge-graph/
├── graph.json # 节点 + 边(结构化,机器读,agent 唯一消费源)
├── meta.json # 构建时间、工具版本、文件清单哈希、范围配置、覆盖率、accuracy_report、script_hash
├── graph.md # 文本邻接表(人类可读兜底,agent 不加载)
└── symbols.json # 符号索引:类名/函数名 → 文件:行号(支撑 --query <symbol>)
include(含 require/require_once/include_once,统一归 include)use(命名空间 use)autoload(new \Ns\Class / \Ns\Class:: 自动加载)extends(类继承)template({include file=}/template())tpimport(ThinkPHP import()/vendor()/Loader::import())import(非 PHP 语言模块导入)cssimport(CSS @import)asset(HTML link/script 资源)calls(跨文件调用)。注:implements/require 非独立边类型——require 并入 include,implements 作为类属性记录、不单独成边。calls 边只记跨文件/跨模块调用,过滤标准库/框架内置/密钥读取等内部调用,避免泄露敏感路径且降噪。{PROJECT_ROOT} 时图谱上下文随之切换,互不干扰。graph.json(节点+边,唯一结构化输入)+ meta.json(构建/覆盖率/新鲜度)+ symbols.json(符号索引)。一律走 --query 查询接口返回裁剪子图 JSON,绝不读全量文件。graph.md(文本邻接表),仅供人类开发者偶尔查看;agent 不加载。graph.mmd / mermaid;不在 README/FAQ 展示渲染图。--query 返回 N 跳子图 token < 10K(深度默认 2 + MAX_NODES=80 上限兜底)。[GRAPH-ACCURACY]。| 失败类型 | 回退动作 | | - | - | | 构建脚本缺失 Python / 权限 / 环境异常 | 降级为"临时 grep 局部依赖",交付标注"图谱不可用,已用临时分析替代",不阻断主任务 | | 图谱过期但重建超时 | 同降级:临时 grep + 标注 | | 符号索引缺失(--query 无结果) | 返回空子图 + 提示"符号索引缺失";仍无果 → 临时 grep | | 图谱影响面与 grep 冲突(G1) | 一律以 grep 为准,图谱仅作提示 | | 动刀前未 grep 复核(G2') | SELF-AUDIT 判不通过,卡交付前,补 grep 证据 | | 不可逆操作仅凭图谱(G3) | 独立 grep 复核 + 用户确认闸门 | | 子图超 80 节点(枢纽模块) | 查询自动截断 + 提示缩小入口/降 depth |
require($base.'/class/'.$c.'.php') / import(resolve(dirname)) 正则抽不出,属通用边界(非某框架特有),README 须明示"不宣称 100% 结构还原";漏抽兜底在查后动作 grep 复核 + 用户反馈纠偏闭环。ECMS_PATH 等常量拼绝对路径,edges 会出现绝对路径 target(如 j:/devx-sim/e),查询时按相对路径匹配会失配——后续可考虑在 build 内归一化(可选优化,不影响现有相对路径边)。