news 2026/8/28 23:40:04

LLM控制确定性代码生成:让vibe coding走向工程化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM控制确定性代码生成:让vibe coding走向工程化

Vibe coding 这个词从 2025 年初开始被反复讨论,但大部分讨论都停留在“AI 帮我写代码”这个表面。真正把它落地到工程里的人会发现一个尴尬的事实:大模型写代码写得好不好,取决于你对“好”的定义。如果你要的是“能跑”,那确实很爽;如果你要的是“可复现、可审查、可测试”,LLM 直接生成代码的行为会让你抓狂。

同一个需求,跑三次,得到三份不同的实现。这让 code review 变成猜谜,让回归测试失去基准,也让“这次改动到底改了什么”变成哲学问题。更麻烦的是,LLM 生成的代码一旦混入生产环境,你很难向团队解释为什么这段代码长这样,也很难在出问题时快速定位是哪个环节引入了缺陷。

Sif 1.0 这个项目的切入点,就是把这个矛盾拆开:LLM 不直接写代码,而是去控制一个 deterministic coder(确定性代码生成器)。LLM 负责理解需求、拆解任务、做决策;确定性引擎负责把决策翻译成代码。一句话概括:LLM 成为 vibe coder,deterministic coder 成为它手里那支笔。

这个思路值得展开讲。它解决的不只是“代码质量”问题,而是 LLM 编程这个模式的工程化问题。本文会从概念拆解、架构思路、最小实现、验证方法和工程建议几个角度,带你完整理解这种“LLM 控制 + 确定性生成”的编程范式。

1. 这篇文章真正要解决的问题

1.1 先看清楚痛点:LLM 直接生成代码的问题

过去一年多,大家已经习惯了让大模型生成函数、补齐单元测试、甚至直接生成整个服务。个人开发者在原型阶段确实体验到了效率提升,但当这个流程进入团队协作和生产环境,问题会集中暴露在三个层面。

第一是不可复现。LLM 的采样机制决定了,即使温度设为 0,输出也不保证完全一致。今天生成的代码加了几个空行,明天生成的代码换了变量命名,后天生成的代码可能改了整体结构。代码 diff 失去了意义,因为你无法区分“这次改动是有意的”还是“模型抽风了”。

第二是难审查。代码审查的本质是判断“意图与实现是否一致”。LLM 输出的代码没有明确的意图文档,reviewer 只能根据代码反推意图,再判断实现是否合理。这等于把审查变成了考古。一旦需求变更是通过多轮对话累积的,最后生成的代码可能已经偏离原始需求,但没人能准确指出是哪一轮跑偏的。

第三是难测试。传统单元测试依赖稳定的函数行为。LLM 每次生成的代码结构不同,你写的测试可能今天能过、明天就挂在导入错误上。测试维护成本会随着生成代码的不可预测性急剧上升。

这三个问题的根源不是“LLM 能力不够”,而是职责错位:LLM 被要求同时完成“理解意图”和“生成实现”两件事,而后者恰恰是需要确定性保证的环节。

1.2 Sif 1.0 的解法:把意图和实现拆开

从项目标题 “LLM control of deterministic coder” 可以清晰看到它的设计立场:LLM 应该处于控制层,而不是执行层。它根据自然语言需求做出决策,输出结构化的规格说明;真正的代码由 deterministic coder 完成——这是一个吃结构化输入、按固定规则产出代码的引擎,保证同一输入永远得到同一输出。

这个设计把“写代码”这个动作重新拆分:

  • LLM 负责的自定义部分:理解需求、选择技术栈、拆分服务边界、决定接口设计。
  • deterministic coder 负责的确定部分:按照 spec 生成 CRUD 接口、路由框架、DTO 结构、基础测试脚手架。

换句话说,LLM 输出的不是代码,而是代码的规格。规格是稳定的、可 diff 的、可审查的。代码只是规格的投影。

这种模式在工程上有一个非常实际的好处:如果你对生成的代码不满意,你不需要在代码层面打补丁,而是回到 spec 层面修改决策。改 spec、重新生成、比较 diff——整个流程是可控的、可回溯的。

