LoginSignup
9
0

More than 1 year has passed since last update.

Pipedreamのカスタムアクションを自作するまで(OpenAIによるChatGPT風アクション編)

Last updated at Posted at 2022-12-17

前書き

まずはこちらの画像と映像を御覧ください。

Slack(画像) Discord(映像)
pipedream-openai-slack.png

※Discord(映像)はYouTubeショートに飛びます

これらは、いずれもPipedreamのワークフローで作った、「質問に対してOpenAIからの回答をレスポンスする」様子です。
今回のアドカレでは、「3部構成(仮)」と称してPipedreamをネタにずっと記事を書いていましたが、今回は自作アクションによる処理の再利用に焦点を当てていこうと思います。

作ったものについて

コード

このGitHubリポジトリにあるopenai/actions/chatgpt.mjsです。
現時点だと、このコードを記事の手順に従って自分のアカウントでpd publishすると、私と同じようにOpenAIと戯れる事ができます。1

OpenAI APIを使って、ChatGPT「風」リアクションを返すためのワークフロー

workflows.png

このような2個のワークフローを作っています。
それぞれ、次のような処理になっています。

  1. プラットフォーム上の新規メッセージを検知する。
  2. 処理対象化を判定する。
    • 対象外ならここで処理を終了する。
  3. OpenAI APIへポストして、結果を受け取る。
    • Slack版のほうがステップが多いが、これはSlack側ではprefix制2にしており、除去する必要があるため。
  4. 結果をプラットフォームに返信する。

Pipedreamにおけるアクションの自作処理おさらい

最初は「Nodeアクション」として内部の処理を直接実装する形式にしていました。

自作処理の中身

action.mjs
import fetch from "node-fetch";

// To use previous step data, pass the `steps` object to the run() function
export default defineComponent({
  async run({ steps, $ }) {
    // Return data to use it in future steps
    const apiUrl = "https://api.openai.com/v1/completions";
    const payload = {
      model: "text-davinci-003",
      prompt: steps.trigger.event.text.substring(9),
      max_tokens: 3000,
      temperature: 0,
    };
    const options = {
      method: "post",
      body: JSON.stringify(payload),
      headers: {
        "content-type": "application/json",
        "authorization": `Bearer ${process.env.OPENAI_APIKEY}` 
      }
    };
    const result = await fetch(apiUrl, options).then(res => res.json());
    console.log(result.choices[0].text.trim());
    return {
      raw: result,
      text: result.choices[0].text.trim(),
    }
  },
})

基本的にはOpenAIが提供しているCompletions APIに所定の形式でPOSTリクエストを投げるだけで、処理の大半をここに費やしています。3 4 API Keyについてはアカウントの作成後に簡単に作成することができるので割愛します。

console.logreturn内のtextで、レスポンスのオブジェクトからピンポイントで値を取ってきているのは、ここが「APIによって生成されたテキスト」=「回答」となっているためです。
また、今回のアクションでは明確に回答に相当する部分のみを必要としているため、APIの結果全体をrawキーにセットするのとは別にtextキーに回答部分のみセットし、後続処理に引き渡しています。

なお、PipedreamのNodeランタイムの関係で、node-fetchが必要となっています。importで宣言しておくだけで、自動的にダウンロードとインポートを実施してくれます。5

Slackワークフローだけでなく、Discordワークフローでも使いたい

最初のSlack用ワークフローは上記の通り、自作処理で実装しました。
この後にDiscord用ワークフローを作るのですが、最初に考える方法が「Nodeアクションのコードをそのままコピペする」です。
しかし、色々と面倒なのもあって「コピペ以外で使い回せないか?」という発想が出てきます。

その場合、「一つのアクションとして登録する」という手段を取ることが出来ます。

自作処理から自作アクションへ

ガイドがきちんと用意されています。より詳細を追いたい場合は、ドキュメントを参照しましょう。

まずはCLIの入手と認証を

アクションを後ほど登録するために、PipedreamのCLIコマンドを入手します。
各種OS用のバイナリが用意されているので、下記URLのガイドに従ってインストールしましょう。

Linux向けもシングルバイナリで提供されているため、セットアップは非常に簡単です。

入手後はpd loginを実行することで、ブラウザの起動とブラウザ上での認証が実行されます。
すでにアカウントがあれば連携画面が出るので、そのまま進みましょう。問題なく進めば、以降はpdコマンドでの各種操作が可能になります。

アクションとしての定義をする

Pipedreamのカスタムアクションは「必要な属性を持つESModule」となっています。アクションとしての要素のみを抜き出すと以下のようになります。

