Image: AI generated
问题
AI 代码智能体(如 Claude Code)通过 grep 查找文件,通过 read 打开文件来导航代码库。read 的单位是文件。
如果一个文件里有 20 个函数,会怎样?
需要一个 CrossError 类型 → read
→ 19 个不必要的函数跟着来了
→ 上下文污染
“Lost in the Middle”(Stanford, 2024)报告称,当相关信息埋在上下文中间时,LLM 性能下降超过 30%。“Context Length Alone Hurts LLM Performance”(Amazon, 2025)发现,即使不必要的 token 是空白的,性能也会下降 13.9~85%。
GSM-DC 研究(Yang et al., EMNLP 2025)更进一步。通过对照实验证明,不相关的上下文不仅仅是"淹没"有用信息,而是显著降低 LLM 的推理路径选择和算术准确性。19 个不必要的函数跟着来不是"浪费"——是"干扰"。
研究证明"上下文越短越好"。但没有工具能从结构上拆分代码,只加载需要的部分。
filefunc 填补了这个空白。它是面向 Go 应用程序开发的代码结构约定和 CLI 工具——后端服务、CLI 工具、代码生成器、SSOT 验证器。
核心原则
一个文件一个概念。文件名 = 概念名。
无论是 func、type、interface 还是 const 组——规则相同。所有规则都由这一原则派生。
# 没有 filefunc
read utils.go → 20 个 func,19 个不必要。上下文被污染。
# filefunc
read check_one_file_one_func.go → 1 个 func。恰好是需要的。
不打开 290 个不必要的文件,比精确挑出 5-10 个更重要。 ASE 2025 代码补全研究(Yusuf et al.)报告称,块级检索比文件级检索将代码补全质量提高 6%,比无上下文提高 16%。决定质量的不是上下文的数量,而是粒度。
第一公民是 AI 智能体
filefunc 的代码结构是为 AI 智能体设计的,不是为人设计的。
AI 智能体用 grep 导航,不用 ls。文件有 500 个还是 1000 个,rg '//ff:func feature=validate' 一次就够了。文件越多,每个文件越小,read 一次带来的噪音越少——反而更有利。Cao et al.(2026)报告的实验结果表明,编码智能体通过文件系统导航(ls、grep、read)处理 3 万亿 token 的语料库,比现有长上下文模型高出 17.3%。AI 智能体的导航单位是文件。
“文件不会太多吗?"——对人来说确实如此。但人的不便是视图层问题(通过 VSCode 扩展等解决)。filefunc 的结构不会为了人的浏览习惯而妥协。
导航流程变了
以前
用户请求
→ 不知道有什么,ls、find
→ 打开文件了解结构
→ 再 grep 找相关文件
→ 打开一个文件,20 个 func,大部分不需要
→ 导航成本 > 实际工作时间
filefunc
用户请求 + 提供代码书
→ 根据代码书立即构造 grep 查询
→ read 20-30 个文件(每个 1 个概念,全部是有效上下文)
→ 开始工作
read 30 个文件不是问题,如果全部是有效上下文。read 1 个文件却带来 30 个文件的噪音才是问题。
代码书
代码书在 filefunc 设计中占据最重要的位置。比注解规则还重要。代码书设计好了 grep 查询才精确,grep 精确了 read 列表才干净。
# codebook.yaml
required:
feature: [validate, annotate, chain, parse, codebook, report, cli]
type: [command, rule, parser, walker, model, formatter, loader, util]
optional:
pattern: [error-collection, file-visitor, rule-registry]
level: [error, warning, info]
required 键必须存在于每个 //ff:func 和 //ff:type 注解中——不允许空缺,以保证 grep 的可靠性。optional 键只在相关时使用。
代码书是 AI 智能体的项目地图。没有代码书,智能体在不知道词汇表的情况下开始导航。有了代码书,智能体可以直接发出精确查询如 feature=validate、type=rule,无需预先探索。
在注解中使用代码书中不存在的值是 ERROR。通过代码书规范化词汇表,可以暴露缺失的 feature、重复的 type、模糊的分类。漏洞可见才能管理。代码书本身也是验证对象——required 中至少 1 个键,不允许重复值,只允许小写字母和连字符。
元数据注解
每个文件顶部附加注解。不需要 read 整个 body,只看顶部几行就能了解元数据。
//ff:func feature=validate type=rule control=sequence
//ff:what F1: validates one func per file
//ff:why Primary citizen is AI agent. 1 file 1 concept prevents context pollution.
//ff:checked llm=gpt-oss:20b hash=a3f8c1d2
func CheckOneFileOneFunc(gf *model.GoFile) []model.Violation {
| 注解 | 内容 | 是否必需 |
|---|---|---|
//ff:func | func 文件的 feature、type、control 等元数据 | func 文件必需 |
//ff:type | type 文件的 feature、type 等元数据 | type 文件必需 |
//ff:what | 一行描述——这个函数/类型做什么 | 必需 |
//ff:why | 为什么这样设计——用户决策的依据 | 可选 |
//ff:checked | LLM 验证签名(llmc 自动生成) | 自动 |
格式为 //ff:key key1=value1 key2=value2。可以用 grep/ripgrep 立即搜索,工具可以作为结构化键值对解析。与 Go 的 //go:generate、//go:embed 约定相同的模式。
control — 1 func 1 control
control= 对每个 func 文件都是必需的。值是三选一:
| control | 含义 | depth 限制 |
|---|---|---|
sequence | 顺序执行 | 2 |
selection | 分支(switch) | 2 |
iteration | 循环(loop) | dimension + 1 |
基于 Bohm-Jacopini 定理(1966)。所有程序都是 sequence、selection、iteration 三种控制结构的组合。filefunc 在函数级别强制执行——一个函数只有一种控制流。
control=iteration 的函数还必须有 dimension=。声明遍历数据的维度。dimension=1 表示扁平列表(depth ≤ 2),dimension ≥ 2 则需要命名类型(struct/interface)嵌套。
filefunc 也验证 control 注解与实际代码的一致性。control=selection 没有 switch,或 control=sequence 有 switch 或 loop,都是 ERROR。
LLM 导航管道
注解起到搜索索引的作用。不需要向量嵌入等重型基础设施。
1. 结构性缩减(不需要 LLM,grep)
基于代码书构造 grep 查询
→ 提取 20-30 个候选文件
2. 元数据判定(不需要 LLM 或超小型)
只 read 每个文件顶部的注解
→ 通过 name/input/output/what 缩减到 5-10 个
3. 精确工作(大型 LLM,最小上下文)
只 full read 5-10 个文件
→ 修改/生成代码
随着阶段推进,上下文越来越小。大型 LLM 投入时,只剩下真正需要的文件。
CLI
validate — 代码结构规则验证
filefunc validate # 当前目录
filefunc validate /path/to/project # 显式项目根目录
filefunc validate --format json
项目根目录需要 go.mod 和 codebook.yaml。只读。违规时 exit code 1。
chain — 调用关系追踪
filefunc chain func RunAll # 1度(默认)
filefunc chain func RunAll --chon 2 # 2度(包含一起调用的函数)
filefunc chain func RunAll --chon 3 # 3度(最大)
filefunc chain func RunAll --child-depth 3 # 仅下游调用
filefunc chain func RunAll --parent-depth 3 # 仅上游调用者
filefunc chain feature validate # 整个 feature
实时 AST 分析。--chon 是关系距离。1度是直接调用/被调用,2度包含一起被调用的函数。
现有的 go callgraph 对所有调用进行静态分析,产生数千个节点。chain 只在同一 feature 内追踪。代码书的 feature 就是缩放级别。
llmc — LLM 验证
filefunc llmc # 当前目录
filefunc llmc --model qwen3:8b
filefunc llmc --threshold 0.9
使用本地 LLM(ollama)验证 //ff:what 是否与 func body 一致。0.0~1.0 评分,默认阈值 0.8。通过后自动记录 //ff:checked llm=模型名 hash=哈希。body 变更后 hash 不同,需要重新验证。
注解漂移的核心问题——自然语言的 //ff:what 无法机械验证——用小型 LLM 解决。1 file 1 func 保证了文件级 1:1 对应,使这种方法可行。
规则
文件结构规则
| 规则 | 违规时 |
|---|---|
| 一个文件一个 func(文件名 = 函数名) | ERROR |
| 一个文件一个 type(文件名 = 类型名) | ERROR |
| 方法:1 file 1 method | ERROR |
| init() 不能单独存在(需与 var 或 func 一起) | ERROR |
_test.go 允许多个 func | 例外 |
| 语义上属于一组的 const 可以在同一文件 | 例外 |
代码质量规则
| 规则 | 违规时 |
|---|---|
| 嵌套深度:sequence=2,selection=2,iteration=dimension+1 | ERROR |
| func 最大 1000 行 | ERROR |
| 建议:sequence/iteration 100 行,selection 300 行 | WARNING |
嵌套深度限制因 control 类型而异。sequence 和 selection 为 depth 2。iteration 为 dimension + 1——dimension=1(扁平列表)时 depth 2,dimension=2(嵌套结构)时 depth 3。结合 Go 的 early return 模式,大多数代码都能舒适地在这些限制内。
selection(switch)的 case 往往较长,所以建议行数为 300。
.ffignore
在项目根目录放置 .ffignore 文件,所有 filefunc 命令都会排除该路径。语法与 .gitignore 相同。
vendor/
*.pb.go
*_gen.go
internal/legacy/
用于排除生成的代码(protobuf、代码生成输出)或外部 vendor 代码,这些代码无法合理地强制执行 filefunc 规则。
与 whyso 联动
func = file,因此函数的变更历史精确对应到文件级别。
whyso history check_ssac_openapi.go # CheckSSaCOpenAPI 函数的变更历史
一个文件有多个函数时,需要翻 diff 才知道哪个函数变了。filefunc 下文件变更 = 函数变更。追踪成本为零。
隐式耦合检测
whyso coupling check_ssac_openapi.go
在同一请求中一起修改的函数:
check_response_fields.go 8 次
check_err_status.go 5 次
types.go 4 次
没有显式关系却在耦合统计中反复出现,是隐藏依赖的信号。可能是从不同角度实现同一业务规则,没有 interface 却隐式匹配格式,或者 bug 总是一起出现。
为什么限于 Go
如果不是 Go,filefunc 的结构化不容易实现。gofmt 强制代码格式,early return 是惯例,没有异常,包 = 目录。扩展到其他语言需要 gofmt 级别的结构强制策略。这超出了 filefunc 的范围。
适用对象也很明确。后端服务、CLI 工具、代码生成器、SSOT 验证器。算法库、底层系统编程、性能关键的热路径不在对象范围内。
总结
LLM 时代的代码结构应该针对 AI 的导航效率进行优化,而不是人的浏览便利。filefunc 是这一转变的第一步。
一个文件一个概念。用代码书规范化词汇,用注解附加元数据,用一次 grep 找到精确的文件。read 一个文件时不会带来不必要的代码。上下文污染在文件结构层面被阻断。
代码:github.com/park-jun-woo/filefunc
参考文献
- Nelson F. Liu, Kevin Lin, John Hewitt, Ashwin Paranjape, Michele Bevilacqua, Fabio Petroni, Percy Liang (2024). “Lost in the Middle: How Language Models Use Long Contexts.” Transactions of the Association for Computational Linguistics, vol. 12. Link
- Yufeng Du, Minyang Tian et al. (2025). “Context Length Alone Hurts LLM Performance Despite Perfect Retrieval.” Findings of EMNLP 2025. Link
- Minglai Yang, Ethan Huang, Liang Zhang, Mihai Surdeanu, William Yang Wang, Liangming Pan (2025). “How Is LLM Reasoning Distracted by Irrelevant Context?” EMNLP 2025 Main Conference. Link
- Uswat Yusuf, Genevieve Caumartin, Diego Elias Costa (2025). “Beyond More Context: How Granularity and Order Drive Code Completion Quality.” ASE 2025. Link
- Weili Cao, Xunjian Yin, Bhuwan Dhingra, Shuyan Zhou (2026). “Coding Agents are Effective Long-Context Processors.” arXiv preprint. Link
- Han Li, Letian Zhu, Bohan Zhang et al. (2026). “ContextBench: A Benchmark for Context Retrieval in Coding Agents.” arXiv preprint. Link
- Kishan Maharaj et al. (2026). “Robustness and Reasoning Fidelity of Large Language Models in Long-Context Code Question Answering.” arXiv preprint. Link
- Carlos E. Jimenez, John Yang, Alexander Wettig, Shunyu Yao, Kexin Pei, Ofir Press, Karthik R. Narasimhan (2024). “SWE-bench: Can Language Models Resolve Real-World GitHub Issues?” ICLR 2024 (Oral). Link
- Corrado Bohm, Giuseppe Jacopini (1966). “Flow Diagrams, Turing Machines, and Languages with Only Two Formation Rules.” Communications of the ACM, vol. 9, no. 5. Link
- David L. Parnas (1972). “On the Criteria to Be Used in Decomposing Systems into Modules.” Communications of the ACM, vol. 15, no. 12. Link
相关文章: Ratchet Pattern — 让智能体坚持到底的方法 — filefunc 的背景模式。基于 Verifier 的单向执行。
相关文章: 模型智商不如反馈拓扑 — 为什么反馈结构比模型性能更能决定结果。
变更历史
- 2026-03-16: 初版