LLM控制确定性代码生成:让vibe coding走向工程化 📅 发布时间:2026/8/28 23:40:13 👁 浏览次数: Vibe coding 这个词从 2025 年初开始被反复讨论但大部分讨论都停留在“AI 帮我写代码”这个表面。真正把它落地到工程里的人会发现一个尴尬的事实大模型写代码写得好不好取决于你对“好”的定义。如果你要的是“能跑”那确实很爽如果你要的是“可复现、可审查、可测试”LLM 直接生成代码的行为会让你抓狂。同一个需求跑三次得到三份不同的实现。这让 code review 变成猜谜让回归测试失去基准也让“这次改动到底改了什么”变成哲学问题。更麻烦的是LLM 生成的代码一旦混入生产环境你很难向团队解释为什么这段代码长这样也很难在出问题时快速定位是哪个环节引入了缺陷。Sif 1.0 这个项目的切入点就是把这个矛盾拆开LLM 不直接写代码而是去控制一个 deterministic coder确定性代码生成器。LLM 负责理解需求、拆解任务、做决策确定性引擎负责把决策翻译成代码。一句话概括LLM 成为 vibe coderdeterministic 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 Agent2.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 只负责构造调用参数不负责生成代码文本。这也是 MCPModel 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 负责输出 specspec 被审查后进入生成流程生成流程的输出是确定性的。如果代码有问题要么是 spec 错了要么是模板错了二选一不会出现“模型随机抽风”这种无法归因的情况。另一个理由是成本结构。让 LLM 直接生成完整代码每个 token 都在消耗推理成本而且越长的代码越容易累积错误。让 LLM 生成 spec 只有很小的 token 开销剩下的代码生成完全不依赖模型调用成本和延迟都可预测。在批量生成场景下这个成本差异非常明显。3.3 这种架构的适用边界需要明确的是Sif 模式不是万能的。它有非常清晰的适用边界。适合的场景包括CRUD 服务生成、内部管理后台脚手架。数据模型的初始化代码和对应迁移文件。SDK 客户端、接口封装层的批量生成。规范化要求高、重复性强的工程代码。需要快速起项目但后续要长期维护的场景。不适合的场景包括探索性架构设计没有明确规范可循的新系统。深度业务逻辑需要大量隐含领域知识的模块。对既有代码库的低侵入式修改需要理解大量上下文。代码本身不是核心资产、以后不会再维护的一次性脚本。判断标准很简单如果你能写出清晰的 spec就适合用这个模式如果你自己都说不清要生成什么指望 LLM 替你“临场发挥”那 Sif 模式也帮不了你。它能约束不确定性但不能创造确定性。4. 环境准备与前置条件4.1 运行环境与依赖本文后续的最小实现会演示“LLM 规划器 deterministic coder”的完整流程。实验环境如下版本请以实际项目为准重点是展示通用思路。操作系统Linux 或 macOS 均可Windows 建议使用 WSL2。Python3.10 及以上需要支持typing.Literal和list[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_urlbase_url, api_keyapi_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( modelmodel, messages[{role: user, content: prompt}], temperature0, 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)几个值得注意的点temperature0并不能保证输出完全一致但它能显著减少随机性。真正保证确定性的是下游的 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, , fapp FastAPI(title{spec.service_name}), , ] for ep in spec.endpoints: # 将 operation_id 转换为合法的 Python 函数名使用 spec 原值 func_name ep.operation_id lines.append(fapp.{ep.method.lower()}({ep.path})) lines.append(fasync 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(parentsTrue, exist_okTrue) for name, content in files.items(): (out / name).write_text(content, encodingutf-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(descriptionLLM control of deterministic coder) parser.add_argument(--requirement, requiredTrue, help自然语言需求描述) parser.add_argument(--config, defaultconfig.yaml, help配置文件路径) args parser.parse_args() config yaml.safe_load(Path(args.config).read_text(encodingutf-8)) llm_cfg config[llm] coder_cfg config[coder] # 阶段 1LLM 生成 spec spec requirement_to_spec( requirement_textargs.requirement, base_urlllm_cfg[base_url], api_keyllm_cfg[api_key], modelllm_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.yamlllm: base_url: http://localhost:11434/v1 # Ollama 兼容地址 api_key: ollama # 本地服务可任意填写 model: qwen2.5-coder:7b coder: language: python output_dir: ./generated这段代码的依赖是openai、pydantic、pyyaml安装时补上pip install pyyaml5.5 关键验证确定性测试整个架构的核心承诺是确定性。这个承诺必须有测试来守护。最直接的方式是用同一个 spec 连续生成两次断言输出完全一致。# test_deterministic.py from coder import generate_python_service from spec import ServiceSpec, EndpointSpec, FieldSpec def sample_spec() - ServiceSpec: return ServiceSpec( service_nameuser_service, endpoints[ EndpointSpec( methodGET, path/users/{user_id}, operation_idget_user, summary查询用户信息, fields[ FieldSpec(nameuser_id, typeinteger, description用户 ID), FieldSpec(namename, typestring, requiredFalse, 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(titleuser_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 如何判断成功判断流程是否走通看三点LLM 输出是否被解析成合法 spec。如果[planner]这行打印出来说明 LLM 的 JSON 输出通过了 Pydantic 校验。生成的文件是否符合预期结构。检查main.py的路由和函数名是否与需求对应。连续两次运行输出是否一致。第三点尤其重要。你可以连续运行两次然后 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 不一致模型本身采样有随机性temperature0不保证完全一致对比两次生成的 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、跑通一次生成再决定要不要把它带进你的真实项目。