コンテンツにスキップ

記事 API

レースに紐づく「レース結果10秒サマリー」記事(article)を管理するエンドポイントです。

記事とレースの関係

各記事は race_id により auto_racing / bicycle_racing / horse_racing テーブルのいずれかのレース(_id)に紐づきます。 記事はカテゴリを持ちません(カテゴリを持つのは blog / old_blog のみ)。

記事のステータス

記事は status フィールドで状態を管理します。

ステータス 説明
generating 生成中。AI による記事生成・ビルドが実行中
draft 下書き。デフォルト
published 公開
failed 生成失敗。generation_error に理由が入る。再生成の起点になる

generating / failed は生成パイプラインが付けるステータスです。 POST /v1/articles・PUT /v1/articles/{article_id} の入力としては指定できません(400 を返します)。

記事スキーマ

フィールド データ型 備考
id string _id
race_id string auto_racing / bicycle_racing / horse_racing テーブルの _id
title string
body_html string HTML形式。AI 生成時は article_builder Lambda が React コンポーネント(RaceSummary)を renderToStaticMarkup で HTML 化したもの
status enum generating / draft / published / failed。デフォルト: draft
opening_at number 公開予定日時。UNIXタイムスタンプ(ミリ秒)
closing_at number | null 公開終了日時。UNIXタイムスタンプ(ミリ秒)
accuracy_test_data object | null 精度検証用データ。scripts/article_accuracy_test/audit.py が参照する
generation_error string | null status=failed のときの失敗理由
custom_prompt string | null 前回の再生成で使った追加指示。入力欄の初期値に使う
created_at number UNIXタイムスタンプ(ミリ秒)
updated_at number UNIXタイムスタンプ(ミリ秒)

POST /v1/articles/generation

概要

生成AIを用いて記事を生成します。

記事を generating 状態(body_html は空)で作成し、SQS(article-generation キュー)経由で article_generation Lambda へ記事生成ジョブを非同期で依頼します。 リクエスト受理後すぐに 202 を返し、バックグラウンドで生成が実行されます。 生成結果は別の SQS(article-builder キュー)経由で article_builder Lambda に渡り、 記事 HTML(body_html)を構築して DocumentDB に保存し、status を draft に遷移させます。

AI生成を伴わずに記事を作成する場合は POST /v1/articles を使用してください。

不成立・中止レースは生成できない

着順が無いレース(不成立・中止)は記事を生成しません。着順を書ける材料が無いまま生成させると AI が勝者を創作してしまうためです。該当する場合は 422 を返します。

リクエストとレスポンス

ヘッダー

リクエストヘッダー

  • Content-Type: application/json

レスポンスヘッダー

  • Content-Type: application/json

URI

POST /v1/articles/generation

リクエストボディ

{
  "race_id": "string",
  "title": "string",
  "opening_at": 1234567890000,
  "closing_at": 1234567890000
}
フィールド データ型 必須 備考
race_id string ◯ auto_racing / bicycle_racing / horse_racing テーブルの _id
title string ◯ 生成する記事のタイトル・トピック
opening_at number ◯ UNIXタイムスタンプ(ミリ秒)
closing_at number UNIXタイムスタンプ(ミリ秒)

レスポンス

202 Accepted

{
  "id": "string"
}
フィールド データ型 備考
id string 作成された記事の _id

例外処理

説明 ステータスコード ステータス名
リクエストボディが不正 422 Unprocessable Entity
race_id が auto_racing / bicycle_racing / horse_racing テーブルのいずれにも存在しない 400 Bad Request
着順が無いレース(不成立・中止)で記事を生成できない 422 Unprocessable Entity
内部サーバーエラー 500 Internal Server Error

処理フロー

  1. リクエストを受信する
  2. race_id が3つのレースコレクションのいずれかに存在するか検証する
  3. レース結果に着順があるか(不成立・中止でないか)を検証する
  4. 記事を generating 状態(body_html は空)で新規作成して保存する
  5. SQS(article-generation キュー)へ {article_id, race_id, racing_type, title, race_result} を送信する
  6. 202 Accepted と作成した id を返す
  7. (非同期)article_generation Lambda がレース解析と記事執筆を行い、article-builder キューへ送信する
  8. (非同期)article_builder Lambda が body_html を構築して保存し、status を draft に遷移させる
