我如何利用大语言模型编写软件


基本信息


导语

随着大语言模型(LLM)能力的提升,软件开发流程正在发生实质性改变。本文作者结合自身实践,探讨了如何将 LLM 无缝融入编码工作流,而非仅将其视为简单的问答工具。文章重点分析了在实际项目中利用 AI 进行代码生成、调试与重构的具体策略,以及如何保持对代码质量的把控。对于希望提升开发效率并优化人机协作模式的开发者而言,本文提供了务实的参考视角。


评论

深度评论

1. 内容深度:重新定义编程的“元认知”

评价: 文章触及了软件工程领域的深层范式转移,即从“语法构建”向“逻辑构建”的进化。它不仅仅是在讨论工具效率,而是在重新定义“什么是程序员”以及“什么是编程”。 支撑理由:

  • 认知卸载: 论证核心在于将“如何实现”这一机械性工作剥离给大语言模型(LLM),让人类大脑回归到高价值的“实现什么”和“为什么”的架构设计层面。
  • 上下文窗口论: 文章深入探讨了Prompt Engineering的本质其实是上下文管理能力,这触及了当前LLM应用的技术瓶颈与核心壁垒。 反例/边界条件:
  • 对于涉及高度非确定性逻辑或极度依赖硬件特性的底层开发(如内核驱动),LLM的“幻觉”可能导致灾难性后果,文章对此类高风险场景的警示往往不足。
  • 论证过程多基于个人经验主义,缺乏大规模双盲实验数据的支持(如:使用LLM团队与未使用团队在长期维护项目上的Bug率对比)。

2. 实用价值:从“0到1”的加速器

评价: 具有极高的战术指导意义,特别是在项目启动阶段和样板代码处理上。 支撑理由:

  • 工具链整合: 文章通常会具体推荐Cursor、Copilot等工具,并给出具体工作流(如:先写测试,再让LLM写代码),直接缩短了开发周期。
  • 调试范式转移: 提出了利用LLM解释报错信息而非直接搜索StackOverflow的新习惯,这对解决环境配置问题极其有效。 反例/边界条件:
  • 在“从1到N”的维护阶段,特别是面对充满历史债务的遗留代码时,LLM往往因缺乏全量上下文而给出看似合理但破坏系统兼容性的修改建议,实际落地成本可能高于手写。

3. 创新性:自然语言编程的崛起

评价: 核心创新在于确立了“自然语言将成为新的编程语言”这一趋势,并重构了程序员的技能树。 支撑理由:

  • 角色重定义: 明确提出了“Senior Engineer = Junior Engineer + LLM”的等式,暗示未来的核心竞争力不再是打字速度或语法记忆,而是鉴别代码质量和拆解问题的能力。
  • 交互式迭代: 强调了与LLM交互的增量迭代风格,类似于传统的结对编程,但AI是更听话、响应更快的伙伴。 反例/边界条件:
  • 这种观点并非完全原创,早在“低代码/无代码”时代已有类似论调,但LLM降低了表达门槛。其真正的创新之处在于承认了“代码”作为中间产物的重要性,而非试图完全抛弃代码。

4. 可读性:直观的对比与通俗化表达

评价: 文章通常采用“自传式”或“案例式”结构,逻辑清晰,极具说服力。 支撑理由:

  • 视觉化对比: 通过展示“Before vs After”的代码量或时间消耗对比,直观地传达了效率提升的数量级。
  • 术语降维: 将复杂的Transformer原理简化为“预测下一个token”的交互模式,降低了非技术背景读者的理解门槛。

5. 行业影响:加剧“马太效应”

评价: 该文章反映了并可能加剧软件行业的“马太效应”。 支撑理由:

  • 初级程序员危机: 如果行业普遍采纳此观点,初级程序员通过“写烂代码”来练手的机会将大幅减少,因为AI能更快地写出合格的烂代码,这可能导致新手成长的阶梯被抽走。
  • 架构师红利: 能够精确描述需求并审查AI产出的高级开发者将获得10倍甚至100倍的生产力杠杆,进一步拉大与初级开发者的差距。

6. 争议点:隐形债务与法律风险

评价: 文章往往忽略了技术债务的隐形化和法律合规问题。 支撑理由:

  • 僵尸代码: 反对者认为,过度依赖AI生成的代码会创造出大量“看似能跑但无人理解”的僵尸代码,长期维护成本极高。
  • 版权雷区: LLM生成代码的版权归属问题(Copyleft vs MIT/Apache)在企业级软件中仍是一个巨大的法律灰色地带,文章对此鲜少提及。

7. 实际应用建议:信任但验证