1.3 谁最应该读这篇文章

这篇文章适合三类读者。

第一类,是正在用 LLM 写业务代码、但被不可预测输出困扰的开发者。你会从这里找到“LLM 负责什么、引擎负责什么”的边界。

第二类,是做内部工具、脚手架、SDK 生成器的平台工程师。你们可能已经意识到,纯模板生成缺少智能,纯 LLM 生成缺少约束,而 Sif 这种混合模式正好补齐两端。

第三类,是关注 LLM Agent 架构的读者。很多人问“LLM 应用为什么需要编排框架”,这个项目的设计就是一个典型答案:编排不是把多个 LLM 调用串起来,而是把 LLM 的决策能力和确定性系统组合成一个可控的工作流。

2. 三个核心概念:vibe coding、deterministic coder、LLM Agent

2.1 vibe coding 到底是什么

Vibe coding 这个说法,来自 Andrej Karpathy 在 2025 年初的一次分享,大意是:开发者用自然语言描述意图,让 AI 把代码写出来,自己只看结果、不太关心实现细节。他对这种模式的描述非常形象——你是在“跟着感觉编程”,不是“逐行编程”。

这个概念的流行有它的合理性。对于原型验证、一次性脚本、探索性数据分析和前端页面原型,vibe coding 确实能把开发周期从几天压缩到几小时。问题在于,很多人把 vibe coding 直接搬进了需要长期维护的工程代码里,结果就是上一节说的那些坑。

Sif 1.0 重新定义了 vibe coder 这个词:LLM 才是 vibe coder,人类开发者成为了它的 reviewer 和 spec 审核者。LLM 用自然语言感受需求、做出判断,然后把它“感觉”到的东西固化成一个结构化决策,交给确定性引擎执行。人类则站在更高一层,审核 LLM 的决策是否合理。

这个视角的转变很关键:vibe 并不等于不严谨,而是把“感觉”放在决策层,把“严谨”放在执行层。

2.2 deterministic coder:确定性才是工程化的底线

Deterministic coder 指的是这类系统:接收定义良好的输入(DSL、JSON Schema、AST、模板参数),按照固定规则生成代码。它不依赖采样、没有随机性、永远不会产生模糊输出。同一个输入,昨天生成的和明天生成的完全一致。

这样的系统在软件行业其实一直存在,只是大家没有用这个名词。代码生成器、脚手架工具、Protobuf 编译器、OpenAPI 生成器、ORM 代码生成工具,本质上都是 deterministic coder。它们的共同特点是:

  • 输入是结构化的,有明确的 schema。
  • 输出是可预期的,同一输入恒等于同一输出。
  • 逻辑是可测试的,单元测试可以覆盖生成规则本身。

传统上,deterministic coder 的能力上限很低,因为它只能按照预设模板机械输出。要让模板覆盖丰富多变的真实业务,需要写大量配置和分支逻辑,维护成本很快失控。这也是为什么很多团队最终放弃生成器、回到手写代码。

Sif 模式的新意,在于用 LLM 补上了 deterministic coder 最缺的部分——从模糊需求到结构化输入的翻译能力。LLM 不生成代码,它生成的是 deterministic coder 的输入。这等于给确定性引擎装了一个自然语言接口,同时没有破坏它的确定性。

2.3 LLM Agent 与这个模式的关系

现在谈到 LLM 应用,绕不开 Agent 这个概念。LLM Agent 的核心是让模型能调用工具、观察结果、迭代决策。常见的 Agent 结构里,代码生成工具是其中一种工具,模型调用它写代码、执行、看报错、再改。

Sif 的模式与 Agent 的关系值得厘清:它不是要取代 Agent,而是给 Agent 的设计提供了一个重要约束——让 Agent 决定“做什么”,让确定性系统决定“怎么做”

很多 Agent 实现的问题在于,模型既做决策又做执行。比如让 Agent 直接修改某个函数,模型会生成新代码、再调用工具写入文件。一个简单的改动可能经历“生成代码 → 执行测试 → 报错 → 再生成”的多轮循环,每一轮都有不确定性叠加。如果让 Agent 先输出一个决定,比如“这个函数应该增加参数 timeout,默认值 30 秒”,然后由确定性工具将这个决定落到代码上,流程就变得清晰多了。

