大模型辅助的 UI 文案生成:让每一个按钮都说"人话"

一、"点击确定代表您同意我们的服务条款和隐私政策以及数据收集协议"

你一定见过这种按钮文案。它的问题不是"写错了",而是"写得太正确了"——正确到用户根本不想读。

UI 文案(UX Writing)是设计中最容易被忽视的环节。设计师会把大量精力花在间距、色彩、动效上,但按钮上的文字往往是最后一分钟随手写的。而事实是:一段好的按钮文案,比一个精美的渐变背景更能提升转化率。

大语言模型(LLM)在这个领域的表现出人意料地好——因为它不需要理解设计,它只需要理解语言。这篇文章,我会讲如何用 AI 为你的 UI 组件生成"说人话"的文案,从单个按钮到完整页面的文案生成管线。

二、UI 文案的生成约束:不是"写得好",而是"写得对"

UI 文案的五种核心类型

类型 示例组件 文案要求 字数限制
操作按钮 CTA、提交、确认 动词开头,结果明确 ≤6 字
提示信息 Tooltip、Toast 一句话说清状态 ≤20 字
错误提示 表单校验错误 说清楚"哪里错了"+"怎么办" ≤40 字
空状态 空列表、无结果 安抚+引导下一步 ≤30 字
占位文本 Input placeholder 给出具体示例 ≤15 字

品牌语调的量化

不同品牌需要不同的文案语调。我定义了一个"语调三元组"来量化:

语调 = (亲和度, 专业度, 简洁度)

- 亲和度 0-10:0=冷漠, 10=像朋友聊天
- 专业度 0-10:0=通俗, 10=学术/法律腔
- 简洁度 0-10:0=啰嗦, 10=电报

不同场景的推荐语调:

场景 语调三元组 按钮示例
医疗健康 (7, 8, 7) "查看检查报告"
金融理财 (4, 9, 9) "确认交易"
社交娱乐 (9, 3, 6) "来找点好玩的!"
企业办公 (5, 7, 8) "提交审批"
教育学习 (8, 6, 5) "开始今天的学习之旅"

三、AI 文案生成引擎的实现

/**
 * AI UI 文案生成引擎
 */

interface CopyContext {
  /** 组件类型 */
  componentType: 'button' | 'toast' | 'error' | 'empty' | 'placeholder';
  /** 业务场景描述 */
  scenario: string;
  /** 品牌语调 */
  brandTone: {
    warmth: number;     // 亲和度 0-10
    professionalism: number; // 专业度 0-10
    conciseness: number;    // 简洁度 0-10
  };
  /** 目标用户画像 */
  audience?: string;
  /** 操作的结果(用户点击后会发生什么) */
  actionResult?: string;
}

interface CopyCandidate {
  text: string;
  score: number;   // 综合评分
  breakdown: {
    lengthScore: number;
    toneScore: number;
    clarityScore: number;
  };
}

class UXCopyGenerator {
  /** 字数限制配置 */
  private readonly LENGTH_LIMITS = {
    button: { max: 6, ideal: 4 },
    toast: { max: 20, ideal: 12 },
    error: { max: 40, ideal: 20 },
    empty: { max: 30, ideal: 15 },
    placeholder: { max: 15, ideal: 8 },
  };

  /**
   * 基于 Prompt 工程生成候选文案
   *
   * 调用的实际是 LLM API,这里展示的是 Prompt 构建逻辑
   */
  buildPrompt(context: CopyContext): string {
    const limits = this.LENGTH_LIMITS[context.componentType];

    return `你是一个专业的 UI/UX 文案设计师。

请为以下场景生成 5 个候选文案:

**组件类型**:${context.componentType}
**业务场景**:${context.scenario}
**用户操作后发生什么**:${context.actionResult || '无特殊说明'}
**目标用户**:${context.audience || '普通用户'}

**文案约束**:
- 字数不超过 ${limits.max} 字,理想是 ${limits.ideal} 字
- 亲和度要求:${context.brandTone.warmth}/10(${this.describeWarmth(context.brandTone.warmth)})
- 专业度要求:${context.brandTone.professionalism}/10(${this.describeProfessionalism(context.brandTone.professionalism)})
- 简洁度要求:${context.brandTone.conciseness}/10(${this.describeConciseness(context.brandTone.conciseness)})

**排版要求**:
${this.getFormatRequirements(context.componentType)}

请直接输出 5 个候选文案,每行一个,不要编号。`;
  }

  /** 打分引擎:评估候选文案 */
  scoreCandidate(text: string, context: CopyContext): CopyCandidate {
    const limits = this.LENGTH_LIMITS[context.componentType];

    // 评分维度一:长度评分
    const length = text.length;
    let lengthScore = 0;
    if (length <= context.brandTone.conciseness * 0.8 + 1) {
      lengthScore = 1.0;
    } else if (length <= limits.ideal) {
      lengthScore = 0.8;
    } else if (length <= limits.max) {
      lengthScore = 0.5;
    } else {
      lengthScore = 0.1; // 超长,严重扣分
    }

    // 评分维度二:清晰度评分
    let clarityScore = 0.5;
    // 有动词 → 加分
    if (this.hasActionVerb(text)) clarityScore += 0.2;
    // 有明确结果 → 加分
    if (text.includes('成功') || text.includes('完成') ||
        text.includes('已') || text.includes('请')) {
      clarityScore += 0.15;
    }
    // 没有模糊词 → 加分
    if (!text.includes('可能') && !text.includes('大概') &&
        !text.includes('或许')) {
      clarityScore += 0.15;
    }

    // 评分维度三:语调匹配度
    const toneScore = this.calculateToneMatch(text, context.brandTone);

    // 加权综合评分
    const total = lengthScore * 0.25 + clarityScore * 0.4 + toneScore * 0.35;

    return {
      text,
      score: total,
      breakdown: { lengthScore, toneScore, clarityScore },
    };
  }

