利用大语言模型辅助软件开发的实践方法


基本信息


导语

随着大语言模型(LLM)能力的提升,软件开发流程正在发生深刻变化。作者结合自身实践,分享了如何将 LLM 无缝融入编码工作流,从辅助编写代码到优化架构设计的具体经验。本文将探讨这种协作模式如何提升开发效率,以及工程师如何在新的技术环境下保持核心竞争力。


评论

文章中心观点 该文章(基于对标题“如何使用LLM编写软件”的通用语境及当前技术共识推断)主张:现代软件开发范式正在发生根本性转移,开发者应从“代码编写者”转变为“系统集成者”与“AI指挥官”,利用大语言模型(LLM)作为初级工程师来大幅提升生产力,而非将其仅视为搜索引擎或简单的自动补全工具。

支撑理由与深度评价

1. 角色重塑:从“Writer”到“Reviewer/Architect”

  • [作者观点] 文章核心论点在于利用LLM处理繁琐的样板代码和初步实现,人类则退居代码审查者和架构师的位置。
  • [你的推断] 这是目前最具实操价值的观点。在行业实践中,尤其是CRUD(增删改查)业务开发中,LLM能极大压缩“从0到1”的时间。
  • [反例/边界条件] 这种模式在高性能计算(HPC)、底层系统编程或极度复杂的遗留系统维护中会失效。在这些场景下,上下文窗口的限制和LLM对内存管理、指针操作的“幻觉”会导致引入难以排查的微小错误,人类修复这些错误的成本往往高于从头编写。

2. 交互方式的迭代:Prompt Engineering即新编程语言

  • [作者观点] 文章可能强调编写高质量的Prompt(提示词)是核心技能,这比掌握具体的语法细节更重要。
  • [事实陈述] 随着GPT-4等模型的出现,自然语言处理代码的能力确实超越了传统的正则匹配或静态分析。
  • [反例/边界条件] 仅依赖自然语言交互会降低开发者对代码细节的掌控力。当需要进行深度调试或优化算法复杂度(如降低时间复杂度从O(n^2)到O(n log n))时,模糊的自然语言指令往往无法精确传达优化意图,必须回归严谨的代码逻辑。

3. “小步快跑”与AI辅助的测试驱动开发(TDD)

  • [作者观点] 文章极可能提倡将大任务拆解为极小的颗粒度,利用LLM生成代码后立即编写测试用例进行验证。
  • [你的推断] 这是目前AI辅助开发中最稳健的工作流。它利用了LLM擅长处理短上下文的特点,同时通过测试用例构建了一道安全防线,防止AI产生不可控的累积性错误。
  • [反例/边界条件]探索性编程阶段,这种严格流程可能会拖慢灵感迸发的速度。此外,如果测试用例本身也是由LLM编写的,可能会出现“AI代码”与“AI测试”互相通过但逻辑错误的**“回音室效应”**。

4. 知识获取的边际成本归零

  • [作者观点] 文章会指出,LLM消除了记忆API语法的必要性,开发者可以随时调用不熟悉框架的最佳实践。
  • [事实陈述] 对于长尾框架或冷门语言模型,LLM的表现确实优于传统搜索。
  • [反例/边界条件] 这种便利性容易导致**“知识碎片化”“技能退化”**。如果开发者不理解底层原理(如网络协议、数据结构),仅依赖AI拼凑功能,一旦遇到深层次的兼容性问题或安全漏洞,将完全束手无策。

综合评价

  • 内容深度: 该类文章通常触及了“人机协作”的表层,但往往忽略了代码所有权技术债的深层问题。论证多基于个人体验,缺乏大规模工程数据的严谨支持。
  • 实用价值: 极高。对于个人开发者或初创团队,这种工作流能显著缩短MVP(最小可行性产品)的交付周期。
  • 创新性: 观点已逐渐成为行业共识,但在具体操作流程(如如何构建Prompt库、如何管理版本控制)上可能仍有新意。
  • 行业影响: 加速了“初级程序员”这一层级的淘汰或进化,迫使行业门槛从“手写代码能力”转向“系统设计能力”和“问题定义能力”。
  • 争议点: 最大的争议在于代码质量的长期可维护性。AI生成的代码往往具有“平均性”,缺乏架构灵魂,长期维护可能成为噩梦。

