谈谈对话式软件开发
创始人
2025-07-05 04:41:29
0

利用大语言模型生成代码是改变生活的方式之一,但与它们就软件开发过程进行交谈也同样重要。Jon Udell继续探索大语言模型为程序员带来的价值。

译自 Let’s Talk: Conversational Software Development 。

以下是启动了一次非常成功的人工智能ChatGPT交互的提示。

PROMPT:
I’ll give you a test script and the output it is testing, and ask about strategies to improve the test, OK? Here’s the script.

#!/bin/bash

set -eu

list_tickets() {
    local output
    output=$($command $subcommand run list_tickets 2>&1) || {
        echo "Command failed with error: $output"
        exit 1
    }
    echo "$output"
}

output=$(list_tickets)
count=$(echo "$output" | jq -r '.tickets_list | fromjson | length')

# Check if count is greater than 0
if [[ $count -gt 0 ]]; then
    echo "PASS: list_tickets count is greater than 0."
else
    echo "FAIL: list_tickets count is 0 or less."
fi

一次过路人的见解

该提示是一个有意设置的朴实版本的bash脚本,我设想的脚本。我本来也打算在最初的提示中提供样本输出,但忘记这么做了。回想起来,不提供输出而先让ChatGPT专注于脚本本身可能是一个好主意。它的一些观察包括:

RESPONSE:
While you’ve used set -eu to exit on any error or unset variable usage, consider using set -euo pipefail to also capture failures in any part of a pipeline.

这是一个很好的关于在编码时如何从LLM中隐性地获取知识的例子。我之前对bash中的严格模式有一些了解,但不完整。因此我们暂停了一会儿来探索这个话题,我加深了知识,并记下考虑使用其他-o参数 —— 尤其是 -x(xtrace) 和 -n(noexec) 用于调试。我们刚刚开始处理手头的实际任务,但这个小小的绕道本身就很有价值。

让我们来谈谈JSON

我现在回头提供了样本输出:

PROMPT:
“tickets_list”: “[{\”associates_rdb\”:null,\”association_type\”:null,\”cc_email\”:{\”cc_emails\”:[],\”fwd_emails\”:[],\”reply_cc\”:[],\”tkt_cc\”:[]},\”created_at\”:\”2023-10-12T12:15:00+01:00\”,\”custom_field\”:{\”cf_reference_number_2939992\”:null},\”datetime_tc01\”:null,\”datetime_tc02\”:null,\”datetime_tc03\”:null,\”deleted\”:false,\”delta\”:true,\”description\”:\”world\”,\”description_html\”:\”\\u003cdiv\\u003eworld\\u003c/div\\u003e\”,\”dirty\”:0,\”display_id\”:17,\”due_by\”:\”2023-10-16T22:00:00+01:00\”,\”email_config_id\”:153000020300,\”frDueBy\”:\”2023-10-12T22:00:00+01:00\”,\”fr_escalated\”:true,\”group_id\”:null,\”id\”:153002218563,\”int_tc01\”:null,\”int_tc02\”:null,\”int_tc03\”:null,\”int_tc04\”:null,\”int_tc05\”:null,\”internal_agent_id\”:null,\”internal_group_id\”:null,\”isescalated\”:false,\”json_tc01\”:null,\”long_tc01\”:null,\”long_tc02\”:null,\”long_tc03\”:null,\”long_tc04\”:null,\”long_tc05\”:null,\”nr_due_by\”:null,\”nr_escalated\”:false,\”nr_reminded\”:false,\”owner_id\”:null,\”parent_ticket_id\”:null,\”priority\”:1,\”priority_name\”:\”Low\”,\”product_id\”:153000018092,\”requester_id\”:153002362554,\”requester_name\”:\”Jon Udell\”,\”requester_status_name\”:\”Open\”,\”responder_id\”:null,\”responder_name\”:\”No Agent\”,\”sl_escalation_level\”:null,\”sl_manual_dueby\”:null,\”sl_merge_parent_ticket\”:null,\”sl_product_id\”:null,\”sl_skill_id\”:null,\”sl_sla_policy_id\”:null,\”sla_state\”:0,\”source\”:2,\”source_name\”:\”Portal\”,\”spam\”:false,\”st_survey_rating\”:null,\”status\”:2,\”status_name\”:\”Open\”,\”subject\”:\”hello\”,\”ticket_type\”:null,\”to_email\”:null,\”to_emails\”:null,\”trained\”:false,\”updated_at\”:\”2023-10-12T22:05:04+01:00\”,\”urgent\”:false},{\”associates_rdb\”:null,\”association_type\”:null,\”cc_email\”:{\”bcc_emails\”:[],\”cc_emails\”:[],\”fwd_emails\”:[],\”reply_cc\”:[],\”tkt_cc\”:[]},\”created_at\”:\”2023-10-12T04:41:12+01:00\”,\”custom_field\”:{\”cf_reference_number_2939992\”:null},\”datetime_tc01\”:null,\”datetime_tc02\”:null,\”datetime_tc03\”:null,\”deleted\”:false,\”delta\”:true,\”description\”:\”Hello there, Our Report metrics over the last week is at zero and can’t be correct? Are you facing any issues?\”,\”description_html\”:\”Hello there, Our Report metrics over the last week is at zero and can’t be correct? Are you facing any issues?\”,\”dirty\”:0,\”display_id\”:6,\”due_by\”:\”2023-10-16T22:00:00+01:00\”,\”email_config_id\”:153000020300,\”frDueBy\”:\”2023-10-12T22:00:00+01:00\”,\”fr_escalated\”:false,\”group_id\”:153000077019,\”id\”:153002214584,\”int_tc01\”:null,\”int_tc02\”:null,\”int_tc03\”:null,\”int_tc04\”:null,\”int_tc05\”:null,\”internal_agent_id\”:null,\”internal_group_id\”:null,\”isescalated\”:false,\”json_tc01\”:null,\”long_tc01\”:null,\”long_tc02\”:null,\”long_tc03\”:null,\”long_tc04\”:null,\”long_tc05\”:null,\”nr_due_by\”:null,\”nr_escalated\”:false,\”nr_reminded\”:false,\”owner_id\”:null,\”parent_ticket_id\”:null,\”priority\”:1,\”priority_name\”:\”Low\”,\”product_id\”:153000018092,\”requester_id\”:153002465401,\”requester_name\”:\”Adam Doe\”,\”requester_status_name\”:\”Open\”,\”responder_id\”:null,\”responder_name\”:\”No Agent\”,\”sl_escalation_level\”:null,\”sl_manual_dueby\”:null,\”sl_merge_parent_ticket\”:null,\”sl_product_id\”:null,\”sl_skill_id\”:null,\”sl_sla_policy_id\”:null,\”sla_state\”:0,\”source\”:1,\”source_name\”:\”Email\”,\”spam\”:false,\”st_survey_rating\”:null,\”status\”:2,\”status_name\”:\”Open\”,\”subject\”:\”Issues with reports\”,\”ticket_type\”:\”Bug\”,\”to_email\”:null,\”to_emails\”:null,\”trained\”:false,\”updated_at\”:\”2023-10-12T04:41:13+01:00\”,\”urgent\”:false}]”
}

