News Hacker|极客洞察

233 67 天前 silly.business
🤔在 agent 时代重估 literate programming:工具、风险与实践
真指望 agents 永远同步那些会腐烂的注释吗?

🎯 讨论背景

literate programming 最早由 Donald Knuth 提出,主张把叙述性说明与源代码合成一体以表达程序的“为什么”。近年来 LLM(大型语言模型)与 agent(以 LLM 为内核的多步自动化实体)能读写代码、执行示例并自动修改文档,因而引发社区重新评估 literate programming 的可行性。讨论围绕两个主线展开:一是把 prose 与可执行示例(notebooks、doctest、nbdev、Mechdown 等)联动,以利用 LLM 的生成与验证能力;二是担心自然语言模糊、文档漂移与 agent 幻觉会把问题放大,因此提出更轻量的注释、测试驱动的文档、CUE 这类确定性配置语言作为 guardrail,以及用 CI/LLM 工具检测或修正文档不一致。实践案例和工具(如 nbdev、Mechdown、Lean/Verso、promptless.ai、Jupyter、Org mode)在评论中被多次引用作为可行或待改进的路径。

📌 讨论焦点

自然语言模糊性与提示工程

评论普遍指出自然语言固有的歧义会削弱以散文指导代码的可预测性:LLM 在海量源码上训练,擅长 code→code 的映射,但把含糊的自然语言提示翻译成确定实现常常失败或产生多种合法实现。多位评论者建议用更刻板的提示语言(类似 legalese)或更精炼、可解析的提示格式来减少歧义,并把上下文塑造成结构化的“查询”(例如要求模型分析相关文件)以引导模型。还有人强调模型输出的非确定性——在相近语义之间可能随机选择行为,因此相比“大量上下文”,更小且真实的上下文往往更可靠。

[来源1] [来源2] [来源3] [来源4] [来源5] [来源6]

以 Notebooks 与专门工具实现的实用 literate programming

许多实践者展示了把 literate programming 与 notebook 工具结合的成功案例:nbdev(基于 Jupyter 的 literate 编程工具)及其与 LLM 集成形成的 Solveit 工作流已被团队广泛采用,说明 notebook+LLM 在实际协作中有用。有人介绍 Mechdown/Mech 的设计把 prose 嵌入语言 AST,使 AI 能访问实时类型和值信息来生成、运行与校验代码;Lean 4 的 Verso、Raku 的 Rakudoc/pod、以及把 notebook 当作可执行文档的做法也被引用为可行路径。这些示例强调把叙述性文本和可运行示例共置能减轻漂移、为 LLM 提供可验证上下文,从而提高协作质量。

[来源1] [来源2] [来源3] [来源4] [来源5] [来源6]

文档易腐、代理幻觉与可测试文档策略

反对者担心把更多叙述性 prose 加入代码会放大 agent 幻觉的空间:文档本就容易“腐烂”,一旦与实现失步,agent 可能自信地输出错误实现。多个评论建议把文档中的关键陈述变为可执行的断言和示例(如 doctest 或可运行 notebook 示例),并在 CI 中强制执行这些示例,使文档成为可测试的工件而非空洞陈述。社区讨论同时提到已有尝试用 LLM 检测或修正文档漂移(例如若干创业公司与 CI 集成方案),但也有人提醒这些自动化修正并非完美,需要人工判断与质量门控。

[来源1] [来源2] [来源3] [来源4] [来源5] [来源6]

轻量记录优先:好命名、测试与简洁注释

不少人认为全面的 literate programming 往往过度,实际收益大多可以靠良好的命名、类型签名、简洁的 docstrings 与完整测试获得。评论强调大量注释往往是代码异味(code smell),建议以模块级别的目的说明和每个函数的单句注释来传达意图,把“为什么”的长说明放在提交信息或 VCS 历史以便追溯。实践上更可行的折衷是文件/包级别的高层说明配合可执行示例,而不是把整份代码写成长篇叙事。

[来源1] [来源2] [来源3] [来源4] [来源5] [来源6]

以约定、小 API 与配置层约束 agents

另一组观点主张通过约定与更窄的目标语言来约束 agent 的行为:例如选用编译快、约定多的语言(如 Go)并要求 agent 遵循风格指南,可以让其产出更统一的代码。把可变部分下沉为配置层或用专门的配置语言(如 CUE)做确定性约束,也被提为降低 agent 出错率的手段。但实际项目经验也提醒:配置经常重复、跨仓库散落且缺乏全面测试,清理和维护这些约定需要成本和工程纪律。

[来源1] [来源2] [来源3] [来源4]

以代码为主、按需生成解释(agent 生成文档)

还有人认为与其维持大量 prose,不如把代码作为稳定契约,让 agent 按需生成或缓存说明:这样 prose 是瞬时视图,代码才是最终接口。实际做法包括保持稀疏注释并维持一个 master prompt、把常用说明以缓存形式贴在代码旁以加速响应,或用模板/质量门控控制 agent 的输出。该思路降低了人工维护文档的成本,但把信任转移到 agent 的稳定性、prompt 管理与变更监控上。

[来源1] [来源2] [来源3] [来源4] [来源5]

📚 术语解释

literate programming: 一种把叙述性 prose 与源代码交织的编程方法(由 Donald Knuth 提出),目的是把程序写成可按故事读的文档;优点是显式表达设计意图,缺点是工具链复杂且 prose 与实现会“漂移”。

Notebook(Jupyter / Org mode): 交互式文档环境,将文本、代码、执行结果与示例混合在同一文件中,便于把文档当作可执行示例来验证与同步(典型实现有 Jupyter notebooks 与 Emacs Org mode)。

nbdev: 基于 Jupyter 的 literate programming 工具链,支持在 notebook 中编写文档、测试并导出为可发布的库;评论中提到其与 LLM 集成形成 Solveit 工作流。

doctest: 把文档中的示例作为可执行测试嵌入(如 Python 的 doctest、Rust 的 doctest 注释),能把 prose 变为可验证断言以减少文档与实现的漂移。

tangling(tangle / weave): Knuth 的 literate workflow 中的术语,指从混合文档中提取源代码(tangle)或生成可读文档(weave);该流程被批评为需要额外工具链并增加维护成本。

CUE: 一种用于配置和验证的声明式语言(基于 value-lattice 的逻辑),可用来对配置与生成结果做确定性约束,常被提作 guardrail 来减少 LLM 生成的配置错误。

docstring: 嵌在函数或模块里的结构化注释(如 Python、Go 风格的 docstrings),可被工具提取为 API 文档;讨论关注其与实现同步的问题。

agents: 以 LLM 为内核、能执行多步任务的自动化实体(agents),可以读写代码、运行测试与修改文档,但存在非确定性、幻觉与权限/回归风险。

LLM: 大型语言模型(Large Language Model),用海量文本与源码训练的统计模型;在讨论中表现为能做 code→code 转换、生成注释与示例,但对自然语言提示敏感且输出非完全确定。