尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

LLM控制确定性编码器:让vibe coding变可信的架构实践

LLM控制确定性编码器:让vibe coding变可信的架构实践 2024 年到 2025 年“vibe coding”让很多不会写代码的人也能快速搭出原型但代码库一变大问题就爆发了模型生成的代码像随机拼接没人能保证它符合预期也没人敢直接合并进生产分支。这里真正值得关注的不是“AI 能不能写代码”而是“AI 写出来的代码能不能被验证、被审计、被信任”。最近看到一个项目标题是 “Show HN: LLM control of deterministic coder – Sif 1.0 – LLMs as vibe coders”。它把问题换了个角度不再让 LLM 直接生成代码而是让 LLM 去控制一个“确定性编码器”让模型负责定义意图、描述需求、规划路径由确定性工具去完成“把需求翻译成代码”的步骤。这个方向的本质变化是LLM 从“执行者”变成了“指挥者”。如果你也在关注 LLM Agent、AI 编程助手、MCP 编排、LLM 应用落地的工程化问题这篇文章会拆解 Sif 1.0 背后的控制范式、适用边界和落地建议。1. 从“模型写代码”到“模型控制编码器”先说一个现实痛点直接让 LLM 写代码在简单 demo 里体验很好但放到真实项目里会迅速暴露三个问题。第一个问题是不可验证。LLM 是概率模型同样的输入两次输出可能完全不同。你无法从“模型结构”上证明它这次生成的代码是正确实现。要确认对错只能靠编译、测试和人工 review。一旦代码量上千行review 成本就会高到团队无法承受。第二个问题是上下文失控。LLM 的上下文窗口再大也不可能装下整个大型代码库。当它在一百个文件里做修改时它其实经常忘记前十个文件的约束。结果是每个文件单独看都还行组合起来全是接口不匹配和逻辑矛盾。第三个问题是幻觉的代价被低估。模型可能生成一个看起来很像标准库的 API但实际不存在它也可能信心满满地写出一段违反项目安全规则的代码。传统开发里编译器会帮你拦住一部分错误但 LLM 生成的代码最大的问题不是语法错误而是“语法正确但语义错误”。Sif 1.0 这个项目选了一条不同的路LLM 不直接生成实现代码而是生成“控制指令”和“任务描述”交给一个确定性的编码引擎去执行。这样做的意义是什么用最直白的话说LLM 负责“做决定”确定性系统负责“做执行”。做决定的部分包括用户想要什么、应该改哪个模块、输入输出是什么、边界条件有哪些。这些任务天然适合 LLM因为它们是自然语言层面的理解与规划问题。做执行的部分则完全不同生成具体的函数实现、按照模板生成模板代码、完成配置项的拼接。这些任务需要的是“确定性的、可重复的、符合语法规则的”转换能力。如果你把代码生成过程完全交给 LLM你得到的是概率。如果你把代码生成过程一半交给 LLM 做规划、一半交给确定性引擎做实现你得到的是“可控制的概率”。后者不是完美方案但它把不可控的部分压缩到了一个可以人工检查的范围内。Sif 1.0 不是第一个提出“LLM 不直接写代码”的项目但它把“LLM as vibe coders”这个定位摆到了明面上——让模型像程序员一样“凭感觉描述需求”而让确定性工具像工程师一样“把需求落地”。这个分工一旦成立LLM 在代码生成里的角色就彻底变了。2. 什么是 deterministic coder为什么需要确定性“deterministic coder”这个词如果直译就是“确定性编码器”。它跟传统编译器的相似度更高而不是更接近 AI 编程助手。传统编译器做的事情是输入源代码输出机器码。这是一个完全确定性的过程——同样的输入永远得到同样的输出。你不会担心编译器“发挥不稳定”。deterministic coder 做的事情是输入一个结构化的任务描述不是自然语言而是受约束的 schema输出满足约束的代码文件或配置片段。整个过程由规则、模板、AST 变换或代码生成器驱动不涉及概率采样。它的特点和编译器非常像可重复相同任务描述相同引擎输出完全一致。可验证输出结果可以通过类型检查、语法解析、构建验证来确认。可审计每一步转换都有记录可以追溯到输入的任务描述。可测试引擎本身可以用测试用例覆盖因为它的行为是确定的。相比之下典型的 LLM 代码补全工具是输入自然语言注释输出概率采样后的代码。这就决定了它天然带三个特征随机性、不可解释性、难以系统化测试。这里需要强调确定性编码器并不是要取代 LLM它是 LLM 的执行手臂。没有 LLM 时deterministic coder 只能处理预先定义好的模板任务灵活度很低。没有 deterministic coder 时LLM 虽然灵活但生成结果不可控。两者结合才形成一个完整链条自然语言需求 → LLM 规划 → 结构化任务描述 → deterministic coder 生成 → 构建验证 → 结果返回。在 Sif 1.0 的思路里LLM 被限制在一个更“安全”的位置它只能输出结构化的任务描述而不是直接输出代码。如果任务描述不符合 schema就重试或者报错。这样的设计让 LLM 的“发散性”被刻意约束不让它的自由发挥污染代码结果。如果你还在用“LLM 直接生成完整函数”的方式做 AI 编程那么可以留意一下这个对比维度传统 LLM 直接生成代码LLM 控制 deterministic coder生成结果稳定性低同一提示多次输出不同高同一任务描述输出一致验证方式编译 人工 review 测试结构化校验 构建验证 人工 review幻觉影响范围直接进入代码范围大被约束在任务描述层范围小适合任务原型、脚本、零散函数批量代码生成、模板化项目、工程化改造主要瓶颈上下文窗口、模型能力、随机性任务描述 schema 设计、确定性引擎能力这不是说“LLM 直接生成代码”没有价值而是说这类方式更适合作“辅助”而不是“主导”。Sif 1.0 的定位是把 LLM 从主导者降级为规划者。这个降级对真实项目的稳定性是有益的。3. LLM 作为 vibe coders 的定位转变先解释“vibe coders”这个词。它原本带一点调侃意味指那些不完全理解代码细节、主要凭感觉和提示词让 AI 生成程序的人。在传统语境里vibe coders 通常被看作“不太专业的开发者”。但 Sif 1.0 在标题里说 “LLMs as vibe coders”给这个词增加了一层新含义LLM 本身就应该像 vibe coders 那样工作——凭意图描述需求把机械性实现留给确定性引擎。这个定位背后有一个关键洞察LLM 最强的能力是理解语义、抽取意图、构建方案而不是精确执行语法规则。你让 LLM 写一个 Python 函数它大概率能写对但你让它在一个 10 万行代码的 Java 项目里精确完成跨 12 个文件的重构它就会开始“幻觉”——因为约束太多超出了它的稳定推理范围。反过来如果你让 LLM 只做一件事把用户的需求转化为规范化的 JSON 任务描述这件事的容错率和可控性会大幅提升。因为任务描述是结构化的、字段固定的LLM 不需要精确到语法层只需要在语义层做转换。这就是 Sif 1.0 里 “vibe coder” 的含义让模型用“感觉”和“意图”去定义代码应该长什么样而不是逐行逐句地“手写”。这种转变带来三个直接好处。第一个好处是幻觉空间被压缩。LLM 不再直接输出代码所以它无法虚构一个不存在的 API、也无法写出一个看似合理但语义错误的函数体。它的输出被限制在任务描述字段里字段的取值范围由 deterministic coder 的 schema 决定。第二个好处是评审对象变了。以前你 review 的是模型生成的几千行代码成本极高现在你 review 的是它生成的任务描述通常只有几十行 JSON。任务描述比代码更容易被人类理解也更容易被自动化校验。第三个好处是工具链可以沉淀。当一个任务描述 schema 稳定后你可以围绕它做缓存、版本管理、权限控制、质量评估。但如果是直接让 LLM 生成代码你能沉淀的只有提示词和补丁很难体系化。当然这个定位也有代价。LLM 不直接写代码意味着它的能力上限会被确定性编码器卡住。如果 deterministic coder 支持不了某个复杂逻辑那么 LLM 描述得再好也没用。这是“控制”的代价也是“确定性”的代价。可以做一个类比传统 AI 编程像是让一个非常聪明但偶尔会编造事实的实习生直接写生产代码Sif 1.0 的思路像是让这个实习生写设计文档再由一个严格的代码生成器根据设计文档出代码。前者灵活但危险后者约束多但每一步都可追溯。如果你正在做一个 AI 编程工具或者正在规划 LLM Agent 来辅助研发这个“降级”思路值得认真考虑。它不一定适用于所有场景但它能解决“LLM 输出不可控”这个核心问题。4. Sif 1.0 要解决的问题与整体架构从项目标题看Sif 1.0 的核心不是一个新的代码生成模型而是“LLM 控制确定性编码器”的完整实现。它要解决的问题可以归纳成一句话怎么让 LLM 在不直接生成代码的前提下仍然能完成代码生成任务。这就需要一个控制层。这个控制层不是简单地把 prompt 发给模型再把输出透传给某个工具而是要完成四件事。第一件事是意图解析。将用户的自然语言需求解析成结构化的任务描述。这个步骤目前几乎只有 LLM 能做因为自然语言理解的泛化能力是规则系统很难实现的。第二件事是任务约束。任务描述不能是自由文本它必须符合 deterministic coder 能理解的 schema。比如任务类型、目标文件、输入参数、期望输出、约束条件。如果 LLM 输出不满足 schema控制层要能检测、修正或重试。第三件事是确定性生成。基于任务描述由确定性编码引擎生成代码文件或配置。这个过程不调用 LLM不涉及概率采样。它的正确性可以通过语法解析和构建工具验证。第四件事是验证与反馈。生成结果要经过构建、测试或类型检查。如果失败把错误信息返回给控制层控制层再决定是修正任务描述还是终止任务。从架构模式来看Sif 1.0 采用的是“LLM 在外层确定性核心在内层”的分层设计。LLM 被当成一个“可变形的语义输入层”确定性编码器是“不可变的执行核心”。数据流是单向的用户需求先进 LLMLLM 输出任务描述任务描述进确定性引擎引擎输出代码代码进验证器。这个架构有一个很大的优点LLM 的升级不会影响底层确定性引擎的稳定性。你不必因为 GPT 出到第 5 代就重写整个代码生成链路因为 LLM 和 deterministic coder 之间只有一个任务描述协议。协议不变两边都可以独立升级。它也有一个明显的脆弱点任务描述 schema 是整个系统的“阿喀琉斯之踵”。如果 schema 设计得不够好LLM 很可能会频繁产出无效描述或者表达不出用户真实需求。而 schema 的设计又高度依赖业务场景没有万能的通用 schema。从工程角度看Sif 1.0 的这个架构思路更像是在做一个“代码生成中间层”而不是在做一个“更聪明的代码补丁工具”。它不追求让 LLM 理解全部代码上下文而是追求让 LLM 准确输出“有限的、结构化的意图信息”然后交给不可出错的部分去执行。这也解释了为什么项目标题会强调 deterministic coder。真正的创新点不是在 LLM 侧而是在执行侧。确定性引擎让 LLM 的随机性变得“局部化”这正是真实项目最需要的能力。5. 最小接入思路与关键代码示例接下来进入实践部分。这里需要先做一点说明Sif 1.0 是项目的公开概念演示范例不同版本在不同仓库中的具体 API 和配置方式会有差异。下面这份示例不是照搬项目源码而是按照“LLM 控制 deterministic coder”这个架构思路写一份最小可复现的控制层模型方便理解核心流程。实际接入时请以项目仓库中对应版本为准。整个接入流程可以分成三步定义任务描述 schema、实现控制层、接入确定性生成器。5.1 定义任务描述 schematask schema 是 LLM 与 deterministic coder 之间的协议。设计原则是字段尽量少枚举尽量明确避免开放式文本。下面是一个任务描述的简化 JSON 示例{ task_type: create_function, target_file: src/utils/string_utils.py, function_name: truncate_string, inputs: [ {name: text, type: str}, {name: max_length, type: int} ], return_type: str, description: Truncate text to max_length without cutting in the middle of a multi-byte character, constraints: [ if max_length 1, raise ValueError, if text is None, return None ] }这个 schema 的特点在于LLM 只需要把用户需求翻译成“函数名 参数 约束描述”并不需要直接写出函数体。函数体的实现细节由 deterministic coder 根据模板和规则生成。5.2 实现控制层控制层的职责是接收用户自然语言需求调用 LLM 生成任务描述校验描述是否符合 schema再交给 deterministic coder。下面是一个用 Python 编写的简化示意# sif_control_demo.py import json from typing import Any class TaskSchemaValidator: 校验 LLM 输出的任务描述是否符合预期结构 REQUIRED_FIELDS { task_type: str, target_file: str, function_name: str, inputs: list, return_type: str, description: str, constraints: list, } classmethod def validate(cls, task: dict[str, Any]) - list[str]: errors [] for field, field_type in cls.REQUIRED_FIELDS.items(): if field not in task: errors.append(fmissing field: {field}) elif not isinstance(task[field], field_type): errors.append(ffield {field} should be {field_type.__name__}) return errors class DeterministicCoder: 确定性编码引擎的简化接口 def __init__(self, template_repo: dict[str, str]): self.template_repo template_repo def generate(self, task: dict[str, Any]) - str: 根据任务描述生成代码这里只演示最简模板拼接 template self.template_repo.get(task[task_type]) if not template: raise ValueError(funsupported task_type: {task[task_type]}) params , .join( f{item[name]}: {item[type]} for item in task[inputs] ) return template.format( function_nametask[function_name], paramsparams, return_typetask[return_type], descriptiontask[description].replace(\n, \n ), constraints\n .join(task[constraints]), ) class LLMClient: LLM 客户端接口实际项目中可以替换为 OpenAI、Claude 或本地模型 def generate_task(self, user_request: str) - dict[str, Any]: # 真实项目中这里需要调用 LLM API # 这个示例里直接返回一段预置的“模拟模型输出” return { task_type: create_function, target_file: src/utils/string_utils.py, function_name: truncate_string, inputs: [ {name: text, type: str}, {name: max_length, type: int}, ], return_type: str, description: Truncate text to max_length without cutting a multi-byte char, constraints: [ if max_length 1, raise ValueError, if text is None, return None, ], } class SifController: def __init__(self, llm: LLMClient, validator: TaskSchemaValidator, coder: DeterministicCoder): self.llm llm self.validator validator self.coder coder def run(self, user_request: str) - tuple[bool, str]: task self.llm.generate_task(user_request) errors self.validator.validate(task) if errors: return False, ftask schema invalid: {errors} try: code self.coder.generate(task) except ValueError as exc: return False, fdeterministic generation failed: {exc} # 此处可继续接入构建验证、类型检查等步骤 return True, code这段代码不依赖任何第三方库核心目的就是展示控制层的骨架。关键逻辑有四处TaskSchemaValidator负责校验 LLM 的输出。这是“控制”的第一步能有效阻断“LLM 输出自由文本然后直接当代码执行”的风险。DeterministicCoder只接受结构化的 task dict。它内部不做任何概率推理只做模板拼接和规则转换。LLMClient.generate_task在真实项目里是唯一可能“发散”的环节但它输出的范围被预期约束在 schema 内。SifController.run是完整的控制流LLM 解析 → schema 校验 → 确定性生成。5.3 接入确定性生成器真正生产级的 deterministic coder 不会只用字符串模板通常会依赖 AST 解析、代码格式化工具、类型系统等。下面是一个使用 Python AST 生成函数的简化示例# deterministic_generator.py import ast from typing import Any def build_function_ast(task: dict[str, Any]) - ast.FunctionDef: 用 AST 节点构建函数定义而不是拼接字符串 args [] for item in task[inputs]: arg ast.arg(argitem[name], annotationast.Name(iditem[type], ctxast.Load())) args.append(arg) body [] if task.get(description): docstring_expr ast.Expr(valueast.Constant(valuetask[description])) body.append(docstring_expr) # 这里只是演示把约束作为字符串常量放进去 # 实际项目中应由可实现语义的规则引擎处理。 for constraint in task[constraints]: body.append( ast.Raise( excast.Call( funcast.Name(idValueError, ctxast.Load()), args[ast.Constant(valuefconstraint not met: {constraint})], keywords[], ), causeNone, ) ) function_def ast.FunctionDef( nametask[function_name], argsast.arguments( posonlyargs[], argsargs, varargNone, kwonlyargs[], kw_defaults[], kwargNone, defaults[], ), bodybody, decorator_list[], returnsast.Name(idtask[return_type], ctxast.Load()), ) return function_def if __name__ __main__: task { task_type: create_function, function_name: truncate_string, inputs: [ {name: text, type: str}, {name: max_length, type: int}, ], return_type: str, description: Truncate text safely., constraints: [max_length must be positive], } node build_function_ast(task) source ast.unparse(node) print(source)这段代码用 Python 标准库ast构建语法树再通过ast.unparse转成源码。它的产出是确定性的同样输入永远得到同样输出。这就是“确定性编码”的含义。6. 运行验证与效果检查上面示例中的SifController.run是最简验证入口。下面给出完整的运行和验证流程。先验证控制层的基本流程python sif_control_demo.py这段代码执行后不会有输出因为run方法只是返回了 tuple。可以在交互式环境里运行验证from sif_control_demo import LLMClient, TaskSchemaValidator, DeterministicCoder, SifController llm LLMClient() validator TaskSchemaValidator() coder DeterministicCoder(template_repo{ create_function: def {function_name}({params}) - {return_type}:\n \\\{description}\\\\n {constraints}\n }) controller SifController(llmllm, validatorvalidator, codercoder) success, result controller.run(write a truncate string function) print(success) print(result)预期输出是True def truncate_string(text: str, max_length: int) - str: Truncate text to max_length without cutting a multi-byte char if max_length 1, raise ValueError if text is None, return None注意这个模板输出只是演示连接是否打通并不代表真实生产代码。真正验证时你需要做三步检查。第一步检查 schema 校验是否能拦下错误格式。把LLMClient.generate_task的返回值改为缺少function_name的 dict再运行应该看到task schema invalid错误。第二步检查确定性生成是否能复现。同一个 dict 作为DeterministicCoder.generate的输入连续运行两次输出必须完全一致。如果不一致说明引擎里有隐藏的随机状态。第三步检查生成结果是否能通过构建。真实项目中controller.run返回代码后还应该触发该语言对应的编译或测试命令。这步是保证“确定性生成 可验证”成立的关键。如果你的任务是生成完整项目文件可以在DeterministicCoder里按文件路径分别构建 AST最后统一写入目标目录。写入前建议先在临时目录生成再执行构建验证验证通过后再替换正式目录。这个流程可以避免生成半成品污染原项目。7. 常见问题与排查思路把 LLM 和 deterministic coder 结合起来时最容易出问题的地方不是模型能力而是控制层设计。下面按出现频率整理了五个问题。问题现象可能原因排查方式解决方案LLM 输出的任务描述频繁缺少字段提示词约束不足或 schema 字段过多记录 LLM 原始输出统计缺字段分布精简 schema在提示词中给出严格 JSON 示例增加自动补全和重试机制deterministic coder 不支持某种任务类型模板库或规则库未覆盖该任务查看引擎报错信息扩展确定性引擎的任务类型或者将该任务标记为“需要人工处理”生成代码能跑但逻辑不对任务描述中的约束表达不完整检查 LLM 生成的 constraints 列表增加约束校验规则对关键业务写入测试用例必要时人工修正任务描述后重生成任务描述 schema 校验通过但生成代码构建失败确定性引擎生成的代码语法错误或依赖缺失查看构建日志在确定性引擎中接入语法解析生成后自动运行编译测试失败则回到控制层修正LLM 调用成本过高每次需求都完整走 LLM 生成流程查看调用日志统计 token 消耗对常见任务建立任务描述缓存同类需求直接复用已有任务描述除了上面这些还有两个容易忽略的地方。第一个是循环重试陷阱。在设计重试逻辑时最常见的问题是LLM 第一次输出不合法重试后 LLM 发现“上一次不对”反而生成一个更复杂的 JSON 来表达歉意而不是重新输出任务描述。解决办法很简单在重试时不要把错误信息直接塞回给模型而是明确要求“只输出修正后的 JSON不要任何解释”。第二个是任务描述审计。LLM 输出的任务描述应该和最终生成的代码一样被纳入版本管理。否则项目出问题后你很难知道这条代码是由哪条任务描述生成的。推荐把每次任务描述、校验结果、生成代码 hash、构建结果都记录到日志中。8. 最佳实践与工程建议从 Sif 1.0 的设计思路延伸出来这类“LLM 规划 确定性执行”的架构在工程落地时有六个实践建议可以帮你少走弯路。第一个建议是设计任务 schema 时优先做“减法”。字段越少LLM 越容易输出合法结果。能用枚举值表达的不要用开放文本能用几个明确参数表达的不要用长描述。schema 是协议不是需求文档它的目的是让 LLM 和确定性引擎之间的交接足够可靠。第二个建议是把确定性引擎当成“编译器”来构建。不要只做字符串拼接尽量使用 AST、代码生成器、模板引擎这类结构化的手段。生成结果要能通过该语言的 lint、编译和测试否则确定性只是“自我安慰”。一个真正可靠的确定性生成器应该有比较高的单元测试覆盖率保证“相同输入 → 相同输出”这一基本承诺。第三个建议是建立任务描述缓存池。实际业务中大量需求是重复的。比如“给某个工具类增加一个校验函数”、“创建一个标准 REST Controller”。这类任务完全可以生成一次、验证通过后缓存后续相同需求直接复用任务描述不需要再调用 LLM。这能同时降低延迟和成本。第四个建议是给 LLM 设置安全边界。LLM 能读取的代码库信息要限制在任务描述所需的最小范围。它不应该拿到整个代码库的原始内容更不应该有直接修改文件系统的能力。在 Sif 1.0 的控制流里真正的“写入”操作应该只发生在确定性引擎和验证阶段LLM 全程不触碰文件系统。第五个建议是强制验证流程而不是相信输出。不管 LLM 的任务描述有多规范不管确定性引擎的设计有多严谨最后都要走一遍真实构建。这个验证步骤不能写成注释里的 TODO而应该是控制器的内置环节。验证失败时宁可终止任务也不要输出“看起来能用”的代码。第六个建议是为每次生成留痕。记录用户需求、LLM 原始输出、schema 校验结果、确定性引擎版本、生成代码哈希、验证结果。这套审计日志不仅能在出问题时帮你回溯还能在训练数据积累后帮你分析“哪类需求最容易导致生成失败”从而持续改进 schema。9. 总结与后续学习方向Sif 1.0 的标题值得读两遍。“LLM control of deterministic coder”解决的是控制问题“LLMs as vibe coders”解决的是角色定位问题。两者加在一起其实是在说一件事LLM 的最大价值不是一个更好的代码补全器而是一个更好的意图转换器。它把不可控的自然语言需求转换成可控的结构化任务描述然后交给确定性的系统去执行。这篇文章没有给你一份 Sif 1.0 的完整 API 手册因为这类项目还在快速迭代中版本之间的差异很大。但控制流的骨架是稳定的需求解析 → schema 校验 → 确定性生成 → 构建验证。把这个骨架理解透你就能根据自己的项目需求搭建一套“LLM 在外面、确定性在核心”的代码生成链路。如果你想继续深入建议按三条线推进。第一条线是学习 AST 与代码生成理解 deterministic coder 底层是怎么构建语法树的第二条线是研究 LLM 结构化输出structured output的工程实现比如 JSON Schema 约束生成第三条线是把这套控制流接入现有 CI/CD让生成代码自动进入测试与发布管道。当前阶段更稳妥的做法是在一个非核心模块里先跑通最小验证。比如选一个模板化程度较高的服务定义好任务描述 schema接一个确定性生成器观察真实需求下的成功率和失败样例。得到有效数据后再决定是否扩大到更大范围的代码库。这个方向的关键不在于“让模型写出所有代码”而在于“让模型只在它擅长的地方做决策”。
返回列表