跳到主要内容

语言参考

Agent Flow 语言的完整参考。权威实现: packages/lang/src/language/agent-flow.langium(文法)与 packages/compiler/src/checker.ts(静态语义)。

词法

注释与空白

  • 行注释 // ...;块注释 /* ... */(可跨行,不可嵌套)
  • 空白与注释是隐藏 token;语言不区分换行

标识符

ID = /[_a-zA-Z][\w$]*/

字符串

形式说明
"..."单行;转义 \" \\ \n \t \r,其余 \x 视为 x
"""..."""多行;内容逐字保留;用于 task,仅裁掉首尾换行

数值与时长

Token正则说明
NUMBER/\d+(\.\d+)?/十进制;无负数字面量
DURATION/\d+(ms|s|m|h)(?![a-zA-Z0-9_])/500ms 5s 10m 2h;统一换算毫秒

分隔符惯例

结构逗号
数组、tools、stage 依赖、调用实参、retry 错误码必需;适用处允许尾逗号
对象、type、limits、parallel 分支必需;允许尾逗号
workflow/stage/agent/verify 语句不使用逗号

类型

TypeRef := PrimaryTypeRef Suffix*
PrimaryTypeRef := 'member' '<' Id '>' | Id
Suffix := '[' RangeBounds? ']'
RangeBounds := NUMBER '..' NUMBER

重复后缀可表达嵌套数组(path[][]);可选字段写作 field?: Type。 读取可选字段得到内部 T?,用 value ?? fallback 消除缺失可能;null 是显式空值。

类型种类、解析顺序、可赋值性与 JSON Schema 映射见 类型系统一页的完整表格。

顶层声明

Workflow := 'workflow' ID '(' 'input' ':' TypeRef (',' 'context' ':' TypeRef)? ')' '->' TypeRef '{' Member* '}'
UseStatement := 'use' ('agent' | 'team') STRING 'as' ID
LimitsBlock := 'limits' '{' LimitField+ '}'
LimitField := ('concurrency' | 'agent_runs' | 'duration') ':' (NUMBER | DURATION)
TypeDeclaration := 'type' ID '{' (FieldName '?'? ':' TypeRef (',' ...)* ','?)? '}'

管道与阶段

Pipeline := 'pipeline' ID? '{' PipelineStatement* '}'
Stage := 'stage' ID ('after' ID (',' ID)*)? '->' TypeRef '{' StageStatement* '}'
  • pipeline 的顶层最后一个语句必须是 return;if 内允许类型安全的提前返回
  • stage 名即结果变量;依赖必须已声明、源序在前; 体内只能读取已声明依赖的结果
  • V1 按源序执行;依赖进入 IR 供 DAG 调度

语句

语句文法约束
letlet ID = 表达式同作用域不可重名
requirerequire 表达式 else fail STRING条件必须 bool
emitemit progress 对象字面量仅 progress 事件
returnreturn 表达式类型须匹配声明
ifif 表达式 { 语句+ } (else { 语句+ })?条件必须 bool;块作用域

表达式

优先级(由紧到松)

后缀 .member [*]投影 f(args)
比较 == != < <= > >= / in a..b(不可链式)
not not (作用于整个比较级操作数)
and and (左结合)
or or (左结合)
空值合并 ?? (右结合,最松)

not a == b 解析为 not (a == b);圆括号可显式分组。 optional ?? fallback 返回非可选的兼容公共类型。

字面量与基本形式

  • 字符串/多行字符串/数值/布尔/时长
  • 数组字面量 [e1, e2](逗号必需;推断兼容的公共元素类型)
  • 对象字面量 { f1, f2: e2 }:name: expr 显式字段;name 简写字段 等价 name: name,name 必须是可见变量
  • 成员访问 expr.member:
    • 对象类型 → 字段类型(不存在报 invalid-member-access)
    • 数组 → 仅 .count(number)、.empty / .any(bool)
    • Verification.passed / .failed / .checks
    • 外部类型 / context(any)→ 任意成员,结果 any
    • 团队绑定 → .main(agent-target)
    • 危险成员名 constructor prototype __proto__ __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf 一律拒绝(dangerous-member-access)
  • 投影 xs[*].field:xs 必须数组;等价 xs.map(x => x.field); 结果为字段类型数组;裸投影 xs[*] 不合法
  • 调用 f(args):仅内建函数team.member(...)(后者只能在 agent() 的 target 位置)

内建函数

