【第5回】AI記事作成 エージェント間の受け渡しをJSON化する|Reviewer AIの判定を安定させる

スポンサーリンク
この記事は約43分で読めます。

第5回では、Reviewer AIのレビュー結果をJSON形式で受け渡す仕組みに改善します。
これまでの自然文レビューでは「判定:OK」「判定:このまま公開可能」など出力に揺れがあり、プログラム側の判定が不安定になる可能性がありました。
自然文レビューは人間向けに残しつつ、Python側で処理する判定結果はJSON化し、status によって保存・修正・再レビューの流れを安定させます。

第4回AI記事作成まででできたこと

第3回AI記事作成までは、Planner AI、Writer AI、Reviewer AI、Python側HTMLチェックの途中結果を保存し、どこで失敗したのかを追跡できるようにしました。

第4回AI記事作成では、その保存処理をさらに整理し、mcp_client.pymcp_server.py を使った疑似MCPツールとして、AI記事作成の本体とファイル保存処理を分離しました。

流れとしては、次のようになります。

main.py

mcp_client.py

mcp_server.py

保存・読み込みツール

これにより、main.py は記事作成の全体フローに集中し、保存や読み込みなどの処理は疑似MCPツール側に任せられるようになりました。

しかし、ここまで進めると、次の課題が見えてきます。

それが、第2回AI記事作成以降に問題となっていたAIエージェント間で受け渡す情報の形式です。

なぜJSON化が必要なのか

第3回までは、Reviewer AIのレビュー結果を自然文で受け取っていました。

たとえば、次のような形式です。

良い点:
- 構成が分かりやすい
- 読者の検索意図に合っている

修正点:
- 導入文の結論をもう少し前に出すとよい

Writer AIへの具体的な修正指示:
- 冒頭に記事の結論を追加してください

判定:OK

この形式は、人間が読むには分かりやすいです。

しかし、プログラムが読むには少し不安定です。

実際には、Reviewer AIが次のような判定を出すことがありました。

判定:OK
判定:
OK
判定:このまま公開可能
判定:A+

人間が見れば「OKに近い」と分かる場合でも、Python側では判断に迷います。

第3回では、この揺れに対応するために、get_review_status() で判定行を読み取る処理を追加しました。

しかし、自然文の出力に頼る限り、出力揺れを完全になくすことは難しいです。

そこで第5回では、Reviewer AIの出力を次のように分けます。

自然文レビュー
→ 人間が読むための説明

JSON判定
→ Pythonが処理するためのデータ

第5回AI記事作成での処理変更

第5回AI記事作成では、Reviewer AIのレビュー結果をJSONで受け渡す仕組みにします。

全体像は次の通りです。

Writer AI
  ↓
Reviewer AI
  ↓
自然文レビュー + 判定JSON
  ↓
Python側でJSONを抽出・検証
  ↓
status に応じて処理を分岐
第5回のチェックフローを示した図。Writer AIが記事本文を作成し、Reviewer AIが自然文レビューと判定JSONを出力し、Python側でJSONを抽出・検証したあと、statusによって処理を分岐する流れがまとめられている。OKの場合はPython側HTMLチェックを経て保存、REVISION_REQUIREDはWriter AIへ修正依頼、UNKNOWNはログ保存または再レビューへ進む構成になっている。
Reviewer AIの判定をJSON化し、Python側で検証したうえで status に応じて保存・修正・再レビューを分岐

JSONの基本形は、以下のようにします。

修正が必要な場合は、次のようにします。

Python側HTMLチェックで問題がある場合は、Python側で次のような判定にできます。

statusの種類を決める

第5回では、まず以下の4種類に整理します。

status意味次の処理
OKReviewer AIが問題なしと判断保存へ進む
REVISION_REQUIRED内容面で修正が必要Writer AIへ戻す
VALIDATION_FAILEDPython側HTMLチェックでNGWriter AIへ戻す
UNKNOWNJSON不正・判定不能ログ保存または再レビュー

ポイントは、Reviewer AIの自然な表現をそのまま分岐条件にしないことです。

プログラム側では、必ず次のような固定値だけを見ます。

OK
REVISION_REQUIRED
VALIDATION_FAILED
UNKNOWN

こうすることで、将来的にイベント駆動型へ進むときも、次のように確実に分岐できるようになります。

OK

article.approved

REVISION_REQUIRED

article.revision_required

VALIDATION_FAILED

article.validation_failed

UNKNOWN

article.review_failed

自然文レビューとJSON判定を分ける

Reviewer AIには、完全にJSONだけを返させる方法もあります。

ただし、AI記事作成では、自然文レビューも残した方が分かりやすいといえます。

理由は、人間が見たときに、なぜOKなのか、なぜ修正が必要なのかを確認しやすいからです。

そのため、第5回AI記事作成では次の形式に変更します。

良い点:
- ...

修正点:
- ...

Writer AIへの具体的な修正指示:
- ...

判定JSON:
{
"status": "OK",
"summary": "記事構成とHTML形式に大きな問題はありません。",
"issues": [],
"next_action": "SAVE"
}

この形なら、人間にも読みやすく、Python側でも扱いやすくなります。

reviewer.py の考え方

第5回AI記事作成では、reviewer.py のプロンプトを変更します。

これまでは、最後に次のどちらかを書かせていました。

判定:OK

または、

判定:修正必要

第5回AI記事作成では、これをJSONに変えます。

Reviewer AIには、次のように指示します。

最後に必ず「判定JSON:」を出力し、その直後にJSONだけを書いてください。
JSONには status、summary、issues、next_action を含めてください。

さらに、JSONの値を制限します。

