天天看點

使用 Open AI 建構用于文本生成的 Strapi 自定義字段

作者:豆豆媽超厲害
使用 Open AI 建構用于文本生成的 Strapi 自定義字段

在本文中,我将教您如何使用 AI 在 Strapi 中生成内容。 Strapi 現在本身不允許您利用 AI 來建立内容,是以我們将使用自定義字段。 事實上,我們會自己從頭開始建構一個。 本文的第二個目标是解釋如何建立他們的 Strapi 自定義字段。

Strapi 自定義字段

Strapi 配備了幾個字段。 從内容建立者的角度來看,這些字段描述了您将輸入的内容類型。 它們的範圍從富文本到 JSON 等等。 您可以在我們的使用者文檔中閱讀有關字段的更多資訊。

自定義字段,顧名思義,讓我們在 Strapi 附帶的預設字段之外定義字段,為我們提供了一種與各種第三方工具內建的很酷的方式。 今天對我們來說,這個工具将是 Open AI。

我們将建造什麼

想象一下在文本字段中鍵入文本。 釋出後,我們會在我們想要發送内容的任何地方顯示輸入文本。 同樣,在 AI 領域,我們會分擔大部分寫作工作,讓 AI 完成繁重的工作,而我們校對它是因為……計算機很笨。

在這一點上,我們對要建構的内容有一個非常可靠的想法。 如果您想在我們學習本教程時參考完成的項目詳細資訊,請參閱此處。

推文解釋器

GitHub 回購

npm 包

建立自定義字段

讓我們首先使用以下指令在我們的終端中建立一個全新的 Strapi 項目:

Npx create-strapi-app@latest plugin-dev —quickstart
           

一旦我們的項目啟動并運作,建立一個管理者使用者并登入到您的管理面闆。 接下來,我們需要建立一個插件,因為自定義字段與 Strapi 的插件 API 一起工作。

在您的終端中,導航到您的項目路徑并輸入以下指令

npm run strapi generate
           

從選項清單中選擇“插件”并将其命名為 ai-text-generation。 命名插件後,我們需要通過将其添加到 ./config/plugins.js 中的 Strapi 插件配置檔案來啟用它。

如果此檔案尚不存在,您可以建立它。

module.exports = {
      // ...
    'ai-text-generation': {
      enabled: true,
      resolve: './src/plugins/ai-text-generation'


    },
//...

    }

           

當我們重新啟動伺服器時,我們應該會在 Strapi 管理面闆中看到我們的插件。 我們将以監視模式重新開機伺服器,這樣我們就不必在每次更改管理代碼時都重新建構管理應用程式。 為此,請在您的終端中輸入以下指令:

Npm run develop –watch-admin
           

在我們可以嘗試我們的自定義字段之前,我們必須做幾件事。 首先,我們需要在我們的伺服器上注冊它。 轉到 ./src/plugins/ai-text-generation/server/register.js 并粘貼以下代碼。

'use strict';

module.exports = ({ strapi }) => {
  strapi.customFields.register({
    name: 'text-ai',
    plugin: 'ai-text-generation',
    type: 'string',
  });
};

           

其次,我們需要在管理面闆中注冊我們的自定義字段。 為此,轉到 ./src/plugins/ai-text-generation/admin/src/index.js 并粘貼以下代碼:

import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import PluginIcon from './components/PluginIcon';


const name = pluginPkg.strapi.name;

export default { 
  register(app) {

    app.customFields.register({
      name: "text-ai",
      pluginId: "ai-text-generation", 
      type: "string", 
      intlLabel: {
        id: "ai-text-generation.text-ai.label",
        defaultMessage: "Text AI",
      },
      intlDescription: {
        id: "ai-text-generation.text-ai.description",
        defaultMessage: "Let AI do your writing!",
      },
      icon: PluginIcon,
      components: {
        Input: async () => import(/* webpackChunkName: "input-component" */ "./components/Input"),
      },
      options: {
      },
    });
  },



  bootstrap(app) {},
  async registerTrads({ locales }) {
    const importedTrads = await Promise.all(
      locales.map((locale) => {
        return import(
          /* webpackChunkName: "translation-[request]" */ `./translations/${locale}.json`
        )
          .then(({ default: data }) => {
            return {
              data: prefixPluginTranslations(data, pluginId),
              locale,
            };
          })
          .catch(() => {
            return {
              data: {},
              locale,
            };
          });
      })
    );

    return Promise.resolve(importedTrads);
  },
};

           

