我对LLM代码风格与Token成本的发现

Hacker News Top 新闻

摘要

本文讨论了LLM代码风格选择如何影响Token消耗和成本,并提供了优化建议,如使用Web API标准和更简单的缩进以减少输出Token。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/25 05:15

# 我发现的关于 LLM 代码风格与 Token 成本问题 来源:https://www.jimmont.com/llm-style-token-costs *花输出 token 来分享它——趁涨价之前。* Jim(https://www.jimmont.com/) --- ## 缘起 过去一年,我一直在用 Claude 创建和审查功能。我观察到 token 消耗与遗留模式之间那种明显的张力。每当我以为某件事已完成,新问题就冒出来了——回归、边界情况之类。与此同时,我眼看着价格稳步、缓慢且不可避免地走向最终的全价费率。伴随这一现象,我长期以来的动力就是始终站在现代 Web 工作的实用前沿——那个几乎所有功能都能减少代码行数并提高质量的最佳点。在这个点上我常常想:为什么我会得到这样的输出?为什么这句代码会出现,而不是那个已经存在多年的方案? 我通常用这样一个可观察到的事实来打消疑虑:Claude 充其量不过是个初级水平,只是面试中那种百科全书式知识的有用近似。为了在某件事上取得进展,我不得不审视自己的实践,并查找那些惊人的 token 用量究竟从何而来。每一个都是输出 token——在 API 定价中,它们比输入 token 贵 **好几倍**(3 到 5 倍!!!)。这些模式更长、更脆弱、更不安全,而且解决的问题平台早已解决——往往在多年前就已经解决了。 这足以让人开始想象,是不是存在某种阴谋,要把整个 Web 平台往回拉——恰恰在 Ryan Dahl、Alex Russell、Dimitri Glazkov(以及许多其他人)让 Web Components 等变得伟大之后。他们真正让整个 Web 平台再次变得伟大。而这一切只是为了从 token 上榨取一点回报。 所以,为了这个阴谋论,下面就是我正在发现的事情。作为一个使用语言、设计字体、早期就开始编程、同时还画画和从事其他各种古怪爱好的人类,我其实把诸如制表符之类的东西视为一项了不起的创新。我可以直接把缩进减少到一个字符,而不是去问别人如何定义或请求使用许可的某种抽象。(我想我只是太过平等主义,以至于无法欣赏整个软件社区的排他态度。)我关心人类,希望事物在某种精简的基准上运转。把东西乘以 4 或某个任意数字——对我来说,这真的没有意义。 我还能继续,但这或许已经确定了我的取向——一个在实际媒介上处理过实际语言、并对什么东西运作良好、什么东西不奏效有自己见解的人。那一部分往往不言自明。我提到这些,是因为它影响了我从一个纯粹实用主义角度所审视的内容。我并不是在主张每个人都应该使用制表符(尽管这一点本身不言自明)。我是在揭示那些塑造了我一直持有的观点的背景——总有一个经济上的论点我保留在心里,而现在它正以真实 API 成本的形式显现出来。我对约定的看法不是这篇文章的重点。我要分享的是 token 使用优化。这样你也能受益。如果你仍想使用多个空格,我会提醒自己:文献说这似乎没问题,而 LLM 也不懂得更好的做法。 ## 地球上最简单的 Token 优化已在运行时中 Deno(https://docs.deno.com/runtime/)以及 Cloudflare Workers 等运行时原生实现了 Web API 接口(https://developer.mozilla.org/docs/Web/API)——`URL`、`URLSearchParams`、`fetch`、`FormData`、`Headers`、`Request`、`Response`、`AbortController`、`ReadableStream`、`crypto` 等等——这些与浏览器中运行的完全相同。这是 Deno 刻意做出的架构选择,而 WinterCG(https://wintercg.org/)也一直在将其正式化为跨运行时的最小公共 API 接口。这具有一个重要的实际后果:**同一个 API 接口同时覆盖浏览器端和服务器端代码**。没有转换层,没有垫片,没有适配成本。平台已经正确、安全、无依赖地解决了一大类问题。Deno 还特别提供了一个标准库(https://docs.deno.com/runtime/reference/std/),以补充可能缺少的东西并提供跨平台解决方案。 如果你不告诉 LLM,它并不知道你环境中的这一点。它的训练语料主要来自这些 API 普及之前的 Node.js 代码——`require('url')`、`querystring.parse()`、`express` 中间件模式、带有自定义超时包装的 `axios`、用于表单解析的 `multer`。这些模式在模型学习到的数据中占据统计主导地位。模型自然会选择它们。 **模型默认选择的模式与平台已经提供的模式之间的差距,正是大部分输出 token 成本的来源。** ## 各模式的规模估算 我一直在一边实践一边估算 token 的经济性。这些估算基于模式的实际长度,而非正式研究,但比例足够一致,具有参考价值。 ### 查询参数解析 ``` // 模型默认——手动解析(约 140 个 token) const parts = rawUrl.split('?'); const pairs = parts[1] ? parts[1].split('&') : []; const params = {}; pairs.forEach(p => { const [k, v] = p.split('='); params[decodeURIComponent(k)] = decodeURIComponent(v); }); // Web API(约 12 个 token) const params = Object.fromEntries(new URL(rawUrl).searchParams); ``` 大约 140 个 token 对比 12 个——每次出现减少约 90%。手动版本在键格式错误时会静默失败,重复参数只保留最后一个值,并且如果键是 `__proto__`,则存在原型污染风险。原生版本根据规范处理所有情况。 ### 表单数据 ``` // 模型默认——每个字段的状态(一个 3 字段表单约 200+ token) const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [role, setRole] = useState(''); const handleChange = (e) => setFields({ ...fields, [e.target.name]: e.target.value }); // Web API(约 14 个 token) const data = Object.fromEntries(new FormData(event.target)); ``` 模型会为每个字段生成状态追踪和变化处理程序。原生版本一次调用即可获取整个表单。大约 200–250 个 token 对比 14 个,具体取决于字段数量——而原生版本处理二十个字段时成本相同。 ### Fetch 生命周期与取消 ``` // 模型默认(约 90 个 token) let timer; const controller = new AbortController(); timer = setTimeout(() => controller.abort(), 5000); try { const res = await fetch(url, { signal: controller.signal }); } finally { clearTimeout(timer); } // Web API(约 12 个 token) const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); ``` 手动版本在重构时如果遗漏了 `finally` 路径,会导致定时器泄漏。原生版本无需管理生命周期。 ### 并行异步与故障隔离 ``` // 模型默认(约 100 个 token) let anyFailed = false; const results = await Promise.all( tasks.map(t => t.catch(e => { anyFailed = true; return null; })) ); if (anyFailed) { /* 现在怎么办?*/ } // Web API(约 10 个 token) const results = await Promise.allSettled(tasks); ``` `Promise.allSettled()` 为每个任务返回一个结构化结果,包含 `"fulfilled"` 或 `"rejected"` 的 `.status` 以及对应的值或原因。手动版本丢失了错误详情,并在每次使用时发明一种新的临时状态约定。 ### UI 组件 ``` // 模型默认——自定义模态框(约 250 个 JS 生命周期管理 token) const [isOpen, setIsOpen] = useState(false); useEffect(() => { if (isOpen) document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; }, [isOpen]); // ... aria 属性、键盘陷阱、背景点击处理程序 ... // 语义化 HTML(约 25 个 token) <dialog>...</dialog> // 浏览器处理焦点陷阱、Escape 键、无障碍树、背景层 ``` `<dialog>` 自 2022 年起在所有主流浏览器中得到支持。`<details>/<summary>` 用于手风琴,原生 `constraint validation`(`required`、`type="email"`、`pattern`、`minlength`)——这些并不冷门。模型选择 JavaScript 实现是因为那是其训练数据中的内容。除非另有指示,否则它会一直这样做。 ### 完整的 Deno 请求处理程序 累积效应在这里变得显著。一个解析请求参数、读取表单体、查询数据库并返回响应的 Deno 处理程序——用模型默认风格编写——仅样板代码就需要 400–600 个输出 token,应用程序逻辑还不包括在内。使用原生 API 编写的相同处理程序只需要 60–90 个 token。这不是边际改进。 ``` // 全程使用原生 Web API(约 70 个基础设施 token) export async function handler(request) { const { searchParams } = new URL(request.url); const tenantId = searchParams.get('tenant'); const data = Object.fromEntries(new FormData(await request.formData())); const result = await db.query(` SELECT id, name FROM records WHERE tenant_id = ? AND active = 1 `).bind(tenantId).first(); return Response.json(result); } ``` ## 安全性与可靠性成为结构性成果 这一点值得直接说明,而不是作为脚注。转向原生 API 不仅能降低 token 成本,还能消除多类错误。手动查询字符串解析使用 `params[key] = value` 是原型污染向量。手动 `decodeURIComponent` 在特定位置出现 `%` 时静默失败。基于 `setTimeout` 的自定义中止模式在重构时跳过清理路径会导致泄漏。自定义表单状态跟踪在添加字段但处理程序未更新时会带来一致性问题。自制模态框焦点管理经常破坏键盘导航和屏幕阅读器。 原生实现符合规范。它们已经过真实网络流量中所有边缘情况的测试。Web Platform Tests 套件针对每个浏览器和运行时运行数万次互操作性测试。`URLSearchParams` 正确处理 `+` 编码、重复参数、空值和 UTF-8 边缘情况,因为它是根据定义正确含义的规范编写的。模型手动编写的等价代码只能处理作者那天想到的情况。 **这不是一个小小的可靠性改进。这是代码由编写规范的人实现一次,与代码由模式匹配系统根据包含部分错误实现的语料库从记忆中编写之间的区别。** ## 注释实际上在做什么 我以前把注释视为文档——对人类有用,对 LLM 中性。MITRE 在 2025 年 6 月发表的研究(Sabetto 等(https://arxiv.org/abs/2506.11007),在 Claude、GPT-4、Llama 和 Mixtral 上进行了测试)改变了这一点。注释并非中性。**模型会遵循注释意图,即使它与代码矛盾。** 不准确的注释——描述重构前代码行为的注释——会积极降低 LLM 的理解能力,甚至低于无注释基线。比沉默更糟。过时的注释不是无害的,而是具有权威性的错误信息。当模型不断返回到我已放弃的模式时,该代码附近的过时注释是一个真正的候选原因。 什么样的注释有价值——实际携带有用信息的是设计意图、约束条件。为什么这个函数不捕获自己的错误。为什么 SQL 在数据库层面过滤而不是在应用程序代码中。重构时哪些绝对不能改变。非显而易见选择的原因。这些是信号。“遍历项目”放在 `items.forEach()` 之上是噪音,只会增加 token 却没有回报。 ACL 2024 关于注释增强的研究(https://aclanthology.org/2024.findings-acl.809)支持了另一个方向:在带有注释的代码上训练的模型比在无注释代码上训练的模型表现更好。注释是语义桥梁。在推理时它们仍然携带信号,因此该信号的内容至关重要。 ## 格式化问题,正确权衡 这里有一个真正的发现。Pan、Sun 等人(https://arxiv.org/abs/2508.13666)(《可读性的隐藏成本》,2025 年 8 月)测量了跨数万个源文件的格式化带来的输入 token 开销。移除缩进、空行和对齐空白,平均减少了 24.5% 的输入 token 计数,且对 Claude 或 GPT-4 的准确性几乎没有影响。这是输入侧,且是真实存在的。 可操作的个体选择——无对齐空白、SQL 左对齐、函数体内无空行——在典型的 JS 条件下合计约能节省 5–10% 的输入 token。但输入 token 的价格是输出 token 的三分之一到五分之一。而原生 API 带来的输出节省不是 5–10%——而是每个模式 85–92%,且在每个出现位置累积。格式化工作值得做,但并非主戏。 我偏好左对齐的 SQL 有合理的技术理由:模型中的 SQL 训练数据主要是左对齐的,因此匹配该分布是有意义的。至于这是否能显著提高准确性,我无法引证一个受控的 JavaScript 研究。对我来说它看起来正确,论证也足够合理。 ## 我在提示词中放入了什么 [以及正在解决的问题] 真正改变模型输出的机制是在会话开始时命名的明确指令。一般的风格指南只会产生边际改进——Wang 等人(https://arxiv.org/abs/2407.00456)(ACM,2024–2025)在对风格感知提示的研究中发现了这一点。更好的方法是明确指定具体的 API,让正确答案在模型抓取训练数据默认值之前就可用。以下是我正在积极研究的内容。请注意常规使用“做这个”和“不要做那个”——这两者一起使用效果最佳。(这是通过在生成前约束概率空间起作用的,并且是你在本文各示例中可以看到的反复出现的建议。) ``` 使用原生 Web API:URL、URLSearchParams、FormData、AbortController、fetch、Headers、Request、Response、Promise.allSettled()、Promise.any() 使用语义化 HTML:<dialog>、<details>/<summary>,配合原生约束验证。 不要用 JavaScript 实现浏览器或 Deno 运行时已原生提供的功能 ``` 结合注释规范: ``` 注释说明设计约束、不变量和原因。而不是代码做什么。 不要写重复下一行代码的注释。 ``` 原生 API 指令是产生最明显输出质量与成本差异的那一条。 ## 结论 核心发现是结构性的,而非技巧。Deno 选择原生实现 Web API 接口,创建了一组一致且相同的抽象,在浏览器和服务器上工作方式完全相同。这个接口正确、安全且免费地解决了一大类问题——而 LLM 目前正在从零开始、糟糕地重新解决这些问题,每次生成都要多花 85–92% 的 token 成本。 注释的发现之所以重要,是因为模型将其视为权威输入而非元数据。过时注释会产生完全错误的输出。准确的设计意图注释则会在有用方向上约束生成。格式化的发现是真实的且值得应用,但相对于 API 问题而言是次要的。 让我印象深刻的是,这里最大的杠杆——在基础设施代码上实现 7–10 倍的输出 token 减少,同时消除整个类别的安全性和可靠性问题——并不是一种新的编码技术,而是使用平台已经构建好的东西。障碍在于模型不知道要使用它,除非你告诉它。一旦你告诉它,它就能保持一致。 模型不知道你的运行时已经内置了什么。必须有人知道——而这正是你雇佣专业人员而非仅仅运行模型的全部原因。 --- **来源** - Pan, Sun 等。《可见格式中的隐藏结构:代码可读性偏好及其对 LLM 效率的影响》(2025 年 8 月)arXiv:2508.13666。 - Sabetto 等。《代码注释对 LLM 代码生成的影响》(2025 年 6 月)arXiv:2506.11007。 - Wang 等。《在代码生成中激发链式风格:一项大规模实证研究》(2024–2025)ACM。 - ACL 2024 研究结果(Fang 等):代码注释增强提升了基于 LLM 的代码生成。ACL 2024 发现卷。

相似文章

降低LLM API成本的10种方法

Reddit r/AI_Agents

一份实用指南,列出了使用LLM API时降低成本的10种策略,包括模型选择、提示缓存、批处理以及监控费用。

大语言模型实际工作原理

Lobsters Hottest

深入剖析现代大语言模型的工作原理,涵盖从分词到下一个词预测的核心机制,无需复杂数学知识。

为了内容而内容

Armin Ronacher

作者探讨了LLM如何影响编码和日常语言中的用词,发现LLM偏好的词汇在编程会话和Google Trends中出现的频率均有所增加,这引发了人们对人类开始采用LLM写作风格的担忧。