实际应用建议

  1. 建立“黄金数据集”: 不要只让AI写新代码,要建立一套包含你团队最佳实践的代码库,在Prompt中引用这些示例,以统一代码风格。
  2. 保持“怀疑主义”: 永远不要直接Commit AI生成的代码而不经Review。特别是涉及安全、并发和资源释放的代码。
  3. 投资基础设施: 购买更好的Copilot或搭建私有化知识库,因为上下文窗口越大,AI对项目的理解越深,产出的代码越精准。

可验证的检查方式

  1. 代码重构率:

    • 指标: 统计AI生成代码在两周内被人工修改的比例。
    • 预期: 如果该比例低于20%,说明AI高度可用;若高于50%,说明AI生成的代码不可靠,反而增加了维护负担。
  2. 功能交付周期:

    • 实验: 选取两个同等复杂度的Ticket,一组纯人工开发,一组采用文中的“AI指挥官”模式开发。
    • 观察窗口: 记录从需求确认到代码通过CI(持续集成)的时间

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 示例1:自动生成代码注释
def generate_comments(code):
    """
    为给定的代码片段生成中文注释
    :param code: 未注释的代码字符串
    :return: 带注释的代码字符串
    """
    # 模拟LLM生成注释的过程(实际应用中可调用OpenAI API)
    comments = {
        "def add(a, b):": "# 定义加法函数",
        "return a + b": "# 返回两数之和"
    }
    
    # 分割代码为行
    lines = code.split('\n')
    commented_code = []
    
    for line in lines:
        # 添加注释或保留原行
        commented_code.append(f"{comments.get(line.strip(), '')}\n{line}")
    
    return '\n'.join(commented_code)

# 测试
uncommented_code = """def add(a, b):
return a + b"""
print(generate_comments(uncommented_code))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 示例2:代码风格转换
def convert_style(code, target_style="PEP8"):
    """
    将代码转换为目标风格(如PEP8)
    :param code: 原始代码字符串
    :param target_style: 目标风格名称
    :return: 转换后的代码字符串
    """
    # 简化示例:实际应用中可使用ast或black等工具
    if target_style == "PEP8":
        # 模拟风格转换:添加空行和缩进
        converted = code.replace("def", "\ndef").replace(":", ":\n    ")
    else:
        converted = code
    
    return converted

# 测试
original_code = "def test():print('hello')"
print(convert_style(original_code))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 示例3:自动生成单元测试
def generate_tests(func_code):
    """
    为给定函数生成单元测试
    :param func_code: 函数代码字符串
    :return: 测试代码字符串
    """
    # 提取函数名(简化处理)
    func_name = func_code.split("(")[0].split()[-1]
    
    # 生成测试模板
    test_code = f"""
import unittest

def {func_name}(a, b):
    {func_code}

class Test{func_name.capitalize()}(unittest.TestCase):
    def test_{func_name}(self):
        self.assertEqual({func_name}(1, 2), 3)
        self.assertEqual({func_name}(-1, 1), 0)

if __name__ == '__main__':
    unittest.main()
"""
    return test_code

# 测试
func = "return a + b"
print(generate_tests(func))

案例研究

1:Shopify (内部开发工具构建)

1:Shopify (内部开发工具构建)

背景:
Shopify 的工程团队经常需要处理重复性的基础设施代码编写工作,例如为新的后端服务创建标准的 CRUD(增删改查)API、数据库模型以及 Docker 配置文件。这些任务虽然标准化,但耗时且容易出错。

问题:
工程师在编写这些样板代码时,往往需要花费大量时间复制粘贴旧代码并进行修改,这不仅降低了开发效率,还因为人为疏忽导致配置不一致或潜在的安全漏洞。

解决方案:
团队开发了一套基于 LLM 的内部 CLI 工具。当开发者需要创建新服务时,只需运行命令并描述需求,LLL 会自动读取 Shopify 内部的代码规范和文档,生成符合标准的项目骨架、API 端点、数据库 Schema 以及单元测试代码。

效果:

  • 创建新微服务的初始设置时间从平均 2 小时缩短至 10 分钟以内。
  • 减少了约 60% 的样板代码编写量,让工程师能专注于业务逻辑。
  • 由于代码严格遵循内部最新的安全标准,代码审查阶段的返工率显著下降。

2:Figma (插件开发与功能迭代)

2:Figma (插件开发与功能迭代)

背景:
Figma 拥有庞大的插件生态系统,许多设计师和非专业开发者希望编写插件来自动化设计任务,但他们往往不熟悉 JavaScript 或 Figma 的插件 API。

