BacklogとAmazon Bedrockで提案書のレビューを自動化してみた
はじめに
提案書のレビューフローを整えることになったので自動化してみました。
みなさんのチームでは以下のような悩みはないでしょうか。
- 誤字脱字の指摘と修正、再依頼の往復だけで数日かかる
- レビュー依頼を出しても誰にも気づかれない
- レビュー中のまま数日放置される
- レビュアーが誤字探しに時間をかけ、肝心の内容判断に集中できない
本記事ではBacklogで運用する提案書レビューのフローにAWS(Lambda + Amazon Bedrock)でAIチェックと通知の自動化を組み込んだ構成を紹介します。
方針
まず検討して出た結論はレビューのボトルネックは主に人の調整にあるというものでした。
誰がいつ見るか、止まっていないか、承認が偏っていないか。ここはAIよりも通知やリマインドといった仕組みで解決していきます。
役割分担を次のように定めました。
| 役割 | 担当 | 理由 |
|---|---|---|
| 起票の通知・停滞の催促 | EventBridge / Webhook(AI不使用) | 機械的にやるべきタスク |
| 誤字脱字・リスク箇所の一次チェック | Bedrock (Claude) | 網羅的な検出はAIが得意 |
| 内容の妥当性判断・承認・ステータス変更 | 人 | 責任を伴う判断はAIに渡さない |
AIの出力はBacklogコメントに書き込むだけでステータスには一切触れません。
AIチェックが未実施でもレビュー依頼は出せるようにして、既存フローの速度を落とさないことを最優先にしました。
アーキテクチャ

提案書の入力はGoogle Drive連携ではなくPDFで
当初はGoogle Drive APIでスライドを自動取得する案もありましたが、提案者がPDFを書き出してBacklog課題に添付する方式にしました。
- GCPサービスアカウントの発行、鍵管理、Workspace管理者との調整が丸ごと不要になる
- 連携先がBacklogとAWSだけになり、構成がシンプルになる
- 修正版を添付し直すと自動で再チェックされるという自然な使い勝手が手に入る
提案者の手作業がひとつ増えるトレードオフは起票テンプレートに添付欄を明記して運用に埋め込みました。
- 提案書PDF:この課題に添付(スライドから ファイル > ダウンロード > PDF)
- 添付すると数分でAIチェック(誤字脱字・要確認事項)が自動でコメントされます
- 修正したらPDFを添付し直すと再チェックされます(未実施・指摘未対応でも依頼可)
実装のポイント
二重コメント防止
Backlogは課題追加と課題更新のWebhookを立て続けに送ることがあり、Lambdaが並行起動すると同じPDFに2回コメントしてしまいます。
DynamoDBの条件付き書き込みで実行権を先に取る方式で対処しました。
def try_claim_attachment(issue_key: str, attachment_id: int) -> bool:
try:
table.put_item(
Item={"issue_key": f"{issue_key}#att#{attachment_id}"},
ConditionExpression="attribute_not_exists(issue_key)",
)
return True
except ClientError as e:
if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
return False # 既にチェック済み or 並行実行中
raise
チェックに失敗したときは実行権を返すので課題を更新するだけで再実行できます。
判定は添付ID単位のため、同名ファイルでも添付し直せば新規としてチェックされます。
AIが失敗してもフローを止めない
AIチェックはあくまで参考情報です。
Bedrockの呼び出しが失敗しても例外でフローを止めず、ログに残してスキップします。サイレント失敗にならないよう失敗時はSlackへ一行だけ通知します。
レビュー結果の出力
出力フォーマットは2部構成に固定しました。
- 誤字脱字・表記: ページ番号つきで最大10件
- 要確認事項: 見落とすと事故につながる観点(対応不可な作業、再委託リスク、準委任なのに成果物保証と読める表現、SLAと体制の矛盾など)を最大5件
長文レポートは禁止、総評や採点もさせません。理由は読まれないからです。
検証で得た教訓
実際のPDFで検証を回して教訓が3つ得られました。
まずGoogleスライドのPDFはテキスト層と見た目が一致しません。スライドから書き出したPDFはテキスト層の文字コードが見た目と異なることがあります。
たとえば「行」が康熙部首の「⾏」だったり、中黒が「‧」として抽出されます。AIはこれを律儀に表記揺れとして指摘してくるため誤検知の原因となりました。対策としてプロンプトで明示的に禁止しています。
文字コードレベルの差異(中黒/中点、全角/半角、異体字・康熙部首などの字形差)は挙げない。
判断基準はただ一つ: 印刷したページを人間が読んで気づく問題か。
次に抑制ルールだけを積むと本物の誤字まで消えます。ノイズを禁止するルールを重ねたところ、今度は本物の誤字まで報告されなくなりました。
抑制と同時に必ず挙げるものの正例を明示することでバランスが取ります。
必ず挙げるもの: 明確な誤字・脱字・変換ミス、漢字/ひらがなの表記誤り(例: 「対応出来る」→「対応できる」)、
取り消し線の残留などの編集途中の痕跡。これらは抑制ルールの対象外で確実に報告する。
どうしても実行ごとのブレは残ります。
AIチェックは参考情報であるという位置づけと免責の一文を出力に含めることが技術的な対策と同じくらい重要です。
なおPDFを直接入力する方式の威力を実感したのはスライドに残っていた取り消し線、つまり編集途中の痕跡を検出したときです。
テキスト抽出では原理的に検出できない指摘で見落としやすいポイントでした。
ハマったところ
Backlogのコメントは4バイトUTF-8の絵文字を受け付けない
コメントの冒頭に 🤖 の絵文字を付けたところ、Backlog APIが400 Bad Requestを返しました。
エラー本文は {"errors":[{"message":"Incorrect String: %F0%9F%A4%96 ..."}]} でこれは🤖のUTF-8表現です。
BMP外の4バイト文字が原因なので投稿前に除去する処理を入れました。
# ⚠(U+26A0)等のBMP内記号は残る。🤖🚩等の4バイト絵文字だけ除去
content = "".join(ch for ch in content if ord(ch) <= 0xFFFF)
CLIでの事前検証ではBacklogに投稿しないためこの問題はテストで初めて発覚しました。
本番と同じ経路を一度通すテストは省略しないことをおすすめします。
エラー時はAPIのレスポンスボディを必ずログに残す
urllibのHTTPErrorをそのまま投げるとHTTP Error 400: Bad Requestしか分からなかったです。
BacklogのAPIはエラー詳細をJSONで返してくれるので、e.read()をログに出すだけで上記のような原因特定が一発になります。
Webhookの認証
BacklogのWebhookには署名機構がないため、URLにランダム文字列のトークンを付けてLambda側で検証しています。
トークンはSSM Parameter StoreのSecureStringで管理し、チームメンバーはAWS権限があれば自分で取得できるようにしました。
Slack通知
起票されると設定したSlackチャンネルに次のような通知が流れます。

