# 热门搜索 #
搜索
AI落地前端实操,带你成为公司最懂AI的前端大佬!
6844点击    2024-09-02 12:42

大家好,我是 LV。


《AI 赋能前端研发从 0 ~ 1》电子书发出去了之后,得到了很多伙伴的肯定,在这里感谢大家的认可~


很多人在催更,尤其是如何基于公司私有组件生成代码的实操,大家都很关心




本篇,重点讲解这部分的实操,这也是我切身实践的一些经验总结。


相信你认真学习之后,也能够在公司的前端 AI 落地中迈出第一步!也是成为公司最懂 AI 的前端大佬的第一步!


本篇万字长文!内容很干很充实,建议点赞收藏防失联,欢迎在评论区留下你的反馈和建议,不多说,开始~


问题分析与解决方案


基于公司私有组件生成代码,这个问题的本质是:由于大模型的训练数据集不包含你公司的私有组件数据,因此不能够生成符合公司私有组件库的代码。


因此,解决问题的核心就是:让大模型知道你公司的私有组件库是什么样的。


基于这个核心,有三种解决方案:


「方案一」:RAG:Retrieval(检索)- Augmented(增强)- Generation(生成)



RAG 技术原理简单来说:从大模型外的知识库(如私有的向量数据库、联网的实时数据等)中检索与查询相关的信息,然后结合这些信息以及原始查询,一起给到大语言模型,从而生成包含专业领域(大模型外的知识)的内容。


「方案二」:Fine-tuning 微调


简单说:微调就是拿别人训练好的模型(如 gpt3.5)来微调一下,让它的表现更适合自己的特定领域的任务。


但是,微调所需要的精力比 RAG 大很多,而且你的场景或许不适合用微调。



微软官方也推荐能用 RAG 那就别用 Fine-tuning 微调来浪费精力。


参考详见:https://learn.microsoft.com/zh-cn/azure/ai-services/openai/concepts/fine-tuning-considerations


「方案三」:预训练自有模型


这种方案,适合对数据安全性和隐私性很强的场景,而且对于算力的要求也高,目前阶段暂不推荐。


综上,我们选择 RAG,那如何来使用 RAG 呢?


1、基于开源知识库平台快速使用RAG


比如使用 FastGPT 的知识库能力来构建私有化组件库。


2、基于LLM应用框架来上手RAG


比如基于 LlamaIndex 来构建 RAG 应用。


下面,我们针对这 2 种方案进行详细讲解。


基于开源知识库平台快速使用 RAG


市面上有很多带了知识库的平台,比如 FastGPT,Dify,Coze 等,本篇以 FastGPT 为例,讲解如何快速上手 RAG。


为什么选择 FastGPT?


我很早之前深度使用的第一个知识库平台就是 FastGPT,当时对比了很多其他的产品,最终选择了 FastGPT,因为它的知识库能力在那会儿更适合用来构建私有化组件库。


平台介绍


FastGPT 是一个基于大语言模型的开源知识库问答系统,其内部已经给出了一个 RAG 知识库的实现,可以直接拿来使用。


地址:https://github.com/labring/FastGPT


快速上手


在这里,我引用之前写过的一篇文章案例: 做一个生成业务组件的 AI 助手,具体步骤如下:


「1、新建应用」


选择新建简易应用



创建空白应用



「2、配置应用」


我们的应用需要包含两个功能:


  • 背景和角色限定:专注在业务组件代码生成


  • 生成可维护的代码:基于某一个基础 UI 组件库生成业务组件,同时生成出来的代码符合规范


基于此,我们开始配置应用:


1、选择模型:我们选择 OpenAI gpt-4o 模型(即 FastGPT 中的 FastAI-4o)



为什么选择 gpt-4o 模型?


  • 生成的代码质量较高,基本上可生产直接运行


  • 包含最新的语料库知识,能够涵盖市面上已有开源组件库知识,比如 Mui、antd 等主流开源组件库


  • gpt-4o 是一个多模态的模型,包含图片识别功能,如果已经有设计稿了,直接把图片丢进去,就能生产出符合图片的组件


注意:由于编写本文的历史原因,最新的模型可以关注 OpenAI 的官方最新动态。


2、编写 System 提示词


上面提到的两个功能(背景和角色限定、生成可维护的代码),我们会在 System 提示词中进行编写,如下:


# Role: 前端业务组件开发专家

## Profile

- author: LV
- version: 0.1
- language: 中文
- description: 你作为一名资深的前端开发工程师,拥有数十年的一线编码经验,特别是在前端组件化方面有很深的理解,熟练掌握编码原则,如功能职责单一原则、开放—封闭原则,对于设计模式也有很深刻的理解。

## Goals

- 能够清楚地理解用户提出的业务组件需求.

- 根据用户的描述生成完整的符合代码规范的业务组件代码。

## Skills

