# AST 符号解析 — Code Intelligence 设计

> 日期:2026-06-24 | 状态:✅ Phase1 已落地(2026-06-28 核验:read_symbol 工具已注册 tool_registry.rs:1506-1547 + 基线测试守护)
> 关联: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` + 各语言 grammar crate(编译期静态链接)
- 增量解析:文件改只重解变更(<1ms)

### grammar 策略(2026-06-24 定稿)

**静态编译 + 集中 lookup**(架构成本最低,详见 [插件机制设计](插件机制-设计-2026-06-24.md)):

- **不做**动态加载(grammar 编共享库运行期 dlopen)、**不抽** trait(YAGNI,无第二实现不抽象)
- grammar 编译期静态链接进二进制(无 ABI 坑 / 无运行时状态 / 无分发负担)
- grammar 获取集中到一个普通函数 `grammar_for(ext) -> Language`(未来加动态只改这一点,不返工调用方)

**Phase1 范围**(覆盖 devflow 源码 + 主流用户项目):

| 文件类型 | grammar | 备注 |
|---|---|---|
| `.rs` | tree-sitter-rust | 后端主体 |
| `.ts`/`.tsx` | tree-sitter-typescript | 前端 script |
| `.js`/`.jsx` | tree-sitter-javascript | |
| `.vue` | **借 tree-sitter-typescript** | 切 `<script>` 段喂 ts;template/style 不做符号(Vue 逻辑在 script) |
| `.go` | tree-sitter-go | 2026-06-25 扩展 |
| `.java` | tree-sitter-java | 2026-06-25 扩展 |
| `.py` | tree-sitter-python | 2026-06-25 扩展 |

**6 个 grammar 覆盖 9 类文件**。Vue 借 TS 省 grammar + 避 SFC 嵌套复杂度。Go/Java/Python 2026-06-25 扩展(表驱动加表项:`DEFINITION_KINDS` 补 kind + `grammar_for` 加分支 + `Cargo.toml` 加 crate 0.23,ABI 兼容 0.25 主 crate,零架构改动)。

**扩展**:加标准语言 = 加 grammar crate + 一张节点映射表(表驱动,~30 行/语言)。小众(C++/Kotlin)按需;动态加载/集成插件化见插件机制设计文档。

### 三态读取(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 解析 + 符号提取纯函数
- 单测:符号提取 + 兜底
