qGuide: OpenAI API にリクエスト

qGuide: OpenAI API にリクエスト

translate qGuide: Request to OpenAI API

文字列 “投入データ” と文字列 “APIキー” を Responses API に CORS 送信します。レスポンス(モデルが生成した文章)は、タスク処理画面上でストリーミング表示されます。プロンプト設定次第で、「誤植チェック機能」「文章リライト機能」「差戻理由の候補列挙機能」といった様々な支援機能(タスク処理者を支援する機能)を提供することが可能です。

Input / Output

  • ← STRING (STRING_TEXTFIELD) q_input
  • ← STRING (STRING_TEXTFIELD) q_instruction
  • ← STRING (STRING_TEXTFIELD) q_openAiApiKey
  • pre#user_result
  • div#user_status

Code Example

HTML/JavaScript (click to close)
<style>
/* AI呼び出しボタン */
.user_aiBtn {
  border: 1px solid #ccc;
  background-color: #fff;
  padding: 6px 12px;
  border-radius: 20px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.2s ease;
  color: #333;
  margin-right: 8px; /* ボタン間の余白 */
  margin-bottom: 12px;
}
.user_aiBtn:hover {
  background-color: #f0f0f0;
  border-color: #bbb;
}
.user_aiBtn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background-color: #eee;
}

/* 結果表示エリア */
#user_result {
  background-color: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 12px;
  min-height: 8em;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
  font-size: 14px;
  line-height: 1.6;
  color: #222;
  margin-top: 4px;
}

/* ステータス表示エリア */
#user_status {
  font: 12px/1.4 system-ui, sans-serif;
  opacity: 0.75;
  margin-top: 8px;
  min-height: 1.4em;
}

/* タイピングカーソルのアニメーション */
@keyframes blink {
  50% { opacity: 0; }
}
.user_cursor {
  display: inline-block;
  width: 8px;
  height: 1em;
  background-color: #333;
  margin-left: 2px;
  animation: blink 1s step-end infinite;
  vertical-align: text-bottom;
}
</style>

<button type="button" class="user_aiBtn">gpt-5-nano</button>
<button type="button" class="user_aiBtn">gpt-5-mini</button>
<button type="button" class="user_aiBtn">gpt-5</button>
<button type="button" class="user_aiBtn">gpt-5-codex</button>
<button type="button" class="user_aiBtn">gpt-4o</button>
<button type="button" class="user_aiBtn">gpt-4o-mini</button>

<pre id="user_result"></pre>
<div id="user_status"></div>