如果您的自定義字段之後中斷,請不要擔心。 在注冊我們的自定義字段時,我們為自定義字段做了三件基本的事情:

我們定義 type: "string" - 它告訴我們自定義字段将儲存什麼類型的資料。 根據您正在建構的内容,您可能想要更改此設定。 對于我們今天的用例,我們将使用類型:“字元串”。

我們在 ./components/Input 中為我們的字段配置設定一個 Input 元件。 所有自定義字段都需要一個管理者使用者可以在内容管理器中通路的輸入元件。 我們可以根據需要使用 Strapi 設計系統自由設計輸入元件,但必須傳回我們存儲在資料庫中的單個值。 我們将在下一節中建立輸入元件時看到更多内容。

我們用圖示定義了一個插件圖示:PluginIcon,這有助于我們識别内容類型生成器中的自定義字段。

建立輸入元件

如上所述,所有自定義字段都需要一個輸入元件。 在 ./src/plugins/ai-text-generation/admin/src/components/ 中,建立一個名為 Input 的檔案夾。 在您的 Input 檔案夾中,建立一個名為 index.js 的檔案并将以下代碼粘貼到其中:

import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import { TextInput } from '@strapi/design-system/TextInput';
import { Stack } from '@strapi/design-system/Stack';
import { Button } from '@strapi/design-system/Button';
import { Textarea } from '@strapi/design-system';
import { auth } from '@strapi/helper-plugin'


export default function Index({
  name,
  error,
  description,
  onChange,
  value,
  intlLabel,
  attribute,
}) {
  const { formatMessage } = useIntl();
  const [prompt, setPrompt] = useState('');
  const [err, setErr] = useState(''); 

  const generateText = async () => {
    try {
      const response = await fetch(`/ai-text-generation/generate-text`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${auth.getToken()}` 
        },
        body: JSON.stringify({
          'model': 'text-davinci-001',
          'prompt': `${prompt}`,
          'temperature': 0.4,
          'max_tokens': 64,
          'top_p': 1,
          'frequency_penalty': 0,
          'presence_penalty': 0
        })
      });

      if (!response.ok) {
        throw new Error(`Error! status: ${response.status}`);
      }

      const result = await response.json();
      const parsedResult = result.choices[0].text.replace(/(?:\r\n|\r|\n)/g, '');

      onChange({ target: { name, value: parsedResult, type: attribute.type } })
    } catch (err) {
      setErr(err.message);
    }
  }

  const clearGeneratedText = async () => {
    onChange({ target: { name, value: '', type: attribute.type } })

  }

  return (
    <Stack spacing={1}>
      <TextInput
        placeholder="Please write a prompt for content to generate"
        label="Prompt"
        name="Prompt"
        onChange={(e) => setPrompt(e.target.value)}
        value={prompt}
      />
      <Stack padding={4} spacing={2}>
        <Textarea
          placeholder="Generated text"
          label="Content"
          name="content"
          onChange={(e) =>
            onChange({
              target: { name, value: e.target.value, type: attribute.type },
            })
          }
        >
          {value}
        </Textarea>
        <Stack horizontal spacing={4}>
          <Button onClick={() => generateText()}>Generate</Button>
          <Button onClick={() => clearGeneratedText()}>Clear</Button>
        </Stack>
      </Stack>
    </Stack>
  )
}
           

這裡發生了幾件事。 我們将檢視主要元素的作用以更好地了解它。

<TextInput /> :充當我們的提示輸入; 我們使用 onChange() ‘watcher’ 在我們輸入資料時更新提示狀态的值。 然後我們将提示存儲在一個名為 prompt 的變量中。 TextArea />:是我們用來存儲我們生成的文本的值。 我們使用 <TextArea /> 來編輯生成的文本。 我們使用 onChange() 觀察器的特殊實作,它允許我們傳遞一個具有多個值的對象,以便在該元件發生更改時進行更新。

我們還有兩個按鈕,所有的魔法都在這裡發生。 一鍵調用 generateText() - 此函數使用 fetch 調用我們的後端服務,/ai-text-generation/generate-text 對 Open AI API 進行經過身份驗證的調用(我們将在接下來建立它)。 為了驗證這一點,我們使用來自 @strapi/helper-plugin 的 auth,它幫助我們向我們将使用 Admins 令牌建立的 Strapi 服務發出安全請求。

然後我們解析我們的結果并使用 onChange() 的特殊實作将解析後的響應作為内容傳遞,這些内容将通過 onChange({ target: { name, value: parsedResult, type: attribute.type } }) 添加到我們的資料庫中 .

另一個按鈕調用 clearGeneratedText() - 這個函數會清除 AI 生成的所有文本,以防我們想要重新開始。 此時,我們的應用程式應該看起來像這樣。

使用 Open AI 建構用于文本生成的 Strapi 自定義字段

在 Strapi 中建構自定義開放式 AI 路由

要通路 Open AI,我們需要提供一個 API KEY; 在管理者中擁有這個密鑰會帶來安全風險,為了解決這個問題,我們将建立一個自定義路由,當調用該路由時,它會對 Open AIs API 進行 API 調用并傳回生成的文本,然後我們可以将其發送給管理者。

是以開始,在我們的 ./config/plugins.js 檔案中添加一個配置對象 below enabled: true,你的配置檔案應該如下所示:

// …
'ai-text-generation': {
      enabled: true,
      config: {
        apiToken: process.env.OPEN_AI_API_TOKEN,
      },
      resolve: './src/plugins/ai-text-generation'
    },

           

現在,在 ./src/plugins/ai-text-generation/server/services 中建立一個名為 open-ai.js 的檔案,并将以下代碼粘貼到其中。

為了使代碼正常工作,您還必須安裝 axios,導航到您的插件根目錄并鍵入 yarn add axios

'use strict';

const axios = require('axios');

module.exports = ({ strapi }) => ({

  async generateText(prompt) {
    try {
      const response = await axios(
        {
          url: 'https://api.openai.com/v1/completions',
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${strapi.plugin('ai-text-generation').config('apiToken')}`
          },
          data: JSON.stringify({
            'model': 'text-davinci-001',
            'prompt': `${prompt}`,
            'temperature': 0.4,
            'max_tokens': 64,
            'top_p': 1,
            'frequency_penalty': 0,
            'presence_penalty': 0
          })
        })

      return response.data;
    }
    catch (err) {
      console.log(err.response)
    }

  }

});

           

