基于大模型的设计系统文档自动生成:从组件代码到规范文档的智能推导

一、设计系统文档的"维护黑洞":从代码到文档的同步困境

设计系统的文档维护是前端工程中最容易滞后的环节。组件的 API 变更后,文档往往需要数天甚至数周才能同步更新。更常见的问题是:文档中的示例代码与组件实际行为不一致,开发者按文档使用组件时发现行为不符预期。

大模型辅助的文档生成,通过分析组件源码自动推导 API 文档、使用示例和最佳实践,显著降低文档维护成本。但 AI 生成的文档需要经过校验,确保与组件实际行为一致。

二、AI 文档生成的架构:从源码分析到文档输出

flowchart TD
    A[组件源码] --> B[AST 解析: 提取 Props/Events/Slots]
    B --> C[类型信息提取: TypeScript 类型]
    C --> D[LLM 文档推导]
    D --> E[API 文档]
    D --> F[使用示例]
    D --> G[最佳实践]
    E --> H[文档一致性校验]
    F --> H
    G --> H
    H --> I{文档与代码一致?}
    I -->|是| J[输出最终文档]
    I -->|否| K[标记差异, 人工审核]

    subgraph 文档输出格式
        L[Markdown: 组件文档]
        M[Storybook: 交互式文档]
        N[JSON Schema: API 描述]
    end

    J --> L
    J --> M
    J --> N

三、生产级代码实现与最佳实践

/**
 * 设计系统文档自动生成器
 * 从组件源码提取信息,交由 LLM 生成文档
 */
import * as ts from 'typescript';

interface ComponentDoc {
  name: string;
  description: string;
  props: PropDoc[];
  events: EventDoc[];
  slots: SlotDoc[];
  examples: ExampleDoc[];
  bestPractices: string[];
}

interface PropDoc {
  name: string;
  type: string;
  required: boolean;
  defaultValue?: string;
  description: string;
}

interface EventDoc {
  name: string;
  payload: string;
  description: string;
}

interface SlotDoc {
  name: string;
  description: string;
}

interface ExampleDoc {
  title: string;
  code: string;
  description: string;
}

class DesignSystemDocGenerator {
  /**
   * 从组件源码生成文档
   * 步骤: AST 解析 → 类型提取 → LLM 文档推导 → 一致性校验
   */
  async generateDoc(sourceFilePath: string): Promise<ComponentDoc> {
    // 1. AST 解析组件源码
    const sourceCode = await fs.readFile(sourceFilePath, 'utf-8');
    const componentInfo = this.parseComponent(sourceCode);

    // 2. 构建 LLM Prompt
    const prompt = this.buildDocPrompt(componentInfo, sourceCode);

    // 3. LLM 生成文档
    const response = await this.llmClient.chat({
      messages: [{ role: 'user', content: prompt }],
      temperature: 0.1,
      response_format: { type: 'json_object' },
    });

    const doc = JSON.parse(response.content) as ComponentDoc;

    // 4. 一致性校验:确保文档中的 Props 与源码一致
    const inconsistencies = this.validateConsistency(doc, componentInfo);
    if (inconsistencies.length > 0) {
      console.warn('文档与源码不一致:', inconsistencies);
      doc.bestPractices.push(
        `⚠️ 以下内容需人工确认: ${inconsistencies.join(', ')}`
      );
    }

    return doc;
  }

  /**
   * AST 解析组件源码
   * 提取 Props 类型定义、Event 发射、Slot 使用
   */
  private parseComponent(sourceCode: string): ComponentInfo {
    const sourceFile = ts.createSourceFile(
      'component.tsx',
      sourceCode,
      ts.ScriptTarget.Latest,
      true,
    );

    const props: PropInfo[] = [];
    const events: string[] = [];
    const slots: string[] = [];

    // 遍历 AST 提取类型信息
    ts.forEachChild(sourceFile, (node) => {
      // 提取 Props 接口定义
      if (ts.isInterfaceDeclaration(node)) {
        if (node.name.text.endsWith('Props')) {
          node.members.forEach((member) => {
            if (ts.isPropertySignature(member)) {
              const propName = member.name.getText(sourceFile);
              const propType = member.type?.getText(sourceFile) || 'unknown';
              const isOptional = !!member.questionToken;

              props.push({
                name: propName,
                type: propType,
                required: !isOptional,
              });
            }
          });
        }
      }

      // 提取 emit 事件
      if (ts.isCallExpression(node)) {
        const expr = node.expression;
        if (ts.isIdentifier(expr) && expr.text === 'emit') {
          const eventName = node.arguments[0]?.getText(sourceFile);
          if (eventName) events.push(eventName);
        }
      }
    });

    return { props, events, slots, sourceCode };
  }