在成熟的 Agent 框架里,这种边界往往通过 tool calling 的结构化参数来实现。你可以把 deterministic coder 封装成一个 MCP 服务或标准工具,Agent 只负责构造调用参数,不负责生成代码文本。这也是 MCP(Model Context Protocol)这类协议存在的意义:把模型与外部系统的交互变成结构化调用。

3. Sif 1.0 的核心架构思路

3.1 分层设计:需求层、决策层、生成层

从架构角度看,Sif 模式做了清晰的三层划分。

第一层是需求层,输入是自然语言、产品文档、Issue 描述等非结构化信息。这一层属于人类和 LLM 的交互区。

第二层是决策层,LLM 在这里把非结构化需求转换为结构化 spec。它可能要输出服务的接口定义、数据模型、技术栈选择、模块划分。这一层输出的产物,必须有明确的格式约束,通常是 JSON Schema 或 DSL。

第三层是生成层,deterministic coder 接收 spec,按照模板和规则生成代码文件、配置文件、测试脚手架。这一层不允许有任何随机行为。

层的边界就是接口。需求层和决策层之间是自然语言,决策层和生成层之间是结构化 spec。这个设计最精妙的地方在于:只有需求到 spec 的转换是不确定的,而 spec 到代码的转换是完全确定的。不确定性被限制在一个极小的范围内,而这个范围恰好是 LLM 最擅长的语义理解。

3.2 为什么 LLM 适合做“控制者”而不是“写码者”

要理解这个设计选择,可以类比自动驾驶的分级:L2 级辅助驾驶,人类驾驶、系统辅助;L5 级全自动驾驶,系统完全接管。目前 LLM 写代码的水平,大概相当于 L2 到 L3 之间——它能完成很多常规工作,但遇到边界情况需要人类兜底。

问题是,L2 和 L3 最难处理的不是“能力不够”,而是责任边界不清晰。系统以为自己在开,人类也以为系统在开,出了事故没人说得清谁该负责。LLM 直接写代码也一样:模型以为自己在交付需求,人类以为模型理解了需求,最后代码跑偏了,review 阶段才发现问题。

Sif 把 LLM 放在控制者位置,实际上是把责任边界画清楚了:LLM 负责输出 spec,spec 被审查后进入生成流程,生成流程的输出是确定性的。如果代码有问题,要么是 spec 错了,要么是模板错了,二选一,不会出现“模型随机抽风”这种无法归因的情况。

另一个理由是成本结构。让 LLM 直接生成完整代码,每个 token 都在消耗推理成本,而且越长的代码越容易累积错误。让 LLM 生成 spec 只有很小的 token 开销,剩下的代码生成完全不依赖模型调用,成本和延迟都可预测。在批量生成场景下,这个成本差异非常明显。

3.3 这种架构的适用边界

需要明确的是,Sif 模式不是万能的。它有非常清晰的适用边界。

适合的场景包括:

  • CRUD 服务生成、内部管理后台脚手架。
  • 数据模型的初始化代码和对应迁移文件。
  • SDK 客户端、接口封装层的批量生成。
  • 规范化要求高、重复性强的工程代码。
  • 需要快速起项目但后续要长期维护的场景。

不适合的场景包括:

  • 探索性架构设计,没有明确规范可循的新系统。
  • 深度业务逻辑,需要大量隐含领域知识的模块。
  • 对既有代码库的低侵入式修改,需要理解大量上下文。
  • 代码本身不是核心资产、以后不会再维护的一次性脚本。

判断标准很简单:如果你能写出清晰的 spec,就适合用这个模式;如果你自己都说不清要生成什么,指望 LLM 替你“临场发挥”,那 Sif 模式也帮不了你。它能约束不确定性,但不能创造确定性。

4. 环境准备与前置条件

4.1 运行环境与依赖

