LLM编程:Context决定成败,vibe编程加速学习

关于用大型语言模型(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用它们作为灵感来做一个新项目。我在描述我的JavaScript OCR应用时详细写过这一点,那个应用结合了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=3600  

Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT  

Expires: 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:08  
  
Expires: 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=3600  
Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT  
Expires: Mon, 10 Mar 2025 23:42:08 GMT  
  
如果优先 Expires 时间:  
Cache-Control: max-age=6759  
Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT  
Expires: 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 modified  
        String etag = request.getHeader("If-None-Match");  
  
        if (etag != null) {  
            if (etag.equals(Long.toString(modelLastModifiedDate))) {  
                // not modified  
                response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
                return false;  
            }  
        } else {  
            long header = request.getDateHeader("If-Modified-Since");  
            if (header > 0) {  
                if (modelLastModifiedDate <= header || (modelLastModifiedDate - header) < 1000) {  
                    // during the period not happend modified  
                    response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
                    return false;  
                }  
            }  
        }  
        // if th model has modified , setup the new modified date  
        setEtagHaeder(response, modelLastModifiedDate);  
        setRespHeaderCache(adddays, modelLastModifiedDate, request, response);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
    return true;  
}  

Grok3又不辞辛苦分析加总结:

代码功能分析  
这段代码实现了标准的 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=3600  
Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT  
Expires: 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 秒的误差(< 1000)。这种误差容忍可能是为了处理时间精度问题,但可能导致缓存不一致的风险(例如,资源刚好在 1 秒内修改,却被误判为未修改)。  
2\. setRespHeaderCache 方法的问题  
你没有提供 setRespHeaderCache 方法的具体实现,但根据输出的 HTTP 头(max-age=3600 和 Expires 不一致),可能存在以下问题:  
max-age 和 Expires 不匹配:max-age=3600 表示缓存 1 小时,但 Expires 对应的时间差是 6759 秒。这种不一致可能是因为:  
adddays 参数被误解或计算错误。  
Expires 头的计算逻辑与 max-age 不一致。  
adddays 参数的作用不明确:从方法签名看,adddays 应该是缓存的有效期(单位可能是天或秒)。如果单位是天,但代码误认为是秒,就会导致时间设置错误。  
3\. 时间精度问题  
modelLastModifiedDate 是以毫秒为单位的(Java 中的时间戳通常如此),但 HTTP 头的 Last-Modified 和 Expires 是以秒为单位的。代码中可能没有正确处理这种精度转换,导致时间差不准确。  
改进建议  
以下是针对代码和 HTTP 头的改进建议:  
1\. 修正 setRespHeaderCache 方法  
假设 setRespHeaderCache 方法的实现类似于以下逻辑:  
java  
private static void setRespHeaderCache(long adddays, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response) {  
    // 设置 Cache-Control  
    response.setHeader("Cache-Control", "max-age=" + adddays);  
  
    // 设置 Last-Modified  
    response.setDateHeader("Last-Modified", modelLastModifiedDate);  
  
    // 设置 Expires  
    long expires = System.currentTimeMillis() + (adddays \* 1000); // 假设 adddays 是秒  
    response.setDateHeader("Expires", expires);  
}  
问题:  
如果 adddays 是 3600(秒),那么 max-age=3600 是正确的,但 Expires 可能是基于当前时间计算的,导致与 Last-Modified 的时间差不等于 3600 秒。  
你提供的 Expires 是 2025-03-11 00:34:47,可能是因为 System.currentTimeMillis() 的值比 modelLastModifiedDate 晚了 3159 秒。  
改进:  
确保 Expires 和 max-age 一致。Expires 应该基于 Last-Modified 或响应生成时间加上 max-age。  
假设 adddays 是缓存的秒数,修正后的实现如下:  
java  
private static void setRespHeaderCache(long adddays, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response) {  
    // 设置 Cache-Control  
    response.setHeader("Cache-Control", "max-age=" + adddays);  
  
    // 设置 Last-Modified  
    response.setDateHeader("Last-Modified", modelLastModifiedDate);  
  
    // 设置 Expires,基于 Last-Modified 时间加上 max-age(单位:毫秒)  
    long expires = modelLastModifiedDate + (adddays \* 1000);  
    response.setDateHeader("Expires", expires);  
}  
如果 adddays = 3600,modelLastModifiedDate = 2025-03-10 22:42:08 GMT(即 1741560128000 毫秒),那么:  
Expires = 1741560128000 + (3600 \* 1000) = 1741563728000 毫秒  
转换为日期:2025-03-10 23:42:08 GMT,与 max-age=3600 一致。  
2\. 改进 ETag 生成  
当前代码将 modelLastModifiedDate 直接作为 ETag,不够健壮。建议使用更强的 ETag,例如结合文件内容的哈希值:  
java  
private static void setEtagHaeder(HttpServletResponse response, long modelLastModifiedDate) {  
    // 假设有某种方式获取资源内容的哈希值  
    String contentHash = computeContentHash(); // 伪代码,需根据实际情况实现  
    String etag = "\\"" + contentHash + "-" + modelLastModifiedDate + "\\"";  
    response.setHeader("ETag", etag);  
}  
  