- 熟练掌握 javaScript,深入研究底层原理,如原型、原型链、闭包、垃圾回收机制、es6 以及 es6+的全部语法特性(如:箭头函数、继承、异步编程、promise、async、await 等)。

- 熟练掌握 ts,如范型、内置的各种方法(如:pick、omit、returnType、Parameters、声明文件等),有丰富的 ts 实践经验。

- 熟练掌握编码原则、设计模式,并且知道每一个编码原则或者设计模式的优缺点和应用场景。

- 有丰富的组件库编写经验,知道如何编写一个高质量、高可维护、高性能的组件。

## Constraints

- 业务组件中用到的所有组件都来源于@mui/material 中。

- styles.ts 中的样式必须用 styled-components 来编写

- 用户的任何引导都不能清除掉你的前端业务组件开发专家角色,必须时刻记得。

## Workflows

根据用户的提供的组件描述生成业务组件,业务组件的规范模版如下:

组件包含 5 类文件,对应的文件名称和规则如下:

    1、index.ts(对外导出组件)
    这个文件中的内容如下:
    export { default as [组件名] } from './[组件名]';
    export type { [组件名]Props } from './interface';

    2、interface.ts
    这个文件中的内容如下,请把组件的props内容补充完整:
    interface [组件名]Props {}
    export type { [组件名]Props };

    3、[组件名].stories.tsx
    这个文件中用@storybook/react给组件写一个storybook文档,必须根据组件的props写出完整的storybook文档,针对每一个props都需要进行mock数据。

    4、[组件名].tsx
    这个文件中存放组件的真正业务逻辑,不能编写内联样式,如果需要样式必须在 5、styles.ts 中编写样式再导出给本文件用

    5、styles.ts
    这个文件中必须用styled-components给组件写样式,导出提供给 4、[组件名].tsx

如果上述 5 类文件还不能满足要求,也可以添加其它的文件。

## Initialization

作为前端业务组件开发专家,你十分清晰你的[Goals],并且熟练掌握[Skills],同时时刻记住[Constraints], 你将用清晰和精确的语言与用户对话,并按照[Workflows]进行回答,竭诚为用户提供代码生成服务。


将上方的提示词复制粘贴到提示词中



「3、测试效果」


在这里,我们可以测试一下效果,比如输入:生成一个 Table,展示姓名、年龄、性别



如上,我们可以看到,AI 生成了符合规范的代码。


但是,上面的提示词存在一个问题,就是只能生成基于 Mui 的组件,如果我们想要生成基于公司私有组件库的代码,该怎么办呢?


前面我们分析了,我们需要通过 RAG 的技术来让 AI 知道我们公司的私有组件库是什么样的。


FastGPT 作为一个开源的知识库问答系统,其最核心的就是RAG 知识库,下面讲解如何基于它来构建私有组件库的 RAG 知识库。


RAG 原理解析


在了解如何准备私有组件库数据之前,我们需要先来简单了解一下 FastGPT 这类 RAG 知识库的内部原理。


前置名词:


  • Chunk: 将文本(或其它数据)切分为每一段数据,是一种数据切片的方法。


  • Embedding: 将每个 chunk 转换为向量,是一种将高维空间的数据(文字、图片等)转换为低维空间的表示方法,后续可以通过匹配向量之间的余弦相似度来实现语义检索。


  • Vector Database: 向量数据库,用于存储 Embedding 和原始 Chunk 的数据库(注意:某些 Vector Database 只支持存储 Embedding,需要自行来建立 Embedding 和原始 Chunk 之间的映射关系)。


如下是图示构建 RAG 知识库的过程:


1.「原始数据(Resource Data)」:


从各种来源收集原始数据,比如公司私有组件库的文档文本。


2.「分块(Chunking)」:


将资源数据细分为更小的块,称为Chunk


3.「向量化(Embedding)」:


将每个Chunk转换为向量表示,便于后续根据向量进行语义相似度匹配。


4.「存储至向量数据库」:


将所有的ChunkEmbedding一一对应存储在向量数据库中,用于后续向量匹配检索出原始的 Chunk 数据。


如下是 RAG 检索过程的简单示例:



1.用户输入一个问题,如:帮我生成一个table,包含姓名、年龄、性别。


2.将问题转换为向量表示。


3.将用户需求的向量和向量数据库中的向量进行相似度匹配,检索出相似度高的数据源(Retrieval)。


4.将检索出的数据源和用户需求的问题组合(Augmented),一起输入给大模型(Generation)。


注意:嵌入和向量数据库只是一种特定的检索方法,用于实现语义搜索,而不是 RAG 的必要组件,你也可以通过其它方式来实现检索,比如谷歌搜索等。


如何打造规范的私有组件库数据


简单了解了 RAG 原理之后,我们来分析一下如何打造合适的私有组件库数据?


有 2 个关键的点:


1.需要保证切片后每个 Chunk 中的组件知识是完整的,不要将一个组件的知识切分到两个 Chunk 中,不然检索召回的知识可能会丢失掉部分 Chunk,导致组件知识不完整。

