コンテンツにスキップ

共有パッケージ(packages)

packages/ には、複数の Lambda から使われる共通ロジックを置いています。 各パッケージは uv のワークスペースメンバーとして管理され、packages.<name>.src で参照します。

パッケージ 役割 主な利用者
models DocumentDB の Beanie モデル定義 全 Python Lambda
race_result_facts レース結果の決定論的な解釈・照合 article_generation, blog_generation, odds_poc_app
race_analysis_agent レース動画・コース画像・レース結果の総合解析エージェント article_generation, blog_generation, blog_rewriter
media_analysis_sdk Bedrock によるメディア解析の低レベル SDK race_analysis_agent
article_generation_agent レース結果10秒サマリーの執筆 article_generation
blog_generation_agent レース回顧・予想ブログの執筆 blog_generation
blog_parts ブロック JSON の解釈と着順テーブル・レースカードの組み立て blog_generation, blog_rewriter
thumbnail_generation_agent サムネイルに最適な瞬間の選定 thumbnail_generation
ng_word_filter NG ワード検査(Aho-Corasick) article_generation, blog_generation, blog_rewriter
tone_of_voice トーンオブボイス ガイドラインの共通文面 article_generation_agent, blog_rewriter

models

DocumentDB のドキュメントモデル(Beanie の Document)を定義します。 コレクションごとに1ファイルで、packages/models/documentDB/ 配下にあります。

各モデルの詳細は データベース を参照してください。


race_result_facts

race_result(公式レース結果)を決定論的に解釈するパッケージです。 記事生成で LLM に任せていた作業のうち、機械で確実にできるものを引き受けます。

用途 関数 目的
生成可否の判定 void_reason / is_void 不成立・中止レース(着順が無い)で記事を生成させない
事実の噛み砕き build_fact_block 周回別・コーナー別の先頭を「番号+名前」まで突合して渡す
生成後の照合 find_fact_errors 番号と名前の対応・払戻の数値が公式と一致するか検査する
値の差し込み placeholders 出走者名・払戻などをプレースホルダーで公式データから差し込む
人気の補正 article_checks 直接書かれた人気・「N番人気が◯着」の取り違えを公式の値へ直す
由来の測定 source_mix 記事がレース結果・動画をどれだけ参考にしたかを測る
予想の事実 prediction レース予想ブログ向け。照合先が公式レース結果ではなく出走表(race.prediction)になるため、確定事実ブロックの組み立てと検査を別に持つ(blog_generation_agent.generate_prediction が使う)

設計の考え方

LLM に値の転記や多段の突合をさせると取り違えが起きます。 そこで 解釈・突合・転記は機械が行い、LLM は日本語の表現だけを担うよう分担しています。

find_fact_errors は「機械的に断定できるもの」だけを検査します。 展開描写(何周目に誰が先頭か)も、文中の出走者が1人に絞れるときに限って検査します (check_lead / check_position / check_lap_range_lead / check_never_led_lap / check_unsupported_running)。主語を特定できない文は判定せず、見逃す側に倒します。 誤検知が1件でもあると、差し戻しの再生成が上限まで走って記事が publish されなくなるためです。

予防(build_fact_block で突合済みの事実を渡す)と検査(find_fact_errors で生成後に照合)は どちらか一方ではなく、二段構えで併用しています。


race_analysis_agent

レース動画・コース画像・レース結果データを総合的に解析し、1つの総合サマリーを生成する AI エージェントです。 LangGraph の ReAct エージェントと Amazon Bedrock(Nova Pro)を利用します。

解析対象 担当
レース動画(mp4) analyze_race_video ツールがマルチモーダル解析する
コース画像(競馬のみ) analyze_course_image ツールが解析する
レース結果 JSON エージェント本体が解析する

エージェントは上記3つを統合し、summary として1つの総合サマリーを出力します。

ツールはメモ化されており、1ツールあたり2回・全体で4回を上限とします。 超過分は打ち切って強制的に出力へ進ませ、エージェント実行が失敗した場合は ツール無しの単発呼び出しへフォールバックします。


media_analysis_sdk

Bedrock でメディア(動画・画像)を解析する低レベル SDK です。 race_analysis_agent のツールから呼ばれます。

動画は S3 URI で渡します(Converse API のインラインペイロード上限 25MB を超えるため)。


article_generation_agent

「レース結果10秒サマリー」(見出し1つ + 3文)を執筆するパッケージです。

  • 一次典拠は race_result_facts.build_fact_block が組み立てる確定事実ブロック
  • 総合分析(race_analysis_agent の出力)は表現の参考にのみ使う
  • 出走者の「番号+名前」・払戻の金額・人気は LLM に書かせず、プレースホルダーで公式データの値を差し込む
  • 生成後は NG ワード・事実照合・プレースホルダー解決・文字数のガードを通し、引っかかると差し戻して作り直す(最大2回)
  • ツールを使わないテキスト処理のため、ReAct エージェントではなく構造化出力の単一呼び出しで生成する

blog_generation_agent

レース回顧・予想ブログの本文(ブロック JSON)と10秒サマリーを執筆するパッケージです。 設計方針は article_generation_agent と同じ(確定事実が一次典拠、値はプレースホルダーで差し込み、 生成後にガードを通す)です。

モデル呼び出しは呼び出し側(apps/blog_generation)から converse 関数を注入します。


blog_parts

blog_rewriter(過去ブログのリライト)と blog_generation(書き下ろし)が共有する、 blog_builder へ渡すデータの組み立て部品です。

モジュール 役割
blocks.py AI が出力する「ブロック JSON」の解釈・検証・修復・テキスト化。ブロックの種類と形式は blog_builder の articleBlockSchema と整合させる
race_card.py レースに紐づくブログ向けの着順テーブル(全着順)と関連情報レースカードの組み立て

thumbnail_generation_agent

レース動画・レース結果・コース画像(競馬のみ)を総合的に解析し、 記事サムネイルに最適な瞬間(動画先頭からの経過秒数)を選定する AI エージェントです。 LangGraph と Amazon Bedrock(Nova Pro)を利用します。

フレーム抽出や S3・DB 操作は呼び出し側(apps/thumbnail_generation)が担います。 出力は {"timestamp_seconds": float, "reason": str} です。


ng_word_filter

生成したテキストに NG ワードが含まれていないかを Aho-Corasick で検査するパッケージです。

関数 役割
load_ng_words() DocumentDB の ng_word コレクションから NG ワードを取得する
build_matcher() 検査対象の語を絞り込んでオートマトンを構築する
NgWordMatcher.find() テキストに含まれる NG ワードを返す

誤検出対策として一部の語は検査対象から除外します(詳細は filter.py のコメントを参照)。


tone_of_voice

オッズパークのトーンオブボイス ガイドラインを、生成プロンプトへ差し込むための共通パッケージです。 レース結果記事(article_generation)とリライトブログ(blog_rewriter)の両方から同じ文面を参照します。

ガイドラインが更新されたときは src/prompts/tone_of_voice.yml だけを直します。