filefunc — 一文件一概念 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=validatetype=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:funcfunc 文件的 feature、type、control 等元数据func 文件必需
//ff:typetype 文件的 feature、type 等元数据type 文件必需
//ff:what一行描述——这个函数/类型做什么必需
//ff:why为什么这样设计——用户决策的依据可选
//ff:checkedLLM 验证签名(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.modcodebook.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 methodERROR
init() 不能单独存在(需与 var 或 func 一起)ERROR
_test.go 允许多个 func例外
语义上属于一组的 const 可以在同一文件例外

代码质量规则

规则违规时
嵌套深度:sequence=2,selection=2,iteration=dimension+1ERROR
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: 初版