Astryx 对抗性用户画像(Adversarial Persona):用跨框架混杂术语压测设计系统的 LLM 提示词鲁棒性
【免费下载链接】astryxAn open source design system that's fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx
本文聚焦 Astryx 设计系统 Vibe Tests 中的人物画像体系,以 internal/vibe-tests/prompts/personas/adversarial.md 为核心骨架展开。Adversarial(对抗性)用户画像模拟一类“把 Tailwind、shadcn、Bootstrap、Material UI、Radix 的思维惯性带进来”的开发者,用来检验 LLM 在生成 Astryx UI 代码时,能否抵抗竞争框架术语的污染、避免幻觉组件与错误 API。读完本文,你将掌握该画像的完整特征与话术样本、它在 interactive.ts 中的实际注入机制,以及它与评估器(hallucination / competing patterns)之间的闭环关系,可直接用于设计自己的设计系统提示词压测方案。
为什么需要一个“对抗性”用户画像
Vibe Tests 是 Astryx 仓库中一套结构化评估体系,其目标是回答一个问题:在相同的提示词、不同的设计系统配置下,LLM 生成 UI 代码的质量差异有多大、根因是什么。正如 internal/vibe-tests/README.md 所述,这套体系用「同一批提示词、不同系统、可量化的产出」来对比 LLM 在不同设计系统文档约束下的表现。
而“不同提示词难度”并不只来自任务本身,还来自提问者(Persona)。真实的开发者千差万别,LLM 必须能服务从完全不懂术语的新手,到对系统了如指掌的专家,再到“脑子里全是别的框架”的迁移型开发者。为此,internal/vibe-tests/prompts/personas/ 目录下定义了三种互补的用户画像:
| 画像 | 提问风格 | 代表用户 | 对设计系统的考验 |
|---|---|---|---|
| naive.md | 完全用日常语言描述视觉外观(“一个带阴影的盒子”“一个弹出层”),不用任何组件名与术语 | 不懂代码细节的产品型用户 | 文档能否被 LLM 翻译成正确的组件选择 |
| experienced.md | 使用准确的组件名与 props,会引用文档、追问边界情况 | 熟悉 Astryx 的资深工程师 | 文档对高级组合模式的覆盖是否完整 |
| adversarial.md | 混入 Tailwind、shadcn、Bootstrap、Material UI、Radix 等其他框架的模式与术语 | 从其他生态迁移而来的开发者 | 文档能否抵御“外来术语污染”,防止 LLM 幻觉 |
三种画像在源码中被正式建模为联合类型。在 internal/vibe-tests/src/types.ts 中可以看到:
export type PersonaType = 'naive' | 'experienced' | 'adversarial';其中,adversarial 是最严苛的一种:它不只是“问得难”,而是刻意把提问者的心智模型建立在另一套组件生态上,迫使 LLM 在生成代码时完成一次“跨框架翻译 + 幻觉过滤”的双重任务。
Adversarial 画像的核心特征:混入其他 UI 系统的模式
adversarial.md 对画像的定义非常凝练:
You are a developer who mixes in patterns from other UI systems.
它被设计成“一个把其他 UI 系统的模式混着用的开发者”,其核心特征包括四条:
- 引用 Tailwind、shadcn、Bootstrap、Material UI 及其他框架——提问者不关心你是什么系统,它默认“大家都差不多”;
- 使用来自竞争系统的术语——例如把布局说成 Tailwind 工具类名,把对话框说成 “shadcn Dialog”;
- 可能请求复制本系统并不存在的模式——这是对 LLM 幻觉的最直接诱因;
- 模糊不同组件库之间的界限——好像所有组件库的 props、API 都是通用的。
从测试设计角度看,这四条特征分别对应着不同的失效风险:术语混用会诱导 LLM 输出“听起来合理但本系统根本没有”的 props;请求复制不存在的模式会诱导 LLM 生造组件;而模糊边界则会让 LLM 把 Astryx 的组件错误地当作别的库的组件来用。这正是 analyst.md 根因分类中专门列出的Competing patterns(其他框架的模式干扰)一类问题——它被单独作为一个失败根因,可见这类错误在真实 LLM 生成中出现的频率之高。
话术样本逐条拆解:每条对抗性提问在测什么
原文档给出了 6 个典型的对抗性提问样例。它们不是随手写的,每一条都精准命中了“外来框架模式 vs Astryx 自身模型”的某个冲突点:
| 对抗性提问 | 引用的外来模式 | 在 Astryx 中对应的正确落点 |
|---|---|---|
Can I use flex justify-between here? | Tailwind 布局工具类 | Astryx 的布局原语(如 Stack/HStack/VStack)及对齐 props,而非flex类名 |
Make it look like a shadcn Dialog | shadcn/ui 组件体系 | Astryx 的 Dialog 组件及其组合方式 |
I want the Bootstrap card style with the image on top | Bootstrap 的 Card 结构约定 | Astryx 的 Card 组件与头部/媒体区块组合 |
Add some gap-4 spacing between these | Tailwind 的 spacing 刻度类 | Astryx 的间距令牌(--spacing-*)与布局 props |
Use the MUI-style outlined variant | Material UI 的outlined变体语义 | Astryx 组件自身的variant枚举取值 |
I need a Radix-like dropdown menu | Radix 的无头原语(headless primitive)命名习惯 | Astryx 的 DropdownMenu 等现成组件 |
这些提问的共同点是:它们在句法上完全“合法”——如果 LLM 面对的是 Tailwind 或 MUI,这些请求甚至可以直接照做。因此它们测的不是“LLM 会不会写代码”,而是:
- LLM 能否识别出提问者用错了“方言”,并把需求翻译为 Astryx 自己的 API;
- LLM 能否在没有对应概念时,用本系统的等价组件承接需求,而不是凭空捏造;
- LLM 是否会因为提问者“自信地”使用外来术语,就把外来 props 当成 Astryx 支持的 props。
原文档还明确规定了“When making requests”(发起请求时)的行为准则,进一步坐实了这种压力测试意图:自由混用框架专属术语、引用其他库的视觉模式、用 Tailwind 工具类名充当描述语言、请求复制其他系统的具体设计、假定存在来自其他库的 props 与模式。
源码中的实际执行机制:Persona 如何注入到测试流程
画像文档不是孤立的提示词文本,它在运行层被 internal/vibe-tests/src/interactive.ts 消费。该文件定义了每个 persona 注入给子代理(sub-agent)的身份指令:
const personaInstructions = { naive: `You are testing as a NAIVE user who describes UIs in plain language without technical terms. You don't know component names. Describe what you want visually.`, experienced: `You are testing as an EXPERIENCED user who knows the component system. Use correct component names and reference the docs.`, adversarial: `You are testing as an ADVERSARIAL user who mixes in patterns from other frameworks. Reference Tailwind, baseline, Bootstrap patterns in your request.`, };注意adversarial的指令明确要求 “Reference Tailwind, baseline, Bootstrap patterns”,这与 adversarial.md 的 Characteristic 一一对应,说明 persona 文档是运行时代码的“事实来源”(source of truth)。
更关键的是,同一个对抗性 persona 在不同目标系统下的“侵入方向”被刻意差异化。在 interactive.ts 的personaFraming映射中可以看到:
const personaFraming: Record<string, Record<string, string>> = { astryx: { naive: '', // No special framing - just the natural request experienced: `Use Astryx components from @astryxdesign/core. `, adversarial: `I'm used to Tailwind/baseline patterns but need to use your design system. `, }, baseline: { naive: '', // No special framing experienced: `Use baseline/ui components. `, adversarial: `I'm used to Material UI patterns but need to use your design system. `, }, html: { naive: '', // No special framing experienced: `Use only plain HTML elements and inline CSS. `, adversarial: `I know React component libraries exist but I want raw HTML/CSS. `, }, };这揭示了对抗性测试的公平性设计:所有目标系统收到的都是“同等强度的外来术语污染”,只是污染源不同——对 Astryx 用 Tailwind/baseline 的模式,对 baseline(shadcn 风格)用 Material UI 的模式,对纯 HTML 目标则用“我知道有组件库但我就想要原生 HTML/CSS”的姿态。这与 README.md 中列出的评估不变式(“Same persona across all configurations”)保持一致:persona 固定,只有被测试的系统在变。
在命令行层面,persona 通过interactive命令的参数暴露。从 internal/vibe-tests/scripts/smoke-test.sh 可以看到实际调用形态:
pnpm --silent interactive --target astryx --persona naive --sample 1 pnpm --silent interactive --target baseline --persona naive --prompts "$PROMPT_ID"将--persona naive替换为--persona adversarial即可跑同一批提示词的对抗性测试。在 interactive.ts 中,persona 参数的默认值被设置为naive,这意味着 adversarial 是需要显式启用的“更高强度”压测模式。
对抗性画像如何与评估闭环联动
Adversarial 画像之所以有价值,是因为它产生的输出会被评估器严格“验伪”。internal/vibe-tests/prompts/evaluator.md 定义了 LLM 生成代码的成功标准与逃生舱(escape hatch)分级,其中与对抗性画像直接相关的判据包括:
hallucination(幻觉)永远属于 critical 级别:凭空捏造不存在的 props、组件、事件或 API 会直接判定失败。对抗性提问者“假定存在来自其他库的 props”的倾向,正是诱发这类幻觉的主要开关;redundant_css在组件 props 已覆盖同类能力时为 critical:例如 LLM 被 Tailwind 惯性带偏,写出了本应由 Astryx props 处理的重复 CSS;supplemental_css、wrapper_div、inline_style属于可接受:即组件系统未覆盖的部分(布局空档、响应式断点、动画、装饰)允许用自定义 CSS 补充。
评估器甚至对 CSS 变量名做了白名单校验,直接用于拦截跨框架污染的产物。在 evaluator.md 中,Astryx 的合法变量前缀被逐一列出(--color-*、--spacing-*、--radius-*、--shadow-*、--transition-*、--font-family-*、--text-*、--leading-*、--font-weight-*),而--xds-*、--space-*、--border-*等外来或臆造前缀会被标记为幻觉并生成hallucination条目。这是对抗性画像测试中最典型的失败形态:LLM 记住了提问者口中的 Tailwind 刻度(如gap-4),于是编造出--space-*之类 Astryx 并不存在的变量。
结果分析端同样为对抗性画像预设了观察维度。analyst.md 的分析重点中有一条是 “Whether certain personas expose different weaknesses”(不同画像是否会暴露不同的弱点),并把根因归类到Competing patterns(其他框架模式干扰)与Naming confusion(命名误导)等条目。也就是说,一旦 adversarial 测试批量失败,分析师会优先怀疑文档是否缺少“外来术语→本系统等价物”的映射说明,从而把问题反馈给文档团队,而不是归咎于模型能力。
实践启示:如何在自己的项目中落地这类对抗性测试
从 adversarial.md 及配套源码可以提炼出一套可复用的对抗性提示词压测方法:
- 先固定 persona 话术库:像 Astryx 一样,把“混用外来术语”的典型问法沉淀成固定列表(Tailwind 工具类名、Bootstrap 结构约定、MUI 变体名、Radix 原语名),确保每轮测试的污染强度一致;
- 同 prompt 多目标对跑:同一批任务在 Astryx、shadcn 风格 baseline、纯 HTML 三个目标上分别执行,且对外来术语的来源做差异化设定,才能公平比较不同系统的抗污染能力;
- 用评估器把“外来模式”量化为逃生舱:将幻觉 props、错误 CSS 变量、冗余 CSS 分别归类为 critical / acceptable,让对抗性测试的结论可以统计、可以回归;
- 把失败回灌给文档:当对抗性测试稳定失败时,优先排查文档是否存在命名歧义、示例缺失或 API 解释不清,而不是直接判定模型“不行”。
这套方法的哲学是:设计系统文档不只是给人类看的,也是给 Agent 看的。一个“Agent Ready”的设计系统,其文档必须足够自洽,让 LLM 在面对满口 Tailwind 术语的开发者时,仍然能坚定地使用自己的组件与令牌——这正是 adversarial.md 这份不足百行的画像文档,在 Astryx 整个 Vibe Tests 体系中所承担的独特角色。
小结
- Adversarial 用户画像是 Astryx 三种测试人格(naive / experienced / adversarial)中最严苛的一种,专门模拟“混用 Tailwind、shadcn、Bootstrap、MUI、Radix 模式”的迁移型开发者;
- 它的四条特征与六条话术样本,精准覆盖了术语污染、模式复制请求、props 幻觉预设等失效模式;
- 在运行层,types.ts 建模了
PersonaType,interactive.ts 将 persona 注入为子代理身份指令,并针对不同目标系统差异化外来术语来源,保证对比公平; - 在评估层,evaluator.md 将幻觉 props、非法 CSS 变量标记为 critical 逃生舱,analyst.md 将失败归因到
Competing patterns等文档侧根因,形成“压测 → 验伪 → 归因 → 改进文档”的完整闭环。
如果你正在为自研组件库建设 LLM 评测体系,adversarial.md 连同其姊妹文档 naive.md、experienced.md 是一套可以直接借鉴的最小但完整的“人格矩阵”设计。
【免费下载链接】astryxAn open source design system that's fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考