  /**
   * 语调匹配度计算
   *
   * 基于关键词和句式特征估算文本的语调
   */
  private calculateToneMatch(text: string, target: CopyContext['brandTone']): number {
    let estimatedWarmth = 5;
    let estimatedProfessionalism = 5;

    // 温暖度估计
    if (/[!!~~]/.test(text)) estimatedWarmth += 1;
    if (text.includes('吧') || text.includes('哦') || text.includes('呢')) estimatedWarmth += 1;
    if (text.includes('您')) estimatedWarmth += 1;
    if (text.includes('请')) estimatedProfessionalism += 1;

    // 专业度估计
    if (text.includes('确认') || text.includes('提交')) estimatedProfessionalism += 1;
    if (text.includes('审核') || text.includes('验证')) estimatedProfessionalism += 1;

    // 计算匹配度
    const warmthDiff = Math.abs(target.warmth - estimatedWarmth) / 10;
    const profDiff = Math.abs(target.professionalism - estimatedProfessionalism) / 10;

    return 1 - (warmthDiff + profDiff) / 2;
  }

  private hasActionVerb(text: string): boolean {
    const verbs = ['查看', '提交', '保存', '删除', '搜索', '分享',
      '下载', '上传', '开始', '取消', '确认', '发送', '添加', '编辑'];
    return verbs.some(v => text.includes(v));
  }

  private describeWarmth(score: number): string {
    if (score >= 8) return '非常亲切,像朋友间的对话';
    if (score >= 6) return '温和友好,保持适度的亲近感';
    if (score >= 4) return '中性,不冷不热';
    return '专业克制,避免情感表达';
  }

  private describeProfessionalism(score: number): string {
    if (score >= 8) return '高度专业,使用行业术语';
    if (score >= 6) return '半正式,兼顾专业和可读';
    return '通俗易懂,避免专业术语';
  }

  private describeConciseness(score: number): string {
    if (score >= 8) return '极度精简,能省则省';
    if (score >= 6) return '适中,说清楚但不啰嗦';
    return '详细说明,允许一定冗余';
  }

  private getFormatRequirements(type: string): string {
    const formats: Record<string, string> = {
      button: '动词开头(如"查看报告"),不使用"确定"这种泛化词',
      toast: '陈述句,说清当前状态(如"保存成功"),不使用疑问句',
      error: '先说"哪里错了",再说"怎么办"(如"邮箱格式不正确,请输入有效的邮箱地址")',
      empty: '先安抚,再引导(如"还没有收藏内容,去发现好文章吧")',
      placeholder: '给出一个具体的示例值(如"例如:张三"),不说"请输入"',
    };
    return formats[type] || '';
  }

  /**
   * 批量生成 + 自动筛选
   *
   * 调用 LLM 生成 5 个候选 → 逐一打分 → 返回 Top 3
   */
  async generateAndSelect(context: CopyContext): Promise<CopyCandidate[]> {
    // 实际项目中使用 LLM API
    const candidates = await this.callLLM(this.buildPrompt(context));

    return candidates
      .map(text => this.scoreCandidate(text, context))
      .sort((a, b) => b.score - a.score)
      .slice(0, 3); // 返回 Top 3
  }

  private async callLLM(prompt: string): Promise<string[]> {
    // 实际调用 OpenAI / Claude / 本地模型
    // 这里返回模拟数据
    return [
      '查看详情',
      '了解更多',
      '点击查看',
      '前往查看',
      '打开详情',
    ];
  }
}

四、边界:AI 文案的三个"不能"

  1. 不能替代法律合规审核:涉及用户协议、隐私政策、金融交易确认等具有法律效力的文案,AI 生成的内容需要法务团队最终审核。一个字的偏差可能导致法律纠纷。

  2. 不能理解品牌的历史包袱:一个品牌可能刻意避免某些词汇(比如"优惠"听起来像打折),AI 不知道这些。品牌语调词典需要人工维护。

  3. 不能判断"场景敏感度":在医疗、金融、法律产品中,一句"轻松搞定"可能让用户觉得你在不尊重严肃的事情。场景敏感度需要人工判断。

五、总结

好的 UI 文案不是"写得漂亮",而是"写得准确"。它需要同时满足四个约束:字数合适、语调一致、动词明确、场景匹配。大语言模型的优势在于它可以同时考虑所有约束来生成候选文案,而传统做法通常只能一条一条人工调整。把 AI 定位为"文案初稿生成器",人在最后把关——这是目前 UI 文案生产的最佳实践。


作者:李慕杰(Leo / 8limujie)
一个终于不用再写"点击确定"这种废话的前端匠人

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