共有パッケージ(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 だけを直します。