Files
DevFlow/docs/02-架构设计/专项设计/AST符号解析-设计-2026-06-24.md
绝尘 8d18918e39 更新: 架构设计文档状态同步代码核验结果
经代码核验发现 9 份设计文档的状态标注严重滞后(标'待实施'但
实际已完整落地),本次批量同步:

已落地(核验确认):
- 局部编辑工具:三层防御+三模式完整,仅文本不支持二进制
- 密钥迁移健壮性:空 key 不覆盖+即时迁移补密钥+阻断保存
- AST 符号解析:符号读取工具已注册+基线测试守护
- 查询能力补全:任务/项目/灵感均多维动态查询
- 条件表达式引擎:手写求值器+JSON Path+执行器集成+前端入口
- 工作流脚本边界:命令白名单/黑名单+危险关键词告警
- 消息拆分存储:消息表+全量迁移+读写全部切换
- 消息级溯源:消息 ID+四场景溯源+切读全部完成

部分落地:
- 全局事件总线:基建+20 余个发射点就位,消费者未接(空转)

归档不实施:
- 规格契约自检:核心价值已被求助协议+自审闸门覆盖,过度设计
2026-06-29 00:16:39 +08:00

14 KiB

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(架构成本最低,详见 插件机制设计):

  • 不做动态加载(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 解析 + 符号提取纯函数
  • 单测:符号提取 + 兜底