ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

LLM辅助编码实战:从Vibe Coding到规格驱动的工作流

LLM辅助编码实战:从Vibe Coding到规格驱动的工作流 LLM 辅助编码的赛道上工具更新速度远快于开发者的学习速度。今天用 A 工具生成代码明天出现 B 框架主打 Agent 编排后天热词又变成 Vibe Coding、Spec Coding。表面看是效率竞赛实际是注意力竞赛真正被消耗的不是推理算力而是开发者反复切换工具、重新学习操作方式、手工验证 AI 输出所花掉的时间。想退出这场 LLM Coding 的“内卷”关键不是等下一个更强模型而是建立一套可复现的工作流把需求写成规格让模型先生成计划再分块写代码用项目知识库对抗上下文丢失最后用自动化验证兜底。这套流程对两类读者最有用一类是已经在用 AI 编程工具、但总觉得生成代码不可控的开发者另一类是准备把 LLM 接入正式项目的团队负责人。读完能得到的不是某个工具的快捷键清单而是一套可以迁移到任何工具上的方法论以及可直接拷贝的 Prompt 模板、代码片段、参数表格和排查清单。1. 先理解“LLM 编码竞赛”为什么越跑越累1.1 Vibe Coding、Spec Coding 与 Coding Plan 分别解决什么问题Vibe Coding 描述的是这样一种使用方式开发者用自然语言和模型对话靠“感觉”不断调整让模型连续生成代码。它进入门槛低适合做原型、Demo 和一次性脚本但缺点是没有明确的验收边界。代码一旦超过几百行模型很容易前后不一致开发者自己也很难准确说出每段代码为什么存在。Spec Coding 是与之相对的思路在生成代码之前先写一份结构化规格包含背景、目标、约束、输入、输出、验收标准和不做什么。模型拿到规格后答案空间被压缩生成结果更可控。这个思路并不新本质上就是把软件工程里的需求分析和编码任务分开只是现在把编码任务交给了模型。Coding Plan 是介于两者之间的关键步骤。很多云平台和 Agent 工具都提供类似“先生成计划再执行”的能力模型先读取需求输出任务拆解、涉及文件和依赖关系然后才进入编码。它解决的是“一步到位生成全部代码导致难以复核”的问题。三者不是互斥关系而是可以组合成一条工作流Vibe Coding 用于快速探索Spec Coding 用于正式实现Coding Plan 用于控制执行过程。工作方式核心动作适合场景主要风险Vibe Coding自然语言对话驱动生成边生成边反馈原型、Demo、一次性脚本代码不可维护、上下文漂移Spec Coding先写结构化规格再按规格实现正式模块、团队协作、需要验收的功能规格写不好时错误被掩盖Coding Plan模型先输出任务拆解再按计划执行多文件改造、重构、跨模块功能计划不等于验收仍需人工核对1.2 持续换工具仍然低效的三个隐性成本很多开发者感觉自己“内卷”是因为把效率提升寄托在工具切换上。今天换编辑器插件明天换 Agent 框架后天换本地推理引擎。每次切换带来的真实损失包括第一是学习成本。新的操作方式、快捷键和配置语法都需要重新熟悉这部分时间不会直接产生代码。第二是上下文重置。旧工具里积累的 Prompt、规则和项目约束不会自动迁移到新工具等于每次换工具都从零开始教模型认识项目。第三是协作断裂。团队里不同成员用不同工具评审标准无法统一生成的代码风格、目录结构、提交粒度都会五花八门。模型能力再强也无法替你确认“这份代码是否符合项目约定、是否通过了测试、是否能在生产环境回滚”。只要这三个问题没有稳定的处理方式换任何工具都只是换一种方式面对同一个问题。1.3 退出竞赛需要建立的三个核心能力跳出这场竞赛需要把注意力从“哪个工具更强”转移到“我怎么让任何工具稳定产出可用代码”上。具体是三个能力第一个是规格化能力。能把一句话需求拆成背景、约束、输入、输出、验收标准和非目标这是所有后续步骤的基础。第二个是上下文管理能力。项目足够大之后模型不可能靠一次 Prompt 记住所有约束。需要把项目文档、依赖版本、代码风格和业务规则沉淀成可检索的知识库在每次生成前动态注入相关片段。第三个是验证闭环能力。AI 生成的代码默认是不信任的必须有单元测试、静态检查、类型检查和人工 diff 审查组成的门禁合格后才允许合并。这三个能力不是任何单一工具能替代的它们需要开发者主动构建。2. 搭建一套可复现的 LLM 编码基线环境2.1 云端 API 与本地推理的取舍先决定代码生成的底座。常见选择是云端 API 和本地推理两者不是替代关系而是适用范围不同。维度云端 API本地推理硬件要求低只需网络和 API Key高显存和内存决定可运行模型规模延迟受网络影响通常几百毫秒到数秒受 GPU 性能影响小模型延迟更低数据隐私数据出本机需确认服务条款数据不出本机成本按 token 计费高频使用成本持续累积一次性硬件投入后续边际成本低配置复杂度低注册账号即可需要 CUDA、推理框架、模型文件典型用途日常编码、快速迭代敏感项目、离线环境、大批量处理选型时有几个关键参数要确认上下文窗口、最大输出 token、价格、延迟、是否支持函数调用或工具调用。对编码场景上下文窗口建议至少 32K否则多文件项目很难一次讲清输出 token 太小长函数或长配置会被截断。2.2 最小 OpenAI 兼容接口接入与参数说明大多数编码工具底层都是调用模型推理接口。下面这个最小示例用来验证“本机能不能打通 LLM API”所有编码实验都从这里开始。import os from openai import OpenAI client OpenAI( api_keyos.environ.get(LLM_API_KEY), base_urlos.environ.get(LLM_BASE_URL, https://api.example.com/v1), ) def chat(prompt: str, system: str 你是一名资深后端工程师。) - str: resp client.chat.completions.create( modelos.environ.get(LLM_MODEL, gpt-4o-mini), messages[ {role: system, content: system}, {role: user, content: prompt}, ], temperature0.2, max_tokens2048, ) return resp.choices[0].message.content if __name__ __main__: print(chat(请实现一个带超时和重试的 HTTP GET 函数。))这里有几个关键点。base_url用来兼容不同厂商的 OpenAI 风格接口temperature设成 0.2 可以降低生成随机性编码场景不建议设太高max_tokens要同时考虑输入和输出输出限制太小时长文件会被截断。注意不同平台的模型名、上下文长度和计费单位不同代码里的model默认值只是占位落地前要改成实际使用的模型。参数含义推荐值调大/调小影响temperature生成随机性0.1 到 0.4调大更容易跑题调小更稳定max_tokens输出上限编码场景 2048 到 8192太小会截断代码top_p核采样概率保持默认或随 temperature 调整一般不需要同时调两个timeout请求超时30 到 120 秒太短长任务会误报失败2.3 API Key、超时与重试的配置规范API Key 不能硬编码在源码里。推荐用环境变量加.env文件# .env LLM_API_KEYsk-xxxx LLM_BASE_URLhttps://api.example.com/v1 LLM_MODELgpt-4o-mini MAX_TOKENS2048 REQUEST_TIMEOUT60Python 侧读取import os from dotenv import load_dotenv load_dotenv() api_key os.environ.get(LLM_API_KEY) if not api_key: raise RuntimeError(缺少 LLM_API_KEY 环境变量)同时把.env加入.gitignore避免密钥进仓库。生产环境更建议使用密钥管理服务并给 API Key 配置最小权限和调用限额避免某个模块泄漏密钥导致整个账号被滥用。注意不要把密钥打印到日志里也不要随错误消息返回给调用方。排查问题时只看 Key 的前几位即可。3. 用“规格 - 计划 - 编码 - 复核”替换“一次生成”3.1 Spec把一句话需求转成模型可执行的结构化描述多数生成结果不可控是因为输入本身太模糊。比如“帮我写个配置读取工具”模型只能猜读什么格式、缓存多久、线程安全吗、出错怎么办。把这些写进规格模型才不用猜。一个能直接用的规格模板如下# 需求规格带缓存的配置读取器 ## 背景 项目启动时需要读取 YAML 配置文件且配置会在运行期被外部修改。 ## 目标 实现一个函数 load_config(path, ttl)返回配置字典在 TTL 内重复调用不重复读文件。 ## 约束 - 使用 Python 3.10 - 依赖 PyYAML - 进程内缓存即可不引入 Redis - 线程安全 ## 输入 path: str配置文件路径 ttl: int缓存有效期单位秒 ## 输出 dict 类型的配置 ## 异常与边界 - 文件不存在时抛出 FileNotFoundError - YAML 解析失败时抛出 yaml.YAMLError - TTL 小于等于 0 时直接读文件不缓存 ## 验收标准 1. 连续两次调用第二次不触发文件读取 2. TTL 过期后重新读取 3. 多线程下不会读到半写入状态 ## 非目标 - 不做配置热更新通知 - 不支持远程配置中心写规格时最容易忽略的是“非目标”和“异常与边界”。没有非目标模型容易过度设计没有边界模型只写主路径出异常时直接崩溃。3.2 Plan先让模型拆任务再进入编码规格写好后不要让模型直接输出完整实现。先让它输出计划你是本项目的技术负责人。不要直接写代码。 请阅读下面的规格输出执行计划 1. 需要创建或修改的文件 2. 每个文件的职责和关键函数签名 3. 依赖关系和处理顺序 4. 测试方案 5. 你判断的风险点 如果规格有歧义先列出需要确认的问题不要自行假设。这一步的好处是模型的“思考过程”被显式打开你可以提前发现它理解错了需求而不是等代码生成后再从头审查。计划里出现无关文件、漏掉异常分支、接口命名和项目现有风格不一致都是这个阶段应该抓住的问题。3.3 编码与复核分文件落地、逐块验证计划确认后再要求模型分文件生成代码。一次只生成一个或两个文件生成后立刻检查、运行测试再进入下一个文件。这样做的原因是错误被隔离在小范围内定位成本低。复核阶段至少要看三个东西git diff逐行确认新增代码而不是只看最终文件。依赖变化是否合理新引入的库是否必须。异常处理和边界条件是否和规格一致。不要直接信任模型在注释里写的“本代码已处理异常”要自己跑一次错误分支。3.4 完整最小示例带缓存的配置读取器按上面规格让模型输出的实现如下import threading import time from pathlib import Path import yaml _cache: dict[str, tuple[float, dict]] {} _lock threading.Lock() def load_config(path: str, ttl: int) - dict: now time.time() with _lock: cached _cache.get(path) if cached and cached[0] ttl now: return cached[1] p Path(path) if not p.exists(): raise FileNotFoundError(fconfig file not found: {path}) with p.open(r, encodingutf-8) as f: data yaml.safe_load(f) or {} if ttl 0: _cache[path] (now, data) return data复核时需要注意锁只保证了读取和写缓存这段临界区安全但ttl 0时直接读文件不缓存这个分支是否符合规格yaml.safe_load返回None时用空字典兜底缓存字典没有最大值限制长期运行可能无限增长。这三个点都属于规格里“边界”范畴复核时逐一对照即可。4. 用项目知识库对抗上下文丢失RAG 与 LLM Wiki 思路4.1 上下文丢失的根因模型之所以“忘记”你的项目约定根本原因是上下文窗口有限而项目信息的总量远超窗口另一个原因是对话越聊越长早期指令会被后续内容稀释。常见表现包括生成的代码不符合项目目录结构使用与项目不一致的依赖忽略团队在代码规范里写的约定重复实现项目中已有的工具函数。一种流行的解法是 LLM Wiki 思路开发者维护一份个人或团队知识库把项目约定、依赖版本、踩坑记录、代码片段写成 Wiki 形式然后在每次请求前检索相关内容注入上下文。这里的核心不只是“建一个知识库”而是把知识库接到模型的输入管线上。4.2 最小检索链路切分、向量化、检索一个最小 RAG 链路包含四步文档切分、向量化、建立索引、检索。先看切分。切分粒度过大会混入无关内容过小会破坏语义。对中文技术文档常见做法是按 300 到 800 字符切块并设置 10% 到 20% 的重叠def build_chunks(text: str, chunk_size: int 500, overlap: int 50): if len(text) chunk_size: return [text] chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) start end - overlap return chunks向量化和检索的示例组合from sentence_transformers import SentenceTransformer import faiss import numpy as np model SentenceTransformer(BAAI/bge-small-zh-v1.5) chunks build_chunks(doc_text) vectors model.encode(chunks, normalize_embeddingsTrue) index faiss.IndexFlatIP(vectors.shape[1]) index.add(np.array(vectors)) def retrieve(query: str, top_k: int 3): qvec model.encode([query], normalize_embeddingsTrue) scores, ids index.search(np.array(qvec), top_k) return [chunks[i] for i in ids[0]]示例使用的库和模型只是众多可选方案之一。实际项目落地前要确认sentence-transformers、faiss的版本兼容关系并考虑是否用更轻量的 BM25 关键词检索作为第一版。4.3 把检索结果注入 Prompt检索不是目的注入才是。注入时把检索片段放在用户消息前部并明确告诉模型这些内容来自项目文档以下是项目文档中的相关内容是本次回答的权威依据 {retrieved_chunks} 请基于以上内容完成下面的任务。如果找到的内容不足以回答请直接说“项目文档中没有找到相关依据”不要自行编造。 任务{task}这里有一个容易被忽略的点检索结果可能互相矛盾也可能和当前任务无关。所以 Prompt 里要允许模型“拒绝回答”或“请求补充材料”否则模型会把不相关片段也强行用于生成产生更严重的幻觉。注意知识库需要持续维护。新产生的踩坑记录、依赖升级、架构调整都要同步写回去否则检索到的内容会慢慢变成过时信息。5. 用 MCP 连接工具链但不要过早引入多 Agent5.1 MCP 解决什么问题MCPModel Context Protocol解决的是模型、应用和工具之间的连接标准化问题。没有 MCP 时每接入一个工具就要写一套定制代码有了 MCP工具提供方暴露为 MCP Server应用侧通过 MCP Client 统一连接。对 LLM 编码场景MCP 的典型价值是让模型能读取项目文件、执行命令、查询构建状态而不是只靠 Prompt 里的静态信息。工具能力被封装成模型可调用的接口整个链路更接近真实的 IDE 操作。5.2 最小 MCP Server 与 Client 示例下面用一个极简的 MCP Server 暴露“列出项目源码目录”的能力示例用于说明接口形态实际项目的工具列表要按自己项目设计。# mcp_tool.py from mcp.server.fastmcp import FastMCP mcp FastMCP(project-tool) mcp.tool() def list_src_dirs() - list[str]: 返回项目源码目录列表供生成代码时参考。 return [src/app, src/core, tests] if __name__ __main__: mcp.run()客户端侧通过标准接口连接# mcp_client.py import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main() - None: params StdioServerParameters( commandpython, args[mcp_tool.py], ) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print(tools) asyncio.run(main())MCP Python SDK 的接口在不同版本上有调整示例代码以 1.x 版本为准。实际项目里建议先跑通官方仓库的 Example再替换成自己的工具函数。5.3 多 Agent 协同的适用边界多 Agent 协同是当前热门方向但它不是默认选项。多个 Agent 意味着更长的执行链、更多的 token 消耗、更难的错误定位。任何一个 Agent 的误判都会沿着链路放大。场景是否适合多 Agent说明单文件小功能不适合单 Agent 加规格即可多文件重构可以尝试需要明确的模块边界跨端联调适合但成本高要设计好 Agent 间协议历史代码全面审计不建议验证成本远大于生成收益判断标准很简单如果单个 Agent 加一份规格、加一个知识库能完成就不要引入多 Agent。多 Agent 的价值在于并行和分工但前提是你已经能稳定验证单个 Agent 的输出。6. 验证与质量门禁让 AI 生成的代码可测试、可回滚6.1 AI 生成代码的高频质量问题问题现象常见原因处理建议调用了不存在的 API模型知识截止时间早于依赖版本先查官方文档再让模型基于文档改写使用错误的依赖版本未指定版本模型按常见版本猜测在规格和 Prompt 中写明版本号缺少异常处理任务描述没有覆盖失败分支在规格中单独列“异常与边界”代码过度设计没有约束范围在规格中明确“非目标”重复实现已有工具函数模型不知道项目里已存在该能力接入项目知识库生成前注入相关代码索引这五个问题里前三个可以通过“把规格写细”缓解后两个必须靠“上下文注入”解决。6.2 用命令和测试搭建验证闭环给配置读取器补一个最小测试# test_config_loader.py from config_loader import load_config def test_cache_within_ttl(tmp_path): cfg tmp_path / app.yaml cfg.write_text(name: demo\n, encodingutf-8) first load_config(str(cfg), ttl10) second load_config(str(cfg), ttl10) assert first second
返回列表