函数参数结果语义
unique(array)数组bool元素(按字符串化)无重复
disjoint(arrayOfArrays)数组的数组bool子数组两两无公共元素
union(arrayOfArrays)数组的数组数组顺序拼接所有子数组

range 与 in

  • a .. b:两侧须数值/时长;语法上只能作为 in 的右操作数
  • x in a..b:闭区间,x >= a && x <= b
  • 比较、in 和范围不可链式书写

agent() 调用

AgentExpression := 'agent' '(' Expression ')' '{' AgentOption* '}'
AgentOption :=
'task' TaskString # 必需,唯一
| 'input' ObjectLiteral
| 'tools' 'none' | '[' (','? Id)* ']'
| 'write' Expression # 类型须为 path[]
| 'expect' TypeRef # 必需,唯一;不可 Verification/外部类型
| 'timeout' DURATION # 正时长
| 'retry' '{' 'attempts' ':' NUMBER ('on' ':' '[' Code (',' Code)* ']')? '}'
Code := 'timeout' | 'rate_limit' | 'transient'

target 三形态:别名 / team.main / team.member(expr)attempts额外次数;on 缺省 ['transient'];判定按错误码。

parallel / parallel map / verify

ParallelExpression := 'parallel' '{' (','? ID '=' Expression)+ '}'
ParallelMapExpression := 'parallel' 'map' Expression 'as' ID ('limit' NUMBER)? '{' Expression '}'
VerifyExpression := 'verify' '{' (','? VerifyCheck)* 'pass' 'when' Expression '}'
VerifyCheck := 'check' ID '=' Expression | 'parallel' '{' (','? 'check' ID '=' Expression)+ '}'
  • parallel:分支并发,barrier 语义;结果 {分支名: 类型}
  • parallel map:源须数组;limit 正整数;结果顺序与输入一致;类型 T[] → U[]
  • verify:check 名唯一;check 初始化在外层作用域,pass when 在 verify 作用域;结果 Verification<{checks...}>

作用域

workflow :input、context(声明类型时强类型,否则 any)、use 别名
pipeline :+ 已完成 stage 结果(源序渐进)
stage :+ 显式声明的依赖
block :if then/else
map :循环变量
verify :check 名(仅 pass when 可见)

保留字与字段软关键字

以下词在变量、stage、type、资源别名、parallel/check/map 名称等位置是保留字:

agent agent_runs and as attempts
check concurrency else emit expect
fail false if in let
limit limits map none not
on or parallel pass pipeline
progress rate_limit require return retry
stage task team timeout tools
transient true type use verify
when write workflow

在 type 字段、对象字段和 .field 成员访问位置,它们是软关键字, type Event { type: text, retry?: bool } 合法。inputcontextdurationmember 也可作普通值标识符。该区分让外部 JSON 字段名 保持自由,同时保护会进入代码生成命名空间的声明名。

JS 风格的 whileforfunctionclass 不是保留字——但 V1 语法不存在这些语句,写出来同样报语法错误。

高频撞名场景

场景
map 循环变量as task / as check / as mapitem / job / slot
check 名check task = ...tests / security / policy
parallel 分支名tools = ...frontend = ...(普通名词)
对象显式字段{ type: value }合法
let 变量/对象简写let expect = ... / { expect }避开关键字

附录:EBNF 文法

grammar AgentFlow

hidden terminal WS: /\s+/;
hidden terminal SL_COMMENT: /\/\/[^\n\r]*/;
hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//;
terminal STRING: /"(\\.|[^"\\\n\r])*"/;
terminal ML_STRING: /"""[\s\S]*?"""/;
terminal DURATION: /\d+(ms|s|m|h)(?![a-zA-Z0-9_])/;
terminal NUMBER: /\d+(\.\d+)?/;
terminal ID: /[_a-zA-Z][\w$]*/;

Id returns string: ID | 'input' | 'context' | 'duration' | 'member';
FieldName returns string:
Id | 'workflow' | 'use' | 'type' | 'pipeline' | 'stage' | 'agent' | 'parallel'
| 'verify' | 'require' | 'emit' | 'return' | 'if' | 'else' | 'let' | 'not'
| 'and' | 'or' | 'in' | 'true' | 'false' | 'task' | 'tools' | 'write'
| 'expect' | 'timeout' | 'retry' | 'pass' | 'when' | 'check' | 'map' | 'as'
| 'after' | 'limit' | 'limits' | 'team' | 'none' | 'on' | 'attempts'
| 'progress' | 'fail' | 'concurrency' | 'agent_runs' | 'rate_limit'
| 'transient' | 'null';
TaskString returns string: STRING | ML_STRING;
BoolKeyword returns string: 'true' | 'false';
LimitValue returns string: NUMBER | DURATION;
RetryErrorCode returns string: 'timeout' | 'rate_limit' | 'transient';