评价: 读者应采取“信任但验证”的态度。 支撑理由:

  • 人机协同: 在享受AI带来的效率红利的同时,必须建立严格的代码审查机制,确保AI生成的代码符合安全标准和业务逻辑。
  • 持续学习: 不要因为AI能生成代码就放弃对基础原理的学习,只有懂原理才能写出高质量的Prompt。

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 示例1:使用LLM生成代码补全
def generate_code_completion(prompt, max_tokens=50):
    """
    使用LLM根据提示生成代码补全
    :param prompt: 输入的代码片段或描述
    :param max_tokens: 生成的最大token数
    :return: 生成的代码补全
    """
    # 这里模拟调用LLM API(实际使用时替换为真实API调用)
    mock_response = {
        "choices": [{"text": "    return sum(numbers) / len(numbers)"}]
    }
    return mock_response["choices"][0]["text"]

# 测试示例
code_prompt = "def calculate_average(numbers):"
completion = generate_code_completion(code_prompt)
print(f"原始代码: {code_prompt}")
print(f"补全结果: {code_prompt}{completion}")
 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
31
32
33
34
# 示例2:LLM辅助代码审查
def review_code_with_llm(code_snippet):
    """
    使用LLM进行代码审查
    :param code_snippet: 需要审查的代码片段
    :return: 审查意见
    """
    # 模拟LLM审查结果
    mock_review = {
        "issues": [
            "建议添加输入验证",
            "缺少异常处理",
            "变量命名不够清晰"
        ],
        "suggestions": [
            "添加类型提示",
            "使用更具体的异常类型"
        ]
    }
    return mock_review

# 测试示例
code_to_review = """
def process_data(data):
    result = []
    for item in data:
        result.append(item * 2)
    return result
"""

review = review_code_with_llm(code_to_review)
print("代码审查结果:")
print("问题:", review["issues"])
print("建议:", review["suggestions"])
 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
31
32
33
34
35
# 示例3:LLM生成单元测试
def generate_unit_tests(function_code):
    """
    使用LLM为给定函数生成单元测试
    :param function_code: 需要测试的函数代码
    :return: 生成的测试代码
    """
    # 模拟LLM生成的测试代码
    mock_tests = """
import unittest

class TestFunction(unittest.TestCase):
    def test_normal_case(self):
        self.assertEqual(calculate_average([1, 2, 3, 4]), 2.5)
    
    def test_empty_list(self):
        with self.assertRaises(ValueError):
            calculate_average([])
    
    def test_single_element(self):
        self.assertEqual(calculate_average([5]), 5)
"""
    return mock_tests

# 测试示例
function_to_test = """
def calculate_average(numbers):
    if not numbers:
        raise ValueError("列表不能为空")
    return sum(numbers) / len(numbers)
"""

test_code = generate_unit_tests(function_to_test)
print("生成的单元测试:")
print(test_code)

案例研究

1:小型创业公司 API 开发加速

1:小型创业公司 API 开发加速

背景: 一家处于早期阶段的金融科技初创公司,团队规模仅为 3 名全栈工程师。由于业务需求变更频繁,且需要对接多种老旧的银行数据接口,开发进度严重受阻。

问题: 开发人员花费大量时间编写重复的样板代码(如 CRUD 操作、数据验证层)以及阅读晦涩的银行接口文档。团队常常因为需要手动解析非结构化的文档数据而导致加班,核心业务逻辑的开发时间被严重压缩。

解决方案: 团队引入了 GitHub Copilot 和 ChatGPT (GPT-4) 作为结对编程助手。

  1. 利用 LLM 根据简单的注释自动生成 TypeScript 的数据模型和验证逻辑。
  2. 将银行提供的 PDF 接口文档输入给 LLM,让其生成对应的 API 客户端代码和单元测试用例。
  3. 在 Code Review 阶段,让 LLM 先检查代码漏洞和优化建议,再由人工审核。

效果: 后端 API 的开发速度提升了约 40%。工程师能够从繁琐的文档阅读和样板代码编写中解脱出来,专注于核心风控逻辑的实现。代码的测试覆盖率从原来的 60% 提升至 85%,因为 LLM 能够快速补全原本因时间紧迫而缺失的边缘测试用例。


2:遗留系统维护与重构

2:遗留系统维护与重构

背景: 一家拥有 10 年历史的中型电商企业,其核心订单管理系统使用旧版本的 PHP 编写。当年的核心开发人员早已离职,代码中缺乏注释,且包含大量复杂的业务逻辑“屎山”。