flowchart TD
    Start([POST /v1/articles/generation]) --> RaceCheck[race_id の存在確認]
    RaceCheck -->|存在しない| E400[400 Bad Request]
    RaceCheck -->|OK| VoidCheck[着順の有無を確認]
    VoidCheck -->|着順なし| E422[422 Unprocessable Entity]
    VoidCheck -->|OK| SaveArticle[記事を generating で保存]
    SaveArticle --> SendSQS[SQS article-generation キューへ送信]
    SendSQS --> Success[202 Accepted]
    SendSQS -.->|非同期| Generate[article_generation がレース解析 + 記事執筆]
    Generate -.-> Build[article_builder が HTML を構築して保存]
    Build -.-> Draft[status を draft に遷移]
    Generate -.->|最終試行で失敗| Failed[status を failed に遷移]

POST /v1/articles/{article_id}/regeneration

概要

生成に失敗した記事(status=failed)を、追加指示つきで作り直します。

同じ入力で作り直しても同じ理由で失敗するため、運用者が書き方の指示(custom_prompt)を 与えられるようにしています。指示を省いた場合は前回の指示をそのまま引き継ぐため、 一覧から連打しても指示が失われません。

対象は status=failed の記事だけです。draft / published を作り直すと、 確認済みの本文を上書きしてしまうため許可していません。

リクエストとレスポンス

ヘッダー

リクエストヘッダー

  • Content-Type: application/json

URI

POST /v1/articles/{article_id}/regeneration
パスパラメータ データ型 備考
article_id string article._id

リクエストボディ

{
  "custom_prompt": "string"
}
フィールド データ型 必須 備考
custom_prompt string 運用者からの追加指示(書き方の希望)。省略・空文字の場合は前回の指示を引き継ぐ

レスポンス

202 Accepted

{
  "id": "string"
}

例外処理

説明 ステータスコード ステータス名
記事が存在しない 404 Not Found
記事のステータスが failed ではない 409 Conflict
race_id が存在しない 400 Bad Request
着順が無いレース(不成立・中止)で記事を生成できない 422 Unprocessable Entity
内部サーバーエラー 500 Internal Server Error

処理フロー

  1. article_id で記事を取得する
  2. status=failed であることを確認する
  3. race_id のレースを取得し、着順があるか検証する
  4. 追加指示を決定する(リクエストが空なら前回の custom_prompt を使う)
  5. 記事を generating に戻し、generation_error をクリアして custom_prompt を保存する
  6. SQS(article-generation キュー)へ custom_prompt を含めて送信する
  7. 202 Accepted を返す
flowchart TD
    Start([POST /v1/articles/article_id/regeneration]) --> Fetch[記事を取得]
    Fetch -->|存在しない| E404[404 Not Found]
    Fetch -->|存在する| StatusCheck[status=failed か確認]
    StatusCheck -->|failed 以外| E409[409 Conflict]
    StatusCheck -->|OK| VoidCheck[レースの着順を確認]
    VoidCheck -->|着順なし| E422[422 Unprocessable Entity]
    VoidCheck -->|OK| Reset[status を generating に戻し custom_prompt を保存]
    Reset --> SendSQS[SQS article-generation キューへ送信]
    SendSQS --> Success[202 Accepted]

キュー投入前にステータスを戻す

article_generation は status=generating の記事だけを処理するため、 キューへ入れる前に generating へ更新しています。


POST /v1/articles

概要

記事を手動で新規作成します。

リクエストとレスポンス

ヘッダー

リクエストヘッダー

  • Content-Type: application/json

レスポンスヘッダー

  • Content-Type: application/json

URI

POST /v1/articles

リクエストボディ

{
  "race_id": "string",
  "title": "string",
  "body_html": "string",
  "status": "draft",
  "opening_at": 1234567890000,
  "closing_at": 1234567890000
}
フィールド データ型 必須 備考
race_id string ◯ auto_racing / bicycle_racing / horse_racing テーブルの _id
title string ◯
body_html string ◯ HTML形式
status enum draft / published。省略時は draft。generating / failed は指定できない
opening_at number ◯ UNIXタイムスタンプ(ミリ秒)
closing_at number UNIXタイムスタンプ(ミリ秒)

レスポンス

201 Created

記事スキーマを返します。

例外処理

説明 ステータスコード ステータス名
リクエストボディが不正 422 Unprocessable Entity
status に generating / failed を指定した 400 Bad Request
race_id が存在しない 400 Bad Request
内部サーバーエラー 500 Internal Server Error

GET /v1/articles

概要

記事の一覧を取得します。

リクエストとレスポンス

URI

