摘要:关于用大型语言模型(LLM)帮忙写代码的网上讨论,总会有一些开发者跳出来说他们的体验很糟糕。他们常常问自己到底哪里做错了——为什么有些人说效果特别好,而他们自己试了却发现问题一大堆?
关于用大型语言模型(LLM)帮忙写代码的网上讨论,总会有一些开发者跳出来说他们的体验很糟糕。他们常常问自己到底哪里做错了——为什么有些人说效果特别好,而他们自己试了却发现问题一大堆?
用LLM写代码其实挺难的,也不那么直观。要想搞清楚怎么用它、什么时候用它好,得花不少功夫,而且现在也没什么指南能帮大家弄明白怎么用才最有效。
如果有人告诉你,用LLM编程很简单,那他们可能(无意中)误导了你。他们可能碰巧发现了一些好用的方法,但这些方法并不是每个人都能轻松掌握的。
我用了两年多的LLM,收获挺大。下面我试着把我的一些经验和直觉分享给你。
上下文是关键Context is King
要想从LLM那里得到好结果,大部分技巧都跟管理它的上下文Context有关——也就是你当前对话中的文字内容。
这个上下文不仅仅是你输入的那条提示:成功的LLM互动通常是以对话的形式进行的,上下文包括你发的每一条消息和LLM回复的每一条内容,它们都存在于当前的对话线索中。
当你开始一个新对话时,上下文会被重置为零。这一点很重要,因为当一个对话变得没用了,通常的解决办法就是清空对话,重新开始。
有些LLM编程工具不仅仅局限于对话。比如Claude Projects,它允许你预先加载大量文本作为上下文——最近还增加了直接从GitHub仓库导入代码的功能,我用得很多。
像Cursor和VS Code Copilot这样的工具会自动包含你当前编辑会话和文件布局的上下文,有时候你还可以用Cursor的@commands这样的机制来引入额外的文件或文档。
我之所以主要直接用ChatGPT和Claude的网页或应用界面,是因为这样更容易让我清楚地知道上下文里到底有什么。那些不让我清楚看到上下文的LLM工具,效果就没那么好。
你可以利用之前回复也是上下文的一部分这一点来帮你。对于复杂的编码任务,可以先让LLM写个简单版本,检查它是否能运行,然后再一步步迭代,构建更复杂的实现。
我经常通过丢进去一些已有的代码来开启一个新对话,然后和LLM一起修改它。
我最喜欢的代码提示技巧是,丢进去几个和我想要构建的东西相关的完整例子,然后提示LLM用它们作为灵感来做一个新项目。我在描述我的JavaScriptOCR应用时详细写过这一点,那个应用结合了Tesseract.js和PDF.js——这两个库我以前用过,所以我可以在提示中提供可运行的例子。
“感觉式编程”是个好学习方法
大概一个多月前,Andrej Karpathy发明了一个词叫“感觉式编程”(vibe-coding),这个词现在挺火的:
“有一种新的编程方式,我称之为‘感觉式编程’,就是你完全跟着感觉走,拥抱指数级变化,甚至忘了代码的存在。[...]我会问一些特别傻的问题,比如‘把侧边栏的内边距减半’,因为我懒得去找。我总是‘全部接受’,不再看差异。当我收到错误信息时,我就直接复制粘贴进去,不加任何注释,通常这样就能解决问题。”
Andrej说这“对于周末的临时项目来说还不错”。这也是探索这些模型能力的好方法——而且特别有趣。
学习LLM最好的方式就是玩它们。把荒谬的想法丢给它们,然后用感觉式编程直到它们勉强能工作,这确实是一个加速你建立直觉的好方法,让你知道什么有用、什么没用。
在Andrej给这种感觉式编程命名之前,我就已经在这么做了!我的simonw/tools GitHub仓库里有77个HTML+JavaScript应用和6个Python应用,每一个都是通过提示LLM构建的。我从构建这个集合中学到了很多,而且我每周都会增加几个新原型。
你可以在tools.simonwillison.net上直接试用我的大部分应用——这是GitHub Pages发布的版本。我在十月份写过一些更详细的笔记,记录了这周我用Claude Artifacts构建的所有东西。
如果你想看每个应用的聊天记录,几乎都可以在提交历史中找到链接——或者访问新的colophon页面,那里有一个包含所有链接的索引。
banq注:主要是这两点,其他点击标题见原文,个人以为使用Grok3/DeepSeek这种深度思考方式,它会逐步提示你分析过程:
1、例如我给出提示:
Cache-Control: max-age=3600Last-Modified: Mon, 10 Mar 2025 22:42:08 GMTExpires: Tue, 11 Mar 2025 00:34:47 GMT 这个时间设置对吗?Grok3巴拉巴拉分析半天:
我们来分析一下这些 HTTP 头字段的时间设置,看看是否有问题。给定的头字段:Cache-Control: max-age=3600表示资源可以缓存 3600 秒(即 1 小时),从响应生成或验证的时间开始计算。Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT表示资源最后修改的时间是 2025 年 3 月 10 日 22:42:08(GMT)。expires: Tue, 11 Mar 2025 00:34:47 GMT表示资源的过期时间是 2025 年 3 月 11 日 00:34:47(GMT),在此时间之前,缓存被认为是新鲜的。问题分析:时间设置是否合理?1. Cache-Control 和 Expires 的关系Cache-Control: max-age=3600 优先级高于 Expires,根据现代 HTTP 规范(RFC 7234),如果两者都存在,max-age 通常会覆盖 Expires。max-age=3600 表示缓存新鲜时间是 1 小时。Expires 给出的时间是 2025 年 3 月 11 日 00:34:47,而 Last-Modified 是 2025 年 3 月 10 日 22:42:08。我们计算一下 Expires 和 Last-Modified 之间的时间差:Last-Modified: 2025-03-10 22:42:08Expires: 2025-03-11 00:34:47时间差:从 22:42:08 到 00:34:47(次日),总共是 1 小时 52 分 39 秒(即 6759 秒)。2. 问题:max-age 和 Expires 不一致max-age=3600 表示缓存有效期是 3600 秒(1 小时)。但 Expires 和 Last-Modified 的时间差是 6759 秒(约 1 小时 52 分),远远大于 max-age 定义的 3600 秒。根据 HTTP 规范,如果 max-age 和 Expires 同时存在,max-age 优先。但这种不一致可能会让客户端(尤其是老旧的实现)感到困惑。3. 假设响应的生成时间HTTP 头的 Last-Modified 并不是响应的生成时间,但通常可以用作参考点。如果假设响应生成时间接近 Last-Modified(例如 2025-03-10 22:42:08),那么:根据 max-age=3600,缓存应该在 2025-03-10 23:42:08 过期。但 Expires 却指定了一个更晚的时间(2025-03-11 00:34:47),这会导致歧义。结论:时间设置有问题不一致性:max-age=3600 和 Expires 之间的有效期不匹配。max-age=3600 意味着缓存有效期是 1 小时。Expires 暗示的有效期是 6759 秒(约 1 小时 52 分)。这会导致客户端在选择缓存策略时可能出现混乱。建议修正:如果你想使用 max-age=3600,那么 Expires 应该设置为响应生成时间(或 Last-Modified 时间)加上 3600 秒。假设响应生成时间接近 Last-Modified(2025-03-10 22:42:08),那么 Expires 应该是:2025-03-10 22:42:08 + 3600 秒 = 2025-03-10 23:42:08 GMT。或者,如果想以 Expires 的时间为准(2025-03-11 00:34:47),那么 max-age 应该设置为 6759 秒。修正后的示例如果优先 max-age=3600:Cache-Control: max-age=3600Last-Modified: Mon, 10 Mar 2025 22:42:08 GMTExpires: Mon, 10 Mar 2025 23:42:08 GMT如果优先 Expires 时间:Cache-Control: max-age=6759Last-Modified: Mon, 10 Mar 2025 22:42:08 GMTExpires: Tue, 11 Mar 2025 00:34:47 GMT总结当前设置不合理,因为 max-age 和 Expires 的有效期不一致。建议根据实际需求选择一个为主(通常推荐 max-age),并让两者保持一致,以避免客户端行为不明确。2、然后我给出生成这个http头部信息的Java代码:
这是JSP页面头部,用Java设置的代码:public static boolean checkHeaderCache(long adddays, long modelLastModifiedDate, HttpServletRequest request,HttpServletResponse response) {if (request.getAttribute("myExpire") != null) {System.err.print(" checkHeaderCache called above twice times :" + request.getRequestURI);return true;}request.setAttribute("myExpire", adddays);// convert seconds to ms.try {// if over expire data, see the Etags;// ETags if ETags no any modifiedString etag = request.getHeader("If-None-Match");if (etag != null) {if (etag.equals(Long.toString(modelLastModifiedDate))) {// not modifiedresponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);return false;}} else {long header = request.getDateHeader("If-Modified-Since");if (header > 0) {if (modelLastModifiedDateGrok3又不辞辛苦分析加总结:
我来帮你分析这段 JSP 页面的缓存控制代码,结合你之前提到的 HTTP 头信息,找出问题并提供改进建议。代码的主要功能是处理 HTTP 缓存控制,通过 ETag 和 If-Modified-Since 检查资源是否修改,并设置响应头(如 Cache-Control, Last-Modified, Expires 等)。代码功能分析这段代码实现了标准的 HTTP 缓存验证逻辑,主要步骤如下:检查重复调用:通过 request.getAttribute("myExpire") 避免重复调用。ETag 验证:检查 If-None-Match 头,如果客户端发送的 ETag 与服务器端的 modelLastModifiedDate 匹配,返回 304 Not Modified。Last-Modified 验证:检查 If-Modified-Since 头,如果资源未修改(或修改时间差小于 1 秒),返回 304 Not Modified。设置响应头:如果资源有修改,调用 setEtagHaeder 设置新的 ETag。调用 setRespHeaderCache 设置缓存相关头(如 Cache-Control, Last-Modified, Expires)。与之前提到的 HTTP 头对应你之前提到的 HTTP 头:Cache-Control: max-age=3600Last-Modified: Mon, 10 Mar 2025 22:42:08 GMTExpires: Tue, 11 Mar 2025 00:34:47 GMT这些头很可能是由 setRespHeaderCache 方法设置的。我们推测 setRespHeaderCache 方法会根据 adddays 和 modelLastModifiedDate 参数设置这些头。之前分析过,这些头的时间设置不一致(max-age=3600 表示 1 小时,但 Expires 和 Last-Modified 的时间差是 6759 秒,约 1 小时 52 分)。问题可能出在 setRespHeaderCache 方法的实现上。问题分析从代码逻辑来看,当前代码在验证缓存方面没有明显问题,但结合你提供的 HTTP 头,可能存在以下问题:1. ETag 和 Last-Modified 的逻辑ETag:代码直接将 modelLastModifiedDate 转换为字符串作为 ETag。这种方式简单,但不够健壮。ETag 通常需要包含更多信息(如文件内容哈希),以更准确地标识资源是否变化。If-Modified-Since:代码比较了 modelLastModifiedDate 和 If-Modified-Since,并允许 1 秒的误差( 0) {// 将时间戳对齐到秒,避免毫秒级误差long modelLastModifiedInSeconds = modelLastModifiedDate / 1000 * 1000;long headerInSeconds = header / 1000 * 1000;if (modelLastModifiedInSeconds 0) {// 精确比较,避免误差if (modelLastModifiedDate3、我按照Grok3建议,修改代码后,将Http头部结果再发给它,果然符合Grok3预取,他一点都不骄傲地说修改符合预期,然后又给出进一步方案,我觉得已经符合我的预期,没有深挖下去,我的思路还没有与它同步深入。
这段代码重构见原文:ToolsUtil.java的checkHeaderCache方法。
原文:LLM编程:Context决定成败,vibe编程加速学习 - 极道
关于Context重要性的原创:Context与Content一字之差藏惊天玄机 - 极道
来源:解道Jdon