action.js
export default {
  key: "openai-shortcut-chatgpt",
  name: "Completions text by OpenAI",
  description: "For creation ChatGPT like answer by OpenAI API",
  version: "0.1.0",
  type: "action",
  props: {
    // TODO: 定義しましょう
  },
  async run() {
    // TODO: 実装しましょう
    return {};
  },};

簡単な説明

名前 役割
key string 識別子
name string アクション選択時の名前
description string アクション選択時の説明
version versioning-string アクションのバージョン
type "action" コンポーネントの種別
props object アクションの引数
run async function アクションの実処理

極端な話、これらが定義・実装されており、全てがexportされていればアクションになります。

とはいえ、これだと独立して何かを実行するだけになってしまうので、実際の処理を移植していきましょう。
重要なのはpropsrunです。

props

propsはアクションの引数です。ここで定義した値のみを用いて実際の処理が動くと考えればよいでしょう。
例えば、今回の処理では「API Key」を使って「質問となる文」をPOSTするため、2項目を必要とします。

export default {
  props: {
    apiKey: {
      type: "string",
      label: "API Key",
      secret: true,
    },
    text: {
      type: "string",
      label: "Target text",
    },
  },
  // 略
}

Pipedreamの画面上ではlabelのが表示され、typeに応じて入力の受付を制御してくれます。(後述)
また、run内の内部処理上ではthis.textのようなpropsのキーがそのままプロパティになります。

run

アクション内の自作と基本的に同じです。ただし、汎用的な処理にする必要があるため、stepsは使えずに前述の通りpropsのみを使用していきます。
細かい制御をさせたい場合は、propsの中身を充実させましょう。

actions.mjs
export default {
  async run({ $ }) {
    const apiUrl = "https://api.openai.com/v1/completions";
    const payload = {
      model: "text-davinci-003",
-     prompt: this.text,
+     prompt: steps.trigger.event.text.substring(9),

      max_tokens: 4000,
      temperature: 0,
    };
    const options = {
      method: "post",
      body: JSON.stringify(payload),
      headers: {
        "content-type": "application/json",
-       "authorization": `Bearer ${process.env.OPENAI_APIKEY}` 
+       "authorization": `Bearer ${this.apiKey}`,
      },
    };

    const result = await fetch(apiUrl, options).then((res) => res.json());
+   $.export("$summary", "Successfully api");
    return {
      raw: result,
      text: result.choices[0].text.trim(),
    };
  },
};

これが実装上の差分です。基本的にOpenAIのAPIに引き渡す情報の出どころが変わっただけとなっています。7

テストする?

このままアップロードしてワークフローでトライ&エラーでもいいのですが、本当はテストがあると良いかもしれません。
ただし、この手の処理は単機能故にシンプルなので、ローカルで動作させるドライバーアプリだけで事足りることも多いです。

今回は、リポジトリ内でopenai/demo.mjsという簡易デモを用意して、実装を直すたびに正しく動くかを簡易的に確認する手法を取りました。

アップロードする

認証済みCLIでpublishコマンドを実行するだけです。

pd publish action.mjs

注意点として、アクションのプロパティにあるkeyversionの組み合わせがユニークになっている必要があります。
そのため、再度アップロードする際は、かならずversionも更新しましょう。

実際に組み込んでみる

アップロード完了後にワークフロービルダーでアクションを追加しようとすると、「My Actions」の中に、アップロード済みアクションがname,descriptionを表示する形式で一覧化されます。

list-myactions.png

利用予定のアクションをクリックすると、先程propsで定義したパラメーターを渡せるようになっています。

openai-action.png

これで、自作のアクションを使い回せるようになりました。

後書き

ここまでで、自分のアカウント内でちょっと複雑な自作アクションを使い回せるようになりました。
このフェーズの先には、Pipedreamの公開リポジトリに提供することでの「公式アクション化」というステージが存在します。

いつか「この(他人の)ワークフローはワシが育てた」的なことを言ってみたいものですね。

最後に

OpenAIの回答は、その性質から必ずしも正しい内容となっている保証は出来ません。
用法用量を守って、適切に試しましょう。

  1. 記事を書いている間に細かい調整がはいっており、実際は記事のコードと違っています。

  2. 最初にある画像の通り、chatgpt: (質問文)という投稿に対してのみ反応する実装です。

  3. ChatGPTのCLI用Golang実装である、kkdai/chatgptの中身を参照しました。6

  4. ので、厳密にはタグとして載せているChatGPTと全く同じ動きで回答を作っているかまでは分かりません。

  5. 忘れてたのですが、@pipedream/axiosで十分でした。

  6. Node系だとchatgpt-apiというより本来の動きに近い実装があります。ただしPipedream上だと動きません。

  7. $.exportについては別の自作アクションからの流用で、無くてもおそらく困りません。

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0