「インタビューの事実のみを使う」
この一文をプロンプトに入れるまで、Geminiは嘘をついた。
「ある市に移住して5年」。「地元で長年愛されてきた」。どちらも、事業者はそんなことを一言も言っていない。Geminiが記事を1,000字に引き伸ばすために、自分で作り上げた。
架空情報の防止が、このシステムのプロンプト設計の核心だった。
インタビュアーとしてのGemini
5ターンのうち2〜5問目の質問はGeminiが生成する。Geminiへの指示はシンプルだ。
const prompt = `あなたはある市の地域情報誌「地域人」のインタビュアーです。
地域の事業者を取材して、読者に届ける記事のための情報を集めています。
以下はこれまでのインタビュー会話です:
${conversationLog}
事業者の回答を受けて、次の質問を1つだけ生成してください。
- 前の回答で興味深かった点を深掘りする
- まだ聞けていない事業者の魅力を引き出す
- 読者が「知りたい」と思う情報を引き出す
- 質問は1文で、自然な日本語で
- 長い質問や複数質問は避ける`;
「1つだけ」という制約を入れた。入れないと「次の2点についてお聞きします。まず〜、そして〜」という形式で複数の質問が来ることがある。LINEのチャットUIで長い質問は圧迫感がある。
5ターン目は別のプロンプトにして「締めくくりのメッセージを引き出す方向で」という指示を加えた。インタビューとして区切りよく終わるようにするためだ。
ライターとしてのGemini
5ターンが完了したら、会話ログ全体を渡して記事を生成する。
const articlePrompt = `あなたは地域情報誌「地域人」の編集者です。
以下のインタビュー会話をもとに、読者に向けた事業者紹介記事を書いてください。
【事業者情報】
店名: ${businessName}
業種: ${category}
【インタビュー会話】
${conversationLog}
【記事要件】
- 800字以上、1200字以内
- タイトルを最初に書く(## タイトル 形式)
- 三人称・客観的な紹介文(「田中さんは…」形式)
- インタビューの事実のみを使う(架空の情報を作らない)
- 読者が来店・問い合わせしたくなるような文章
- 末尾に「取材:地域人編集部」と入れる`;
最初は字数を多く要求していた。Geminiは指定字数を達成するために、インタビューに出てきていない情報を補いはじめた。
字数を800〜1,200字に下げて「インタビューの事実のみを使う」という制約を明記した。それでも架空情報が入ることがあるため、管理者の確認フローを設けた。AIが作った記事を人間が確認する。この組み合わせが誠実な設計だ。
GASの30秒制限への対処
GeminiのAPI呼び出しは数十秒かかることがある。GASのウェブアプリは1回の実行に30秒の制限がある。
記事生成を同期処理にすると、LINEのWebhookがタイムアウトして二重送信が起きる。
LINEから「5ターン終了」を受け取ったとき、GASはすぐに「少々お待ちください。記事を作成しています」とLINEに返信してセッションをGENERATINGに変更する。
別のGASトリガー(1分ごとの定期実行)が、GENERATINGのセッションを見つけたら記事生成を実行する。完了したら「記事ができました。写真を送ってください」というpushMessageを事業者に送る。
同期ではなく、キューとトリガーで非同期にする。GASの制限を受け入れた設計だ。
確認依頼フロー
管理者が「確認依頼」ボタンを押すと、事業者のLINEに記事の全文が送られる。
LINE1通のメッセージ上限は5,000字だ。1,200字の記事ならほぼ1通で収まるが、長い場合は複数メッセージに分割して送る。
管理者「確認依頼」ボタン
→ 事業者LINEに記事全文送信
→ 事業者「OK」等で承認 → APPROVED → 公開ボタン活性化
→ 事業者が修正内容を返信 → Geminiが修正版生成 → 再送(REVIEW_SENT)
修正指示が来たとき、「この修正指示に基づいて記事を書き直す」プロンプトでGeminiが再生成する。事業者とのやりとりをAIが代行するが、最後に管理者が承認する。自動化の中に人間の確認が挟まっている。
*次回は「S3への直接PUT・雑誌レイアウト・号編成——地域メディア配信システムの全貌」*
*シンプルシステム株式会社 代表 伊藤勝彦*