這裡我們定義了一個函數 generateText() ,它将提示作為參數。 然後,我們在請求 Open AI 時使用提示作為參數。

接下來,在同一個服務檔案夾中,将以下代碼粘貼到您的 index.js 檔案中。

'use strict';

const openAi = require('./open-ai');

module.exports = {
  openAi,
};

           

在我們這樣做之前,這段代碼在 Strapi 中注冊了一個我們從控制器調用的服務。 我們需要建立一個控制器。 在 ./src/plugins/ai-text-generation/server/controllers 中,建立一個名為 ai-controller.js 的檔案并将以下代碼粘貼到其中:

'use strict';

module.exports = ({ strapi }) => ({
  async generate(ctx) {
    ctx.body = await strapi
      .plugin('ai-text-generation')
      .service('openAi')
      .generateText(ctx.request.body.prompt);
  },
});

           

在這個檔案中,我們調用我們在剛剛建立的服務中定義的 generateText() 函數并傳遞 ctx.request.body.prompt(來自我們的 POST 請求)。

在同一個檔案夾中,我們需要将以下代碼粘貼到我們的 index.js 檔案中:

'use strict';

const aiController = require('./ai-controller');

module.exports = {
  aiController,
};

           

最後,我們必須建立一個路由來通路我們剛剛建立的控制器。 在 ./src/plugins/ai-text-generation/server/routes 我們需要粘貼以下代碼:

module.exports = [
  {
    method: 'POST',
    path: '/generate-text',
    handler: 'aiController.generate',
    config: {
      policies: [],
    },
  },
];

           

在這個檔案中,我們定義了端點的行為方式。 使用此代碼,我們在 /generate-text URI 上建立一個 POST 方法,并傳遞我們建立的 aiController 控制器來定義其行為。

在您首選的 API 用戶端中,您現在可以向 localhost:1337/ai-text-generation/generate-text 送出請求,您應該會得到 AI 生成的文本作為響應。 請務必将您的提示添加為參數,并通過您的管理者 JWT 進行身份驗證。

當我們輸入提示并在我們的自定義字段中單擊“生成”時,我們也應該得到響應。

使用 Open AI 建構用于文本生成的 Strapi 自定義字段

釋出到 npm

來自 Strapi 團隊的 Maxime 寫了一篇關于如何将你的插件釋出到 npm 的精彩文章,如果你有任何問題,你可以參考我的 package.json 檔案來了解如何建構你的插件。

您還可以将您的自定義字段提升到一個新的水準,并将其送出到官方 Strapi 市場。

希望您現在對如何建構自定義字段有了更好的了解,更好的是,您擁有一個使用 Open AI 為您生成内容的自定義字段。

繼續閱讀