status は OK または REVISION_REQUIRED のどちらか
next_action は SAVE または REVISE のどちらか

これにより、Reviewer AIが自由に

このまま公開可能
A+
非常に高品質

のような判定を出しにくくなります。

Python側でJSONを検証する

JSON化しただけでは、まだ十分ではありません。

LLMは、JSONを少し崩して出力する場合があります。

たとえば、次のような問題が起きる可能性があります。

・JSONの前後に余計な文章が入る
・カンマが抜ける
・statusの値が想定外になる
・issuesが配列ではなく文字列になる
・next_actionが抜ける

そのため、Python側でJSONを検証する処理を入れます。

第5回AI記事作成では、新しく review_parser.py のようなファイルを作ると分かりやすいです。

役割は以下です。

review_parser.py

Reviewer AIの出力から判定JSONを取り出す
JSONとして読み込む
必要なキーがあるか確認する
statusの値が許可されたものか確認する
失敗したら UNKNOWN にする

review_parser.py の役割

第5回AI記事作成で追加する review_parser.py は、Reviewer AIの自然文レビューからJSON部分を取り出します。

イメージはこうです。

Reviewer AIの出力

「判定JSON:」以降を抽出

json.loads() で読み込み

status / summary / issues / next_action を検証

Python側で使いやすいdictに変換

JSONが壊れていた場合は、次のように扱います。

これにより、判定不能な場合もプログラムが止まらず、安全に処理できます。

main.py の分岐も分かりやすくなる

第3回・第4回AI記事作成では、main.py で次のように判定していました。

第5回では、これを次のように変更します。

そして、分岐は次のようになります。

このようにすると、自然文の細かな表現ではなく、JSONの status によって処理を分岐できます。

Python側HTMLチェックとの関係

第5回でも、Python側HTMLチェックは残します。

理由は、Reviewer AIがOKを出しても、HTML形式に問題が残る可能性があるからです。

つまり、保存条件はこれまでと同じです。

Reviewer AIのJSON判定:OK
かつ
Python側HTMLチェック:OK

この2つがそろったときだけ保存します。

逆に、Reviewer AIがOKでも、Python側HTMLチェックがNGなら保存しません。

Reviewer AI:OK
Python側HTMLチェック:NG

VALIDATION_FAILED としてWriter AIへ戻す

つまり、第5回では、判定の役割を次のように分けます。

Reviewer AI
→ 内容面の確認

Python側HTMLチェック
→ HTML形式・Markdown混入の確認

JSON
→ エージェント間の安定した受け渡し

第5回AI記事作成のファイル構成

第5回AI記事作成では、以下のような構成になります。

article-agent/
├ main.py
├ llm_client.py
├ validators.py
├ review_parser.py
├ agents/
│ ├ planner.py
│ ├ writer.py
│ └ reviewer.py
├ mcp_client.py
├ mcp_server.py
└ output/

今回新しく追加するのは、主に以下です。

review_parser.py

変更するファイルは、主に以下です。

main.py
validators.py(バグ修正)
agents/reviewer.py

第5回AI記事作成 追加・変更ファイル

追加:review_parser.py

変更:agents/reviewer.py

変更:main.py

validators.py(バグ修正)

第5回AI記事作成 実行結果

ターミナル出力

(UTF-8)

(SHIFT-JIS)

作成記事

(UTF-8)

(SHIFT-JIS)

第5回AI記事作成で保存されるファイル

第5回でも、途中保存の仕組みは維持します。

たとえば、以下のようなファイルを保存します。

plan.txt
draft_1.html
review_1.txt
review_result_1.json
validation_1.txt
draft_2.html
review_2.txt
review_result_2.json
validation_2.txt
final_article.html
run_summary.txt

ポイントは、自然文レビューとは別に、JSON判定も保存することです。

review_1.txt
→ Reviewer AIの自然文レビュー全文

review_result_1.json
→ Pythonが処理するための判定JSON

このように分けると、あとから確認するときも分かりやすくなります。

今後のイベント駆動型へのつながり

第5回でJSON化する理由は、単に判定を安定させるためだけではありません。

今後、イベント駆動型マルチエージェントへ進むための準備でもあります。

イベント駆動型では、AIの出力をもとに次のイベントを発火します。

review_result.status == "OK"

article.approved

review_result.status == "REVISION_REQUIRED"

article.revision_required

validation.status == "VALIDATION_FAILED"

article.validation_failed

このとき、自然文のレビューをそのままイベント分岐に使うのは危険です。

しかし、JSONの status であれば、イベント分岐がしやすくなります。

つまり、第5回は、後のイベント駆動型マルチエージェントに進むための重要な土台です。

まとめ:AIエージェント間の受け渡しはJSON化すると安定する

第5回では、Reviewer AIの判定結果をJSON形式で受け渡す構成に改善しました。

第3回・第4回では、Reviewer AIの自然文レビューから 判定:OK判定:修正必要 を読み取っていました。

しかし、LLMの自然文出力は揺れます。

そこで第5回では、自然文レビューは人間が読むために残し、Pythonが処理する判定結果はJSONとして分離します。

自然文レビュー
→ 人間が読む

JSON判定
→ Pythonが処理する

この分離により、AI記事作成システムはさらに安定します。

特に、将来的にイベント駆動型へ進む場合、status による明確な分岐は重要です。

第5回は、AIマルチエージェント記事作成システムを、単なる順番実行から、安定したエージェント間連携へ進めるための回です。

第6回では、Planner AI、Writer AI、Reviewer AIに加えて、調査役となるResearcher AIを追加します。

【第6回】AI記事作成 Researcher AIとWeb検索機能で最新情報に対応する