尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

BAML 属性(Attributes)完全指南:用 description / alias / skip / assert / check 精确控制 LLM 输出

发布时间:2026/9/26 2:51:07

资讯中心
01
ARTICLE

BAML 属性(Attributes)完全指南:用 description / alias / skip / assert / check 精确控制 LLM 输出

BAML 属性(Attributes)完全指南:用 description / alias / skip / assert / check 精确控制 LLM 输出
编程语言AI Agent编译器CLI人工智能【免费下载链接】bamlThe programming language for agents项目地址https://gitcode.com/gh_mirrors/ba/baml点击查看免费下载BAMLBoundaryML 的 Agent 编程语言允许在类型、字段和枚举值上声明属性attributes从而改变类型在 prompt 中的呈现方式以及生成 SDK 代码中的行为。本篇以 typescript2/app-website/lessons/03-types/pages/04-attributes/index.md 课程页为骨架结合仓库中的参考文档与编译器实现系统讲解五种最常用属性——description、alias、skip、assert、check的语法、作用域、对 prompt 的影响及源码级原理读完你可以为任意 BAML 类型设计出既对 LLM 友好、又带运行时校验的稳健 schema。一、认识 BAML 属性与两种作用域在 BAML 中属性用于给类型提供额外的元数据或行为可作用在字段级field-level或块级block-level具体取决于使用意图字段级属性直接作用于 class 或 enum 内的单个字段/值修改该字段的行为或元数据例如alias、description、skip、assert、check。块级属性作用于整个 class 或 enum影响块内所有字段/值例如dynamic运行时动态增删字段、assert、check、alias作用于枚举整体。一个典型示例同时使用多种属性class MyClass { property1 string alias(name) description(The name of the object) age int? check(positive, {{ this 0 }}) }从编译器 AST 看属性被建模为独立的节点engine/baml-lib/ast/src/ast/attribute.rs定义了Attribute结构体包含属性名name、参数列表arguments以及parenthesized标志——该标志用于保护括号内的属性不被从 Union 变体提升到整个 Union 层级这一点对assert与 Union 的组合至关重要后文会展开。属性可以挂在AttributeContainer枚举所覆盖的任意节点上Class、ClassField、Enum、EnumValue 或 TypeAlias因此/的作用域在语法解析阶段就已经被严格限定。二、description补充 prompt 中的类型上下文description为字段或枚举值提供额外上下文帮助 LLM 理解该字段的真实含义。当类型本身不足以说明全部故事时例如缩写字段名、领域专有名词用它补充说明。对 class 字段的影响class MyClass { property1 string description(The name of the object) }对应的ctx.output_format渲染为{ // The name of the object property1: string }对枚举值的影响enum MyEnum { Value1 description(The first value) Value2 description(The second value) }渲染为MyEnum --- Value1: The first value Value2: The second value对枚举整体的影响块级enum MyEnum { Value1 Value2 description(This enum represents status codes) }渲染为MyEnum: This enum represents status codes --- Value1 Value2要点description只影响 prompt 呈现ctx.output_format不会改变生成 SDK 中的字段名或类型你的代码里字段名始终是 BAML 源码中写下的名字。三、alias为 LLM 换一个更合适的名字alias为字段或枚举变体提供在 prompt 中呈现的另一个名字而生成代码仍使用你在 BAML 源码中定义的原始名称。当对 LLM 更好的名字与对代码库更好的名字不一致时使用它是实现 prompt 工程与保持代码稳定兼得的常用手段。对 class 字段的影响class MyClass { property1 string alias(name) }ctx.output_format从{ property1: string }变为{ name: string }——LLM 看到的是name你的 SDK 类型仍是property1。对枚举值的影响enum MyEnum { Value1 alias(Something) }渲染为MyEnum --- Something对枚举整体的影响块级注意alias作用于枚举本身而非枚举值enum MyEnum { Value1 // Note that alias is applied to the enum itself, not the value alias(My Name) }渲染为My Name --- Value1四、skip从 prompt 与解析结果中排除字段skip将字段或枚举变体从发送给 LLM 的内容以及解析的响应中排除适用于只有你的代码库关心、LLM 不需要知道的字段如内部 ID、缓存标记、审计信息等。enum MyEnum { Value1 Value2 skip } class MyClass { field1 string field2 string? skip }带skip后ctx.output_format变为MyEnum --- Value1 { field1: string, }Value2与field2都不会出现在 prompt 中LLM 也不会被要求生成它们。关键约束被跳过的 class 字段必须可空官方参考文档 skip.mdx 明确给出警告class 字段使用skip时字段类型必须是 nullablestring?以便解析那些不包含该字段的 LLM 响应class MyClass { field1 string field2 string? skip // OKfield2 是可空类型 }class MyClass { field1 string field2 string skip // Error: Field with skip attribute must be optional. }非可空字段被skip会直接触发编译错误因为 LLM 响应中缺失该字段时无法构造出合法的非空值。五、assert硬校验LLM 输出护栏assert强制某个不变式invariant对类型成立。一个值若未通过其类型的断言永远不会到达你的客户端代码——它是典型的 LLM guardrail把数值必须在合理范围这类规则直接写进类型系统。class Foo { bar int assert(between_0_and_10, {{ this 0 and this 10 }}) } function NextInt8(a: int) - int assert(ok_int8, {{ this -128 and this 127 }}) { client GPT4 prompt #Return the number after {{ a }}# }如果函数返回的Foo.bar不在 010 之间BAML 会抛出异常同理NextInt8若返回 128 也会触发异常。断言可以命名也可以匿名class Foo { // 命名断言错误信息里会带上 between_0_and_10 bar int assert(between_0_and_10, {{ this 0 and this 10 }}) // 匿名断言错误信息显示断言表达式本身 baz int assert({{ this 0 and this 10 }}) }作用范围字段、参数、块assert不仅能挂在字段上还能作用于函数参数、以及通过assert作用于整个块此时this指向块自身可引用块内多个字段function MyFunction(x: int assert(between_0_and_10, {{ this 0 and this 10 }})) { client openai/gpt-4o prompt #Hello, world!# }class Foo { bar int baz string assert(baz_length_limit, {{ this.baz|length this.bar }}) }容器内的断言逐元素校验assert应用到数组类型时会对每个元素逐一校验class MyClass { // assert 会应用到数组中的每个元素 my_field (string assert(is_valid_email, {{ this|regex_match() }}))[] }与 Union 组合必须明确断言挂载点参考文档 checks-and-asserts.mdx 特别提醒使用 Union 时运行期才能确定值属于哪个分支因此必须明确assert挂载在 Union 的哪个成员上this也指向该成员的值class Foo { bar (int assert(positive, {{ this 0 }}) | bool assert(is_true, {{ this }})) }这里assert分别作用于int与bool两个 Union 变体而不是作用于整个Foo.bar字段。这正是 attribute.rs 中parenthesized标志存在的意义括号内的属性不会在 lowering 时被错误提升到 Union 整体层级。断言链从左到右短路求值同一字段可叠加多个assert按从左到右的顺序求值第一个断言失败后后续断言不再执行class Foo { bar int assert(between_0_and_10, {{ this 0 and this 10 }}) baz int assert(positive, {{ this 0 }}) assert(less_than_10, {{ this 10 }}) }bar与baz的校验语义等价只是baz拆成了两个可独立命名的断言。Jinja 表达式与 this断言本质是 Jinja 表达式常用内建过滤器实现长度、正则、比较等约束this当前被校验的值this.fieldthis上下文中的某个字段可用.链式访问嵌套字段常用过滤器length、regex_match、map(attribute...)、unique等。class Person { resume Resume assert({{ this.experience|length 0 }}, Nonzero experience) }六、check软校验运行时可见的结果check与assert类似也是校验某个属性是否成立但校验结果会被计算并随返回值一起呈现给客户端而不是产生硬失败。它适合那些你希望在运行时响应失败的场景例如在 UI 中把错误信息渲染到有问题的字段旁边。class Foo { bar int check(less_than_zero, {{ this 0 }}) }class Bar { baz int quux string check(quux_limit, {{ this.quux|length this.baz }}) }check 在 SDK 中的类型体现带check的字段在生成代码中会包装为值 检查结果的结构各语言 SDK 签名如下来源 checks-and-asserts.mdxtype Bar ( bar int check(less_than_zero, {{ this 0 }}) )[]PythonBar List[Checked[int, Dict[Literal[less_than_zero]]]]TypeScripttype Bar Checkedint,less_than_zero[]Gotype Bar []baml.Checked[int, map[string]baml.CheckResult]map 中键为less_than_zeroRustVecCheckedi64, HashMapString, CheckResult客户端可通过value取原始值、通过checksmap/字典按名称查询每个 check 的status或用get_checks()遍历所有 check 结果。assert 与 check 同场一个会失败一个只会标记下面例子中quote挂了checkline_number挂了assert。line_number校验失败时GetCitation()不会返回任何Citation而exact_citation_match失败不会中断返回客户端代码仍能拿到结果并检查checksclass Citation { quote string check( exact_citation_match, {{ this|length 0 }} ) line_number string assert( has_line_number, {{ this|length 0 }} ) } function GetCitation(full_text: string) - Citation { client GPT4 prompt # Generate a citation of the text below in MLA format: {{full_text}} {{ctx.output_format}} # }Python 端检查结果from baml_client import b from baml_client.types import Citation, get_checks citation b.GetCitation(SpaceX, ...) # 原始值 quote citation.quote.value # 访问单个 check quote_match_check citation.quote.checks[exact_citation_match].status # 遍历所有 check for check in get_checks(citation.quote.checks): print(fCheck {check.name}: {check.status})TypeScript、Go、Rust 的对应写法均可直接在仓库的 checks-and-asserts.mdx 中找到完整示例。与 assert 的关键差异维度assertcheck失败行为值不返回顶层类型触发BamlValidationError异常容器内则被移除数据照常返回失败仅记录在 checks 结果中客户端可见性不可见除非捕获异常校验结果随返回值暴露给客户端求值语义断言链从左到右短路失败即停所有 check 都会求值即使某个失败典型用途LLM guardrail、硬性数据契约运行时响应式处理、UI 错误渲染参考文档 check.mdx 给出的两大收益正是非侵入式校验不打断数据处理流程与运行时检查结果可见。七、组合进阶链式校验与块级交叉字段校验check与assert可以任意混挂在同一个字段上class Foo { bar string check(bar_nonempty, {{ this|length 0 }}) assert(bar_no_foo, {{ this|regex_match(foo) }}) check(bar_no_fizzle, {{ this|regex_match(fizzle) }}) assert(bar_no_baz, {{ this|regex_match(baz) }}) }注意语义差异check会全部求值即使其中一个失败assert一旦失败立即中断解析并抛异常。块级assert还能表达跨字段依赖例如未成年人不允许在美国class Person { name string assert(valid_name, {{ this|length 2 }}) age int assert(valid_age, {{ this 0 }}) address Address assert(not_usa_minor, {{ this.age 18 or this.address.country ! USA, }}) }再如库中所有 ISBN 唯一的校验利用map、unique过滤器class Book { title string assert(this|length 0) author string assert(this|length 0) isbn string assert( {{ this|regex_match(^(97(8|9))?\d{9}(\d|X)$) }}, Invalid ISBN format ) publication_year int assert(valid_pub_year, {{ 1000 this 2100 }}) genres string[] assert(valid_length, {{ 1 this|length 10 }}) } class Library { name string books Book[] assert(nonempty_books, {{ this|length 0 }}) assert(unique_isbn, {{ this|map(attributeisbn)|unique()|length this|length }} ) }八、编译器如何落地属性源码级印证属性的解析与 lowering 在仓库中有明确的实现与测试证据AST 建模engine/baml-lib/ast/src/ast/attribute.rs 定义了Attribute结构体name、arguments、parenthesized、span并声明AttributeContainer枚举表明属性可挂载在 Class、ClassField、Enum、EnumValue、TypeAlias 五种节点上Lowering 验证baml_language/crates/baml_compiler2_ast/src/lib.rs 中的class_field_attributes_lower_onto_the_field测试直接验证了alias(bar)、stream.done、custom(read-back)、description(...)等属性按声明顺序精确落到对应字段上interface_field_attributes_lower_onto_the_field测试则验证了 interface 字段的alias(label)同样被正确挂载。这从编译器层面印证了无论写在字段同一行还是下一行属性都会以声明顺序关联到目标节点。也就是说你在 BAML 里书写的每一个/属性都会经过语法解析 → AST 挂载 → 类型 lowering的完整链路最终分别作用于 prompt 渲染ctx.output_format与运行时校验assert/check两条路径。结语五种属性的分工非常清晰description负责给 LLM 补上下文alias负责在代码名与LLM 名之间解耦skip负责把内部细节挡在 prompt 之外assert负责立下不可逾越的硬性护栏check负责把校验结果交给运行时处理。结合本仓库的参考文档attributes-overview.mdx、assert.mdx、check.mdx、alias.mdx、skip.mdx、description.mdx与编译器实现你可以在自己的 BAML schema 中放心组合使用这些属性构建既对 LLM 友好、又对代码库稳健的类型系统。赞分享编程语言AI Agent编译器CLI人工智能【免费下载链接】bamlThe programming language for agents项目地址https://gitcode.com/gh_mirrors/ba/baml点击查看免费下载相关推荐Yank Note 元素属性Element Attributes语法完全指南用 {.class} 与 {style...} 精确控制 Markdown 渲染Yank Note 元素属性Element Attributes语法完全指南用 {.class} 与 {style...} 精确控制 Markdow桌面应用代码编辑器Lightdash User Attributes 用户属性完全指南SQL 变量、行级安全与属性级访问控制Lightdash User Attributes 用户属性完全指南SQL 变量、行级安全与属性级访问控制 用户属性User Attributes是 Li后端前端数据分析数据可视化人工智能AI AgentHugo sites matrix 完全指南用 language、role、version 三维矩阵精确控制站点输出Hugo sites matrix 完全指南用 language、role、version 三维矩阵精确控制站点输出 本文围绕 Hugo 多维内容模型中的核心开发工具前端CLI上一篇RIOT unicoap 传输驱动全解析CoAP over UDP / DTLS / Slipmux 的模块选型与源码实现下一篇如何用5分钟解决八大网盘下载难题LinkSwift直链下载神器全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。