◉GEMINI LABEN
●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…
記事一覧/API / SDK
◈ API / SDK/2026-04-17初級

Gemini APIのレスポンスにMarkdownが混入する問題 — プレーンテキスト出力の設定と対処法

Gemini APIのレスポンスに「**」や「##」などのMarkdown記号が含まれてしまう原因と、response_mime_typeやSystem Instructionを使ったプレーンテキスト出力の設定方法を実践的に解説します。

gemini-api288troubleshooting59markdownplain-textresponsesystem-instructions4python107fix10

APIから返ってくるテキストに **太字** や ## 見出し や - 箇条書き が混じっていて、アプリ上での表示がガタガタになる——Gemini APIを使い始めた開発者がほぼ必ず一度はぶつかる問題です。

Markdownで装飾されたテキストはチャットUIなら見栄えがよいのですが、音声合成(TTS)に渡したり、PDFに書き出したり、データベースに保存したりする場面では完全に邪魔者になります。私自身、Gemini APIをバックエンドにしたモバイルアプリを作っていたとき、音声読み上げで「アスタリスク、アスタリスク」と読まれてしまい途方に暮れた経験があります。

ここでは原因の整理から実際に動くコードまで、プレーンテキスト出力を得るための手法を3段階で紹介します。

なぜGemini APIはMarkdownを返すのか

Gemini 2.x以降のモデルは、デフォルトで「読みやすい形式」で応答するよう調整されています。ユーザーが明示的に指定しない限り、リストや見出しを使った構造化されたテキストが返ってくるのはモデルの標準的な動作です。

特にこんなプロンプトはMarkdownが出やすくなります。

  • 「〜を教えてください」「〜をまとめてください」といった説明系プロンプト
  • 「手順を教えてください」「利点を列挙してください」といったリスト要求
  • 「比較してください」「違いを説明してください」といった対比系

逆に「これを翻訳してください」「次の文章の誤りを直してください」といった変換系タスクではMarkdownは出にくいです。モデルがMarkdownを返すかどうかは、タスクの性質にも依存しているわけです。

方法1: response_mime_type を "text/plain" に設定する

最もシンプルで確実な方法です。GenerationConfig に response_mime_type を指定すると、モデルはMarkdownではなくプレーンテキストで応答するよう制御されます。

Python の場合

import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
 
model = genai.GenerativeModel(
    model_name="gemini-2.5-pro",
    generation_config=genai.GenerationConfig(
        response_mime_type="text/plain",  # ← これが効く
    )
)
 
response = model.generate_content(
    "クラウドコンピューティングの主な利点を3つ教えてください"
)
print(response.text)
# 出力例: "1. コスト削減: 初期設備投資が不要で、使用量に応じた課金になります。
#          2. スケーラビリティ: 需要に応じて..."
# ← ** や ## が含まれない

Node.js / TypeScript の場合

import { GoogleGenerativeAI } from "@google/generative-ai";
 
const genAI = new GoogleGenerativeAI("YOUR_GEMINI_API_KEY");
 
const model = genAI.getGenerativeModel({
  model: "gemini-2.5-pro",
  generationConfig: {
    responseMimeType: "text/plain", // ← キャメルケースに注意
  },
});
 
const result = await model.generateContent(
  "クラウドコンピューティングの主な利点を3つ教えてください"
);
console.log(result.response.text());

response_mime_type: "text/plain" を設定したとき、モデルの応答品質は落ちません。装飾が外れるだけで、内容の密度はそのままです。実際に試してみると、箇条書きは「1. 〜 2. 〜 3. 〜」という番号つき文章になり、強調語は単なるテキストとして文中に溶け込みます。

方法2: System Instruction でMarkdownを禁止する

response_mime_type だけでは不十分な場合(特に長い会話セッション)や、特定の出力スタイルをより細かく制御したい場合は、System Instruction を組み合わせます。

import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
 
system_prompt = """あなたは情報提供アシスタントです。
以下のルールを必ず守ってください。
マークダウン記法(アスタリスク、シャープ記号、バッククォートなど)を一切使用しないこと。
箇条書きが必要な場合は「1. 2. 3.」の番号付き形式にすること。
コードを示す場合もコードブロック記法は使わず、インデントのみで表現すること。
見出しは使わず、段落の区切りは空行のみで行うこと。"""
 
model = genai.GenerativeModel(
    model_name="gemini-2.5-pro",
    system_instruction=system_prompt,
    generation_config=genai.GenerationConfig(
        response_mime_type="text/plain",
    )
)
 
response = model.generate_content("Pythonでリストを逆順にする方法を教えてください")
print(response.text)
# 出力例(Markdownなし):
# Pythonでリストを逆順にするには、主に3つの方法があります。
# 1. reversed()関数を使う方法...

System Instruction は「役割」「制約」「出力形式」を事前に定義する場所です。Markdownを使わないよう明示的に伝えると、モデルはほぼ確実にプレーンテキストで返してくるようになります。

ただし注意点があります。System Instruction は「指示」であって「保証」ではありません。プロンプトが複雑になったり、会話ターンが増えるにつれて、まれにMarkdownが再出現することがあります。そのため、方法3の後処理と組み合わせるのがおすすめです。

方法3: 後処理でMarkdown記号を除去する

どうしてもMarkdownが混入する場合の保険として、後処理でMarkdown記号を除去する関数を用意しておくと安心です。

import re
 