本文后续的最小实现会演示“LLM 规划器 + deterministic coder”的完整流程。实验环境如下,版本请以实际项目为准,重点是展示通用思路。

  • 操作系统:Linux 或 macOS 均可,Windows 建议使用 WSL2。
  • Python:3.10 及以上,需要支持typing.Literallist[str]语法。
  • LLM 推理服务:任意兼容 OpenAI Chat Completions 协议的服务,包括本地部署的 Ollama、vLLM,以及各类云端模型 API。本文示例以本地 Ollama 为默认配置。
  • 依赖库:openai(用于调用兼容协议)、pydantic(用于 spec 校验)。

安装依赖:

python -m venv .venv source .venv/bin/activate pip install openai pydantic

如果使用 Ollama 作为本地推理服务,先确保已安装并拉取模型:

ollama pull qwen2.5-coder:7b

关于模型选择,这里给一个实用建议:这类的 spec 转换任务对模型的长文本生成能力要求不高,但对 JSON 输出的稳定性要求较高。7B 级别的代码模型通常够用,如果你在 JSON 解析上频繁失败,再考虑换更大的模型。

4.2 前置条件的核心理念

环境准备阶段,容易忽略的不是安装依赖,而是确认你的 LLM 服务支持 JSON 输出约束。Ollama 和 vLLM 都支持response_format={"type": "json_object"},OpenAI 兼容接口也支持。这个能力很重要:它能让模型大概率输出合法的 JSON,大幅减少下游解析失败的概率。

如果模型不支持 JSON 输出约束,你仍然可以运行,但需要在提示词里强约束,并在解析层做更健壮的容错。后面常见问题章节会专门讲这个坑。

5. 最小实现:让 LLM 控制 deterministic coder 输出服务代码

下面用一个典型案例跑通整个流程:给定一段自然语言需求,LLM 输出服务规格,deterministic coder 根据规格生成一个 FastAPI 风格的 Python 服务。这个例子刻意保持最小,方便你理解核心原理后自行扩展。

5.1 定义 spec 结构

spec 是整个架构的中间协议,也是唯一需要严格定义的接口。我们用 Pydantic 来定义它,好处是自带校验、错误信息清晰。

# spec.py from typing import Literal from pydantic import BaseModel, Field class FieldSpec(BaseModel): """接口字段定义""" name: str = Field(description="字段名,如 user_id") type: Literal["string", "integer", "boolean", "float"] = Field(description="字段类型") required: bool = True description: str = "" class EndpointSpec(BaseModel): """接口定义""" method: Literal["GET", "POST", "PUT", "DELETE"] path: str = Field(description="路由路径,如 /users/{user_id}") operation_id: str = Field(description="函数名,如 get_user") summary: str = Field(description="接口说明") fields: list[FieldSpec] = [] class ServiceSpec(BaseModel): """服务级定义,deterministic coder 的输入""" service_name: str language: Literal["python"] = "python" endpoints: list[EndpointSpec]

这里的关键设计是Literal类型。它把允许的值范围限定死,LLM 不能凭感觉输出一个"str""String"之类的变体。这样 deterministic coder 往下走的时候,不需要做任何兜底判断。

5.2 LLM 规划器:把需求转成结构化 spec

规划器的作用,是把一段自然语言需求变成上面的ServiceSpec。核心代码如下。

# planner.py import json from openai import OpenAI from spec import ServiceSpec def requirement_to_spec(requirement_text: str, base_url: str, api_key: str, model: str) -> ServiceSpec: client = OpenAI(base_url=base_url, api_key=api_key) prompt = f""" 你是一名系统架构师。请根据下面的需求,输出一个 JSON 对象,用于驱动代码生成引擎。 需求: {requirement_text} 要求: 1. 只输出 JSON,不要输出任何解释或 Markdown 代码块标记。 2. 字段必须符合以下结构: - service_name: 服务的短名称,如 user_service - language: python - endpoints: 接口数组,每个接口包含 method、path、operation_id、summary、fields - fields 中的 type 只能是 string、integer、boolean、float 3. 接口命名遵循 RESTful 风格。 """ resp = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], temperature=0, response_format={"type": "json_object"}, ) raw = resp.choices[0].message.content # 兼容模型偶尔输出 Markdown 代码块的情况 raw = raw.strip() if raw.startswith("```"): raw = raw.removeprefix("```json").removeprefix("```").removesuffix("```").strip() data = json.loads(raw) return ServiceSpec(**data)