在另一个情景下,我可能会花时间优化格式化这个 JSON 输出。但是 LLM —— 像编译器和解释器 —— 不关心这些。更棒的是,它们可以容忍会使传统 JSON 格式化程序死机的语法。如果你把上面的例子粘贴到传统格式器,会遇到两个问题。首先,如果不用花括号包裹就不是有效的 JSON。其次, tickets_list 的值不会是 JSON 对象,而是字符串,因为该值是 JSON 的字符串化表示。非常神奇的是,LLM “知道” 你的意图并做出正确反应:

RESPONSE:
{
          "tickets_list": [
            {
              "associates_rdb": null,
              "association_type": null,
              "cc_email": {
                "cc_emails": [],
                "fwd_emails": [],
                "reply_cc": [],
                "tkt_cc": []
              },
              "created_at": "2023-10-12T12:15:00+01:00",
              "custom_field": {
                "cf_reference_number_2939992": null
              },
            ...
            }
        ]
    }

这是非常琐碎的东西,但它是可能影响你的工作流程并浪费最好留给高级功能的认知资源的东西。在这一点上,我还进行了我称之为可解释性测试: LLM是否解释了1) 代码片段需要被花括号包裹,以及2) 嵌套的值需要被解析?以下是结果。

工具

llm 提供者

解释需要使用{}

解释tickets_list需要解析

chatgpt

openai

copilot chat

开放ai

claude

anthropic

cody

anthropic

这引起我的兴趣,因为虽然我知道 Copilot Chat 使用 OpenAI ,Cody 使用 Anthropic,但这不总是明显的。各自的聊天界面增强了提示,使回复不同于从底层 LLM 获得的回复。但在这个例子中,工具似乎与它们各自的界面行为一致。

让我们来讨论测试策略

ChatGPT 对 set -euo pipefail 的观察只是我们可以探索的多个方向之一,但我把话题引回到了手头的任务上: 完善我在最初提示中包含的测试脚本。该脚本测试的是 Freshdesk 票务系统的输出结果,而天真的测试只是检查我们正在测试的工具是否可以调用 Freshdesk 的 API 并返回多于 0 张票。更好的测试会是什么样的呢?ChatGPT 的建议包括检查优先级和状态是否与预期值匹配、空值或长列表的票务是否被优雅处理,以及所有日期字段是否存在并包含有效日期。

我选择了最后一种方法,ChatGPT 相应地修改了测试脚本。这需要编写一个正则表达式来匹配日期,并在每张票的一组日期字段中循环应用该正则表达式。这感觉太复杂了,所以我要求将范围限制为仅检查一个日期字段: created_at。即使这样仍然感觉太复杂,所以我们舍弃了循环,将测试简化为仅检查单张票中的有效日期。之后我们转向了下一个话题。

让我们来讨论如何将测试推广到一个测试套件