GET /v1/articles

クエリパラメータ

パラメータ データ型 必須 備考
race_id string レースでフィルタ
status enum generating / draft / published / failed でフィルタ
page number 取得するページ番号。デフォルト: 1
size number 1ページあたりの件数。デフォルト: 10
order enum asc / desc(updated_at 基準)。デフォルト: desc

レスポンス

200 OK

{
  "list": [
    {
      "id": "string",
      "race_id": "string",
      "title": "string",
      "body_html": "string",
      "status": "draft",
      "opening_at": 1234567890000,
      "closing_at": null,
      "accuracy_test_data": null,
      "generation_error": null,
      "custom_prompt": null,
      "created_at": 1234567890000,
      "updated_at": 1234567890000
    }
  ],
  "current_page": 1,
  "total_count": 10
}

生成失敗の一覧

管理画面の「生成失敗一覧」は GET /v1/articles?status=failed で取得します。


GET /v1/articles/{article_id}

概要

指定した記事を取得します。

リクエストとレスポンス

URI

GET /v1/articles/{article_id}
パスパラメータ データ型 備考
article_id string _id

レスポンス

200 OK

記事スキーマを返します。

例外処理

説明 ステータスコード ステータス名
記事が存在しない 404 Not Found
内部サーバーエラー 500 Internal Server Error

GET /v1/races/{race_id}/articles/{article_id}

概要

指定したレースに紐づく記事を取得します。 記事の race_id が race_id と一致しない場合は 404 を返します。

競技種別ごとにエンドポイントを分けていた /v1/auto_racings/... 等は、 レースの統合(/v1/races)にあわせてこのエンドポイントへ統合されました。

リクエストとレスポンス

URI

GET /v1/races/{race_id}/articles/{article_id}
パスパラメータ データ型 備考
race_id string レースの _id
article_id string article._id

レスポンス

200 OK

記事スキーマを返します。

例外処理

説明 ステータスコード ステータス名
記事が存在しない 404 Not Found
記事の race_id がパスの race_id と一致しない 404 Not Found
内部サーバーエラー 500 Internal Server Error
flowchart TD
    Start([GET /v1/races/race_id/articles/article_id]) --> Fetch[DBから検索]
    Fetch -->|存在しない| E404[404 Not Found]
    Fetch -->|存在する| RaceCheck[race_id の一致確認]
    RaceCheck -->|不一致| E404_race[404 Not Found]
    RaceCheck -->|一致| Success[200 OK]

PUT /v1/articles/{article_id}

概要

指定した記事を更新します。

リクエストとレスポンス

ヘッダー

リクエストヘッダー

  • Content-Type: application/json

URI

PUT /v1/articles/{article_id}
パスパラメータ データ型 備考
article_id string _id

リクエストボディ

{
  "race_id": "string",
  "title": "string",
  "body_html": "string",
  "status": "published",
  "opening_at": 1234567890000,
  "closing_at": 1234567890000,
  "accuracy_test_data": {}
}
フィールド データ型 必須 備考
race_id string auto_racing / bicycle_racing / horse_racing テーブルの _id
title string
body_html string HTML形式
status enum draft / published。generating / failed は指定できない
opening_at number UNIXタイムスタンプ(ミリ秒)
closing_at number UNIXタイムスタンプ(ミリ秒)
accuracy_test_data object 精度検証用データ。構造は article テーブル を参照

指定されなかった(null の)フィールドは更新しません。

body_html と accuracy_test_data は一緒に直す

本文を機械的に直したとき(「逃げ切り」→「押し切り」など)に body_html だけを更新すると、 監査(scripts/article_accuracy_test/audit.py)が読む accuracy_test_data が古いまま残り、 表示と検証結果が食い違います。両方を1回の更新で揃えられるようにしてあります。

レスポンス

200 OK

更新後の記事スキーマを返します。

例外処理

説明 ステータスコード ステータス名
リクエストボディが不正 422 Unprocessable Entity
status に generating / failed を指定した 400 Bad Request
race_id 指定時、レースが存在しない 400 Bad Request
記事が存在しない 404 Not Found
内部サーバーエラー 500 Internal Server Error

DELETE /v1/articles/{article_id}

概要

指定した記事を論理削除します。

リクエストとレスポンス

URI

DELETE /v1/articles/{article_id}

レスポンス

204 No Content

例外処理

説明 ステータスコード ステータス名
記事が存在しない 404 Not Found
内部サーバーエラー 500 Internal Server Error