拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

BAML 类型系统测验(type_quiz):用真实编译器驱动的自适应类型系统教学引擎

BAML 类型系统测验(type_quiz):用真实编译器驱动的自适应类型系统教学引擎 编程语言AI Agent编译器CLI人工智能【免费下载链接】bamlThe programming language for agents项目地址https://gitcode.com/gh_mirrors/ba/baml点击查看免费下载本文围绕 baml_language/tools/type_quiz/README.md 展开介绍 BAML 项目中一个特殊而精密的工具一个用于教授 BAML 类型系统的自适应测验包。它从 TYPE_SYSTEM.md 的规则表中自动生成案例每一个案例都被真实编译器验证因此测验永远不会与语言实际行为脱节。读完本文你将理解它的分层架构engine / algebra / bank / conformance、案例如何由事实fact、站点site、反例foil与形状shape构成、自适应选题背后的 Fisher 信息与 DINA 学习者模型、以及它作为dogfooding工具为编译器本身暴露的十余个真实缺陷——其中部分至今仍可复现。为什么需要一个类型系统测验包BAML 是一个面向 agent 的编程语言其类型系统是集合论式的子类型即子集关系S : T当且仅当S的值集是T的子集而非继承关系类型在运行期依然存在reflection 是一等公民。TYPE_SYSTEM.md 用 639 行完整规定了这一系统的预期行为并明确声明它是规范性prescriptive而非描述性descriptive的文档文档描述类型系统应该如何工作而实现可能落后于规范。type_quiz 正是为弥合这一差距而生。其 README 的第一段话点明了设计目标一个通过让学习者预测编译器的回答来教授 BAML 类型系统的测验这段程序能否编译为什么。案例由../../TYPE_SYSTEM.md中的规则生成并且每个案例都经过真实编译器验证因此只要 CI 在运行测验就不可能偏离语言实际。关键在于每个案例都跑在真实编译器上。这使测验同时具备三重身份教学工具——让学习者通过预测编译结果来掌握子类型、协变性、一致性coherence等概念规范与实现的验证器——任何规范说 A、编译器做 B的偏差都会被 conformance 套件捕获编译器的 dogfooding 压力测试——构建测验本身暴露了十几个编译器缺陷其中一部分至今未修复。包的分层架构与双账本五个命名空间Layers包被严格划分为五个命名空间README 中的表格完整定义了各自的职责与何时需要改动命名空间角色何时需要改动ns_engine基座种子seeds、案例cases、声明claims、判定verdicts、验证器verifier、采样与会话类型系统变化时永不改动ns_algebra被测材料Ty模型、渲染rendering、关系relations、站点sites类型系统变化时改动ns_bank被测材料引自规范的规则表、事实生成器fact generators、条目items、朴素模型naive models、特征features、权重weights、名称池name pools教学内容或教学方式变化时改动ns_conformance绊线套件tripwire suites—main.baml组合根composition root唯一同时知道 engine 与 bank 的文件也是唯一与终端对话的文件—依赖方向是单向的bank → algebra → engine见 lint.sh 第 62-69 行它显式扫描禁止反向引用。这条依赖规则无法用 BAML 语言本身表达——BAML 命名空间不声明依赖方向——所以由 lint 脚本强制执行。main.baml是所有顶层函数的唯一出口它只依赖 enginethe only file that knows both the engine and the bank通过root.engine.*与root.bank.*组合二者。两份并行的账本代码之外还挂着两份账本它们把规范未覆盖与规范与实现冲突变成可追踪、可关闭的条目SPEC_GAPS.md记录规范遗漏或表述过松的原则。每条目格式为id、种类omitted/under-specified/doc-vs-compiler、原则、建议措辞、被谁引用。例如G-001子类型在何处被检查被规则flow/requires-subtype引用——因为规范定义了子类型关系却从未说明它应用在哪些位置G-002函数类型中void对null则直接来自引擎passes工作区的一个真实缺陷。一条没有章节可引用的规则必须引用账本条目因此缺口不可能在无记录的情况下存在。COMPILER_DIVERGENCE.md记录编译器与规范矛盾之处。条目格式为id、规范原文、编译器实际行为标注观测的 canary commit、矛盾种类Accepts编译器接受了规范拒绝的Rejects拒绝规范接受的Misreports同样拒绝但报错码或位置不同、以及它阻塞的条目。被标记为CompilerDiverges的条目必须引用这里的记录conformance 套件断言该分歧仍然存在——一旦编译器修复套件立刻失败直到该条目被升级promote并关闭。以CD-003为例let left: bool | (string | int) right这类顶层联合嵌套括号联合的注解被编译器向后检查导致5n这样的值错误通过并反射为bigint。它由近反例near missunion_regrouped_differs发现——此前的union_associates一直在用等价的A | B | C拼写放在绑定处而等价关系无论流向如何都编译通过因此从未暴露问题。现在该条目阻塞unions/nested-in-let-annotation并持有所有会拼写出此类注解的配对root.algebra.sites_for。案例是如何制造的fact、item、site、foil 与 shape这是整个包最核心的机制。理解它需要一串环环相扣的概念事实fact与关系relation一个fact将一条规范规则应用于种子类型seeded types或将一个上下文规则context rule数组、映射、类参数、函数参数、返回、错误、联合成员应用于另一个 fact并记录随之而来的关系。事实是规则在具体类型上的实例化。站点site流向站点与一致性站点一个item将 fact 放到一个站点上。站点由种子从两类中选择五个流向站点flow sites绑定binding、调用参数call argument、返回return、字段初始化器field initializer、数组元素array element。流向站点以某个方向承载这对类型——正向或反向——因此程序的外形永远不会泄露答案一个形状看起来像等价的程序可能恰恰是被拒绝的那一个。一致性站点coherence site为同一个接口写两个implements块。流向站点与一致性站点的语义截然相反一个流向关系成立子类型时编译通过而两个implements块在目标类型等价时被拒绝——因为一个类型对同一接口至多只能有一个实现无论两块如何拼写。于是得出等价关系的 fact重排的联合、别名、bool对true | false恰恰是流向总是接受、一致性总是拒绝的那一批。学习者必须读懂眼前的是哪种构造而不是靠识别外形答题。只有当一个implements块可以同时命名两种类型时配对才能到达一致性站点这排除了联合、字面量、接口、never、unknown和递归别名。被拒绝的键与诊断被拒绝的案例会携带诊断代码和编译器必须指向的探针字节区域byte region——由站点在渲染时计算。items.baml 第 6-15 行的rejection_code表明五个流向站点一律报E0001一致性站点报E0132。解释explanation与追踪trace每一条被实例化的规则构成一个声明claim声明的轨迹就是解释最后一个声明指明哪个类型遇上了哪个槽位which type met which slot见site_claim。这是教学的核心材料——学习者答完题后看到的为什么。反例foil同一程序反向流动当 fact 陈述的关系是严格关系时同一 fact 反向流动就是一个编译器给出相反回答的程序item 将其作为foil携带。两者是同一程序除了交换套件要求它们除流向方向外是同一串字符见 README 第 53-55 行。因此向学习者同时展示两个并让 ta 判断哪个能编译几乎无法从表面读出答案——几乎而非完全检查会把括号放在一边letters因为联合作为数组元素需要括号、作为参数则不需要所以约 1/25 的配对恰好差这两个括号而那其中带括号的程序 4/5 能编译。等价或无关的配对在两个方向读取相同因此没有反例一致性站点也没有反向抽取只是两块上下颠倒。README 的统计银行供应的四十个条目中二十二个成对且四十个中的每一个都跨种子双向作答。形状shapefact 与其近反例合二为一如果银行只由等价则必编译、无关则必拒绝的 fact 构成它教的就不是规则而是外形。README 记录了第一版银行的失败递归别名 100 次里编译 93 次map19 次整体五题对三注意到这一点的学习者不推理也能答对——估计器却把这也算作掌握了规则。因此每个单向作答的 fact 旁边都必须有一个披着同样外形、答案相反的 factA | B对B | C重排联合的反例A | (B | C)对A | B | D重新分组type A int | A[]对type B string | B[]同一递归的两种拼写A对string | A[]其展开每个枚举的两个变体对枚举自身的变体true | 1对true | false一个字面量对不属于它的类型什么都不会吸收它一个类对自身 对 两个同形的类int[]对unknown[]——unknown唯一一个双向都被拒绝的位置。一个 fact 与其近反例是一个条目——一个shape它的每次抽取是硬币的一面。两个独立条目会各自单向作答任何重加权都无法再信任能保持问题种类的平衡。作为单一条目平衡属于条目本身四十个条目每一个都双向作答。不变量包装invariant wrappers也是 shapeBoxS对BoxT基于严格对总是拒绝旁边是同基于等价对的总是编译。形状泄露测试tells.baml与两条仍未达标的特征ns_conformance/tells.baml 用 128 个种子对每个 fact 测量案例表面的任何特征构造、声明、站点、源码中的词编译频率不得偏离整体超过 10 个百分点且整体频率须在 50%±5 以内。测试必须针对银行成立因为选择selection买不到它强制答案均衡反而强制条目失衡。两条特征仍然打破该标准测试如实记录而非声称达标。两者都是关于配对而非任一类型的事实这就是为什么按类型构建的词汇表看不到它们两个类型以相同方式写出时编译概率 90%——等价关系同拼写只能在流向站点编译而没有近反例可以平衡它同拼写的近反例就是同一个类型一个拼写出现在另一个内部时编译概率 75%。因此回答同样的词出现两次所以能编译的学习者十次里对九次。关闭这两条是银行工作尚未完成。银行覆盖什么子类型、一致性、接口与泛型README 的 What the bank covers 一节列出了教学内容的完整范围子类型及其变异性分类法、联合、字面量、never与unknown、函数参数、返回与错误类型、别名与递归、以及每个容器的不变性。一致性作为关于配对的另一个问题两个implements块恰在目标类型相同时冲突因此银行陈述的每个等价关系同时是一个关于重叠实现的案例。接口与泛型作为子类型类适配它实现的接口期望的位置requires另一接口的接口适配被要求者的位置有界类型变量适配其边界的位置实现者的联合适配接口的位置——而这一切反方向都不成立一个不声明implements块的类无论外形多吻合都与接口无关。这些关系都是严格的因此每个都陈述一个能编译和一个不能编译的案例且都带反例。它们与变异性规则组合因此案例可以是函数错误类型内部的实现者联合。值得注意的是银行声明的接口没有成员——在一致性站点与 fact 中都是如此。每个案例都围绕哪些类型实现了接口而非接口要求它们什么因为方法会成为学习者必须略过的文本。这留下的空白成员解析、Self与分派、一致性、合法实现目标属于合法性而非流向类案例尚未构建。深度案例真正考验什么案例的解释列出其推导应用的每条规则但学习者被认定掌握的范围更窄只有答案所依赖的声明。关键概念是传递carried一个把Sub、Super和Unrelated一视同仁映射的传递会屏蔽其下的一切——不变性正是如此——所以BoxS对BoxT只需知道两种类型写法不同即可作答底层规则属于解释的一部分而不属于问题。等价则反过来不变性原样传递等价Boxbool对Boxtrue | false考验的是两者是同一类型包装决定不了任何东西也不归功于任何人。每个声明携带它是否承重bears追踪器只读那些。深度由此来自承载关系的包装协变位置保持关系逆变位置翻转它。carried_2、carried_3、carried_4三个条目让一个严格对穿过这么多层函数位置判定取决于翻转次数的奇偶——学习者任何一处数错就答错。银行的深度是套件钉死的可测量数字它能抽取的案例中分别考验一条、两条、三条、四条规则的各有多少。选择下一个问题Fisher 信息、抽签与铸型castings按期望信息量选题选择按答案预期携带的信息量成比例抽取——能力与每条规则偏移上的 Fisher 信息取自与更新相同的梯度——并提升到inform次幂。一个学习者必对的案例携带零信息必错的同样为零一条在几条半掌握规则上的案例比单规则携带更多。于是已掌握公理的学习者自然被问到更深的问题而新手不会被推进深度因为必败的案例也一文不值。inform为零时改为瞄准成功率aim。exposure惩罚每个已服务过的条目因此一条规则可以按估计器的意愿频繁出现而同一模板不会。这些都在套件中测量完美学习者的题目在一次会话中不会变浅任何条目在一百多题中不会出现超过六次认证一个学习者需 81–133 题——其中 12 次会话中有 2 次在预算内无法完成认证原因见下文。选择看不到硬币铸型castings选择的致命盲点是它看不到一次抽取的硬币落向哪边。两枚硬币决定答案配对流向哪边、一个 shape 的抽取是哪个 fact。若选择直接读取案例就会作弊朴素模型是倾斜的于是它会服务能骗过其中之一的那个方向函数类型被拒绝的会话十次里十一次追踪器想要每条规则的证据而等价规则的证据只来自编译通过的案例于是它服务等价联合被接受十次里七次——两者都来自一个每种问题都双向作答的银行。因此候选携带其铸型castings其抽取可能被服务的每种方式每种都带追踪器会读出的规则、会骗过的模型、以及采样器评分的数字。选择只读这些把抽取当作它所是的彩票来评分其期望信息量与其余各项的均值银行的分数是铸型的均值任何铸型超过硬上限则丢弃该抽取。答案是关于什么的证据仍然是所服务的那个案例。套件要求每一次抽取、在其硬币的每种落法下、得分精确到最后一位数字一致并要求每个铸型精确持有追踪器从其案例读出的规则。为什么默认预算是 140铸型系统有代价。等价的规则只在条目的一半抽取即等价那半上被证实所以全对的学习者需要 81–133 题才能被认证而之前只要 60–68 题——这是问题类型对其答案只字不提的代价既摊在学习者实际被问的问题上也摊在银行上。包装以同样的方式、同样的原因耗费预算。不变性原样传递等价因此把等价放进Box、数组或映射的案例考验的是等价而非包装对包装的任何解读都不会给出不同答案。包装的规则因此只在包装起决定作用处严格对之上被证实——12 次会话中有 2 次现在耗尽预算而非 1 次。那些案例过去给出的证据从来不是挣来的。趣味性Interestingness与朴素模型一个案例有趣当一条貌似合理的错误直觉预测了错误的判定。ns_bank/models.baml中的每个朴素模型是一个它不同意的规则的稀疏列表在该模型下重放案例的推导得到模型的判定不匹配即为该模型的陷阱。其中shape模型是为近反例而存在的学习者它把案例读作它貌似相似的等价关系因此每个近反例都陷阱住它——否则近反例陷阱不住任何人、被读作无趣、比旁边的等价被服务得更少倾斜立即回归。采样器按以下标准给候选打分陷阱数、规则数、关系翻转、以及大小/深度/联合宽度上的铰链惩罚hinged penalty加上所有模型一致同意的单规则案例的软惩罚。会话在查看候选之前用公平硬币固定每一步的判定在预算内重抽直到某候选有该判定——因此案例看起来多有趣永远不预测它的答案硬币独立步索引与前一个答案都不预测下一个。只有编译器验证过同意的条目才会被服务套件验证固定会话服务的每个案例。依赖方向与 lint 的结构不变量依赖流 bank → algebra → engine。lint.sh 在以下情况失败反向引用ns_engine引用root.algebra|bank|conformance等见第 64-69 行引擎之外的通配 match 臂第 77-78 行——这样新的类型种类或变体是编译错误而非静默取臂。引擎豁免因为其通配是错误兜底目录消失require_paths第 15-23 行——否则检查会通过扫描了空气而静默失效。引擎不拥有任何编译器能回答的东西关于 BAML 的唯一判断来自reflect.Package.compile。测验界面Quiz surface无状态、纯函数测验通过main.baml的顶层函数被询问和回答——这正是生成 SDK 导出的内容。没有循环调用之间没有状态。页面为每个问题保存其案例来源与学习者所言其余一切按需从这两者重新计算。main.baml开头的注释道破了无状态设计的哲学一次会话由其种子和长度标识一个问题由其中索引标识。调用之间不保留任何东西同一种子每次计划出同一会话因此重新询问比记忆更便宜学习者永远不会在拿到问题的同时拿到答案。关键函数表完整继承 README函数用途fresh_profile()一个模型对其一无所知的学习者覆盖银行能得出结论的每条规则next_prompt(profile, knobs, session, step)下一个要问的案例以及当案例考验学习者卡住的规则时先教那条规则put_at(knobs, item, seed)案例如何被放置学习者看到和被告知什么。它携带答案因此供已持键的测试或工具使用answer_case(profile, knobs, item, seed, given)回答后的档案、要展示的交流、编译器自己的话、以及分数replay(knobs, taken)一组答案从零开始的档案如何恢复已保存的会话adaptive_json(session, full, knobs, taken)会话作为学习者带走的 JSON附模型结论engine.default_knobs()、engine.standing(profile, knobs)、engine.points(knobs)可调参数、会话进展与结束原因、答案价值describe_model(id)按朴素模型推理的学习者相信什么用于读出sitting_length、prompt_at、answer_at、compiler_report、sitting_json仅凭种子计划的会话无学习者模型由 conformance 套件驱动review(path)读回下载的会话并重新检查其案例一个提示prompt持有一个程序当问题是编译器接受两者中的哪一个时持有两个——连同它们来自何处——既不带键也不带推导。学习者能说什么由所见决定两者永不可能不一致。由于案例可从其 item 与 seed 重新生成答案永远不必摆在学习者面前。种子以bigint跨边界流状态用 63 位int经 JavaScript number 到达页面只保 53 位。界面上不能出现什么枚举的陷阱README 与main.baml头注释都警告跨入此界面的任何东西都不得是枚举也不得是持有枚举的类。原因TypeScript 网络桥把枚举成员编码为裸字符串TS 字符串枚举的成员本身就是字符串无法区分引擎把该字符串放入类型为枚举的槽位后match会 panicVM internal error: type error: expected variant, got string而会安静地回答 false——后者危险一个把所有答案都判错的测验却没有任何错误出现。因此学习者的答案是Given { said, reasoning, mark, marked_by }字符串结构见 main.baml 第 67-80 行由main.baml转换为引擎的枚举。学习者模型DINA 与高斯后验学习者模型活在ns_engine/learner.baml。一个问题的正确答案是学习者应用其案例考验的每条规则并全身而退因此概率是各规则持有强度的乘积乘上被每个陷阱抓住的概率。三种结果——对、错、弃答——是合取DINA模型弃答是独立的结果而非打了折扣的错答。文件头注释给出了精确公式s_r ability offset(r) q Π σ(s_r) · (1 − trap_slip)^traps P(right) q(1 − slip) (1 − q)·commits/2 P(wrong) q·slip (1 − q)·commits/2 P(withheld) (1 − q)(1 − commits)每条规则强度 学习者总体能力 先验以零为中心的偏差即池化。一次回答按一次观测的高斯Laplace后验更新均值按对数似然分数移动方差按结果实际携带的 Fisher 信息下降——模型已预期的回答不缩小任何东西对已持有规则的案例若还考验学习者不懂的东西则既不移动也不缩小该规则。归因是推导而非分摊联合证据不计作各规则的独立证据。学习者多常对没有把握的答案作出承诺是从会话估计的而非从门槛假设的从不说不确定的学习者以均等概率猜测若把 ta 的正确回答当作非猜测来读会在证据的一小部分上就认证 ta。这个估计也正是读出readout显示的校准——因为它是自适应选择唯一不混淆的东西。停止规则与其他可调参数其余机制分数由要求学习者承诺的门槛推导对每个朴素模型的怀疑是单步残差读作 z-score停滞时教学有反例的案例按一定比率作为两个程序之间的选择而非单一判定停止规则三选一mastered掌握、budget预算、stalled停滞。每个界限都在confidence / rules处取得因此同时测试 25 条规则仍与声明一致target设置为银行实际能证明的内容——对完美学习者约为四分之三的规则——提高它是写更难条目的理由而非移动数字。每个可调参数都是Knobs的一个字段。ns_conformance/learner.baml中的脚本学习者是被钉住的标准专家在预算内被认证且校准从不承诺的学习者永不被认证像 TypeScript 那样推理的学习者被识破为 TypeScript而按无关比特作答的不会被怀疑任何事只懂公理的学习者不会被认证任何变异性规则。review面向终端的唯一函数review是唯一为终端设计的函数指向下载的转录报告会话进展、模型结论、以及每个案例是否仍如被问当时那样表现。# 从 baml_language/tools/type_quiz在 mise 下 baml run review -- --path ~/Downloads/type-quiz-142593372.json运行与测试mise 任务、CI 与缓存卫生从 baml_language/ 下的 mise 配置mise.toml 第 80-121 行运行mise run type-quiz-test # 整个套件离线 profile mise run type-quiz-lint # 分层、禁用 API、通配臂 mise run fmt-type-quiz # 本包保持的格式化器CI 中crates/baml_tests/tests/type_quiz.rs运行全部三者套件、lint、以及每个源文件都是格式化器会写出的样子的检查该测试还排除了.baml/缓存与target/构建产物避免对生成文件做格式检查。它们是测试而非钩子因为钩子只在装有它的那台机器上把关提交而这些把关合并。格式化器本身同时保留为 prek 钩子——钩子能修复而检查只能报告。type-quiz-test任务mise.toml 第 85-116 行的注释揭示了几条重要的环境卫生规则故意不设sources套件的真实输入包括整个编译器因为它的职责是察觉编译器变更使测验过期只列包自身文件会让最新检查跳过恰恰最重要的那次运行CLI 的 home、cache 与 profile 流都被重定向到target/下BAML_HOME、BAML_CACHE_DIR、BAML_PROFILE_DIR否则直接运行baml-cli test --from tools/type_quiz会在tools/type_quiz/.baml留下数百 MBCLI 标记忽略但不清理默认关闭 profilingBAML_PROFILE0该套件是百万级小 BAML 调用、每个都被追踪的最坏形态实测占一个学习者测试 47% 的 CPU、每次运行写约 1.5 GB单测试超时提高到 900000 ms900 秒因为套件编译数千个生成程序几个测试在负载机器上要跑数分钟。baml.toml定义了测试 profiledefault offline-x ::live::CI 与裸baml-cli test运行liveprofile-i ::live::校准评分器。liveprofile 无条件注册一个测试使无键运行有选择而非以空选择退出 5两个调用模型的测试仅在提供者密钥在环境中时才注册。live校准只在校准是关乎提示与规则而非提供者时在ANTHROPIC_API_KEY或OPENAI_API_KEY二者之一存在时运行——CI 离线运行从不调用模型。生成器是typescript/webpreserve-case命名测验的 UI 是浏览器页面因此包生成为 web SDK消费它的应用通过--output-dir决定输出位置本包不持有任何前端路径。判断器Judge让模型批改推理在问为什么的会话中模型按案例自身解释批改学习者的推理没有模型可问时学习者自己的手作为替补engine.judge_reasoning取被揭示的案例整体算出模型被告知什么——程序文本、编译器对每个程序的判断、评分细则、学习者声称什么、被要求解释什么——返回Grade { understanding, feedback, missed }sound/partial/wrong给学习者的一段短反馈以及其推理未触及的细则行。正确答案配上错误理由是错的。判定本身永不由模型决定键决定了它引擎中的judge只读键。谁批改了案例随它旅行作为Given.marked_by模型名或学习者自己批改时的空。判读器被告知的一切都从答案推出因此不可能属于另一个问题。said_of一次读取一个答案的五个词rubric、answer_sentence、asked_sentence都从它返回的值工作。对两个程序细则 所示程序共同同意的部分 学习者说编译器拒绝的那个为真的事实——后者正是实际决定案例的东西也正是被要求解释的。若只按共同部分批改会为设置setup记功而不问差异。对单程序声明全部共享、自身为空同一表达式覆盖两种情况。做不出的判断以值而非抛错返回judge_reasoning返回Grade | UnjudgedUnjudged.why携带失败自身的渲染——是拒绝的键、不可达的模型还是耗尽的配额是学习者唯一能行动的信息转述它反而丢掉信息。catch 命名客户端调用的整个错误通道ai.errors.Failure和运行时自身的四个因此新通道使包停止编译而非作为空白到达学习者。可请求判断的模型是一张表offered()表面只显示这些并交回一个 idjudge_client只为这些构建客户端——可选择但不可问或可问但不出现在表里的模型不存在增加一个是增加一行。哪个提供者回答由行决定而非 id没有任何东西读模型名来猜测它的出处。键是学习者的不进入我们任何一方。页面按提供者在浏览器中各持一把把正确的那把传进每次调用因此键到不了未签发它的提供者judge_client用该键构建该提供者的客户端对浏览器发出的请求用 Anthropic 文档化的请求头当行指明 Anthropic 时且只碰那一次请求。站点是静态的——没有服务器——因此键无处可送只有提供者。本包在测验路径上不读任何环境变量。让语言模型替学习者坐场ns_harness/take_quiz.bamlns_harness/take_quiz.baml让语言模型以掌握模式坐测验与页面完全一致相同的自适应选择、相同的三种回答、相同的逐步揭示。它通过adaptive_step在进程内驱动因此一步只花模型作答的时间baml run take_quiz --from tools/type_quiz -- \ --sessions 3 --models opus,sonnet,haiku --out target/type-quiz-llm从baml_language运行那里有 TYPE_SYSTEM.md 与 target/。每个模型坐每一场--concurrency限制并发场数--max N在 N 个回答后停止一场廉价端到端检查。直到回答分道扬镳它们被问相同的问题。运行边写边续因为一场会话的全部状态就是会话号与迄今给出的回答——也正是adaptive_step接受的输入。模型不能作弊——不是因为被要求别作弊。它是claude -p会话所有工具被拒、无 MCP 服务器没有编译器、没有文件、没有搜索。它拿到 TYPE_SYSTEM.md 和评分规则然后推理。测验对一步的答案分两部分返回asked学习者之前能看到的一切与marked最后答案值多少——因此 harness 不可能传递从未收到的键。两个程序间的差异在marked而非asked因为页面在揭示中才说它先说的驱动是在问更简单的问题。每次运行写model-session.json回答、模型完整回复、每个回答的价值、以及学习者会下载的转录——baml run review像读任何其他转录一样读回它。它花什么ANTHROPIC_API_KEY优先于 claude.ai 登录因此把环境传下去的 harness 会在无人选择时花 API 额度。实测而非假设无效键直接失败且 CLI 说明键赢过登录键未设置则登录成功。因此--billing plan是默认并通过env -u为模型取消键--billing api需要显式请求。运行报告的成本是 token 按 API 价率的估算——plan 上不收费只是已耗配额的代理。在会话开头上下文最小时测量haiku 约 $0.02/题、sonnet $0.10、opus $0.16随会话进行对话增长大约翻倍。编译器问题清单构建测验就是 dogfoodingREADME 的最后一节记录了构建测验暴露的编译器缺陷编号被代码注释引用因此一个条目一旦有编号就保留编号。以下是完整清单其中 1 个已修复其余仍可复现复现均为最小单文件包FIXED on canary2026 年 9 月 canary 合并后不再复现#4891的闭包捕获与内存效应重构是可能原因对闭包内捕获的存在类型调用接口方法时发射 panic。crates/baml_compiler2_emit/src/emit.rs:2025对items().filter_map((item) - { [0].every((i) - { item.id() x }); null })panicundefined function: user.Item.id——内部闭包调用从外部闭包捕获的item的方法。对闭包自身参数的方法调用在任何深度都正常把捕获值交给做该调用的函数也正常——这正是ns_engine/sample.baml中root.engine.generate的用途。函数值的运行期成员性是精确匹配而非子类型检查且Package.tests()谎报其值类型映射声明为mapstring, () - null throws unknown但值反射为() - void throws never因此if let f: () - null throws unknown t.get(k)与匹配的is对存在键都判 false而get_functionF的值精确匹配F。要么void/never应在函数子类型下满足null/unknown要么tests()应声明其真实返回。ns_engine/verify.baml有工作区passes另见 SPEC_GAPS.md G-002。块边界的解析歧义else { none }把块解析为映射字面量expected :then 块是裸模板字面量的if let … { … } else …失败expected expression, found else。先绑定到局部变量可同时避开两者。baml fmt把空类字面量渲染为Foo { }两个空格、空类声明为class Foo {\n}。纯外观但它是格式化器的规范输出本包保留而非对抗钩子。baml fmt无条件把?? return断行let s span ?? return null;变成两行且??悬空带括号的守卫被拆成三行。见ns_engine/verify.baml的within。与 4 同策略保留格式化器输出。带throws子句的函数类型无法在调用点尖括号内解析reflect.Type.of(int) - string throws never()与pkg.get_function() - reflect.Type throws never(...)失败expected lambda body {, found 而get_function() - bool(…)与同一类型经type别名均可用。引擎的 oracle 与 verifier 走别名。反射无法分解若干种类unknown、never、递归别名、reflect.Type都归为primitiveprimitive 与 literal 视图只暴露to_string类视图暴露字段但不暴露类型实参接口视图不暴露其 pins。因此忠实的from_reflect今天不可能实现——渲染往返比较的是编译器自身的值parse-then-print 不动点以及与构造器构建类型的一致性而非反射分解。函数类型的to_string有损(x: int, b?: int) - int throws never打印为(int, int) - int throws never——名字与可选标记被丢弃打印出的拼写表示的函数类型与值描述的不同。方法调用的接收者读取先前闭包调用赋值的捕获局部变量会使编译器崩溃items.reduce((acc, s) - { … }, items[0].score)后接items.map((s) - { (s.score - top).exp() })baml checkpanicinternal error: entered unreachable code: Error is not a valid RuntimeTycrates/baml_type/src/runtime_ty.rs:278无诊断、无 span。2026 年 9 月 canary 合并后更糟而非更好之前同样代码编译但在运行期失败。工作区仍然承重去掉方法调用、pickns_engine/sample.baml与 conformance 的hundredths使用自由函数拼写baml.Float.exp(...)。从带.map臂的match推断的局部变量类型错误对其方法调用在运行期失败let lines match (e) { … w.values.map((v) - { v }), … [s] };后接lines.join(,)失败expected map, got array——方法按映射分派。注解局部变量、经自由函数消费、或直接返回match可避免。本包选择注解。字符串字面量没有数字或 unicode 转义\u{1F411}是九个字符、\x41是四个只有\\、\、\n、\r、\t是转义。渲染器的quotens_algebra/render.baml因此根本无法拼写控制字符名称池持有它们意指的字符。联合中的空类匹配任何 JSON 对象其兄弟被解码为它type M Empty | Named时from_stringM把Named { model: m }静默读回Empty——解码取第一个匹配成员空类匹配一切把它列在最后碰巧可用使正确性依赖类型别名的顺序。转录的 who marked this 因此携带可选模型 id 而非联合。生成 SDK 的字节码戳有 git commitweb 应用每次提交都坏baml_sdk把工具链记录为 commit 哈希桥拒绝不匹配——generated bytecode: toolchain a; this runtime: b。提交本包就足以搁浅一小时前构建的桥wasm 构建要数分钟。按字节码格式与编译器自身源码的指纹才会拒绝真正不一致的对。枚举无法从生成的 TypeScript SDK 作为参数传入生成器发射字符串枚举其成员在运行期就是字符串桥的setInboundValue把它降为stringValue调用在 VM 内死亡expected variant, got string。解码正常返回的枚举作为成员回来因此破坏是静默的直到有东西反向调用——而且这是运行期 panic 而非类型错误。本包暴露给页面的每个函数因此只取类与字符串。注解为嵌套联合的let被向后检查错误类型的值通过已在 COMPILER_DIVERGENCE.md 一节详述let left: bool | (string | int) right对bool | string | bigint类型的right编译通过through(5n)返回在bool | string | int槽位反射为bigint的值。平面拼写被 E0001 拒绝。触发条件精确顶层联合成员中有括号联合仅限绑定。由近反例union_regrouped_differs发现记录为 CD-003带一个修复时使套件失败的条目此前没有会拼写此类注解的配对被放到绑定处。结语测验作为语言进化的传感器type_quiz 的价值远超一个教学工具它的架构——规范引用的规则表、双向形状、铸型评分、conformance 绊线——使类型系统的教学与编译器实现互相钉死。SPEC_GAPS 与 COMPILER_DIVERGENCE 两份账本让规范缺口和编译器偏差成为可追踪、可关闭的条目type_quiz.rs在 CI 中运行整套套件编译器任何改变判定、诊断码或反射种类的地方都会先在这里失败。对于希望深入 BAML 类型系统实现的人TYPE_SYSTEM.md 是规范源头rules.baml 是规则表tells.baml 是形状防泄露的测量learner.baml 是学习模型——而 README.md 是这一切的地图。赞分享编程语言AI Agent编译器CLI人工智能【免费下载链接】bamlThe programming language for agents项目地址https://gitcode.com/gh_mirrors/ba/baml点击查看免费下载相关推荐BAML 类型系统实战用强类型为 LLM 应用构建可靠的输出契约BAML 类型系统实战用强类型为 LLM 应用构建可靠的输出契约 类型Types是 BAML 这门专为 Agent 而生的编程语言的核心支柱。在 BAML编程语言AI Agent编译器CLI人工智能深入理解LLVM类型系统编译器类型检查的完整指南深入理解LLVM类型系统编译器类型检查的完整指南 LLVM类型系统是现代编译器技术的核心支柱它为程序代码提供了严格的类型安全保证和高效的优化基础。作为LLV编译器编程语言语言运行时标准库高性能计算如何将 Puput 集成到现有 Django 项目独立安装 10 步完全指南如何将 Puput 集成到现有 Django 项目独立安装 10 步完全指南 想给现有的 Django 项目快速加上一个功能完整的博客Puput 正是为此而创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表