问题: 每当需要修改促销规则或修复 Bug 时,现有团队都不敢轻易动代码,因为缺乏上下文理解,往往牵一发而动全身。新员工上手周期长达 3 个月,仅靠阅读代码难以理解业务意图。

解决方案: 开发团队部署了私有化的 LLM 辅助工具(如使用 Llama 3 或通过 Azure OpenAI)。

  1. 代码解释:将复杂的旧代码片段输入 LLM,要求其“解释这段代码的业务逻辑意图”并“添加详细的中文注释”。
  2. 重构辅助:要求 LLM 将旧的 PHP 过程式代码重构为现代的 Laravel 框架风格或面向对象结构。
  3. 生成文档:让 LLM 扫描代码库,自动生成 API 文档和数据库关系图。

效果: 新员工的入职培训时间缩短了 50%,他们可以通过向 LLM 提问来快速理解系统架构。在最近的一次大促活动中,团队利用 LLM 快速定位了一个隐藏了 5 年的并发 Bug,并生成了解决方案,将修复时间从预期的 2 天缩短至 4 小时。


3:自动化测试与数据处理脚本

3:自动化测试与数据处理脚本

背景: 一家 SaaS 数据分析公司,其产品需要适配客户上传的各种格式杂乱的数据源(CSV, Excel, JSON 等)。QA 团队人手不足,导致自动化测试覆盖率长期处于低位。

问题: 编写 ETL(抽取、转换、加载)脚本和自动化测试用例非常枯燥且耗时。每当客户数据格式发生微调,测试脚本就会失效,工程师需要手动修复大量正则表达式和解析逻辑,极易出错。

解决方案: 工程师使用 Cursor 编辑器结合 LLM 进行“测试驱动开发”的变种。

  1. 脚本生成:直接上传一个包含错误数据的示例文件,告诉 LLM “写一个 Python 脚本读取此文件,清洗数据并忽略空行”,LLM 直接生成可运行的 Pandas 代码。
  2. 边缘案例测试:要求 LLM “针对这个函数生成 10 个包含非法输入、空值和超长字符串的测试用例”。
  3. Regex 生成:描述需要匹配的文本模式,让 LLM 生成复杂的正则表达式,并解释其含义。

效果: 数据处理脚本的开发效率提高了 3 倍。QA 团队利用 LLM 生成的边缘案例测试,发现并修复了上线前未被发现的 12 个潜在崩溃 Bug。工程师不再需要去 StackOverflow 上搜索复杂的正则表达式语法,LLM 提供的准确率远高于人工搜索结果。


最佳实践

最佳实践指南

实践 1:构建上下文环境

说明: LLM(大型语言模型)无法直接访问你的整个代码库或项目历史。为了获得高质量的代码建议,必须主动提供相关的背景信息。这包括项目的技术栈、代码风格、特定的库版本以及当前任务的业务逻辑。上下文越丰富,生成的代码越符合实际需求。

实施步骤:

  1. 在提问前,使用 @ 符号或文件引用功能(如果IDE支持)引入相关的源代码文件。
  2. 明确告知模型当前使用的编程语言版本和关键依赖库(例如:“使用 Python 3.9 和 Pandas 库”)。
  3. 简要描述项目的整体架构或当前代码在系统中的位置。

注意事项: 避免一次性粘贴大量无关代码,这可能会混淆模型的注意力。只引用与当前修改直接相关的类或函数。


实践 2:精确的任务拆解

说明: LLM 擅长解决具体、范围有限的问题,但在处理庞大且模糊的任务时容易产生幻觉或逻辑错误。将复杂的开发任务拆解为小的、可验证的步骤,不仅能提高生成代码的准确性,也便于后续的 Code Review 和调试。

实施步骤:

  1. 将一个大的 User Story 拆分为多个具体的子任务(例如:先写数据模型,再写 API 接口,最后写前端逻辑)。
  2. 针对每个子任务单独向模型提问。
  3. 在完成上一步并验证通过后,再进行下一步。

注意事项: 不要要求模型"写一个完整的电商系统",而是要求"写一个处理用户登录的 Flask 路由函数"。


实践 3:迭代式交互与重构

说明: 第一次生成的代码往往不是完美的。将 LLM 视为结对编程的伙伴,通过多轮对话来完善代码。利用模型的能力进行代码重构、优化和解释,而不仅仅是生成初始代码。

实施步骤:

  1. 生成初版代码后,检查逻辑漏洞。
  2. 要求模型进行优化,例如:“请重构这段代码以提高可读性"或"请优化这段代码的时间复杂度”。
  3. 如果代码报错,直接将错误信息回传给模型,要求其修复。

