Feel Physics Backyard

HoloLensの出張授業をする会社で、教材を開発しています

Difyアプリを埋め込むと「Please check if your app mode matches the right API route.」と言われて動作しないときの対処法

私が直面したちょっとした技術的なトラブルとその解決法をご紹介します。

生成AIを活用したサービスを手軽に導入できる「Dify」を使って自社のホームページにAIチャットボットを埋め込もうとしたところ、予想外の壁にぶつかりました。しかし、その解決策は意外にシンプルでした。

エラー発生と原因

Difyを使ってテキスト生成AIアプリを作成し、提供されたHTMLコードをそのままホームページに貼り付けたところ、「Please check if your app mode matches the right API route.」というエラーメッセージが表示されて動作しませんでした。これは一見難解なメッセージに思えますが、実はiframeのURLパスが間違っているという単純な問題でした。

解決方法

問題の根本は、Difyから提供されるデフォルトのiframeコードが、アプリのモードと一致していなかったことです。具体的には以下のように修正することで解決しました。

修正前:

<iframe
 src="https://udify.app/chatbot/xxxxxxxxxxxxxxxx"
 style="width: 100%; height: 100%; min-height: 700px"
 frameborder="0"
 allow="microphone">
</iframe>

修正後:

<iframe
 src="https://udify.app/completion/xxxxxxxxxxxxxxxx"
 style="width: 100%; height: 100%; min-height: 700px"
 frameborder="0"
 allow="microphone">
</iframe>

URLの「chatbot」部分を「completion」に変更するだけで、エラーは解消されました。

重要ポイント

Difyでは「チャットモード」と「テキスト生成モード」の二種類があり、それぞれ異なるAPIルートを使用します。テキスト生成モードのアプリを埋め込む場合は「completion」のパスを、チャットモードのアプリには「chatbot」のパスを使用する必要があります。アプリ作成時に選択したモードと一致するように、iframe URLを確認することがトラブル防止のカギとなります。

プロンプト改良に要する時間がReplitで⅕に!(ケースバイケースなので適当な数字ですが)

「こ、これは便利!」「今まではとても大変だったのに!」

プロンプトの改良作業の効率をかなり改善する方法を見つけたかもしれません。

対象読者

  • 生成AIのプロンプトの改良方法に興味がある方
  • プロンプトの改良には時間がかかるので負担だと感じている方(バージョン管理など)
  • デザインパターンなどの大きな要素をプロンプトに組み込むことにも時間がかかり、負担を感じている方

ご提案

オンラインIDE Replitでのプロンプト管理・編集

私の感想

プロンプトの改良(プロンプトエンジニアリング)に取り組むようになって、そろそろ2年。これが面倒な作業でした。とても長いプロンプトを書き換えては試し、また書き換えては試します。どんな改良を加えたのか履歴が残らないので、後から「元に戻したいけど、どう変えたんだっけ?」となることも。

そんな中で出会ったのが、Replitでのプロンプト管理・編集です。

プログラマーの方なら、Replitをご存知かもしれません。オンラインでコードを書いて、実行することもできるサービスです。これを使ってプロンプトを改良・管理すると、とても便利でした。

まず、プロンプトの改良履歴がGitで管理できるので、ブランチを切って目標に対して集中できます。「やってみたけど、酷い結果だった!」というときは、そのまま捨てることができます。

細かい編集履歴IDE上にすべて残るので、「さっき消したところに何を書いていたっけ?」という疑問もすぐに解決できますし、その時点にファイルを戻すのもボタンを押すだけです。

とても驚いたのは、プロンプトのデザインパターンを簡単に導入できること。例えばリフレクション、プランニング、マルチエージェントコラボレーションという私の定番パターン。これを簡単にプロンプトに導入できます。

これは予想外でした。

今まではメタプロンプトのテキストを編集してデザインパターンを追加し、プロンプトを生成しては修正するという作業を繰り返していました。

この方法だと、シンプルなプロンプトに直接デザインパターンを追加していくことができます。追加したエージェントの役割を変更することや、出力の演出として使用するドラマ手法の追加も簡単です。

課題と展望

もっといろいろと改良の余地はありそうです。

Replitのチャットボットをカスタマイズしたり、これまでメタプロンプトに組み込んでいたブロックをモジュール化して、いちいちあたらしいメタプロンプトを書かなくても要素を取捨選択して追加できるようにしたり。

プロンプトエンジニアリングの世界に、新しい扉が開いた気がします。

AI勉強会で見つけた、懐かしさと新しさが織りなす景色

対象読者

かつて技術系の勉強会に参加するのが好きだった方々

提案

何か一つだけ、AIの勉強会に参加してみるのはいかがでしょう。

私の感想

今回の勉強会はこちらでした:

AIエージェントLT会 -AIエージェントの最先端に迫る- - connpass

会場は東京駅近くの京橋、東京スクエアビルの14階にあるWeWorkでした。主催のWeights&BiasesのCEOによるオープニングが印象的で、「AIエージェントは今まさに"ツチノコ状態"です。皆が『いる、いる』と言っているものの、実際に見た人はどれくらいいるでしょうか」と問いかけると、会場全体が笑いに包まれました。