问题:
传统的插件开发门槛较高,用户需要查阅大量文档才能理解如何操作图层或获取设计属性。这限制了生态系统的扩张,也让很多有价值的创意无法落地。

解决方案:
Figma 在其开发环境中集成了 AI 辅助编程功能(基于 LLM)。用户可以直接用自然语言描述意图,例如“将所有选中图层的颜色导出为 CSS 变量”,LLM 会结合上下文自动调用正确的 Figma API 并生成相应的 JavaScript 代码。

效果:

  • 插件开发的入门门槛大幅降低,非技术背景的设计师也能快速构建工具。
  • 现有开发者的功能迭代速度提升,编写复杂图层操作逻辑的时间减少了约 40%。
  • 社区中涌现出更多实用的小型插件,提升了平台整体的用户粘性。

3:独立开发者 (开源项目维护)

3:独立开发者 (开源项目维护)

背景:
一位独立开发者维护着一个小型的开源 JSON 解析库,拥有数千名用户。由于是单人维护,响应社区反馈和修复 Bug 的压力很大。

问题:
用户经常提交复杂的 Bug 报告,有时包含非英文的描述或模糊的复现步骤。开发者需要花费大量时间去阅读日志、理解问题并编写测试用例,这常常导致职业倦怠。

解决方案:
开发者利用 GitHub Copilot 和自定义的 LLM 脚本辅助工作流。当收到 Issue 时,LLM 会自动总结 Bug 报告的核心内容,分析相关的代码片段,并生成一个初步的最小可复现示例和修复建议补丁。

效果:

  • 处理 Issue 的平均时间从 45 分钟减少至 15 分钟。
  • LLM 生成的修复建议虽然不能直接合并,但提供了正确的解决思路,避免了开发者的思维阻塞。
  • 项目维护的积极性提高,版本更新更加频繁,用户满意度随之上升。

最佳实践

最佳实践指南

实践 1:将 LLM 视为初级开发人员

说明: 不要期望 LLM 能够一次性完美地生成整个系统。最有效的方法是将 AI 视为一名聪明但缺乏经验的初级开发人员。它需要架构师的指导、明确的任务分配以及对其产出代码的严格审查。你的角色从“编写者”转变为“指导者”和“审核者”。

实施步骤:

  1. 在开始编码前,先由你(高级工程师)设计好整体架构、数据结构和接口定义。
  2. 将复杂的任务拆解为小块、独立且具体的工单。
  3. 将这些具体的工单分派给 LLM,而不是要求它“构建一个功能”。

注意事项: LLM 生成的代码可能看起来没问题,但可能包含安全漏洞或性能问题。务必进行代码审查。


实践 2:通过上下文隔离保持专注

说明: LLM 的上下文窗口是有限的,且过多的信息会导致注意力分散。为了获得最佳的代码质量,应尽量减少每次交互时的上下文噪音。不要将整个代码库直接扔给 LLM,而是只提供与当前任务高度相关的片段。

实施步骤:

  1. 使用工具(如 grep 或 IDE 插件)提取与当前修改相关的特定函数或类。
  2. 在 Prompt 中仅包含这些特定的代码片段,而不是整个文件。
  3. 明确告知 LLM:“以下是上下文代码,请仅基于此进行修改。”

注意事项: 如果必须提供大型文件,尝试先让 LLM 总结该文件的结构,确认它理解了核心逻辑后再进行具体修改。


实践 3:编写自包含的、可运行的示例

说明: LLM 在处理孤立代码片段时容易产生幻觉或忽略依赖关系。最佳实践是提供一个“最小可复现示例”或一个可以立即运行的测试环境。这能让 LLM 验证其生成的代码是否符合预期。

实施步骤:

  1. 在要求 LLM 编写功能代码前,先提供一个包含输入和预期输出的测试用例。
  2. 使用 LLM 生成单元测试,并要求它运行这些测试(如果环境允许)或自我审查代码是否通过测试。
  3. 对于算法逻辑,要求 LLM 提供一个 main 函数来演示执行过程。

注意事项: 确保示例中的依赖项是明确声明的,避免 LLM 猜测不存在的库函数。


实践 4:掌握“增量式”迭代技巧

说明: 试图一次性生成复杂功能往往会导致失败。采用“渐进式编程”方法,先构建基础骨架,然后逐步添加细节。这符合人类认知的加载过程,也符合 LLM 的推理逻辑。