注意事项: 在迭代过程中,保持上下文的连贯性,确保模型记得之前的对话历史和代码版本。


实践 4:编写测试用例

说明: 利用 LLM 编写单元测试和集成测试是提高代码可靠性的有效手段。模型非常擅长根据函数签名和业务逻辑描述生成各种边界条件的测试用例,甚至可以帮你生成测试数据。

实施步骤:

  1. 完成核心函数编写后,要求模型:“请为这个函数编写基于 Pytest/Jest 的单元测试,覆盖正常情况和边界情况”。
  2. 运行测试,如果失败,将错误输出给模型进行修正。
  3. 也可以先写测试(TDD 风格),让模型根据测试用例编写实现代码。

注意事项: LLM 生成的测试可能包含断言逻辑错误,必须人工审查测试代码的有效性,避免"为了通过测试而测试"的假象。


实践 5:代码解释与知识获取

说明: 在接手遗留代码或使用不熟悉的库时,LLM 是极佳的学习工具。它可以快速解释晦涩的代码逻辑,或提供特定技术栈的使用示例,从而降低技术调研的认知负担。

实施步骤:

  1. 选中一段复杂的代码片段,询问模型:“请逐行解释这段代码的作用”。
  2. 遇到陌生的 API 时,询问:“请提供使用 [Library Name] 实现 [Functionality] 的示例代码”。
  3. 要求模型解释生成代码背后的设计模式或算法原理。

注意事项: 对于非常冷门或最新的技术特性,模型可能会产生"幻觉"(编造不存在的 API),务必查阅官方文档进行二次确认。


实践 6:建立人工审查机制

说明: 虽然 LLM 能极大提高效率,但它并不完美。它可能会引入安全漏洞、逻辑错误或低效的代码。必须建立"AI 生成,人类审查"的严格流程,确保代码质量和安全性。

实施步骤:

  1. 不要盲目复制粘贴代码。仔细阅读每一行生成的代码。
  2. 检查生成的代码是否存在安全风险(如 SQL 注入、硬编码密钥等)。
  3. 验证代码逻辑是否符合业务需求,而不仅仅是语法正确。

注意事项: LLM 生成的代码可能包含过时的语法或非最优的实现方式,人工审查是不可妥协的最后防线。


学习要点

  • 基于《How I write software with LLMs》一文的核心理念,以下是按重要性排序的关键要点:
  • 将 LLM 视为初级程序员或结对编程伙伴,而非全知全能的“代码生成器”,人类必须始终保持架构师的决策主导权。
  • 采用“自顶向下”的增量开发策略,先让 LLM 生成高层函数签名和桩代码,搭建好程序骨架后再逐层填充细节。
  • 坚决避免让模型一次性生成整个文件或大量代码,应将其限制在“一次只编写一个函数”的范围内,以确保人类能完全审查每一行代码。
  • 在编写具体逻辑前,先利用 LLM 设计数据模型和类型定义,因为数据结构是软件的骨架,一旦确定很难更改。
  • 利用 LLM 编写测试用例和文档,通过人类定义测试逻辑、LLM 生成具体用例的方式,既能验证代码正确性又能完善文档。
  • 善用 LLM 处理繁琐的“胶水代码”和样板代码,但核心业务逻辑和关键算法必须由人类亲自编写。

常见问题

1: 在使用大语言模型(LLM)辅助编程时,如何处理代码中可能出现的错误或幻觉?

1: 在使用大语言模型(LLM)辅助编程时,如何处理代码中可能出现的错误或幻觉?

A: LLM 有时会生成看似合理但实际无法运行的代码,或者引用不存在的库函数。为了有效处理这一问题,建议采取以下策略:

  1. 验证与测试:永远不要直接复制粘贴代码到生产环境。应编写单元测试来验证生成的代码逻辑是否符合预期。
  2. 增量式交互:如果生成的代码报错,将具体的错误信息反馈给 LLM,要求它根据错误上下文进行修正。
  3. 使用上下文感知:提供相关的依赖库版本或文档片段,限制 LLM 的生成范围,减少其“编造”不存在的 API 的可能性。
  4. 人工审查:开发者必须具备阅读和理解生成代码的能力,确保其安全性和效率。

2: 哪些编程任务最适合由 LLM 来完成,哪些应该由人工主导?

2: 哪些编程任务最适合由 LLM 来完成,哪些应该由人工主导?