def strip_markdown(text: str) -> str:
    """Gemini APIのMarkdownレスポンスをプレーンテキストに変換する"""
    # 見出し(# ## ###)を除去
    text = re.sub(r'^\#{1,6}\s+', '', text, flags=re.MULTILINE)
    
    # **太字** と *斜体* を除去(テキストは残す)
    text = re.sub(r'\*{1,2}([^*]+)\*{1,2}', r'\1', text)
    
    # __太字__ と _斜体_ を除去
    text = re.sub(r'_{1,2}([^_]+)_{1,2}', r'\1', text)
    
    # コードブロック(```...```)の記号を除去
    text = re.sub(r'```[a-z]*\n?', '', text)
    text = re.sub(r'```', '', text)
    
    # インラインコード(`code`)の記号を除去
    text = re.sub(r'`([^`]+)`', r'\1', text)
    
    # 箇条書き(- や *)を点記号に変換
    text = re.sub(r'^[\-\*]\s+', '• ', text, flags=re.MULTILINE)
    
    # 水平線(---)を除去
    text = re.sub(r'^-{3,}$', '', text, flags=re.MULTILINE)
    
    # 連続する空行を1行に圧縮
    text = re.sub(r'\n{3,}', '\n\n', text)
    
    return text.strip()
 
# 使い方
response = model.generate_content("Pythonのリスト内包表記について説明して")
clean_text = strip_markdown(response.text)
print(clean_text)

正規表現による除去は確実性が最も高い方法です。ただし、コードブロック内のコードも除去されてしまうため、コードを含む応答を扱う場合はさらに工夫が必要です。

TypeScript版の後処理関数

function stripMarkdown(text: string): string {
  return text
    .replace(/^\#{1,6}\s+/gm, "")              // 見出しを除去
    .replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1")   // 太字・斜体を除去
    .replace(/_{1,2}([^_]+)_{1,2}/g, "$1")     // 下線太字・斜体を除去
    .replace(/```[\w]*
?/g, "")               // コードブロック開始を除去
    .replace(/```/g, "")                       // コードブロック終了を除去
    .replace(/`([^`]+)`/g, "$1")               // インラインコードを除去
    .replace(/^[\-\*]\s+/gm, "• ")             // 箇条書きを記号に変換
    .replace(/^-{3,}$/gm, "")                  // 水平線を除去
    .replace(/
{3,}/g, "
 
")                // 連続空行を圧縮
    .trim();
}
 
const response = await model.generateContent("Pythonのリスト内包表記について説明して");
const cleanText = stripMarkdown(response.response.text());
console.log(cleanText);

マルチターン会話(Chat)での注意点

ChatSession を使うとMarkdownが再発しやすくなります。会話ターンが増えるとモデルがコンテキストに引きずられて書式スタイルを変えることがあるためです。

import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
 
model = genai.GenerativeModel(
    model_name="gemini-2.5-pro",
    system_instruction="Markdownを使わずプレーンテキストのみで応答してください。",
    generation_config=genai.GenerationConfig(
        response_mime_type="text/plain",
    )
)
 
chat = model.start_chat(history=[])
 
# 1ターン目
response1 = chat.send_message("Pythonとは何ですか?")
print(strip_markdown(response1.text))  # 後処理も併用
 
# 2ターン目(Markdownが再発しても後処理で対応)
response2 = chat.send_message("その特徴をもう少し詳しく教えてください")
print(strip_markdown(response2.text))

response_mime_type と system_instruction を両方設定した上で、後処理の strip_markdown() を組み合わせるのが最も安定した構成です。

どの方法をどの場面で使うか

私がプロジェクトで使っているのは次の組み合わせです。

シンプルな用途(1回完結のAPI呼び出し)は response_mime_type: "text/plain" のみで十分です。System Instructionを追加しても悪くはありませんが、トークンを消費するため、単純なケースでは省略しています。

音声合成(TTS)パイプラインには response_mime_type と system_instruction と strip_markdown() の3段構えを採用しています。TTS系のAPIはMarkdown記号を音読してしまうことがあるため、ここだけは保険を厚めにかけています。

長い会話セッションでは response_mime_type と system_instruction を設定し、strip_markdown() を各レスポンス取得後に必ず実行しています。

どの方法も、APIの料金には影響しません。トークン数が変わるわけではないので、安心して組み合わせてください。

全体を振り返って

まずは generation_config に response_mime_type: "text/plain" を1行追加するところから試してみてください。多くのケースはこれだけで解決します。音声合成や高品質な文章処理が必要な場面では、System Instruction と後処理の strip_markdown() を組み合わせると、より安定したプレーンテキスト出力が得られます。

プレーンテキストの扱いに慣れてきたら、今度は逆に「構造化されたJSONを安定して取り出す方法」も試してみてください。response_mime_type: "application/json" と response_schema の組み合わせが、より複雑なデータ処理のニーズに応えてくれます。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◈ API / SDK2026-10-16
models は 200 なのに generateContent だけ 403 になる日 — 切り分けの順番を先に決めておく
モデル一覧の取得は 200 で通るのに、生成リクエストだけが 403 permission_denied で止まるときの切り分け手順です。キー・プロジェクト・請求の順に確かめる小さな診断スクリプトと、リトライしてはいけない理由、サポートへ渡す材料の揃え方をまとめました。
◈ API / SDK2026-05-17
Gemini API で画像バッチ分析の INVALID_ARGUMENT エラーを診断する
Gemini API で複数画像を一括分析するとき INVALID_ARGUMENT エラーが出て詰まった経験はありませんか。MIMEタイプ・サイズ制限・リクエスト構造という3つの原因を診断し、File API と inline data を使い分けて根本解決する実践ガイドです。
◈ API / SDK2026-09-04
ループ音源のプチノイズは、Gemini に聴かせる前に波形の端で切り分けられます
環境音のループ再生で折り返しだけがプチッと鳴る。音声理解に丸ごと投げる前に、波形の端の段差を内部の変化量で正規化してふるい分ける手順と、配布形式への変換で継ぎ目が作り直される落とし穴をまとめました。