用 AI 写 spintax 模板
你只为模板向模型付一次钱。之后引擎在本地免费渲染任意数量的变体。本页讲的就是这两个事实之间的部分:给模型什么、要求什么、怎么检查返回的结果,以及每个模型都会在 spintax 里犯的错误。
最短版本
把文档给模型,把你写完的文章给模型,要求一个模板。就按这个顺序。下面的一切都是这三步的细节。
注意模型在这里做的是什么:它不是在写作,而是在给你已经批准的文本做标记。两件事一起要,你得到的就是一个由“从没有人当作句子读过的句子”拼出来的模板。先把文章写好,写成一版你可以原样发布的干净文本。然后再交出去。
给模型什么
本站以机器可读的形式发布自己的文档,所以你不需要向任何东西解释 spintax。按你的工具选一个入口。
| 入口 | 是什么 | 什么时候用 |
|---|---|---|
/llms-full.txt |
文档核心合成一个文件:语法参考、嵌套指南和全部七篇创作指南。100 KB 出头。(英文——这是有意的:一份索引只有一张地图。) | 默认选它。一次抓取,或往聊天里粘一次。 |
spintax-authoring |
同一套方法压缩成两页,为模型而不是读者而写。 | 你的 agent 支持技能,或上下文预算紧张。 |
/docs/variables.md 等 20 个 |
任何文档页的纯 Markdown 版。URL 后加 .md,或发送 Accept: text/markdown。 |
干活途中的一个具体问题。 |
https://spintax.net/mcp |
MCP 服务器:校验、渲染、分析模板的三个工具。 | 你想让模型在给你看之前先检查自己的活。 |
authoring 技能旁边还有两个。spintax-syntax 讲各个构件和每个构件里的坑;spintax-engines 讲怎样从 JavaScript、PHP 或 Python 安装并调用引擎。写模板要用的是第一个。
提示词
这是我们自己在用的提示词。它假定模型能抓取 URL;如果你的不能,把 llms-full.txt 粘在它上面,并删掉提到抓取的那两行。提示词保留英文原文——它指向的文档表面就是英文的,而且硬规则以规范措辞为准;模型输出的语言跟随你粘贴的文章,所以拿中文文章喂它,回来的就是中文模板。
You are converting a finished article into ONE spintax template.
Read the syntax and authoring rules first:
https://spintax.net/llms-full.txt
If you cannot fetch URLs, say so and I will paste the file instead.
Priorities, in this order:
1. Every rendered variant must be grammatical.
2. Every rendered variant must read like something a person wrote.
3. Variety comes last. A difference the reader would not notice
is not worth a branch.
Hard rules:
- Never put a branch boundary inside a phrase whose grammar binds:
subject and verb, preposition and object, article and noun.
- Do not repeat fixed text in both branches. Fork the smallest span
that differs.
- Use %VAR% for anything that changes per site or per product.
- Use #def, not #set, when two references to the same value must agree.
- Any number followed by a noun goes through {plural %n%: …}. Never
hand-roll the forms as {item|items}.
- Use only the syntax in the rules above. Do not borrow from ICU,
Handlebars or Jinja.
- Do not spin brand names, product names, prices, URLs or legal
wording. Vary the copy around them.
- Do not invent facts. If a slot needs a fact I did not give you,
make it a variable.
Give me back:
1. The template, in one code block.
2. The variables you introduced, with an example value for each.
3. Anything in the article you could not express in spintax, and why.
Here is the article:
[paste your article here]
它有两处是刻意为之的。
它指向规则,而不是复述规则。语法不在提示词里,也不该在。文档只有一次抓取的距离,而且始终是最新的;半年前粘进提示词的副本可不是。
它把优先级排在规则前面。放任不管,模型会优化可见的多样性,因为模板看起来就是为此而生。语法和可读性必须被明确排在它前面,否则每个槽位四个选项,输出成一锅粥。
三种跑法
聊天窗口
任何大上下文窗口的模型都行。粘 llms-full.txt,粘提示词,粘你的文章。整个文档核心约 100 KB,对当前模型很从容,但不是免费的:长会话里,authoring 技能只花它的零头,覆盖同一套方法。
这里没有校验。模型没有办法运行模板,所以把它的输出当草稿,下一步拿去演练场EN。
能读文档的 agent
在 Claude Code、Cursor 或任何会浏览的 agent 里,把它指向技能,让它按需拉取单页:
Load https://spintax.net/.well-known/agent-skills/spintax-authoring/SKILL.md
and follow it. Fetch the guides it links when you need the detail.
技能里的每条链接都已指向 .md 镜像,agent 读我们的文档从来不用解析网页。
MCP 服务器
值得多花五分钟的就是这套。https://spintax.net/mcp 是一个在线 MCP 服务器,无需认证,三个工具:
validate_spintax——诊断,带严重级别、稳定的 code 和从 1 起数的行列。没有 error 就可以放心渲染。render_spintax——最多 20 个变体,seed 保证可复现,变量表当上下文。analyze_spintax——模板需要什么、包含什么:引用的变量、#set与#def定义、include、构件计数。
Claude Code 里一行搞定:
claude mcp add --transport http spintax https://spintax.net/mcp
吃配置文件的客户端:
{
"mcpServers": {
"spintax": { "type": "http", "url": "https://spintax.net/mcp" }
}
}
然后在提示词末尾加上:
You have the spintax.net MCP server. Before you show me anything:
call validate_spintax and fix every diagnostic with severity "error",
then call render_spintax with count 5 and read the variants yourself.
If a variant reads wrong, fix the template and repeat.
差别不是装饰性的。没有工具,你看到的第一版模板是模型的第一次猜测,你的审读时间花在找没闭合的花括号上。有了工具,没闭合的花括号模型自己已经找完了,你的审读时间花在文案好不好上。
限制先说清楚,方便你规划:每次调用模板上限 8 KB,每次渲染最多 20 个变体,服务器上 #include 被禁用。大文档按小节组装、逐节校验。服务器的完整描述在它的 server card 里。
能抓住问题的循环
不管用哪种工具,审读都是同样三步,而被跳过的永远是第三步。
- 校验。结构性错误最便宜,而且引擎替你找:演练场EN边打字边划线,
validate_spintax把它们当数据返回。 - 渲染二十个,不是一个。六个分支的模板有几百种结果。一次渲染几乎什么都说明不了——而让你批准了坏模板的,通常恰恰是那一次渲染。
- 读它们。真的把二十个读完。检查每个条件的两种状态、每种复数形式(包括选中罕见形式的数:俄语里是 1、2、5、11、21),以及最短和最长的排列结果——分隔符和空格的毛病都在那里现形。
读出问题时,改模板而不是改变体,然后再渲染二十个。模型接上 MCP 工具后可以自己驱动这个循环——这正是接它们的全部理由。
模型会做错什么
这些不是随机错误。它们跨模型重复出现,因为根源是模型在别处学到的东西。知道这份清单,就能把含糊的“这模板哪里不对劲”变成两分钟的核对。
注意它们几乎都不抛错。引擎有意宽容:不认识的构件丢掉花括号、以普通文字落进正文。模板校验全绿、渲染却是错的——这正是循环以“读变体”而不是以绿勾结束的原因。
1. 它把分支切进了绑定的语法里
最常见的失败,也是产出“错误”而不仅仅是“乏味”文本的那种。主语和它的动词、介词和它的宾语、冠词和它的名词,不能各自独立变化。
{Our platform|Our tools} {is|are} built for small teams.
→ four combinations, two of them ungrammatical
{Our platform is|Our tools are} built for small teams.
→ two combinations, both correct
详见:语法安全的同义替换。
2. 引用必须一致时,它却伸手拿 #set
#set 的值是宏:每次引用重新抽取。模型默认选它,因为它读起来像编程语言里的赋值。
#set %tool% = {Slack|Jira|Linear}
Connect %tool% in one click. %tool% syncs both ways.
→ "Connect Slack in one click. Linear syncs both ways."
#def %tool% = {Slack|Jira|Linear}
Connect %tool% in one click. %tool% syncs both ways.
→ "Connect Jira in one click. Jira syncs both ways."
详见:变量与多站点复用。
3. 它用别家库的语法写复数
模型见过的 ICU MessageFormat 远多于 spintax,这一点藏不住。它们还默认两种形式——对俄语、乌克兰语、白俄罗斯语和塞尔维亚语是错的。
We support {count, plural, one {# language} other {# languages}}.
→ "We support count, plural, one # language other # languages."
We support %n% {plural %n%: language|languages}.
→ "We support 3 languages." / "We support 1 language."
这个错误的两半表现完全不同,值得知道。借来的 ICU 是安静的错:完全没有诊断,因为不带竖线的花括号组是合法的,引擎丢掉括号、打印里面的内容。形式数量不对是响亮的错:俄语要三种形式 {plural %n%: язык|языка|языков},在俄语 locale 下只给两种会返回 plural.arity 错误,构件原样留在输出里。同一个习惯,只有一半会被替你抓住。
详见:复数一致。
4. 它发明条件语法
Handlebars 和 Jinja 的习惯会产出 {if …} 块,而引擎把它们当字面文本渲染。
We ship worldwide. {if %HasCrypto%}We also accept crypto.{/if}
→ "We ship worldwide. If 1We also accept crypto. /if"
We ship worldwide. {?HasCrypto?We also accept crypto.}
→ "We ship worldwide. We also accept crypto."
第一个渲染就是构件泄漏的样子:花括号没了,变量在没人想要的位置被代入,闭合标记成了一个词。
条件由值驱动。当选择真的是任意的时,{a|b} 才是对的,条件不是。
详见:条件 spintax。
5. 它把方括号当成了选择
[a|b|c] 是把三项全部打乱再拼接,默认空格分隔。它不是选一个。想表达“选一个”却写了方括号的模型,会在你只要一个形容词的地方产出三个。
Try our [fast|simple|affordable] tool.
→ "Try our fast simple affordable tool."
Try our {fast|simple|affordable} tool.
→ "Try our simple tool."
Try our [<minsize=2;maxsize=3;sep=", ";lastsep=" and ">fast|simple|affordable] tool.
→ "Try our simple, affordable and fast tool."
→ "Try our simple and fast tool."
详见:排列实战。
6. 它给每个从句都上变体
没有优先级时,模型把变化密度当目标,往每个短语里塞分支。输出不再像任何东西。
{Our|The} {platform|system} {helps|lets} {teams|groups} {ship|release}
{faster|quicker|sooner}.
有价值的变体是读者会注意到的那些。一段里两个放对位置的分支,胜过一句里的十二个。提示词里的优先级块就是为这个准备的。
7. 它把固定部分重复进两个分支
重复的文本就是漂移的邀请函:下次修改会改了一个分支、忘了另一个。
{Spintax (spin syntax) is|Spintax — "spin syntax" — is} a template language.
→ "Spintax" and "is" duplicated across both branches
Spintax {(spin syntax)|— "spin syntax" —} is a template language.
→ one copy of the fixed words, only the difference forked
详见:逆向创作思维。
8. 它把外部值直接丢进上下文
变量值默认是可携带标记的:包含 {、[ 或 % 的值会被引擎再次解析。数据库里的商品名、用户提交的字符串、带一个流浪花括号的标题——你的模板就渲染出了你从没写过的东西。不可信的值先过 neutralize(),再进上下文。
详见:变量与多站点复用。
9. 它写一个巨无霸模板
要一整个落地页,你就会得到一个无法审读的大块头。超过一屏就拆:条目模板渲染一个单元,小节模板组装条目,编排模板组装小节。每一块都能独立测试,MCP 服务器的 8 KB 上限也不再是上限。
详见:模板组合。
模板发布之前
- 渲染并真正读过二十个变体。
- 每个条件的两种状态都见过。
- 每种复数形式都见过,包括罕见的那一档。
- 两处引用必须一致的地方没有
#set。 - 模板引用的每个变量在渲染时都有值。
- 不可信的值已经过防护。
- 最短和最长的排列结果标点都正确。
这要花多少钱
一个模板就是与模型的一次对话,之后的渲染免费:引擎在本地运行,确定性,每个变体零 API 调用。这就是这套工作流的全部经济学论证——按模型逐一算清、带当前价格的版本在 AI 内容的真实成本里。
然后去渲染
你刚做好的模板在这个家族的每个引擎上原样运行:JavaScript 里的 @spintax/core,PHP 里的 spintax/core,Python 里的 spintax-core,还有 WordPress 插件。同一套语法,同样的输出,一套共享测试语料。挑一个就去看四个引擎。