チャンネルに流れるのはこの起票の初報だけにし、停滞したときの催促は同じスレッドへの返信にしました。
更新が数日止まっている課題があると毎朝の巡回がスレッドに返信するので放置された課題だけが浮かび上がってきます。
毎日チャンネルに流れるようなリマインドは無視されがちなので、
初報だけ、催促はスレッド返信という構成はそれを避けるための設計です。
リマインドの判定ロジック
リマインドはEventBridgeのスケジュールで平日の朝に一度だけ動きます。流れは次のとおりです。
- Backlog APIで対象プロジェクトのレビュー中・レビュー済みの課題を検索する
- 課題ごとに最終更新からの経過日数を計算し、しきい値(初期値3日)未満ならスキップ
- しきい値を超えていても前回のリマインドから間隔(初期値3日)が空いていなければスキップ
- 残った課題だけスレッドに返信し、リマインドした日時をDynamoDBに記録する
2と3を分けているため、停滞が続く限り3日おきに催促は繰り返されますが毎朝は鳴りません。
経過日数は課題の最終更新からの累積なので返信のたびに3日、6日、9日と数字が育っていき、放置の深刻さがスレッドを見るだけで分かります。
しきい値と間隔はCloudFormationのパラメータにしてあるのでコード変更なしで調整できます。
Slack App側は最小構成で済む
この構成のSlack Appに必要な権限はchat:writeひとつです。
ボットはSlackからのイベントを一切受信せず投稿するだけで済みます。
運用フロー
- 提案者がPDFを添付して起票すると数分でAIチェックがコメントされる
- 提案者は指摘を見て修正し、PDFを添付し直す。自動で再チェックされるので人を待たずに何往復でも回せる
- 納得したらステータスを進めて人のレビューへ
- レビュアーはAIチェック済みの前提で内容の判断に集中する
- 承認とクローズは従来どおり人が行う
このフローの効果でいちばん大きいのは誤字指摘、修正、再依頼という人を挟む往復が1回分消えることだと考えています。
運用しながら育てる仕組みに
これは作って終わりのシステムではありません。リリース時点のプロンプトはあくまで最初のバージョンでチューニングに必要な材料は運用が始まってから集まります。
改善は次のループで回していきます。
- 集める。的外れだった指摘と人のレビューでは見つかったのにAIが挙げなかった見逃しを提案者とレビュアーから集める。
- 測る。1〜2ヶ月後に提案書の品質と提案までのスピードが改善されたか導入前後で比較する。改善が見えれば定着が進み、なければ次の打ち手を考える材料にする。
- モデルを替える。誤字検出の安定性はモデル性能に依存する部分が残るため、新しいモデルが使えるようになったら再検証して差し替える。
まとめ
- AIレビューの自動化は、AIに何を任せて何を人で対応するか決める
- レビューを速くするのは通知やリマインドといった地味な自動化
- Slack通知はメンションなしの初報だけに絞り、停滞の催促は同じスレッドへの返信にする
- PDFをBedrockに直接入力すると取り消し線のようなテキスト抽出では拾えない見た目の事故まで検出できる
- リリースは終わりではなく改善ループの始まり。集めて、測って、モデルを替えながら育てる
同じようにレビュー周りで悩みを抱えたチームの参考になれば幸いです。