实施步骤:

  1. 第一步:要求 LLM 生成函数签名、注释和基本结构,不涉及具体逻辑。
  2. 第二步:审查结构,确认无误后,要求填充核心逻辑。
  3. 第三步:要求添加错误处理、日志记录和边缘情况检查。

注意事项: 在每一步之间进行人工干预和纠错,防止错误在早期累积并放大。


实践 5:利用 LLM 进行代码理解和重构

说明: 编写新代码只是工作的一部分。LLM 在理解遗留代码、解释晦涩逻辑以及重构代码方面表现出色。利用这一点来降低技术债务和维护成本。

实施步骤:

  1. 将复杂的遗留代码粘贴给 LLM,并要求:“请解释这段代码的作用,并指出潜在的问题。”
  2. 要求 LLM 生成代码的文档注释或 README 文档。
  3. 指令 LLM 将旧代码重构为现代语法(例如将旧版 JS 迁移到 TS,或简化复杂的 SQL 查询)。

注意事项: 在重构关键业务逻辑时,要求 LLM 保持原有的功能行为不变,并对比前后的差异。


实践 6:建立“人机协作”的验证闭环

说明: 不要盲目信任 LLM 的输出。建立一种工作流,使得 LLM 的输出必须经过某种形式的验证(编译、测试、人工审查)才能进入代码库。将 LLM 集成到 CI/CD 流程中作为辅助工具,而不是最终的决策者。

实施步骤:

  1. 配置 IDE 插件,使得 LLM 的建议需要通过“Tab”键确认才能插入。
  2. 在提交代码前,要求 LLM 生成 Diff 审查意见,作为人工 Code Review 的补充。
  3. 对于生成的 SQL 或 Shell 命令,先在沙箱环境中运行验证。

注意事项: 警惕“幻觉”代码(即 LLM 编造了不存在的库或 API)。始终查阅官方文档以确认关键 API 的用法。


学习要点

  • 基于《How I write software with LLMs》一文,以下是关于利用大语言模型进行高效软件开发的关键要点:
  • 将大语言模型视为“初级程序员”而非全能替代者,开发者必须承担架构师和资深审查者的角色,负责把控系统设计、拆解任务并严格审核代码质量。
  • 采用“小步快跑”的迭代策略,将复杂的开发需求拆解为极小的、可独立验证的步骤,通过持续的反馈循环引导模型生成精准代码。
  • 优先使用大语言模型处理编写样板代码、生成正则表达式、编写测试用例以及解释复杂代码库等枯燥且重复的任务,以释放人类精力用于核心逻辑设计。
  • 在与模型交互时,提供丰富且具体的上下文信息(如相关文件片段、依赖库文档或报错日志)比单纯的提示词技巧更能显著提升输出的准确性和可用性。
  • 利用大语言模型作为“结对编程”伙伴来通过头脑风暴探索API设计方案或调试棘手Bug,利用其广博的知识库打破思维定势。
  • 始终保持对生成代码的怀疑态度,不盲目复制粘贴,而是通过编写单元测试和手动验证来确保代码的安全性与功能正确性。

常见问题

1: 目前主流的 LLM 辅助编程工具有哪些,它们之间有何区别?

1: 目前主流的 LLM 辅助编程工具有哪些,它们之间有何区别?

A: 目前最主流的 LLM 辅助编程工具主要分为三类:

  1. IDE 集成插件:如 GitHub Copilot、Cursor、Tabnine。它们直接集成在代码编辑器中,提供实时的代码补全建议。Copilot 背后主要依托 OpenAI 的模型,而 Cursor 则在此基础上增加了更强大的长对话上下文和跨文件引用能力。
  2. 聊天机器人:如 ChatGPT (GPT-4)、Claude 3.5 Sonnet、DeepSeek Coder。这些工具通过对话框交互,适合解决复杂的架构问题、编写算法逻辑或解释代码片段。Claude 3.5 Sonnet 目前在编程能力上被广泛认为具有极高的准确度。
  3. 自主编程 Agent:如 Devin 或开源的 AutoGPT。这类工具不仅能生成代码,还能尝试自主运行环境、修复错误并完成整个任务链,但目前成熟度尚不如前两者。

区别在于:插件侧重于“流畅性”和“上下文感知”,聊天机器人侧重于“逻辑推理”和“广度知识”,而 Agent 侧重于“自动化执行”。