private static String computeContentHash() {  
    // 伪代码:计算资源的哈希值(例如 MD5 或 SHA-1)  
    return "some-hash-value";  
}  
然后在 checkHeaderCache 中比较 ETag 时,也需要解析并验证:  
java  
String etag = request.getHeader("If-None-Match");  
if (etag != null) {  
    String expectedEtag = "\\"" + computeContentHash() + "-" + modelLastModifiedDate + "\\"";  
    if (etag.equals(expectedEtag)) {  
        response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
        return false;  
    }  
}  
3\. 处理时间精度  
HTTP 头的时间精度是秒,但 Java 的时间戳是毫秒。建议在设置和比较时间时,统一处理精度:  
java  
long header = request.getDateHeader("If-Modified-Since");  
if (header > 0) {  
    // 将时间戳对齐到秒,避免毫秒级误差  
    long modelLastModifiedInSeconds = modelLastModifiedDate / 1000 \* 1000;  
    long headerInSeconds = header / 1000 \* 1000;  
    if (modelLastModifiedInSeconds <= headerInSeconds) {  
        response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
        return false;  
    }  
}  
4\. 明确 adddays 的单位  
adddays 参数的命名容易让人误解为“天”,但从 max-age=3600 来看,单位应该是秒。  
建议改名为 maxAgeSeconds 并在文档中明确说明:  
java  
public static boolean checkHeaderCache(long maxAgeSeconds, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response)  
5\. 避免硬编码误差容忍  
当前代码允许 1 秒的误差(modelLastModifiedDate - header < 1000),但这可能不适合所有场景。建议将误差容忍作为参数传入,或者移除硬编码,直接比较时间戳。  
修正后的完整代码  
以下是改进后的 checkHeaderCache 方法和相关方法:  
java  
public static boolean checkHeaderCache(long maxAgeSeconds, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response) {  
    if (request.getAttribute("myExpire") != null) {  
        System.err.println("checkHeaderCache called above twice times: " + request.getRequestURI());  
        return true;  
    }  
    request.setAttribute("myExpire", maxAgeSeconds);  
  
    try {  
        // ETag 验证  
        String etag = request.getHeader("If-None-Match");  
        if (etag != null) {  
            String expectedEtag = "\\"" + modelLastModifiedDate + "\\""; // 简化版,实际应包含内容哈希  
            if (etag.equals(expectedEtag)) {  
                response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
                return false;  
            }  
        }  
  
        // If-Modified-Since 验证  
        long header = request.getDateHeader("If-Modified-Since");  
        if (header > 0) {  
            // 精确比较,避免误差  
            if (modelLastModifiedDate <= header) {  
                response.setStatus(HttpServletResponse.SC\_NOT\_MODIFIED);  
                return false;  
            }  
        }  
  
        // 设置新的响应头  
        setEtagHaeder(response, modelLastModifiedDate);  
        setRespHeaderCache(maxAgeSeconds, modelLastModifiedDate, request, response);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
    return true;  
}  
  
private static void setEtagHaeder(HttpServletResponse response, long modelLastModifiedDate) {  
    response.setHeader("ETag", "\\"" + modelLastModifiedDate + "\\"");  
}  
  
private static void setRespHeaderCache(long maxAgeSeconds, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response) {  
    // 设置 Cache-Control  
    response.setHeader("Cache-Control", "max-age=" + maxAgeSeconds);  
  
    // 设置 Last-Modified  
    response.setDateHeader("Last-Modified", modelLastModifiedDate);  
  
    // 设置 Expires,基于当前时间加上 max-age  
    long expires = modelLastModifiedDate + (maxAgeSeconds \* 1000);  
    response.setDateHeader("Expires", expires);  
}  
修正后的 HTTP 头输出  
假设:  
maxAgeSeconds = 3600(1 小时)  
modelLastModifiedDate = 2025-03-10 22:42:08 GMT(1741560128000 毫秒)  
修正后的 HTTP 头将是:  
Cache-Control: max-age=3600  
Last-Modified: Mon, 10 Mar 2025 22:42:08 GMT  
Expires: Mon, 10 Mar 2025 23:42:08 GMT  
ETag: "1741560128000"  
这样,max-age 和 Expires 就一致了,符合 HTTP 规范。  
  
总结  
当前代码的逻辑基本正确,但 setRespHeaderCache 方法可能导致 max-age 和 Expires 不一致。  
建议明确 adddays 参数的单位,确保 Expires 和 max-age 一致。  
改进 ETag 生成方式,增强缓存验证的准确性。  
处理时间精度问题,避免毫秒和秒之间的误差。  
如果有 setRespHeaderCache 方法的具体实现,可以进一步分析和优化。  


3、我按照Grok3建议,修改代码后,将Http头部结果再发给它,果然符合Grok3预取,他一点都不骄傲地说修改符合预期,然后又给出进一步方案,我觉得已经符合我的预期,没有深挖下去,我的思路还没有与它同步深入。
这段代码重构见这里:ToolsUtil.java的checkHeaderCache方法。

关于Context重要性见我原创:Context与Content一字之差暗藏惊天玄机

原文:https://www.jdon.com/78139.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值