几个值得注意的点:

  • temperature=0并不能保证输出完全一致,但它能显著减少随机性。真正保证确定性的是下游的 deterministic coder,而不是 LLM。
  • response_format是对模型能力的约束,不是强制保证。解析层仍然要处理可能出现的异常 JSON。
  • 提示词里明确要求“只输出 JSON”,能给模型最直接的指令。

如果这里的 JSON 解析失败,下一步你会看到json.JSONDecodeError。不要急着改提示词,先看完整输出,很多问题是模型把解释和 JSON 混在一起造成的。

5.3 确定性生成器:spec 到代码

这是整个系统里唯一允许生成代码的地方,也是确定性要求最严格的部分。我们实现一个函数,接收ServiceSpec,输出一个文件名到文件内容的字典。这个函数没有任何随机行为,也没有外部依赖。

# coder.py from spec import ServiceSpec def generate_python_service(spec: ServiceSpec) -> dict[str, str]: """根据 spec 生成 FastAPI 风格服务代码。 确定性保证:同一 spec 输入,永远返回完全相同的文件内容。 """ lines = [ '"""Auto-generated by deterministic coder. DO NOT EDIT MANUALLY."""', "from fastapi import FastAPI", "", f'app = FastAPI(title="{spec.service_name}")', "", ] for ep in spec.endpoints: # 将 operation_id 转换为合法的 Python 函数名,使用 spec 原值 func_name = ep.operation_id lines.append(f'@app.{ep.method.lower()}("{ep.path}")') lines.append(f"async def {func_name}():") lines.append(f' """{ep.summary}"""') # 根据字段生成基础的参数返回示例,保持最小化 sample = ", ".join(f'"{f.name}": None' for f in ep.fields) lines.append(f" return {{{sample}}}") lines.append("") return {"main.py": "\n".join(lines).strip() + "\n"} def write_files(output_dir: str, files: dict[str, str]) -> None: """将生成的文件写入磁盘""" from pathlib import Path out = Path(output_dir) out.mkdir(parents=True, exist_ok=True) for name, content in files.items(): (out / name).write_text(content, encoding="utf-8")

注意看,这个生成器完全没有 LLM 参与。它做的事情非常机械:拼字符串、拼路由装饰器、拼函数签名。这就是 deterministic coder 的本质——可预测、可单测、可审查

如果要扩展到 Go、Java 或者其他语言,只需要新增一个类似的生成函数。spec 保持不变,变的只是模板。

5.4 编排与 CLI

有了规划器和生成器,接下来把它们接起来,提供一个命令行入口。

# main.py import argparse import yaml from pathlib import Path from planner import requirement_to_spec from coder import generate_python_service, write_files def main() -> None: parser = argparse.ArgumentParser(description="LLM control of deterministic coder") parser.add_argument("--requirement", required=True, help="自然语言需求描述") parser.add_argument("--config", default="config.yaml", help="配置文件路径") args = parser.parse_args() config = yaml.safe_load(Path(args.config).read_text(encoding="utf-8")) llm_cfg = config["llm"] coder_cfg = config["coder"] # 阶段 1:LLM 生成 spec spec = requirement_to_spec( requirement_text=args.requirement, base_url=llm_cfg["base_url"], api_key=llm_cfg["api_key"], model=llm_cfg["model"], ) print(f"[planner] spec 生成成功: {spec.service_name}, endpoints: {len(spec.endpoints)}") # 阶段 2:确定性生成代码 files = generate_python_service(spec) output_dir = f"{coder_cfg['output_dir']}/{spec.service_name}" write_files(output_dir, files) print(f"[coder] 代码已生成: {output_dir}") for name in files: print(f" - {name}")

对应配置文件config.yaml

llm: base_url: "http://localhost:11434/v1" # Ollama 兼容地址 api_key: "ollama" # 本地服务可任意填写 model: "qwen2.5-coder:7b" coder: language: python output_dir: ./generated

