qGuide: 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>
自由改変可能な HTML/JavaScript コードです (MIT License)。いかなる保証もありません。
(JavaScript を用いたデコレーションは Professional editionでのみ利用可能です: M213)
(JavaScript を用いたデコレーションは Professional editionでのみ利用可能です: M213)
Capture



Prompt Example (q_instruction)
誤植を指摘し、行ごとにMarkdown形式の箇条書きでわかりやすく修正内容を示してください。必要に応じて複数の誤植を漏れなく指摘し、各行には簡潔な指摘文を記載してください。出力例を参考に、必ず以下の出力フォーマットを守ってください。
---
## 出力フォーマット
- 各誤植ごとに「* 」で始めるMarkdownベースの箇条書き
- [誤植文] 誤植のある文を記載
- [正] 修正後の正しい文を提示
- どこが間違いか・どのように直すべきかを端的に述べる
---
## 例
* [誤植文] 今日は天気が良いですした。
* [正] 今日は天気が良かったです。
* 「良いですした」は誤り。「良かったです」と正しく修正。
* [誤植文] 彼は昨日、映画を見いった。
* [正] 彼は昨日、映画を見に行った。
* 「見いった」は「見に行った」に修正。
---
### 注意事項・追加ガイドライン
- 誤植が複数ある場合はすべて列挙してください。
- 指摘文は一行で簡潔にまとめてください。
- 出力は必ずMarkdown形式で箇条書きにしてください。