<script>
// --- 全てのボタンにイベントリスナーを設定 ---
const aiButtons = document.querySelectorAll(".user_aiBtn");
aiButtons.forEach(button => {
  button.addEventListener("click", async (event) => {
    // --- Questetraのデータ項目から値を取得 ---
    const strKey = qbpms.form.get("q_openAiApiKey");
    const strInstruction = qbpms.form.get("q_instruction") || ""; // 任意
    const strInput = qbpms.form.get("q_input");

    // --- 必要なHTML要素を取得 ---
    const resultElement = document.getElementById("user_result");
    const statusElement = document.getElementById("user_status");

    // --- クリックされたボタンのテキストをモデル名として取得 ---
    const modelName = event.currentTarget.innerText.trim();

    // --- 入力チェック ---
    if (!strKey || !strInput) {
      statusElement.innerText = "APIキーと入力内容は必須です。";
      resultElement.innerText = "";
      return;
    }

    // --- UIを処理中状態に更新 ---
    aiButtons.forEach(btn => btn.disabled = true);
    statusElement.innerText = "AIに接続中...";
    resultElement.innerHTML = '<span class="user_cursor"></span>';
    const cursor = resultElement.querySelector(".user_cursor");

    try {
      // --- APIリクエスト(Responses API / SSE ストリーミング) ---
      const response = await fetch("https://api.openai.com/v1/responses", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${strKey}`
        },
        body: JSON.stringify({
          model: modelName,
          instructions: strInstruction,
          input: strInput,
          stream: true
        })
      });

      // --- エラーレスポンスの処理 ---
      if (!response.ok) {
        let msg = `API Error: ${response.status}`;
        try {
          const err = await response.json();
          msg += `\n${JSON.stringify(err, null, 2)}`;
        } catch (_) {}
        throw new Error(msg);
      }

      statusElement.innerText = "AIが文章を生成中...";

      // --- Responses API のSSEを読み取り(event: と data: を処理)---
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";
      let currentEvent = "";

      const flushLines = (chunkText) => {
        buffer += chunkText;
        const lines = buffer.split(/\r?\n/);
        buffer = lines.pop() || ""; // 最後の不完全行はbufferに戻す

        for (const line of lines) {
          if (!line.trim()) continue;
          if (line.startsWith("event:")) {
            currentEvent = line.replace(/^event:\s*/, "").trim();
            continue;
          }
          if (line.startsWith("data:")) {
            const dataRaw = line.replace(/^data:\s*/, "").trim();
            if (dataRaw === "[DONE]") {
              // 公式の終端トークン
              return "DONE";
            }
            try {
              const data = JSON.parse(dataRaw);
              if (currentEvent === "response.output_text.delta" && data.delta) {
                cursor.insertAdjacentText("beforebegin", data.delta);
              }
              // モデル側で最終化イベント
              if (currentEvent === "response.completed") {
                return "DONE";
              }
              // エラーイベント(まれ)
              if (currentEvent === "response.error") {
                throw new Error(data?.error?.message || "Unknown streaming error");
              }
            } catch (e) {
              // JSONでない行は無視(キープアライブ等)
              console.error("SSE JSON parse error:", dataRaw, e);
            }
          }
        }
      };

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const text = decoder.decode(value, { stream: true });
        const status = flushLines(text);
        if (status === "DONE") break;
      }

      statusElement.innerText = "生成が完了しました。";

    } catch (error) {
      console.error("Request failed:", error);
      statusElement.innerText = "エラーが発生しました。";
      document.getElementById("user_result").innerText = String(error.message || error);
    } finally {
      // --- UIを元に戻す ---
      const c = document.querySelector("#user_result .user_cursor");
      c?.remove();
      aiButtons.forEach(btn => btn.disabled = false);
    }
  });
});
</script>
HTML/JavaScript (chat completion api version)
<style>
/* AI呼び出しボタン */
.user_aiBtn {
  border: 1px solid #ccc;
  background-color: #fff;
  padding: 6px 12px;
  border-radius: 20px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.2s ease;
  color: #333;
  margin-right: 8px; /* ボタン間の余白 */
  margin-bottom: 12px;
}
.user_aiBtn:hover {
  background-color: #f0f0f0;
  border-color: #bbb;
}
.user_aiBtn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background-color: #eee;
}

/* 結果表示エリア */
#user_result {
  background-color: #f9f9f9;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 12px;
  min-height: 8em;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
  font-size: 14px;
  line-height: 1.6;
  color: #222;
  margin-top: 4px;
}

/* ステータス表示エリア */
#user_status {
  font: 12px/1.4 system-ui, sans-serif;
  opacity: 0.75;
  margin-top: 8px;
  min-height: 1.4em;
}

/* タイピングカーソルのアニメーション */
@keyframes blink {
  50% { opacity: 0; }
}
.user_cursor {
  display: inline-block;
  width: 8px;
  height: 1em;
  background-color: #333;
  margin-left: 2px;
  animation: blink 1s step-end infinite;
  vertical-align: text-bottom;
}
</style>

<button type="button" class="user_aiBtn">gpt-5-nano</button>
<button type="button" class="user_aiBtn">gpt-5-mini</button>
<button type="button" class="user_aiBtn">gpt-5</button>
<button type="button" class="user_aiBtn">gpt-5-codex</button>
<button type="button" class="user_aiBtn">gpt-4o</button>
<button type="button" class="user_aiBtn">gpt-4o-mini</button>

<pre id="user_result"></pre>
<div id="user_status"></div>


<script>
// --- 全てのボタンにイベントリスナーを設定 ---
const aiButtons = document.querySelectorAll(".user_aiBtn");
aiButtons.forEach(button => {
  button.addEventListener("click", async (event) => {
    // --- Questetraのデータ項目から値を取得 ---
    const strKey = qbpms.form.get("q_openAiApiKey");
    const strInstruction = qbpms.form.get("q_instruction");
    const strInput = qbpms.form.get("q_input");

    // --- 必要なHTML要素を取得 ---
    const resultElement = document.getElementById("user_result");
    const statusElement = document.getElementById("user_status");

    // --- クリックされたボタンのテキストをモデル名として取得 ---
    const modelName = event.currentTarget.innerText.trim();

    // --- 入力チェック ---
    if (!strKey || !strInput) {
      statusElement.innerText = "APIキーと入力内容は必須です。";
      resultElement.innerText = "";
      return;
    }

    // --- UIを処理中状態に更新 ---
    aiButtons.forEach(btn => btn.disabled = true);
    statusElement.innerText = "AIに接続中...";
    resultElement.innerHTML = '<span class="user_cursor"></span>';
    const cursor = resultElement.querySelector(".user_cursor");

    try {
      // --- APIリクエストを送信 ---
      const response = await fetch("https://api.openai.com/v1/chat/completions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${strKey}`
        },
        body: JSON.stringify({
          model: modelName,
          messages: [
            { role: "system", content: strInstruction },
            { role: "user", content: strInput }
          ],
          stream: true
        })
      });

      // --- エラーレスポンスの処理 ---
      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(`API Error: ${response.status}\n${JSON.stringify(errorData, null, 2)}`);
      }

      statusElement.innerText = "AIが文章を生成中...";

      // --- レスポンスをストリーミングで処理 ---
      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        const lines = chunk.split("\n").filter(line => line.trim().startsWith("data:"));

        for (const line of lines) {
          const rawJson = line.replace(/^data: /, "").trim();
          if (rawJson === "[DONE]") break;

          try {
            const parsed = JSON.parse(rawJson);
            const content = parsed.choices[0]?.delta?.content || "";
            if (content) {
              cursor.insertAdjacentText('beforebegin', content);
            }
          } catch (e) {
            console.error("JSON parse error:", rawJson, e);
          }
        }
      }
      statusElement.innerText = "生成が完了しました。";

    } catch (error) {
      console.error("Request failed:", error);
      statusElement.innerText = "エラーが発生しました。";
      resultElement.innerText = error.message;
    } finally {
      // --- UIを元に戻す ---
      cursor?.remove();
      aiButtons.forEach(btn => btn.disabled = false);
    }
  });
});
</script>
warning 自由改変可能な HTML/JavaScript コードです (MIT License)。いかなる保証もありません。
(JavaScript を用いたデコレーションProfessional editionでのみ利用可能です: M213)

Capture

Prompt Example (q_instruction)

誤植を指摘し、行ごとにMarkdown形式の箇条書きでわかりやすく修正内容を示してください。必要に応じて複数の誤植を漏れなく指摘し、各行には簡潔な指摘文を記載してください。出力例を参考に、必ず以下の出力フォーマットを守ってください。

---

## 出力フォーマット
- 各誤植ごとに「* 」で始めるMarkdownベースの箇条書き
- [誤植文] 誤植のある文を記載
- [正] 修正後の正しい文を提示
- どこが間違いか・どのように直すべきかを端的に述べる

---

## 例

* [誤植文] 今日は天気が良いですした。  
    * [正] 今日は天気が良かったです。  
    * 「良いですした」は誤り。「良かったです」と正しく修正。  
* [誤植文] 彼は昨日、映画を見いった。  
    * [正] 彼は昨日、映画を見に行った。  
    * 「見いった」は「見に行った」に修正。  

---

### 注意事項・追加ガイドライン
- 誤植が複数ある場合はすべて列挙してください。
- 指摘文は一行で簡潔にまとめてください。
- 出力は必ずMarkdown形式で箇条書きにしてください。

See Also

上部へスクロール

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む