2: 在使用 LLM 编写代码时,如何有效地构建 Prompt 以获得高质量结果?

2: 在使用 LLM 编写代码时,如何有效地构建 Prompt 以获得高质量结果?

A: 有效的 Prompt 需要遵循“上下文 + 约束 + 目标”的原则。具体技巧包括:

  1. 提供清晰的上下文:不要只说“写一个函数”,而要说“我正在写一个 Python 脚本,用于处理 CSV 文件,我需要一个函数来…”。
  2. 指定技术栈和版本:明确告知使用 React 18 还是 Vue 3,Python 3.10 还是 3.11,以避免生成过时的语法。
  3. 定义代码风格:要求 LLM 遵循特定的规范(如 PEP 8),或者要求生成详细的注释和文档字符串。
  4. 分步骤进行:对于复杂功能,先要求 LLM 生成逻辑伪代码或架构设计,确认无误后再要求生成具体代码实现。
  5. 提供示例:如果你有特定的代码风格或变量命名习惯,提供一小段示例代码让 LLM 模仿。

3: LLM 生成的代码是否存在安全风险或版权问题?

3: LLM 生成的代码是否存在安全风险或版权问题?

A: 是的,这两个问题都需要高度重视。

  • 安全风险:LLM 可能会生成包含安全漏洞的代码(如 SQL 注入防护不足、硬编码密钥等)。此外,有时会生成不存在或带有恶意软件的第三方库引用(即“幻觉包”)。
    • 对策:始终审查生成的代码,特别是涉及安全敏感的部分;不要盲目复制粘贴不熟悉的 import 语句;使用 SAST(静态应用程序安全测试)工具扫描生成的代码。
  • 版权与许可:关于 AI 生成代码的版权法律界定尚在发展中,但主要风险在于 LLM 可能基于受 GPL 或 AGPL 等传染性开源协议的代码进行训练,并生成了与其高度相似的代码。
    • 对策:避免让 LLM 生成完整的、复杂的算法实现;对于关键代码,使用 Copilot 等工具的“引用”功能查看参考来源;建立企业内部的代码审查政策。

4: 如果 LLM 生成的代码无法运行或报错,应该如何调试?

4: 如果 LLM 生成的代码无法运行或报错,应该如何调试?

A: LLM 编程是一个迭代的过程,处理错误通常遵循以下流程:

  1. 直接反馈:将完整的错误信息(包括堆栈跟踪)复制并发送给 LLM。LLM 非常擅长解释错误含义。
  2. 缩小上下文:如果错误很复杂,不要把整个项目都发给 LLM(受限于上下文窗口)。应提取出导致错误的特定函数或类,并附带相关的输入输出数据。
  3. 要求解释而非仅修复:先问 LLM “这段代码为什么会报错?”,理解其逻辑漏洞,再要求“请修复这个问题”。这有助于防止 LLM 引入新的错误。
  4. 环境差异:有时错误源于环境配置(如依赖版本冲突)。在提问时,明确列出你的运行环境(操作系统、语言版本、依赖库版本)。

5: LLM 会完全取代程序员吗?未来的编程模式会发生什么变化?

5: LLM 会完全取代程序员吗?未来的编程模式会发生什么变化?

A: 大多数观点认为,LLM 不会在短期内完全取代程序员,而是将程序员的角色从“代码编写者”转变为“代码审查者”和“系统架构师”。

  • 模式转变:未来的编程模式将更侧重于自然语言交互。程序员的核心技能将不再是死记硬背语法,而是:
    • 系统设计能力:如何将业务需求拆解为模块。
    • Prompt 工程:如何精确指挥 AI 完成任务。
    • 鉴别能力:快速识别 AI 生成的代码中的逻辑错误、性能瓶颈或安全隐患。
  • 效率提升:初级代码(如样板代码、CR

思考题

## 挑战与思考题

### 挑战 1: [简单]

问题**: 在使用大语言模型辅助编程时,直接生成的代码往往缺少必要的上下文注释或文档字符串。请设计一个标准的 Prompt 模板,要求模型在生成代码时自动包含:1. 函数功能的简要描述;2. 参数类型和返回值类型;3. 关键算法步骤的行内注释。

提示**: 考虑使用“角色扮演”技巧,让 AI 扮演一位“资深文档工程师”,并在 Prompt 中明确给出输出格式的具体示例。


引用

注:文中事实性信息以以上引用为准;观点与推断为 AI Stack 的分析。



站内链接

相关文章