这段代码的依赖是openaipydanticpyyaml,安装时补上:

pip install pyyaml

5.5 关键验证:确定性测试

整个架构的核心承诺是确定性。这个承诺必须有测试来守护。最直接的方式是:用同一个 spec 连续生成两次,断言输出完全一致

# test_deterministic.py from coder import generate_python_service from spec import ServiceSpec, EndpointSpec, FieldSpec def sample_spec() -> ServiceSpec: return ServiceSpec( service_name="user_service", endpoints=[ EndpointSpec( method="GET", path="/users/{user_id}", operation_id="get_user", summary="查询用户信息", fields=[ FieldSpec(name="user_id", type="integer", description="用户 ID"), FieldSpec(name="name", type="string", required=False, description="用户名"), ], ) ], ) def test_generation_is_deterministic() -> None: spec = sample_spec() files1 = generate_python_service(spec) files2 = generate_python_service(spec) assert files1 == files2, "同一 spec 两次生成结果不一致" def test_generated_code_contains_expected_route() -> None: spec = sample_spec() files = generate_python_service(spec) main_py = files["main.py"] assert '@app.get("/users/{user_id}")' in main_py assert "async def get_user():" in main_py

运行测试:

python -m pytest test_deterministic.py -v

这个测试如果通过,说明生成器符合 deterministic coder 的基本要求。它验证的不是“代码能不能跑”,而是“同一个 spec 是否永远生成同一份代码”——这是整个模式区别于纯 LLM 生成的核心特征。

test_generated_code_contains_expected_route这个测试更大的意义,是它验证了 deterministic coder 的输出是可单测的。你不需要担心模型随机改函数名,因为函数名来自 spec,而 spec 是确定的。

6. 运行结果与效果验证

6.1 运行命令与预期输出

启动 LLM 服务后,运行:

python main.py \ --requirement "创建一个用户服务,提供两个接口:POST /users 用于创建用户,参数为用户名和邮箱;GET /users/{user_id} 用于查询用户,返回用户 ID 和用户名。" \ --config config.yaml

预期输出:

[planner] spec 生成成功: user_service, endpoints: 2 [coder] 代码已生成: ./generated/user_service - main.py

生成的./generated/user_service/main.py大致如下:

"""Auto-generated by deterministic coder. DO NOT EDIT MANUALLY.""" from fastapi import FastAPI app = FastAPI(title="user_service") @app.post("/users") async def create_user(): """创建用户""" return {"username": None, "email": None} @app.get("/users/{user_id}") async def get_user(): """查询用户""" return {"user_id": None, "username": None}

6.2 如何判断成功

判断流程是否走通,看三点:

  1. LLM 输出是否被解析成合法 spec。如果[planner]这行打印出来,说明 LLM 的 JSON 输出通过了 Pydantic 校验。
  2. 生成的文件是否符合预期结构。检查main.py的路由和函数名是否与需求对应。
  3. 连续两次运行,输出是否一致

第三点尤其重要。你可以连续运行两次,然后 diff 两次的输出目录:

python main.py --requirement "..." --config config.yaml cp -r generated generated_run1 python main.py --requirement "..." --config config.yaml diff -r generated_run1 generated

如果diff没有任何输出,说明确定性成立。这个实验虽然简单,但它验证了整个架构最核心的承诺。

如果这一步失败,比如两次生成的 spec 或代码不一致,问题几乎一定出在 LLM 规划器环节,而不是 deterministic coder。排查顺序应该是:先看两次输出的 spec JSON 是否一致,再确认是不是模型对提示词的理解有波动。

7. 常见问题与排查思路