需要测试的用例有:

  • 列出 Freshdesk 票据
  • 创建新 Freshdesk 票据
  • 删除 Freshdesk 票据
  • 检索特定 Freshdesk 票据的详细信息
  • 更新 Freshdesk 票据
  • 向 Freshdesk 票据添加注释

我很少使用 bash 函数,但我觉得在这里它们可能会很有用,所以当看到一个将 bash 脚本结构化为与六个测试用例对应六个函数的回复时,我很高兴。“看起来不错,”我说,“请给我一个完整的构建如此结构化的版本,并包含第一个测试。”

此时,一个在我身边观看的同事想知道是否对 LLMs 使用“请”和“谢谢”会带来更好的结果。这打败了我!我只是因为感觉很好才这样做。这是一种危险的人格化吗?也许吧。但即使它们是非人类,我也看不出欺负我的助手有任何益处。

然后同一位同事问及另一种从表格驱动测试的替代策略。我们问:

PROMPT:
We are wondering if it would be better to have a data structure for these tests instead of a set of loose functions, what do you think are the pros and cons?

回应提到了优点(可维护性、重用性、并行执行、一致的日志记录、清晰度)和缺点(额外开销、复杂度、迁移时间、学习曲线)。它以这段总结结束:

RESPONSE:
Given that your test suite might grow or change over time, adopting a structured approach earlier can be beneficial. However, it’s essential to weigh the initial time investment against the long-term benefits. If you think your test suite will remain relatively small and static, the current loose function setup might suffice. But if you anticipate growth or frequent changes, the structured approach will pay off in the long run.

由于我们处于快速原型设计阶段,还没有准备好对此测试策略进行长期投资,所以我们选择保持松散函数的方式。但我们都觉得花几分钟考虑替代方法是非常有价值的。

让我们来讨论持久化票据 ID 的方法

要编写的下一个测试是创建票据的测试。ChatGPT 建议存储票据的 ID 以在其他测试中使用,以测试读取、更新和向票据添加注释的功能。并且它编写了一个将 ID 持久化到文件的函数。这让我感到疑惑,并要求评估在文件和变量之间保存的权衡取舍。这里是对回复的摘要,以表格形式进行了修订(感谢 ChatGPT)。

保存于文件


保存于变量


优点

缺点

优点

缺点

持久性

速度较慢

速度快

挥发性

进程间通信

复杂度高

简单

限于单个脚本/进程

适合大数据

需清理

无文件依赖

内存限制

并补充了这个结论:

RESPONSE:
If you’re writing a single script, and the data doesn’t need to persist beyond the script’s execution, a variable might be simpler and faster.

我甚至没有考虑基于文件的替代方法。当 ChatGPT 使用那种方法时,它促使我考虑并讨论不同方案的权衡取舍。由于我最终做了与使用 LLM 之前完全一样的事,这次绕路就是无谓的吗?我不这么认为。考虑替代方案总是有价值的。这次绕路花费的时间很少,且虽然它没有改变我最终的做法,但过程感觉很有价值。

在为 get_ticket 函数编写测试时,我们也有过类似的交互。检查获取的票据 ID 是否与我们存储的 ID 匹配就足够了吗?ChatGPT 在效率和彻底性之间划清了界限,列出了进行更彻底检查的选项,并建议对于基本的冒烟测试,优先考虑效率是有意义的。同样,我已经会做更简单的事了,所以这并没有改变任何东西。但尽管我知道我们当然是在进行冒烟测试,大声说出来还是显得很有帮助。

让我们来讨论 Bash

这里有一个交互极大地改变了结果。我们向测试套件添加了第三个测试,但只运行了前两个。出了什么问题?调试过程与使用 LLM 之前一样,但 ChatGPT 可以更快地在代码中插入打印语句,因此速度快了很多。在进行几次这种暴力试错法后,我们仍然无法通过第二个测试。

然后我想到问: "关闭 set -euo pipefail 会有意义吗?" 当我这样做时,脚本就完整运行了 - 即所有 6 个测试都运行了 - 但第二个测试在本应成功的情况下失败了。随后我恍然大悟。我问: "我们应该从 test_create_ticket 返回 $TICKET_ID 吗?" ChatGPT 提醒我一个我读过但从未遇到过的行为:

RESPONSE:
In the context of bash scripts, when you return a number from a function, it’s analogous to an exit code.

我的天啊!对了!我们需要该函数设置全局变量为所创建票据的值,但返回零以避免过早退出,同时保持严格模式仍然生效。这是最后的突破;在那之后,一切顺利。

当橡皮鸭说话时

我不断回到这一系列文章的第一个主题: 当橡皮鸭说话时。大声思考总是有帮助的。理想情况下,你可以与一个人类伙伴一起这样做。橡皮鸭虽然是个糟糕的替代品,但远胜于一无所有。

与 LLM 对话不像这些选择中的任何一个,这完全是另一回事;我们都在努力弄明白它如何发挥作用。要求 LLM 编写代码,神奇地出现代码?这明显是一个改变生活的事情。与 LLM 就你们合作编写的代码进行交谈?我认为这同样是一个不太明显但同样深刻的改变生活的事情。

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...