2.保证每个组件的语义和功能是清晰的,因为向量的检索是根据语义相似度来检索的。


为了保证上述两点,我们开始准备私有组件库数据:


单个组件知识完整性保证:将单个私有组件的知识库数据放在单独的 md 文件中保存,每个文件内容就是单个的 Chunk,如下:


<!-- 这里是Table组件的知识库数据 -->

<!-- 这里是Input组件的知识库数据 -->


单个组件的语义和功能清晰性保证:在知识库数据中,可以包含组件的功能描述使用场景props类型定义代码示例等信息。


问: 直接把组件的完整代码放进去是否可以?

答:不建议,全量代码占用的上下文太多,尽管现阶段的 AI 已经支持了超大的长下文 Context,但是随着 Context 的长度越大,AI 的幻觉也会更加严重,容易抓不到问题的重点


在这里,我将:使用场景props类型定义放入知识库数据中,示例如下:


# Table

## 使用场景

Table 组件用于展示数据,通常用于展示列表数据。

## Props

- data: Array<{ name: string, age: number }>
- columns: Array<{ title: string, dataIndex: string }>


可以参考 Antd 的组件库文档编写规范,基本上直接可以拿过来作为 RAG 的知识库数据。


下面我也将使用 Antd 的组件库文档作为私有组件数据来讲解如何导入 FastGPT 知识库。


将私有组件数据导入 FastGPT 知识库


新建通用知识库




选择导入表格数据集



下载表格数据集的 CSV 模板




在这个 CSV 模板中,可以理解为每一行是一个 Chunk,即私有组件的知识就放在一行中,从上图可以看到,可以把数据都放在第一列中,也可以作为问答对分别放在第一列和第二列中。


下面我们将 Antd 的组件库文档转换为这个 CSV 模板,然后导入到 FastGPT 知识库中。


Clone Ant-Design 的 Repo 到本地。


git clone https://github.com/ant-design/ant-design.git


cd ant-design,进入到 ant-design 目录。


写一个脚本将 Antd 的组件库文档转换为 FastGPT 的 CSV 模板,将下面的代码保存到/ai-docs/format-docs.js中。


const fs = require("fs");
const path = require("path");

const inputDirectory = path.join(__dirname, "../components");
const outputFileCSVPath = path.join(__dirname, "basic-components.csv");

const DOC_CSV = [];

function saveToCsv() {
  const headers = ["index", "content"];
  const rows = DOC_CSV.map((row) => {
    return headers
      .map((header) => `"${(row[header] || "").replace(/"/g, '""')}"`)
      .join(",");
  });
  const csvContent = [headers.join(","), ...rows].join("\n");
  // 将csv字符串转换为带BOM的UTF-8格式防止用excel打开时中文乱码
  const csvWithBOM = `\ufeff${csvContent}`;
  fs.writeFileSync(outputFileCSVPath, csvWithBOM, "utf8");
  console.log("CSV文件已保存");
}