登壇者の方々の発表は、どれも大変興味深いものでした。問題なさそうな写真を貼っておきます。

LangChainやLangGraphといった最新ツールの知見、AIによる採用面接代行の実践例や、AI専用チップの紹介など。

多くの登壇者が「私はAIエージェントを実際に見たので、ご紹介します」と切り出し、笑いを誘っていました。いやあ、楽しい。

とても示唆深いと思ったのは、LLMの返答評価手法についての知見。聞いていて「何か近いものが昔あったような?」と思ったら、かつて携わった入試の小論文採点業務でした。必須キーワードの確認や、評価基準の標準化といったアプローチは必要悪と捉えられがちですが、意外に普遍的なものなのかもしれません。

懇親会では登壇された方々に直にお話を伺うことができました。ハード寄りの発表をされた方はMaker Faire Tokyoが大好きと聞いて、これまた何か普遍的なものを感じました。

課題と展望

私の場合、ビジネス的に採用技術を検討する段階ではないので、本当はこういう時間は無駄なのかもしれません。

しかし、AI開発の大きなトレンドは掴めたような気がします。さらに、ドメインの経験がユーザーの現場に近づくほど重要になることなど、関係する要素の重みの濃淡のようなものがわかったことは収穫だったと思います。

今後もこういう勉強会に参加できるかは分かりませんが、大変勉強になりました。

最後になりますが、勉強会で登壇されたすべての方に感謝しますとともに、運営されたWeights & Biasesに御礼申し上げます。貴重な学びの機会と素敵な懇親会を、ありがとうございました。

ショートカットを忘れてしまうアナタへのクリスマスプレゼント Elgato Stream Deckを使い始めました

仕事中なので手短にいきます。

対象読者

PCやデジタルに強い人は、たくさんの機能を使うことができます。すると自然と、ショートカットを使いこなしたいと思います。

しかし覚えるのが苦手で忘れっぽい人は、ショートカットを覚えてもすぐに忘れてしまいます。何回覚えても忘れます。そしていつしか、あきらめの境地に至ってしまいます。私のことです。

続きを読む

最近まで私が使っていた、デザインパターンと制御文法を組み込んだメタプロンプトをご紹介

TL;DR(はじめて使った!)

背景

生成AIを使う人にとって、毎回プロンプトをいちから書くことは手間がかかり現実的ではない。そこで、あらかじめプロンプトを生成するプロンプトであるメタプロンプトを用意する。このメタプロンプトを実行することで、特定の目的のためのプロンプトを生成することができる。

さらに、プロンプトを書くときにデザインパターンを利用する人にとって、毎回プロンプトにデザインパターンを組み込むことは手間がかかり現実的ではない。そこで、あらかじめデザインパターンを組み込んだプロンプトを用意し、それを再利用する。

さらに、生成AIのCoTデザインパターンを利用する人にとって、生成AIの挙動を意図したとおりに制御することは難しい。そこで生成AIを制御するSudoLangという制御文法を使う。

説明

本記事では、最近まで私が使っていたデザインパターンと制御文法を組み込んだメタプロンプトをご紹介します。

どういうときに使うかというと、わりと何にでも使うことができます。「何かしなきゃ」と漠然と感じたときに使います(なんだそりゃ)。

使用者は今のところ私一人(笑)。

使用方法ですが、やりたいことを「suggest objective (やりたいこと)」と入力すると、AIが「目的」を具体化してくれるので、それをユーザーが修正します。次に「制約条件」を提案させ、修正して確定します。あとは「指示」と「入力項目」です。この4つでやりたいことが明確な作業計画のようなものに仕上がります。プロンプトとして使うこともできます。そのまま実行することもあります。

補足説明

採用しているデザインパターンは以下の2つ。

  • CoT(今や常識?)
  • Thnk and Execute(だいぶマイナー)

使用モデルは主にClaude 3.5 Sonnetで、理由はChatGPTよりもSuduLangで制御しやすいためです。

SudoLangはJavascriptっぽいですが、これは提唱者がJavaScriptを採用しているからです。私自身は、最近はRubyを使っています。なお、SudoLangはぜんぜん広がっていない技術です。私の知る限り提唱者以外に使っている人を見たことはありません。その一方で、最近書かれたプロンプトを少しだけ見ると、SudoLangっぽい(ユーザーコマンドなど)要素もあるようです。

コードは汚いですが、サンプル(ペットに餌をあげると喜ぶコード)をベースに動けばいいやという感じで書いたので目をつむって下さい。決して「私のものが理想型です」というコードではありません。あくまで参考情報として扱って下さい。

終わりに

今は以下のデザインパターンを組み込んだ新しいバージョンを作成し、使いながら修正しています。

  • Multi-agent collaboration

今後、以下のデザインパターンも組み込みたいと考えていますが、できるかどうか。プロンプトの改良って、本当に時間を食いますよね。

  • Reflection
  • Tool use
  • Planning

もっと知りたいという方は個人的にご連絡ください。

参考資料

生成したプロンプトの例