entry FlowFile: elements+=Workflow+;

Workflow:
'workflow' name=ID '(' param=WorkflowParam (',' contextParam=WorkflowContextParam)? ')' '->' outputType=TypeRef
'{' members+=WorkflowMember* '}' ;

WorkflowParam: name=Id ':' type=TypeRef ;
WorkflowContextParam: 'context' ':' type=TypeRef ;

TypeRef:
({infer MemberTypeRef} 'member' '<' team=Id '>' | {infer NamedTypeRef} name=Id)
suffixes+=TypeSuffix* ;
TypeSuffix: '[' bounds=RangeBounds? ']' ;
RangeBounds: min=NUMBER '..' max=NUMBER ;

TypeDeclaration: 'type' name=ID '{' (fields+=TypeField (',' fields+=TypeField)* ','?)? '}' ;
TypeField: name=FieldName (optional?='?')? ':' type=TypeRef ;

UseStatement: 'use' kind=('agent' | 'team') resourceId=STRING 'as' name=ID ;
LimitsBlock: 'limits' '{' fields+=LimitField (',' fields+=LimitField)* ','? '}' ;
LimitField: name=('concurrency' | 'agent_runs' | 'duration') ':' value=LimitValue ;

Pipeline: 'pipeline' name=ID? '{' body+=PipelineStatement* '}' ;

Stage:
'stage' name=ID
('after' dependencies+=ID (',' dependencies+=ID)*)?
'->' returnType=TypeRef
'{' body+=StageStatement* '}' ;

LetStatement: 'let' name=Id '=' value=Expression ;
RequireStatement: 'require' condition=Expression 'else' 'fail' message=STRING ;
ReturnStatement: 'return' value=Expression ;
EmitStatement: 'emit' 'progress' payload=ObjectLiteral ;
IfStatement: 'if' condition=Expression '{' thenBody+=StageStatement+ '}' ('else' '{' elseBody+=StageStatement+ '}')? ;

Expression: CoalesceExpression ;

CoalesceExpression infers Expression:
OrExpression ({infer BinaryExpression.left=current} operator='??' right=CoalesceExpression)? ;

OrExpression infers Expression:
AndExpression ({infer BinaryExpression.left=current} operator='or' right=AndExpression)* ;
AndExpression infers Expression:
NotExpression ({infer BinaryExpression.left=current} operator='and' right=NotExpression)* ;
NotExpression infers Expression:
{infer UnaryExpression} 'not' operand=NotExpression
| ComparisonExpression ;
ComparisonExpression infers Expression:
PostfixExpression (
{infer BinaryExpression.left=current} operator=('==' | '!=' | '<' | '<=' | '>' | '>=') right=PostfixExpression
| {infer BinaryExpression.left=current} operator='in' right=RangeExpression
)? ;
RangeExpression infers Expression:
{infer RangeExpression} start=PostfixExpression '..' end=PostfixExpression ;
PostfixExpression infers Expression:
PrimaryExpression (
{infer MemberAccess.target=current} '.' member=FieldName
| {infer Projection.target=current} '[*]'
| {infer CallExpression.target=current} '(' (args+=Expression (',' args+=Expression)*)? ')'
)* ;
PrimaryExpression infers Expression:
AgentExpression
| ParallelExpression
| ParallelMapExpression
| VerifyExpression
| {infer VariableRef} name=Id
| {infer StringLiteral} value=STRING
| {infer MLStringLiteral} value=ML_STRING
| {infer NumberLiteral} value=NUMBER
| {infer BoolLiteral} value=BoolKeyword
| {infer NullLiteral} 'null'
| {infer DurationLiteral} value=DURATION
| ArrayLiteral
| ObjectLiteral
| '(' Expression ')'
;
ArrayLiteral infers Expression: {infer ArrayLiteral} '[' (elements+=Expression (',' elements+=Expression)*)? ']' ;
ObjectLiteral infers Expression: {infer ObjectLiteral} '{' (fields+=ObjectField (',' fields+=ObjectField)* ','?)? '}' ;
ObjectField: name=FieldName (':' value=Expression)? ;