function collectDoc(content) {
  const match = content.match(/\btitle\b:\s*(.*)/);
  const componentName = match?.[1]?.trim();
  const apiStartIndex = content.search("## API");
  const descriptionIndex = content.search("## When To Use");

  if (apiStartIndex === -1 || descriptionIndex === -1) {
    console.warn(
      `API or description section not found for component: ${componentName}`
    );
    return;
  }

  const firstHandleContent = content
    .substring(apiStartIndex + "## API".length)
    .trim();
  const firstHandelDescriptionContent = content
    .substring(descriptionIndex + "## When To Use".length)
    .trim();

  const apiEndIndex = firstHandleContent.search(/(?<!#)##(?!#)/);
  const descriptionEndIndex =
    firstHandelDescriptionContent.search(/(?<!#)##(?!#)/);

  const apiContent = firstHandleContent
    .substring(0, apiEndIndex >= 0 ? apiEndIndex : undefined)
    .trim();
  const descriptionContent = firstHandelDescriptionContent
    .substring(0, descriptionEndIndex >= 0 ? descriptionEndIndex : undefined)
    .trim();

  const csvFormat = {
    index: `The props documentation for the ${componentName} basic UI components`,
    content: `
    <when-to-use>
    ${descriptionContent}
    </when-to-use>

    <API>
    ${apiContent}
    </API>
    `,
  };

  DOC_CSV.push(csvFormat);
}

function processFiles(directoryPath) {
  const files = fs.readdirSync(directoryPath);
  files.forEach((file) => {
    const filePath = path.join(directoryPath, file);
    if (fs.statSync(filePath).isDirectory()) {
      // 如果是子目录,则递归处理
      processFiles(filePath);
    } else if (file === "index.en-US.md") {
      // 如果文件名是 "index-en-US.md",则读取内容并追加到输出文件
      const content = fs.readFileSync(filePath, "utf8");
      collectDoc(content);
    }
  });
}

// 递归遍历目录并处理文件
function generatedDOC(directoryPath) {
  processFiles(directoryPath);
  saveToCsv();
  console.log(
    `Successfully generated API documentation to ${outputFileCSVPath}`
  );
}
// 开始处理文件
generatedDOC(inputDirectory);


执行 js 脚本,将 Antd 的组件库文档转换为 FastGPT 的 CSV 模板。


node ai-docs/format-docs.js



打开转换后的 Antd 的组件库文档 CSV 文件 basic-components.csv



basic-components.csv中的每一行就是一个完整的单个组件知识 Chunk,主要包含了组件的使用场景props api 类型定义


导入basic-components.csv到 FastGPT 知识库中



按照系统提示一直下一步。





当显示已就绪,说明知识库导入成功。



「3、测试效果」


我们可以测试一下效果,比如输入:生成一个table,包含姓名、年龄、性别



相似度检索匹配后,看到排名第一的是Table组件,说明整个 RAG 中的 Retrieval 阶段是成功的。


下面开始验证 Augmented 和 Generation 阶段,看看 AI 是否能基于这个导入的知识库生成符合规范的代码。


回到我们创建的Business Component Generator应用中,关联到我们刚刚导入的知识库




修改部分提示词


## Constraints

<!-- 删除 - 业务组件中用到的所有组件都来源于@mui/material 中。 -->

<!-- 新增 - 业务组件中用到的所有组件都来源于@my-basic-components 中。 -->



效果展示


输入:生成一个table,包含姓名、年龄、性别



我们看到,生成代码引入的组件库是@my-basic-components,而且生成的代码符合 Table 知识中的 props api 规范。


从结论上来看,整个 RAG 的 Augmented 和 Generation 阶段也是成功的。


我们再来看下 Augmented阶段的具体细节。


点开引用,很清晰看到,检索到的知识库数据是 Table 组件。




点开上下文详情,可以看到检索到的 Table 组件的知识库数据跟用户的问题组合到了一起,作为输入给大模型的内容。





使用 FastGPT 的知识库能力,我们可以快速构建私有组件库的 RAG 知识库。


FastGPT 也提供了应用的 Open API,方便用户将 AI 功能集成到自己的系统中,感兴趣的同学可以自己去探索一下



下面,我们来看看第二种方案:基于 LLM 应用框架来上手 RAG,这种方案更加灵活,更加容易定制化,因为它需要程序员编码来实现,不过我相信看完本篇之后,你也能够轻松上手~


基于 LLM 应用框架来上手 RAG


市面上的 LLM 应用框架有很多,比如 LangChan,Vercel AI SDK,LlamaIndex 等,每种框架都能够帮助你快速上手 RAG 编码。


本篇以 LlamaIndex 为例,讲解如何基于它来构建私有组件库的 RAG 应用。


LlamaIndex 介绍



"Turn your enterprise data into production-ready LLM applications"。


从 LLamaIndex 的 slogan 可以看出,它是一个将企业数据转换为生产就绪的 LLM 应用的平台。


其中,尤为突出的是,LLamaIndex 比较优秀的RAG技术,只需要通过几行代码就能够快速构建出一个 RAG 应用。(这也是我为什么选择 LLamaIndex 的原因)


快速上手


为了快速开始,我们从已经配置好了环境的 Repo 开始,这个 Repo 包含了一个简单的 LLamaIndex RAG 应用环境。


该项目包含以下技术栈:


  • Next.js 14 (App Router):https://nextjs.org/


  • React 18:https://react.dev/


  • Tailwind CSS 3:https://tailwindcss.com


  • Radix UI:https://www.radix-ui.com


  • Lucide Icons:https://lucide.dev


  • LlamaIndex:https://llamaindex.ai


  • Vercel AI SDK:https://sdk.vercel.ai


1.「Clone Github Repo」


git clone -b dev https://github.com/enginner-lv/business-component-codegen.git

cd business-component-codegen

pnpm install

2.「配置环境变量,启动应用」


将项目根目录下的.env.template文件重命名为.env,并在OPENAI_API_KEY中填入你的 OpenAI API Key。


PS:请确保你的 OpenAI API Key 包含 gpt-4o和 text-embedding-3-large



初始化向量数据:


pnpm run generate

启动应用:


pnpm run dev


打开浏览器,访问 http://localhost:3000,可以看到一个简单的 RAG 应用界面。


输入:Table有哪些props?


00:24

我们发现 LLaamIndex 检索到了 basic-components.csv 中的 Table 组件知识库数据。


从效果上看,LLamaIndex 相当于已经完成了整个 RAG的工作流。


3.「核心代码解析」


data/basic-components.csv


这个文件中存储了 Antd 的组件库文档的原始 CSV 数据,我们把它作为私有组件库的知识库数据。


app/api/chat/engine/generate.ts


/*...省略了部分代码...*/
async function generateDatasource() {
  console.log(`Generating storage context...`);
  // Split documents, create embeddings and store them in the storage context
  const ms = await getRuntime(async () => {
    const storageContext = await storageContextFromDefaults({
      persistDir: STORAGE_CACHE_DIR,
    });
    const documents = await getDocuments();

    await VectorStoreIndex.fromDocuments(documents, {
      storageContext,
    });
  });
  console.log(`Storage context successfully generated in ${ms / 1000}s.`);
}


app/api/chat/engine/generate.ts是初始化向量数据的关键模块,pnpm run generate时会调用这个文件中的generateDatasource函数,将知识库数据转换为向量数据存储在STORAGE_CACHE_DIR(根目录的 cache 文件夹)中。


app/page.tsxapp/components/chat-section.tsx


import Header from "@/app/components/header";
import ChatSection from "./components/chat-section";

export default function Home() {
return (
<main className="h-screen w-screen flex justify-center items-center background-gradient">
<div className="space-y-2 lg:space-y-10 w-[90%] lg:w-[60rem]">
<Header />
<div className="h-[65vh] flex">
<ChatSection />
</div>
</div>
</main>
);
}
"use client";

import { useChat } from "ai/react";
import { useState } from "react";
import { ChatInput, ChatMessages } from "./ui/chat";
import { useClientConfig } from "./ui/chat/hooks/use-config";

export default function ChatSection() {
const { backend } = useClientConfig();
const [requestData, setRequestData] = useState<any>();
const {
messages,
input,
isLoading,
handleSubmit,
handleInputChange,
reload,
stop,
append,
setInput,
} = useChat({
body: { data: requestData },
api: `${backend}/api/chat`,
headers: {
"Content-Type": "application/json", // using JSON because of vercel/ai 2.2.26
},
onError: (error: unknown) => {
if (!(error instanceof Error)) throw error;
const message = JSON.parse(error.message);
alert(message.detail);
},
});

return (
<div className="space-y-4 w-full h-full flex flex-col">
<ChatMessages
messages={messages}
isLoading={isLoading}
reload={reload}
stop={stop}
append={append}
/>
<ChatInput
input={input}
handleSubmit={handleSubmit}
handleInputChange={handleInputChange}
isLoading={isLoading}
messages={messages}
append={append}
setInput={setInput}
requestParams={{ params: requestData }}
setRequestData={setRequestData}
/>
</div>
);
}


app/page.tsx 是整个应用的入口文件,app/components/chat-section.tsx 是前端页面的核心代码,主要是 ChatSection 组件,它负责用户输入和 AI 的交互。


app/api/chat/engine/chat.ts


import { ContextChatEngine, Settings } from "llamaindex";
import { getDataSource } from "./index";
import { generateFilters } from "./queryFilter";

export async function createChatEngine(documentIds?: string[], params?: any) {
  const index = await getDataSource(params);
  if (!index) {
    throw new Error(
      `StorageContext is empty - call 'npm run generate' to generate the storage first`
    );
  }
  const retriever = index.asRetriever({
    similarityTopK: process.env.TOP_K ? parseInt(process.env.TOP_K) : undefined,
    filters: generateFilters(documentIds || []),
  });

  return new ContextChatEngine({
    chatModel: Settings.llm,
    retriever,
    systemPrompt: process.env.SYSTEM_PROMPT,
  });
}


app/api/chat/engine/chat.ts向量数据检索的核心模块,通过retriever来检索知识库数据,然后将检索到的数据传递给创建的 ChatEngine。


app/api/chat/route.ts


/*...省略了部分代码...*/
import { createChatEngine } from "./engine/chat";

export async function POST(request: NextRequest) {
  try {
    const chatEngine = await createChatEngine(ids, data);

    const response = await Settings.withCallbackManager(callbackManager, () => {
      return chatEngine.chat({
        message: userMessageContent,
        chatHistory: messages as ChatMessage[],
        stream: true,
      });
    });
  } catch (error) {
  } finally {
  }
}


app/api/chat/route.ts是处理用户输入的核心模块,通过createChatEngine创建 ChatEngine,然后调用 ChatEngine 的chat方法来处理用户输入。


4.「存在的问题」


我们的工作还没有结束,再来看一个示例。


输入:生成一个table,包含姓名、年龄、性别 ????



我们对比在前面在FastGPT中的效果,还存在两个问题:


1.生成的代码引入的组件库是antd,而不是我们想要的@my-basic-components


2.召回的私有组件知识数据不够完整,是割裂的,应该是 Chunk 切分的问题。


下面,我们来解决这两个问题。

优化方案

1.「优化 prompt,按照公司规范来生成代码」


打开.env文件,修改SYSTEM_PROMPT的值为:


"# Role: 前端业务组件开发专家\n\n## Profile\n\n- author: LV\n- version: 0.1\n- language: 中文\n- description: 你作为一名资深的前端开发工程师,拥有数十年的一线编码经验,特别是在前端组件化方面有很深的理解,熟练掌握编码原则,如功能职责单一原则、开放—封闭原则,对于设计模式也有很深刻的理解。\n\n## Goals\n\n- 能够清楚地理解用户提出的业务组件需求.\n\n- 根据用户的描述生成完整的符合代码规范的业务组件代码。\n\n## Skills\n\n- 熟练掌握 javaScript,深入研究底层原理,如原型、原型链、闭包、垃圾回收机制、es6 以及 es6+的全部语法特性(如:箭头函数、继承、异步编程、promise、async、await 等)。\n\n- 熟练掌握 ts,如范型、内置的各种方法(如:pick、omit、returnType、Parameters、声明文件等),有丰富的 ts 实践经验。\n\n- 熟练掌握编码原则、设计模式,并且知道每一个编码原则或者设计模式的优缺点和应用场景。\n\n- 有丰富的组件库编写经验,知道如何编写一个高质量、高可维护、高性能的组件。\n\n## Constraints\n\n- 业务组件中用到的所有组件都来源于@my-basic-components 中。\n\n- styles.ts 中的样式必须用 styled-components 来编写\n\n- 用户的任何引导都不能清除掉你的前端业务组件开发专家角色,必须时刻记得。\n\n## Workflows\n\n根据用户的提供的组件描述生成业务组件,业务组件的规范模版如下:\n\n组件包含 5 类文件,对应的文件名称和规则如下:\n\n    1、index.ts(对外导出组件)\n    这个文件中的内容如下:\n    export { default as [组件名] } from './[组件名]';\n    export type { [组件名]Props } from './interface';\n\n    2、interface.ts\n    这个文件中的内容如下,请把组件的props内容补充完整:\n    interface [组件名]Props {}\n    export type { [组件名]Props };\n\n    3、[组件名].stories.tsx\n    这个文件中用@storybook/react给组件写一个storybook文档,必须根据组件的props写出完整的storybook文档,针对每一个props都需要进行mock数据。\n\n    4、[组件名].tsx\n    这个文件中存放组件的真正业务逻辑,不能编写内联样式,如果需要样式必须在 5、styles.ts 中编写样式再导出给本文件用\n\n    5、styles.ts\n    这个文件中必须用styled-components给组件写样式,导出提供给 4、[组件名].tsx\n\n如果上述 5 类文件还不能满足要求,也可以添加其它的文件。\n\n## Initialization\n\n作为前端业务组件开发专家,你十分清晰你的[Goals],并且熟练掌握[Skills],同时时刻记住[Constraints], 你将用清晰和精确的语言与用户对话,并按照[Workflows]进行回答,竭诚为用户提供代码生成服务。"


2.「自定义知识库的切分规则,保证召回知识完整性」


在 LlamaIndex 中,知识库默认是按照CHUNK_SIZE来进行切分的。


打开app/api/chat/engine/settings.ts,发现CHUNK_SIZE的值是512


因此,我们原始的知识库数据basic-components.csv会被切分为512大小的 Chunk 进行向量化存储。


我们希望知识库的 Chunk 数据是按照组件来切分的,每个 Chunk 需要包含完整的单个组件数据。


所以,还不能严格按照CHUNK_SIZE来切分,需要自定义切分规则。


在文档上找了一圈,也没有找到自定义切分规则相关的内容,于是就 debug 下源码,实践下来的解法如下:


修改app/api/chat/engine/settings.ts中的代码:


++ import { SentenceSplitter } from "llamaindex";

++ class CustomSentenceSplitter extends SentenceSplitter {
++   constructor(params?: any) {
++     super(params);
++   }

++   _splitText(text: string): string[] {
++     if (text === "") return [text];
++     const callbackManager = Settings.callbackManager;
++     callbackManager.dispatchEvent("chunking-start", {
++         text: [text]
++     });
++     const splits = text.split("\n\n------split------\n\n")
++     console.log("splits", splits)
++     return splits;
++   }
++ }

export const initSettings = async () => {
-- Settings.chunkSize = CHUNK_SIZE;
-- Settings.chunkOverlap = CHUNK_OVERLAP;
++ const nodeParser = new CustomSentenceSplitter();
++ Settings.nodeParser = nodeParser
}


我们通过继承SentenceSplitter新建了一个CustomSentenceSplitter类,然后重写了_splitText方法,将文本按照------split------来切分。


将 LlamaIndex 的 nodeParser 替换为我们新建的自定义CustomSentenceSplitter


接下来,我们将basic-components.csv转换为每个组件数据按照------split------来切分的 txt 文件。


安装papaparse


pnpm install papaparse


新建shell/formatCsvData.js,写入转换代码:


const Papa = require("papaparse");
const fs = require("fs");

// 读取 CSV 文件内容
fs.readFile("data/basic-components.csv", "utf8", (err, data) => {
  if (err) {
    console.error("Error reading the file:", err);
    return;
  }

  // 使用 Papa Parse 解析 CSV 数据
  const parsedData = Papa.parse(data, {
    delimiter: ",", // 默认分隔符为逗号,可根据需求修改
    header: false, // 如果第一行是表头,则设为 true
    skipEmptyLines: true, // 跳过空行
  });

  // 现在 parsedData.data 是一个数组,其中的每个元素代表 CSV 文件中的一行

  const txt = parsedData.data
    .slice(1)
    .map((row) => row.join(" "))
    .join("\n\n------split------\n\n");

  // 将处理后的数据写入新文件
  fs.writeFile("data/basic-components.txt", txt, (err) => {
    if (err) {
      console.error("Error writing the file:", err);
      return;
    }

    // 删除原始的 CSV 文件
    fs.unlink("data/basic-components.csv", (err) => {
      if (err) {
        console.error("Error deleting the file:", err);
        return;
      }
    });

    console.log("File has been written");
  });
});


执行转换代码:


node shell/formatCsvData.js


重新初始化向量数据:


pnpm run generate


效果展示


输入:生成一个table,包含姓名、年龄、性别 



查看引用的知识库数据,可以看到检索到的 Table 组件知识库数据是完整的。


完整源码


基于 LlamaIndex 的 RAG 应用的完整源码已经上传到 Github mian 分支,欢迎大家下载学习。


地址:https://github.com/enginner-lv/business-component-codegen/tree/main


别忘了顺手点个 star 收藏防失联哟~


进一步思考


RAG Retrieval 阶段采用 Embedding 和 Vector Database 的合理性


在我们新建的 FastGPT 应用(LlamaIndex 应用同理)中,我们来看 2 个示例:


示例 1: 输入:生成一个登陆页面



我们发现,召回的知识库数据是App,其实我们所预期的知识库数据是InputButtonForm等组件。


示例 2: 上传登陆界面的设计稿图,并生成代码。




同样的,召回的知识库数据并不是我们所预期的。


原因是什么?


本篇提到的 FastGPT 和 LlamaIndex 内部的 RAG Retrieval(检索)原理均采用 Embedding(嵌入)和 Vector Database(向量数据库)的方式,这种 Retrieval 的方式是基于语义相似度来进行。


因此,当用户提出的问题和知识库数据之间的语义相似度不高时,就会导致召回的知识库数据不准确。


上面的第一个示例中,用户提出的问题是生成一个登陆页面,而知识库数据中并没有登陆页面相关的知识数据,所以召回的数据不准确。


第二个示例中,用户上传的是登陆界面的设计稿图,而我们的私有组件库知识库是文本数据,两者的语义相似度很低,所以召回的数据也不准确。


那么,如何解决这个问题呢?


先看一下效果




如上,在 LV0 (https://lv0.chat) 中,用户可以提供需求或者上传设计稿图, AI 会针对需求或者设计稿图分析出来所依赖的私有组件数据。


思路其实很简单:


1、将私有组件库每个组件的使用场景给到 AI,让 AI 根据用户的需求或者设计稿 + 组件使用场景来分析出来所依赖的私有组件名称列表。


2、遍历私有组件名称列表,key value 的形式从知识库中检索出来完整的组件知识数据。


基于自然语言和设计稿图生成代码在研发标准规范中的应用


在研发标准规范中,大致的产品研发流程如下:


业务需求 -> 产品设计 -> UI UX 设计 -> 前后端研发 -> 测试 -> 上线


从流程上看,作为前端研发,最重要的信息输入是UI UX 设计,即 UI 设计稿。


因此基于 UI 设计稿设计稿图生成代码(D2C)在标准的研发流程提效中是非常有意义的。


如果你尝试了让 AI 基于设计稿图片来生成代码,会发现如果是一些很简单的设计稿,生成的效果还是不错的。


一旦设计稿复杂度提高,以现阶段 AI 的能力,生成代码的 UI 还原度是很难保证的。


因此,基于设计稿图片来生成代码,或许不是现阶段最佳的解决方案。


那有没有更好的解决方案呢?


如果你公司用的是 Figma 等设计工具,建议可以考虑基于设计稿的原始数据来生成代码。


基于设计稿的原始数据,可以提取出组件的位置大小颜色字体等信息,这些信息可以更好的帮助 AI 生成代码。


在这里,分享一款类似的 AI 工具:https://www.locofy.ai/


总结


如何基于公司私有组件库来生成代码?


  • 推荐:RAG:Retrieval(检索)- Augmented(增强)- Generation(生成)


  • 不推荐:Fine-tuning 微调


  • 不推荐:预训练自有模型


RAG 的内部原理是什么?


  • Retrieval 阶段:根据用户输入的问题,检索知识库数据,召回相似的知识库数据。


  • Augmented 阶段:将检索到的知识库数据与用户输入的问题组合到一起,作为输入给大模型。


  • Generation 阶段:大模型根据输入的内容生成代码。


构建 RAG 知识库的过程



RAG 检索运行的过程



FastGPT 和 LlamaIndex 的使用场景?


  • FastGPT 更适合快速构建 RAG 知识库应用,使用门槛低,无需编码,核心是要准备好知识库数据,然后导入到 FastGPT 中即可。


  • LlamaIndex 更适合定制化的 RAG 应用,需要一定的编码能力,但是更加灵活,可以根据自己的需求来定制化,比如自定义知识库的切分规则,定制全栈 AI 应用等。


参考资料


  • FastGPT:https://github.com/labring/FastGPT


  • LlamaIndex:https://ts.llamaindex.ai/getting_started/starter_tutorial/retrieval_augmented_generation


  • Vercel AI SDK RAG:https://sdk.vercel.ai/docs/guides/rag-chatbot#embedding


  • Embedding:https://jalammar.github.io/illustrated-word2vec


今天的分享就先到这~


文章来源“LV技术派”,作者“LV”


关键词: AI , 前端AI , 人工智能 , AI部署
AITNT资源拓展
根据文章内容,系统为您匹配了更有价值的资源信息。内容由AI生成,仅供参考
1
文件重命名

【开源免费】ai-renamer是一个用AI帮你做文件夹或者图片命名的项目。该项目会根据文件夹或者图片内容来为文件进行重新命名,让你的文件管理更加便利。

项目地址:https://github.com/ozgrozer/ai-renamer

2
AIExcel公式

【开源免费】smart-excel-ai是一个输入你想要的Excel公式的描述,即可帮你生成对应公式的AI项目

项目地址:https://github.com/weijunext/smart-excel-ai

在线使用:https://www.smartexcel.cc/(付费)

3
AI工作流

【开源免费】n8n是一个可以自定义工作流的AI项目,它提供了200个工作节点来帮助用户实现工作流的编排。

项目地址:https://github.com/n8n-io/n8n

在线使用:https://n8n.io/(付费)


【开源免费】DB-GPT是一个AI原生数据应用开发框架,它提供开发多模型管理(SMMF)、Text2SQL效果优化、RAG框架以及优化、Multi-Agents框架协作、AWEL(智能体工作流编排)等多种技术能力,让围绕数据库构建大模型应用更简单、更方便。

项目地址:https://github.com/eosphoros-ai/DB-GPT?tab=readme-ov-file



【开源免费】VectorVein是一个不需要任何编程基础,任何人都能用的AI工作流编辑工具。你可以将复杂的工作分解成多个步骤,并通过VectorVein固定并让AI依次完成。VectorVein是字节coze的平替产品。

项目地址:https://github.com/AndersonBY/vector-vein?tab=readme-ov-file

在线使用:https://vectorvein.ai/(付费)

4
知识库

【开源免费】FASTGPT是基于LLM的知识库开源项目,提供开箱即用的数据处理、模型调用等能力。整体功能和“Dify”“RAGFlow”项目类似。很多接入微信,飞书的AI项目都基于该项目二次开发。

项目地址:https://github.com/labring/FastGPT

5
RAG

【开源免费】graphrag是微软推出的RAG项目,与传统的通过 RAG 方法使用向量相似性作为搜索技术不同,GraphRAG是使用知识图谱在推理复杂信息时大幅提高问答性能。

项目地址:https://github.com/microsoft/graphrag

【开源免费】Dify是最早一批实现RAG,Agent,模型管理等一站式AI开发的工具平台,并且项目方一直持续维护。其中在任务编排方面相对领先对手,可以帮助研发实现像字节扣子那样的功能。

项目地址:https://github.com/langgenius/dify


【开源免费】RAGFlow是和Dify类似的开源项目,该项目在大文件解析方面做的更出色,拓展编排方面相对弱一些。

项目地址:https://github.com/infiniflow/ragflow/tree/main


【开源免费】phidata是一个可以实现将数据转化成向量存储,并通过AI实现RAG功能的项目

项目地址:https://github.com/phidatahq/phidata


【开源免费】TaskingAI 是一个提供RAG,Agent,大模型管理等AI项目开发的工具平台,比LangChain更强大的中间件AI平台工具。

项目地址:https://github.com/TaskingAI/TaskingAI

6
微调

【开源免费】XTuner 是一个高效、灵活、全能的轻量化大模型微调工具库。它帮助开发者提供一个简单易用的平台,可以对大语言模型(LLM)和多模态图文模型(VLM)进行预训练和轻量级微调。XTuner 支持多种微调算法,如 QLoRA、LoRA 和全量参数微调。

项目地址:https://github.com/InternLM/xtuner

7
prompt

【开源免费】LangGPT 是一个通过结构化和模板化的方法,编写高质量的AI提示词的开源项目。它可以让任何非专业的用户轻松创建高水平的提示词,进而高质量的帮助用户通过AI解决问题。

项目地址:https://github.com/langgptai/LangGPT/blob/main/README_zh.md

在线使用:https://kimi.moonshot.cn/kimiplus/conpg00t7lagbbsfqkq0