  /**
   * 构建文档生成 Prompt
   * 将组件信息结构化呈现,引导 LLM 生成完整文档
   */
  private buildDocPrompt(info: ComponentInfo, sourceCode: string): string {
    const propsDesc = info.props.map(p =>
      `- ${p.name}: ${p.type}${p.required ? ' (必填)' : ' (可选)'}`
    ).join('\n');

    return `你是一个前端设计系统文档专家。

组件源码:
\`\`\`tsx
${sourceCode.substring(0, 3000)}
\`\`\`

组件 Props:
${propsDesc}

组件事件:
${info.events.map(e => `- ${e}`).join('\n')}

请生成完整的设计系统文档,包含:
1. 组件描述(50-100字)
2. Props 文档(每个 Prop 的用途说明)
3. Events 文档(每个事件的触发时机和载荷)
4. 2-3 个使用示例(含代码和说明)
5. 最佳实践(3-5 条)

输出 JSON 格式。`;
  }

  /**
   * 一致性校验
   * 确保文档中的 Props 与源码提取的 Props 一致
   */
  private validateConsistency(
    doc: ComponentDoc,
    info: ComponentInfo,
  ): string[] {
    const inconsistencies: string[] = [];
    const docPropNames = new Set(doc.props.map(p => p.name));
    const sourcePropNames = new Set(info.props.map(p => p.name));

    // 检查文档中是否有源码不存在的 Prop
    for (const name of docPropNames) {
      if (!sourcePropNames.has(name)) {
        inconsistencies.push(`文档中的 Prop "${name}" 在源码中不存在`);
      }
    }

    // 检查源码中是否有文档遗漏的 Prop
    for (const name of sourcePropNames) {
      if (!docPropNames.has(name)) {
        inconsistencies.push(`源码中的 Prop "${name}" 在文档中遗漏`);
      }
    }

    return inconsistencies;
  }
}

interface ComponentInfo {
  props: PropInfo[];
  events: string[];
  slots: string[];
  sourceCode: string;
}

interface PropInfo {
  name: string;
  type: string;
  required: boolean;
}

四、AI 文档生成的局限:行为描述准确性与示例代码可运行性

行为描述准确性。LLM 对组件行为的理解基于源码和类型信息,但某些行为(如条件渲染逻辑、副作用处理)可能无法从类型定义中推断。AI 生成的描述可能遗漏边界条件。建议对 AI 生成的行为描述进行代码审查,特别是涉及错误处理和边界情况的描述。

示例代码可运行性。AI 生成的示例代码可能引用不存在的导入路径或使用过时的 API。建议在文档生成后,自动执行示例代码的编译检查,确保代码可运行。

文档更新频率。组件频繁变更时,文档需要频繁重新生成。建议在 CI 流水线中集成文档生成步骤,每次组件代码变更后自动更新文档。

适用边界:AI 文档生成适用于 API 稳定、类型定义完善的组件库。对于频繁重构的组件,文档生成可能产生大量不一致告警,反而增加审核成本。

五、总结

AI 辅助的设计系统文档生成,通过 AST 解析组件源码提取类型信息,交由 LLM 推导 API 文档、使用示例和最佳实践。一致性校验确保文档与源码同步。但 AI 生成的行为描述可能遗漏边界条件,示例代码需要编译验证。工程实践中,建议在 CI 流水线中集成文档生成,每次组件变更后自动更新文档,并通过编译检查和人工审核确保文档质量。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