POML 模板引擎(Template Engine)
概览
POML 的模板引擎让你在提示中使用"变量、表达式、循环、条件、文件包含"等动态能力,从而构建可复用、可组合的复杂提示。
表达式(Expressions)
- 基本用法:使用双花括号
{{ }}
在内容位置插入变量或表达式结果。
poml
<poml>
<p>Hello, {{ name }}!</p>
</poml>
- 属性中的表达式:也可用于属性值。
poml
<poml>
<task caption="Task #{{ index }}">This is task No. {{ index }}.</task>
</poml>
- 支持的表达式(JavaScript 语法):
- 变量:
{{variableName}}
- 算术:
{{a + b}}
、{{x * y}}
、{{count / total}}
- 字符串拼接:
{{firstName + " " + lastName}}
- 数组访问:
{{myArray[0]}}
- 对象属性:
{{myObject.property}}
- 函数调用:
{{myFunction(arg1, arg2)}}
(若函数在上下文中可用) - 三元运算:
{{condition ? A : B}}
- 循环变量:
{{loop.index}}
等(见下文)
- 变量:
提示:属性中的表达式会先求值再进行类型自动转换(见"类型自动转换")。
let 变量与数据导入(Let Expressions)
<let>
用于定义变量、导入外部数据,并在模板内复用。
1) 从字面量设置变量
- 内容作为字面量:
poml
<poml>
<let name="greeting">Hello, world!</let>
<p>{{ greeting }}</p>
</poml>
- 使用
value
属性(会按 JS 表达式求值,注意字符串需要引号):
poml
<poml>
<let name="greeting" value="'Hello, world!'" />
<p>{{ greeting }}</p>
</poml>
2) 从文件导入数据(命名变量)
- 将
users.json
的内容导入到users
变量:
poml
<poml>
<let name="users" src="users.json" />
<p>First user: {{ users[0].name }}</p>
</poml>
type
可指定文件类型(如json
、text
、csv
),不提供时按扩展名推断。
3) 从文件导入(不命名)
- 直接把 JSON 顶层的键注入到当前上下文:
poml
<poml>
<let src="config.json" />
<p>API Key: {{ apiKey }}</p>
</poml>
config.json
示例:
json
{ "apiKey": "your_api_key" }
4) 使用内联 JSON 与类型标注
poml
<poml>
<let name="person">{ "name": "Alice", "age": 30 }</let>
<p>Name: {{ person.name }}, Age: {{ person.age }}</p>
<let name="count" type="integer">5</let>
<p>Count: {{ count }}</p>
</poml>
5) 基于表达式设置变量
poml
<poml>
<let name="base" value="10" />
<let name="increment" value="5" />
<let name="total" value="{{ base + increment }}" />
<p>Total: {{ total }}</p> <!-- 输出:15 -->
</poml>
类型自动转换(Type-Autocasting in Attributes)
- 组件属性会根据其定义的类型自动转换:
- Boolean:
"true"
、1
、"1"
、{{true}}
→true
;"false"
、0
、"0"
、{{false}}
→false
- Number:
"123"
、45.6
、{{anyNumber}}
、{{myNumber+1.3}}
→ 相应数值 - Object:尝试解析为 JSON 对象,例如:
data='{"name":"John","age":30}'
data="{{{name: 'John', age: 30}}}"
(三花括号语法,直接提供对象字面量)
- String:不做转换
- Boolean:
示例(概念演示):
poml
<poml>
<let name="boolVar" type="boolean" value="true"/>
<let name="numVar" type="number" value="42"/>
<let name="objVar" type="object" value="{{ { key: 'value' } }}"/>
<MyComponent
boolProp="{{ boolVar }}"
numProp="{{ numVar }}"
objProp="{{ objVar }}"
stringProp="hello"
/>
</poml>
for 循环(For Attribute)
- 语法:
for="itemName in listExpr"
poml
<poml>
<list>
<item for="item in ['apple', 'banana', 'cherry']">{{ item }}</item>
</list>
</poml>
循环变量(Loop Variables)
loop.index
:当前索引(从 0 开始)loop.length
:总长度loop.first
:是否第一项(true/false)loop.last
:是否最后一项(true/false)
poml
<poml>
<let name="all_demos" value='[
{ "input": "What is your name?", "output": "My name is POML." },
{ "input": "What can you do?", "output": "I can generate prompts." }
]'/>
<examples>
<example for="example in all_demos" chat="false"
caption="Example {{ loop.index + 1 }}" captionStyle="header">
<input>{{ example.input }}</input>
<output>{{ example.output }}</output>
</example>
</examples>
</poml>
条件渲染(If Condition)
- 使用
if
属性按条件渲染元素:
poml
<poml>
<let name="isVisible" value="true"/>
<let name="isHidden" value="{{ !isVisible }}"/>
<p if="isVisible">This paragraph is visible.</p>
<p if="isHidden">This paragraph is hidden.</p>
</poml>
if
的值可直接写变量名(按布尔解释)或任意表达式。
文件包含(Include Files)
- 使用
<include src="..." />
将其他 POML 片段注入当前文件;被包含文件可访问当前上下文的变量。 for
与if
可与include
同用:
poml
<poml>
<include src="row.poml" for="i in [1,2,3]" />
<include src="footer.poml" if="showFooter" />
</poml>
路径
src
相对于当前 POML 文件;避免循环包含。
实战组合示例
- 目标:从 JSON 读取人员列表,循环渲染为多行,页脚按条件包含。
目录结构:
people.json
row.poml
main.poml
people.json
:
json
[
{ "name": "Alice", "role": "PM" },
{ "name": "Bob", "role": "Engineer" }
]
row.poml
:
poml
<p><b>{{ person.name }}</b> - {{ person.role }}</p>
main.poml
:
poml
<poml>
<let name="people" src="people.json" />
<let name="showFooter" value="true" />
<section>
<include src="row.poml" for="person in people" />
</section>
<footer if="showFooter">
<p>Total: {{ people.length }}</p>
</footer>
</poml>
实践建议与常见坑
- 字符串字面量在
value
中必须加引号;内容写法则按字面量处理。 - 对象型属性可用 JSON 字符串或三花括号对象字面量:
data='{"name":"John","age":30}'
data="{{{name: 'John', age: 30}}}"
- 文件路径相对当前
.poml
;注意编码与格式(如 JSON 的逗号与引号)。 - 循环与条件可叠加在
include
上,便于模块化拆分。 - 谨慎使用自定义函数调用;优先使用纯表达式与
let
,保证可移植性与可调试性。