问题现象可能原因排查方式解决方案
LLM 返回内容解析 JSON 失败模型在 JSON 前后加了 Markdown 或解释文字打印原始raw内容确认格式在解析前去除代码块标记;启用response_format强约束
生成的 spec 字段值是非法类型模型输出了Literal之外的值,如"str"查看 Pydantic 校验错误信息在提示词中重复约束可选值;必要时加一次重试机制
同一需求两次生成的 spec 不一致模型本身采样有随机性,temperature=0不保证完全一致对比两次生成的 spec JSON接受 spec 层存在合理波动,但确保 deterministic coder 对同一 spec 输出一致
生成代码缺少某个接口LLM 在理解需求时漏掉了接口检查 spec 的 endpoints 数量改进提示词,明确要求“逐条列出所有接口”;或拆分成更小的需求
本地小模型生成 JSON 不稳定模型规模小,指令遵循能力弱先手写几个测试用例验证模型输出能力换更大模型;或增加“根据示例格式输出”的 few-shot 提示
生成的代码不符合团队规范确定性模板没有对齐团队规范审查coder.py的模板规则修改 deterministic coder 模板,而不是去改生成结果
项目开始要求“可复现”,但迭代中模板经常改deterministic coder 的模板演化没有版本管理记录模板变更历史给模板加版本号,将 spec 与模板版本绑定,保证历史可回溯

这里最容易被误解的一点,是“同一需求生成同一代码”。细心的读者已经注意到,我们的架构保证的是“同一 spec 生成同一代码”,而不是“同一需求生成同一代码”。需求到 spec 之间的转换仍然有波动空间,这是 LLM 的特性,不需要完全消除。真正的工程底线是:一旦 spec 被确认,后续链路必须完全确定

8. 最佳实践与工程建议

8.1 Spec 是核心资产,代码只是产物

采用 Sif 模式后,项目里真正需要长期维护的,不是生成的代码,而是 spec。spec 是需求的可执行表达,也是代码生成器的输入。它比自然语言更精确,比代码更易读。团队 review 的对象应该是 spec diff,而不是代码 diff。

建议把 spec 文件纳入版本管理,生成代码标记为自动产物,并用.gitattributes之类的机制让它们在 diff 中默认折叠。当需求变更时,第一步是修改 spec,第二步是重新生成代码,第三步是跑测试。如果生成结果和预期不符,优先检查是 spec 的问题还是模板的问题,不要直接在生成代码上打补丁。

8.2 安全边界与权限控制

LLM 接入任何生产流程,都必须画清楚权限边界。这里有几个具体建议。

第一,LLM 调用只读环境。规划器只接收需求文本,不读取生产数据,不访问数据库,不接触线上配置。如果需求来源包含敏感信息,要确保提示词不会把无关的隐私数据带进模型上下文。

第二,输出即隔离。生成的代码在沙箱或临时目录中构建和测试,通过验证后才允许合入主分支。不要让生成器直接写入生产代码目录。

第三,依赖最小化。deterministic coder 本身应该是纯函数,不需要访问网络,不需要调用模型。它存在的意义就是确定性和可测试性,引入任何外部状态都会破坏这个前提。

第四,对 LLM API 做超时、重试和熔断。如果模型服务不稳定,不要让重试逻辑无限执行。建议设置明确的超时时间(比如 30 秒)、最多重试 2 次、失败后返回可读错误信息,让上层决定是降级还是终止。

8.3 从脚手架场景开始落地

如果你要在团队里推广这种模式,不建议一开始就用来生成核心业务逻辑。更稳妥的路线是从三个场景切入。

第一个是项目脚手架。新项目初始化时,用 LLM 解析团队规范文档,输出模块结构 spec,再由 deterministic coder 生成基础框架。这比手工拷贝模板目录好用,因为 spec 可以根据项目类型调整。

第二个是接口层代码。REST API 的 controller 层、参数校验、基础响应结构,天然适合确定性生成。业务逻辑仍然手写,但接口层的一致性会大幅提升代码评审效率。

第三个是测试脚手架。根据 spec 自动生成基础测试用例和 mock 数据。生成的测试可能不完整,但作为起点能减少大量重复劳动。

在这些场景里,即使 LLM 的 spec 输出有波动,影响范围也被限制在可审查的产物内,不会直接污染手写代码。

8.4 模板的版本管理与回归测试

deterministic coder 的模板会随着团队规范演进。这个进化过程必须受控。模板更新后,所有历史 spec 都应该重新生成一次,跑完整的回归测试,确保新模板没有破坏旧 spec 的输出。这本质上是把“模板”当作一个需要持续测试的库来对待。