AgentExpression infers Expression: {infer AgentExpression} 'agent' '(' target=Expression ')' '{' options+=AgentOption* '}' ;
AgentTask: 'task' value=TaskString ;
AgentInput: 'input' payload=ObjectLiteral ;
AgentTools: 'tools' (none?='none' | '[' names+=ID (',' names+=ID)* ','? ']') ;
AgentWrite: 'write' paths=Expression ;
AgentExpect: 'expect' type=TypeRef ;
AgentTimeout: 'timeout' value=DURATION ;
AgentRetry: 'retry' '{' 'attempts' ':' attempts=NUMBER (',' 'on' ':' '[' codes+=RetryErrorCode (',' codes+=RetryErrorCode)* ','? ']')? ','? '}' ;

ParallelExpression infers Expression: {infer ParallelExpression} 'parallel' '{' branches+=ParallelBranch (',' branches+=ParallelBranch)* ','? '}' ;
ParallelBranch: name=Id '=' value=Expression ;
ParallelMapExpression infers Expression: {infer ParallelMapExpression}
'parallel' 'map' source=Expression 'as' item=Id ('limit' limit=NUMBER)? '{' body=Expression '}' ;
VerifyExpression infers Expression: {infer VerifyExpression} 'verify' '{' checks+=VerifyCheck+ 'pass' 'when' condition=Expression '}' ;
CheckItem: 'check' name=Id '=' value=Expression ;
ParallelCheckGroup: 'parallel' '{' checks+=CheckItem+ '}' ;

WorkflowMember: UseStatement | LimitsBlock | TypeDeclaration | Pipeline ;
PipelineStatement: Stage | LetStatement | RequireStatement | EmitStatement | ReturnStatement | IfStatement ;
StageStatement: LetStatement | RequireStatement | EmitStatement | ReturnStatement | IfStatement ;
AgentOption: AgentTask | AgentInput | AgentTools | AgentWrite | AgentExpect | AgentTimeout | AgentRetry ;
VerifyCheck: CheckItem | ParallelCheckGroup ;

附录:诊断码目录

编译期

级别含义
syntax / lexerror解析/词法错误
duplicate-workflowerrorworkflow 重名
duplicate-aliaserroruse 别名重复
duplicate-limits / duplicate-pipelineerror多个 limits / pipeline
duplicate-type / duplicate-type-fielderrortype / 字段重名
recursive-typeerror类型递归引用
unknown-teamerrormember<T> 引用非 team 别名
invalid-boundserror数组界非法
external-type / unknown-typeinfo / error边界 external;内部未声明类型拒绝
duplicate-stageerrorstage 重名
unknown-stage / duplicate-dependencyerror依赖不存在或重复
forward-dependencyerror依赖源序更晚的 stage
undeclared-dependencyerrorstage 读取未声明依赖的结果
missing-pipeline / missing-inputerror缺少 pipeline / input 形参
workflow-param-nameerror形参未命名为 input
missing-return / pipeline-return-positionerrorpipeline return 缺失/位置错误
stage-missing-return / stage-return-positionerrorstage return 缺失/位置错误
stage-return-mismatch / workflow-return-mismatcherror返回类型不匹配
duplicate-variableerror同作用域变量重名
unknown-variableerror未定义变量(含简写字段)
heterogeneous-arrayerror数组元素类型不兼容
require-not-bool / if-not-bool / verify-predicate-not-boolerror条件非 bool
dangerous-member-accesserror访问危险成员
invalid-member-accesserror类型上不存在该成员
optional-member-accesserror未先用 ?? 处理缺失值便继续访问其成员
invalid-projectionerror[*] 作用于非数组,或裸投影
unknown-builtinerror调用非内建函数
invalid-builtin-callerror内建函数参数形态不符
invalid-agent-targeterroragent() 目标形态非法
invalid-member-argumenterrorteam.member() 参数非成员 id
agent-missing-task / agent-missing-expecterror缺少必需选项
duplicate-agent-option / duplicate-toolerroragent 选项/工具名重复
expect-invalid-typeerrorexpect 类型不可生成 Schema
write-not-path-arrayerrorwrite 非 path[]
invalid-timeout / invalid-retry / invalid-limit / invalid-limitserror选项值非法
invalid-emiterroremit 缺少载荷对象
duplicate-branch / duplicate-check / duplicate-object-fielderror重名分支/检查/字段
operator-typeerror运算符操作数类型不符
parallel-map-source-not-arrayerrorparallel map 源非数组

运行期

运行时、沙箱与错误