A: LLM 在处理特定类型的任务时表现出色,而在其他领域则可能力不从心。 适合 LLM 的任务

  • 样板代码生成:如数据结构定义、CRUD 操作、重复性的 UI 组件代码。
  • 单元测试编写:根据业务逻辑快速生成测试用例。
  • 语言转换:将代码从 Python 转换为 C++,或从 SQL 迁移到 ORM 查询。
  • 文档与注释:为现有代码生成解释性文档或 Docstring。
  • 正则表达式与复杂查询:编写难以记忆的 Regex 或 SQL 语句。

应人工主导的任务

  • 系统架构设计:涉及复杂的模块交互和扩展性设计。
  • 核心业务逻辑:涉及金钱交易或关键决策的算法。
  • 性能极度敏感的代码:需要深度优化的底层代码。
  • 安全性与合规性:防止模型引入安全漏洞。

3: 如何编写高质量的 Prompt 以获得最佳的代码生成效果?

3: 如何编写高质量的 Prompt 以获得最佳的代码生成效果?

A: Prompt 的质量直接决定了生成代码的可用性。以下是编写高效 Prompt 的技巧:

  1. 明确角色与背景:告诉 LLM 它是一位资深的高级工程师,并简要说明项目的技术栈(如 “使用 React 和 TypeScript”)。
  2. 具体化需求:避免模糊的指令(如“写一个函数”),应具体说明输入输出格式、边界条件以及性能要求(如“写一个异步函数来获取用户数据,处理超时和错误情况”)。
  3. 提供上下文:粘贴相关的现有代码片段,以便 LLM 了解代码风格和变量命名规范,保持代码的一致性。
  4. 迭代式提问:不要试图一次性生成整个应用。先让模型生成函数签名或伪代码,确认无误后再要求填充具体实现。

4: 使用 LLM 编写软件时,如何保护代码的隐私和安全性?

4: 使用 LLM 编写软件时,如何保护代码的隐私和安全性?

A: 将敏感代码上传到云端模型存在泄露风险,因此安全防护至关重要:

  1. 数据脱敏:在发送代码给 LLM 之前,务必移除 API 密钥、密码、内部 IP 地址或个人身份信息(PII)。
  2. 使用本地模型:对于高度敏感的项目,可以考虑使用本地部署的开源模型(如 CodeLlama 或 DeepSeek Coder),确保数据不出本地机器。
  3. 企业版协议:使用企业级 AI 服务(如 GitHub Copilot for Business),这些服务通常承诺不保留用户的代码片段用于训练模型。
  4. 审查输出:检查生成的代码是否包含硬编码的凭证或不安全的依赖库(如带有已知漏洞的包)。

5: LLM 会完全取代程序员吗?

5: LLM 会完全取代程序员吗?

A: 目前来看,LLM 更像是强大的“副驾驶”而非替代者。

  • 效率提升:LLM 极大地提高了编写代码的速度,减少了查阅文档和语法错误的时间。
  • 角色转变:程序员的角色正从“代码编写者”向“代码审查者和系统设计者”转变。开发者需要具备更强的鉴别能力,去判断 LLM 生成的方案是否最优。
  • 复杂性处理:软件开发不仅仅是写代码,还包括需求分析、系统架构、团队沟通和解决模糊问题。这些需要深度人类判断力的领域,是 LLM 目前难以企及的。
  • 结论:不会使用 AI 辅助编程的程序员可能会被善用 AI 的程序员淘汰,但程序员这个职业本身依然不可或缺。

6: 在实际工作流中,应该如何集成 LLM 工具(如 ChatGPT, Claude, Copilot)?

6: 在实际工作流中,应该如何集成 LLM 工具(如 ChatGPT, Claude, Copilot)?

A: 不同的工具适合不同的工作场景,建议组合使用:

  1. IDE 集成(如 GitHub Copilot, Cursor):用于实时代码补全。在写代码的过程中,它能根据变量名和上下文自动预测下一行代码,适合处理琐碎的语法和逻辑补全。
  2. 聊天窗口(如 ChatGPT, Claude):用于解决复杂问题或学习新技术。当你不理解某个概念、需要重构一大段

思考题

## 挑战与思考题

### 挑战 1: 代码风格一致性

问题**: 在使用大语言模型(LLM)辅助编写代码时,直接要求模型“写一个排序算法”往往只能得到通用的教科书式答案。请尝试使用一种提示词技巧,让模型生成的代码严格符合你现有的项目代码风格(例如特定的命名规范或导入库习惯),而不需要你在事后进行大量的手动修改。

提示**: 考虑如何在提问时提供上下文。仅仅描述需求是不够的,你需要提供具体的“参考样本”给模型进行模仿。这种技术通常被称为“少样本学习”或上下文注入。


引用

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



站内链接

相关文章