你可以在 CI 里加一个任务:用固定的 spec 样本集跑模板生成,然后与基线输出做 diff。只要 diff 不为空,就有人工检查。这样的自动化能防止模板在无意识中发生行为变化。

9. 总结与后续学习方向

Sif 1.0 这个项目带来的最有价值的启发,不是某个具体的生成器,而是“LLM 控制 deterministic coder”这个分工范式。它把 LLM 编程从“让 AI 写代码”推进到了“让 AI 做决策,让引擎写代码”。前者是不可控的创作,后者是可控的工程。

如果你正在做 LLM Agent 开发,可以沿着这个思路重新审视你的工具链:哪些环节需要模型发挥理解能力,哪些环节应该固化成交互协议和确定性执行。如果你在用 vibe coding 做原型,也可以考虑在进入生产阶段后,把非确定性代码逐步收敛到 spec 加确定性生成的结构里。

下一步的实践路径可以从一个小实验开始:挑一个你团队里重复性最高的代码生成场景,定义 spec 结构,写一个最小 deterministic coder,再把 LLM 接到规划器位置。跑通之后,你会直观感受到“不确定性被关进笼子”是什么体验。

这个话题还有几个延伸方向值得继续深入:一是带约束的 spec 生成与校验,比如 EBNF 语法约束;二是 deterministic coder 的多语言模板抽象;三是把这种模式嵌入主流 Agent 框架,让它成为 Agent 工具链中的一个标准环节。结合 MCP 这类结构化协议,这个方向的想象空间还在扩大。建议收藏本文,先从最小实现开始,手写一个 spec、跑通一次生成,再决定要不要把它带进你的真实项目。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/28 23:37:58

PDF自动化测试实战:用Python与pytest构建可靠断言

在业务系统里,我们经常接触 PDF 相关的功能:导出对账单、生成合同、转换发票、合并文件、加密归档……但很多团队对 PDF 的验证,长期停留在“人工打开看两眼”的阶段。直到某次上线后发现生成的合同少了一页、金额千分位丢失、加密 PDF 在客户…

作者头像 李华
网站建设 2026/8/28 23:32:58

从训练到部署:Paddle DeepSpeech语音识别模型工业级落地实战

1. 项目背景与核心价值:从“训练完毕”到“落地可用”的鸿沟“模型训练完毕”这六个字,对于任何一个投入过时间、算力和心血在深度学习项目上的开发者来说,都像是一声清脆的里程碑钟声。2021年10月13日,当我看到日志里跳出“Paddl…

作者头像 李华
网站建设 2026/8/28 23:31:46

大模型混合精度省了40%成本,灰度时它却把客户当成了内部文档

大模型混合精度省了40%成本,灰度时它却把客户当成了内部文档 周一例会后,老板把一段生成式AI客服的demo投在屏幕上:“咱内部知识库问答系统,两周上线。”作为技术负责人,我能扛住排期,但扛不住算力账单。全精度跑一次fine-tune,g5.12xlarge要烧掉近两千刀,而团队对混合精度的共…

作者头像 李华
网站建设 2026/8/28 23:31:33

给 CodeWhisperer 和 Copilot 同一段订单代码,一个补全了空指针,另一个没管

给 CodeWhisperer 和 Copilot 同一段订单代码,一个补全了空指针,另一个没管 去年底我一咬牙报了深度学习入门,初衷很朴素--同事聊天时总提 Transformer、注意力机制,我插不上嘴。课程第一周就让我用 PyTorch 搭了个三层全连接网络,说实话当时觉得这跟日常写业务代码离得有点远…

作者头像 李华
网站建设 2026/8/28 23:31:10

偏最小二乘回归(PLSR)实战指南:从原理到建模全流程解析

1. 项目概述:从“黑箱”到“白箱”,偏最小二乘回归的建模哲学在数据科学和多元统计建模的实战中,我们常常会遇到一个经典的“拦路虎”:当你想用一堆自变量(X)去预测一个或多个因变量(Y&#xff…

作者头像 李华