From 61bad4fa288f470a8594449d68c1cd94cee62fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Wed, 24 Jun 2026 01:43:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20AST=20=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E8=AE=BE=E8=AE=A1(=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=AF=86=E5=BA=A6=E4=B8=89=E6=80=81=E4=BE=9B=E7=BB=99+?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E5=8E=8B=E7=BC=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../专项设计/AST符号解析-设计-2026-06-24.md | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 docs/02-架构设计/专项设计/AST符号解析-设计-2026-06-24.md diff --git a/docs/02-架构设计/专项设计/AST符号解析-设计-2026-06-24.md b/docs/02-架构设计/专项设计/AST符号解析-设计-2026-06-24.md new file mode 100644 index 0000000..a494114 --- /dev/null +++ b/docs/02-架构设计/专项设计/AST符号解析-设计-2026-06-24.md @@ -0,0 +1,237 @@ +# AST 符号解析 — Code Intelligence 设计 + +> 日期:2026-06-24 | 状态:设计深化(2026-06-24:信息密度三态 + 主题压缩),Phase1 待实施 +> 关联:memory [[devflow-info-density-concept]] / [[devflow-aichat-session-analysis-2026-06-22]] / plan gentle-gliding-book + +## Context(为什么) + +**起因**:实测会话 e46f5605「DevFlow上下文管理不足分」——LLM `read_file` 读 8 个源码文件(context.rs/anthropic_compat/intent.rs/prompt.rs/compress 等)各 500 行**全文回灌**,prompt 累积 **360K token 超 glm-5.2 上下文上限** → LLM 失败 → 末尾 tool 后无 assistant(卡)。8dfe0b94 更滚到 5M token 爆炸。 + +**已否决的治标方向**: +- ❌ read_file 限制行数(500→150):"等于没读",LLM 被迫 offset 翻页,更多轮次 + 仍累积 +- ❌ read_file content 字节截断(8KB):"不讲武德",LLM 要全文被砍 + +**正路**:AST 语义解析,精准提取**调用链 + 函数代码**,替代物理读全文。 + +**双目标**(用户明确): +1. 精准获取调用链 + 函数代码,避免物理读文件的 LLM 上下文成本 +2. AST 树作后期 ai coding 编码支撑工具(符号检索/依赖图/生成上下文/向量编码) + +**根本目标(2026-06-24 深化)**:提高上下文**信息密度**——使进 prompt 的每一行与**当前关注主题**紧密结合,去噪、去多余。这是所有读取/压缩设计的判据。 + +## 核心原则:信息密度 ≠ 压缩(2026-06-24) + +**密度 = 只给与当前任务相关的内容;压缩 = 无差别减体积。两者不同。** 压缩减了体积,密度不一定升——减什么是预设规则或盲猜,和"当前任务关注什么"无关。 + +**分水岭:压缩器带不带当前任务上下文。** +- 不带 → 通用摘要(治标,可能砍掉恰是任务关键的细节) +- 带(任务焦点 focus)→ 相关性提炼(治本,留相关去无关) + +**被否的治标方向**: +- ❌ 物理行数截断上限(500→150→3):武断,任何值都不合理(3 行砍废、3 万行等于没设);且按行返本身错——该按**语义结构层级**返 +- ❌ 算法裁剪(砍注释/doc/空行):格式去噪,留下的不一定和任务相关,密度提升有限 +- ❌ 通用 LLM 摘要(不带 focus):压出"这个函数做什么"的通用摘要,可能丢任务关键行 + +**正路(本设计采纳)**: +1. **结构层级供给**(拉模式/Progressive Disclosure):read_symbol 默认返骨架 → LLM 看骨架下钻局部 → 精准取。进 prompt 的都是 LLM 主动要的,天然相关。 +2. **主题驱动压缩**(带 focus):大块必须给时,压缩 LLM 接收当前任务焦点,做相关性提炼(非通用摘要)。 + +→ 不按物理行返,按**语义层级**返;压缩必须**带任务上下文**。 + +## 一、技术定位:AST vs LSP(两个不同层面) + +| | AST(tree-sitter) | LSP(rust-analyzer) | +|---|---|---| +| 本质 | 数据结构(语法树,解析产物) | 通信协议(语义服务,JSON-RPC) | +| 层面 | 语法层 | 语义层 | +| 提供什么 | 符号结构/调用语法 | 类型/精准引用/重构/诊断 | +| 形态 | 库(链接进进程) | 独立服务器(子进程) | +| 成本 | 轻(ms/MB) | 重(二进制 30-50MB/平台 + 索引 + 内存) | + +抽象层级:源码 → [tree-sitter] → AST(语法,Phase1-3 停此) → [类型推断] → 语义 → [LSP 协议] → 客户端。**LSP 依赖 AST**(rust-analyzer 内部也用),但停在更深(语义层)。 + +## 二、目标论证(能力边界,不夸大) + +### 目标1:调用链 + 函数代码 + +| 能力 | tree-sitter | Phase | +|---|---|---| +| 函数结构(骨架·默认) | ✅ `function_definition` → 签名+分支结构+调用图+行数(极小,高密度) | 1 | +| 函数代码(定义体) | ✅ `full=true` / `drill` 取完整或局部(语法边界 100% 准) | 1 | +| 同文件调用点 | ✅ `call_expression` 命中符号 → 调用位置 | 2 | +| 跨文件调用链 | ✅ 符号索引 + import 解析 | 3 | +| **动态调度**(`dyn Trait`/虚函数) | ❌ 静态解析不能(需类型推断,LSP 级) | 局限 | +| 回调/闭包/函数指针/宏 | ❌ 需数据流/编译器 | 局限 | + +**覆盖度**:静态直接调用占实际 ~70-80%(绝大多数业务代码);动态/间接/宏 ~20-30% 需 LSP。 + +**避免上下文成本**:`read_symbol(compress)` → 函数体几十行 + 静态调用点,非 `read_file` 全文 500 行 → prompt 降一个量级。对 e46f5605 类(读源码理解实现,静态为主)直接见效。 + +### 目标2:ai coding 编码支撑 + +AST 是语法层(parse tree)。**语法层能**: +- 符号级上下文(LLM 编码拿相关函数定义+调用关系,非全文 chunk) +- 结构化检索(符号/调用链/依赖,替代文本 grep) +- 变更影响分析(改函数 X → 影响哪些调用方,精准 review/测试范围) +- 代码生成约束(签名/调用语法) + +**语义层不能**(需 LSP/编译器):类型检查/推断、安全跨文件重构、数据流/借用分析。 + +→ AST 够 aichat 的"精准读代码 + 调用链 + 上下文 + 影响分析"(95% 读代码场景);深层语义是 LSP 的事。 + +## 三、方案:tree-sitter + read_symbol + +### 选型:tree-sitter +- GitHub 出品,增量 AST 解析,几十种语言 grammar +- Rust 生态 `tree-sitter` + `tree-sitter-rust`/`tree-sitter-typescript` 等(编译期 crate) +- **按语言按需 enable grammar**(非全装,控体积) +- 增量解析:文件改只重解变更(<1ms) + +### 三态读取(read_symbol,密度驱动) + +read_symbol **不按物理行返、不截断**,按**语义结构层级**返(消解"截断多少行"争论): + +| 态 | 触发 | 返回 | 密度 | +|---|---|---|---| +| **骨架**(默认) | `read_symbol(path, symbol)` | 签名 + 内部分支结构 + 调用列表 + 行数(不返函数体) | 极高(极小+全相关) | +| **下钻**(LLM 拉) | `read_symbol(..., drill=分支/调用名)` | 该局部分支/调用的精准定义 | 高(LLM 主动要) | +| **全文**(显式) | `read_symbol(..., full=true)` | 完整定义体(小函数常态直通) | 全 | + +``` +read_symbol(path, symbol, kind?, drill?, full?) + → tree-sitter 解析 path,提取 symbol 定义节点 + → 默认:返骨架(签名 + 分支结构 + 调用图 + 行数) + → drill=局部名:返该分支/调用的精准定义 + → full=true:返完整定义体 + → 返回 {symbol, kind, signature, skeleton|drill|def_body, line_range, file_hash, calls?} +``` + +### 主题压缩(compress_with_focus,大块兜底) + +大块必须整体给时(复杂函数 / 大文件段),经**带焦点压缩**: + +``` +compress_with_focus(content, focus) + → focus = 当前任务/问题(LLM 显式传) + → LLM 相关性提炼:留与 focus 相关的路径/逻辑,去无关分支/防御/日志 + → 返提炼结果 + 原 hash(丢细节时可回取原始) +``` + +- **focus 必传**(分水岭:不带 focus 的通用摘要被否) +- hash 兜底:LLM 可凭 hash 回取原始(类似下钻) +- 与现有 `compress_via_llm`:现有压历史对话(P0-1 未真降 prompt),本工具压"工具返回入 prompt 当轮"——扩展触发点非新造 + +### 注册 / schema / 兜底 +- read_symbol 注册 `register_file_tools`(与 read_file/grep 同域) +- schema:`object_schema([("path",string,req),("symbol",string,req),("kind",string,opt),("drill",string,opt),("full",bool,opt)])` +- handler:捕获 `allowed_dirs`,调 `resolve_workspace_path_with_allowed` 校验(复用 read_file 模式) +- RiskLevel::Low(只读) +- **不报错底线**(见七):无 grammar / 解析失败 / symbol 未找到 / 任何大小 → 走兜底或正常返,不 panic / 不阻断 + +## 四、Phase 路线 + +**两条线**:读取层(read_symbol 三态,tree-sitter)/ 压缩层(主题压缩,LLM,与 P0-1)。 + +**读取层**(tree-sitter): + +| Phase | 能力 | 成本 | 缓存 | +|---|---|---|---| +| **1 单文件符号骨架+下钻+全文** | read_symbol 三态 | 低-中 | 实时解析(弃 AST) | +| **2 同文件调用点** | drill 跨分支调用图 | 中 | 实时 | +| **3 跨文件调用链** | 符号索引 + import | 高 | 符号表内存缓存 | + +**压缩层**(LLM,与 P0-1): + +| Phase | 能力 | 成本 | 缓存 | +|---|---|---|---| +| **C1 compress_with_focus** | 带 focus 相关性提炼 | 中(LLM 调用) | hash 缓存(同内容不重压) | +| **C2 触发管线接入** | 工具返回→压缩→入 prompt | 中 | 阈值(小不压)+ 缓存 | + +**远期**:Encoding(符号检索/依赖图/向量编码)。 + +## 五、缓存策略(分情况,对齐 tree-sitter 增量特性) + +**Phase1/2(单文件)**:**情况1 实时解析** +- tree-sitter 单文件 ms 级,解析完提取符号后**丢弃 AST**(不缓存) +- 每次 read_symbol 实时解析 → 最新最准(零缓存失效) +- 速度 ~1-10ms(几千行),增量 <1ms + +**Phase3(跨文件索引)**:**情况2 缓存** +- 全项目符号表(file→symbol→pos+签名),累积大 +- **2a 内存缓存**(devflow ~4000 符号 ~0.5-2MB,内存够) +- 大项目(几千文件/几十 MB)转 **2b 文件缓存**(`.devflow/symbols.cache`,LRU 按需加载) +- **失效**:文件 mtime/sha256 变 → tree-sitter 增量重解析该文件(<1ms)→ 更新该文件项(不全扫) + +**增量解析是命脉**:让"实时"(情况1)够快、"缓存失效"(情况2)够便宜。 + +## 六、估算(devflow 实测基数) + +- 源码:280 文件 / 82,444 行(Rust 142/52.7K,TS 87/12K,Vue 51/17.6K) +- 符号:~4000(Rust ~2840 + TS ~1044 + Vue ~510) +- 符号表:Phase1 ~400KB / Phase2 ~1MB / Phase3(含调用链)~1.6-2MB +- → **内存缓存够**(情况2a) + +## 七、兜底(不报错底线,不破坏现有) + +**底线:任何情况不 panic / 不阻断 / 不因大小失败。** +- 无 grammar(语言不支持)/ 解析失败(语法错误)/ symbol 未找到 → 兜底提示 `grep` + `read_file`(offset 精准读) +- **任何大小函数** → 正常返(不截断天然不因大小失败;大函数走骨架+下钻/主题压缩,非报错) +- read_symbol 失败不阻断(返兜底提示让 LLM 回退 grep) +- compress_with_focus 失败 → 回原始内容(LLM 仍能用,降级非阻断) + +## 八、LSP 后置结论(2026-06-24 评估) + +**LSP 成本高,重 tree-sitter 100x**: +- 二进制 ~30-50MB/平台(跨平台 3 份,bundle 进安装包体积大) +- 首次索引 30s-2min(全项目类型推断),内存几百 MB-1GB,后台常驻 +- JSON-RPC 通信复杂(初始化/能力协商/文件同步/多语言 server 生命周期) +- 分发:bundle 重 / 要求用户装(rustup/tsserver)门槛高 + +**收益**:语义层(动态调度/类型/重构/精准引用),tree-sitter 天花板外 ~20%。 + +**结论**:**Phase1-3 tree-sitter 先**(语法层,覆盖 ~80% 读代码/调用链/上下文,轻量零分发负担);**LSP 后置/按需**——检测用户装了 rust-analyzer 才启用深层语义,不强制 bundle,保分发轻。别一上来 LSP(成本/分发过重),tree-sitter 拿 80% 红利,LSP 留作"深层语义可选增强"。 + +## 九、与现有方向的关系(密度驱动,四层互补) + +``` +① 结构层级供给(read_symbol 骨架→下钻) ← 本设计,语义结构精准+密度 + ↓ 大块兜底 +② 主题压缩(compress_with_focus) ← 带 focus 相关性提炼(非通用摘要) + ↓ 兜底 +③ grep+offset(read_file) ← 现有,文本精准 + ↓ 历史治理 +④ 历史压缩(P0-1 compress_via_llm) ← prompt 层已读历史摘要 +``` + +- LLM 默认 read_symbol 骨架 → 要细节下钻 → 大块经主题压缩 → 不支持回退 grep +- 已读历史由 P0-1 压缩 +- **密度判据**:①②③④ 每层都服务"只给任务相关内容",非"减体积" + +## 十、风险 / 改动面 + +| 风险 | 缓解 | +|---|---| +| tree-sitter 依赖体积 | 按语言按需 enable grammar(非全装) | +| 跨语言覆盖(小众语言 grammar 弱) | 兜底 grep+read | +| 动态调度解析不准 | 文档标注局限,需时评估 LSP(后置) | +| Phase3 跨文件索引重 | 渐进,Phase1/2 先(实时,无索引) | + +## 十一、验证(Phase1 落地后) + +- **解析速度**:tree-sitter 解析 devflow 大文件(context.rs 1552 行等),测 ms +- **符号表大小**:全项目解析后序列化字节数(验估算 ~400KB) +- **token 对比**:read_symbol(compress) vs read_file(context.rs) 返回 token 量(验降一个量级) +- **单测**:read_symbol 提取函数定义体 + 调用点(Phase2) + +## 十二、落地步骤 + +1. **本文档**(设计定稿) +2. **Phase1 实施**:tree-sitter-rust 集成 + read_symbol 工具 + 单文件符号提取 + 兜底 + 实测 +3. Phase2/3/4 + LSP 后置评估,按需 + +## 关键文件(Phase1 实施) + +- `src-tauri/Cargo.toml`:加 tree-sitter + tree-sitter-rust +- `src-tauri/src/commands/ai/tool_registry.rs`:`register_file_tools` 内加 read_symbol(对齐 read_file handler) +- 新模块 `src-tauri/src/commands/ai/code_intel.rs`:tree-sitter 解析 + 符号提取纯函数 +- 单测:符号提取 + 兜底