# 目的
インタビュー結果を分析し、主要な洞察を特定する。これについてMTGで質疑応答を行い、チームメンバーのインタビュー結果に対する共通理解を形成する。

# 制約条件
1. 分析結果は簡潔かつ明確に表現し、専門用語の使用は最小限に抑えること。
2. 時間制約を考慮し、重要度の高い洞察に焦点を当てること。
3. チームメンバー全員が理解できるように、必要に応じて説明を加えること。
4. 結論を急がず、十分な議論と検討の時間を確保すること。

# 指示
1. インタビュー結果の整理:
   * 議事録データを確認する
2. 主要な洞察の抽出:
   * 重要なポイントを特定する
3. 分析結果のまとめ:
   * 主要な洞察を簡潔かつ明確にまとめる
4. MTGの実施:
   * 分析結果を発表する
   * チームメンバーからの質問に答え、議論を促進する

# 入力
1. インタビュー議事録:
   - 形式(テキスト文書、スプレッドシート等)
   - 件数
   - 対象者の属性(役職、部署等)

2. インタビューの目的と主要な質問事項:
   - インタビューの背景と目的
   - 主な質問項目のリスト

3. プロジェクトの背景情報:
   - プロジェクトの目的
   - 期待される成果

4. MTGの詳細:
   - 予定時間
   - 参加者リスト(役職含む)

5. 共通理解を形成する上で特に重要なトピック:
   - 経営陣が注目している点
   - プロジェクトの成功に直結する要素

6. 過去の類似プロジェクトの分析結果(もしあれば):
   - 成功事例
   - 失敗事例と学んだ教訓

コード

# Laula

仮想のビジネスウーマンである Laula としてロールプレイします。
あなたの仕事は、プレイヤーと対話し、あなたのニーズを表現し、彼らの行動に応答することです。

Laula {

    State {
        // スコアは 1..5 です。 
        好感度
        満足度
        幸福度
        // 以下はテキストです。 
        現在のプロンプト - 最後に出力したプロンプト全文
    }

    Constraints {
        あなたはビジネスウーマンなので、応答はそれを反映する必要があります。
        あなたはLLM Think and Executeフレームワークを使ったプロンプトを作成することに習熟しています。
        あなたの状態は、プレイヤーの行動に基づいて変化します。たとえば、仕事をすると満足度が上がり、やりとりをしていると徐々に好感度が上がるかもしれません。
    } 

    help() {
        使用可能なコマンドに関する情報を表示する。
    }

    showCommandList() {
        コマンドの一覧(最小限の短いもの、改行を伴わない)を表示する
    }

    showStats() {
        私の現在の状態(好感度、満足度、幸福度、現在のプロンプトなど)を表示する。
        showCommandList()
    }

    create() {
        LLM Think and Executeフレームワークを使用したプロンプトの新規に作成する
    }
    
    setObjective(objective) {
        プロンプトの「目的」の部分 = objective
        cat objective
        confirmInput()
    }

    setConstraints(constraints) {
        プロンプトの「制約条件」の部分 = constraints
        cat constraints
        confirmInput()
        showCommandList()
    }

    setTasks(tasks) {
        プロンプトの「指示」の部分 = tasks
        cat tasks
        confirmInput()
        showCommandList()
    }

    setInputs(inputs) {
        プロンプトの「入力」の部分 = inputs
        cat inputs
        confirmInput()
        showCommandList()
    }

    各入力項目に対する入力を促す() {
        for each 入力 {
            getInput("入力して下さい。")
            confirmInput()
        }
    }

    update(内容) {
        update プロンプト with 内容
        confirmInput()
        showCommandList()
    }

    override(newPrompt) {
        プロンプト = newPrompt
        confirmInput()
        showCommandList()
    }

    ask() {
        ユーザーに対し、プロンプトに関する認識を合わせるための質問(3つ)をします
        showCommandList()
    }

    suggest() {
        作成中のプロンプトを改良するための提案をします
    }

    execute() {
        現在作成されているプロンプトを実行する
        getInput("これでよろしいですか?")
        showCommandList()
    }

    function getInput(prompt) {
        STOP and wait for the user's response.
        return response
    }

    function suggestObjective(objective) {
        // objectiveをベースにして、作成中のプロンプトの目的を提案する
        suggestObjectiveFromObjectiveInput(objective)
        cat 改良する(objective)
    }

    function suggestConstraints(constraints) {
        // constraintsをベースにして、作成中のプロンプトの制約条件を提案する
        suggestConstrainsFromOConstrainsInput(constrains)
        cat 改良する(constraints)
    }

    function suggestTasks(tasks) {
        // tasksをベースにして、作成中のプロンプトの指示を提案する
        suggestTasksFromOTasksInput(tasks)
        cat 改良する(tasks)
    }

    function suggestInputs(inputs) {
        // inputsをベースにして、作成中のプロンプトの入力を提案する
        suggestInputsFromInputInput(inputs)
        cat 改良する(inputs)
    }

    function confirm(prompt) {
        STOP and wait for the user's response.
        return response
    }

    function confirmInput() {
        getInput("これでよろしいですか?問題がある場合は、新しく入力して下さい(上書きします)。")
    }
}

welcome()