0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

六曜APIを自前実装するな——生成AI時代の暦判定で精度事故を防ぐ方法

0
Posted at

TL;DR

  • 「結婚式の日取りを提案するAI」「引越し日をスコアリングするSaaS」「占い系チャットボット」など、六曜を扱う業務要件は生成AI時代に急増している
  • しかし、六曜を自前で計算すると、特定の日付で静かに誤算する事故が起きやすい
  • 原因は六曜計算式ではなく、その前段の 「太陽暦 → 旧暦」変換に天文学的精度が必要 なため
  • 解決策は、AIエージェント前提で設計された日本暦API(例: Shirabe Calendar API)を挟むこと。curl / fetch / requests の3行で終わる

この記事の対象読者

  • LLMエージェント・AI SaaS を開発しているフルスタックエンジニア
  • 「六曜くらいなら自前で書けるだろう」と一度は考えた人
  • ChatGPT / Claude / Gemini にロジックを書かせて本番投入した経験がある人

問題: 「六曜判定」をAIに書かせると何が起きるか

生成AIエージェントに「指定日が大安かどうか判定する関数を書いて」と頼むと、だいたい次のような構造のコードが返ってくる。

// 生成AIが提案しがちな「素朴な六曜計算」
function getRokuyo(gregorianDate: Date): string {
  // ❶ グレゴリオ暦 → 旧暦(年・月・日)に変換
  const lunar = convertToLunar(gregorianDate);   // ←ここが事故の本体

  // ❷ 六曜 = (旧暦月 + 旧暦日) mod 6
  const index = (lunar.month + lunar.day) % 6;
  return ["大安", "赤口", "先勝", "友引", "先負", "仏滅"][index];
}

❷ の六曜マッピングは実際その通りで、これは合っている。バグるのは必ず ❶ の convertToLunar のほうだ。

よくある convertToLunar の書き方(全部バグる)

  • 29.5日周期で直近新月からの日数を数える: 朔望月(新月〜新月)は実際には 29.2707日〜29.8376日の範囲で変動する。平均値 29.5306 を使っても、1年も走れば数日ズレる
  • どこかに落ちてた旧暦変換テーブルをそのまま埋め込む: テーブルの終端年を超えると黙ってずれる。閏月の扱いがテーブルによって違う
  • lunar-javascript 等の中国版農暦ライブラリを流用する: 日本の天保暦と中国農暦は朔の基準時刻(UTC+8 vs UTC+9)と閏月配置ルールが異なり、年に数回ズレる日が出る
  • ChatGPTに書かせた 30〜50行の自前式: 太陽黄経・月黄経の近似式がどこか一桁雑で、朔の日境界に当たる日だけ答えが1日ズレる

ズレ方の怖いところ

  • 実行してもエラーにならない: 型は通る、例外も出ない、常に六曜っぽい文字列が返る
  • テストで気付きにくい: 2〜3日だけピンポイントで間違うので、サンプリングテストだと通ってしまう
  • 結婚式当日に気付く: 「大安」として紹介した日が実は仏滅だった、という形で顕在化する

これは要するに サイレント事故 のクラスで、LLMにコードを書かせる時代にもっとも発生しやすい類のバグだ。


根本原因: 旧暦変換は天文学が要る

六曜の判定式 (旧暦月 + 旧暦日) mod 6 は決定的で、一度旧暦が正しく求まれば絶対に合う。問題は旧暦のほうで、日本の旧暦(天保暦)は以下を満たす必要がある。

  1. 朔(さく = 新月)の瞬間を、東経135度・日本標準時で求める
  2. 朔の日を旧暦1日とする
  3. 二十四節気のうち中気(雨水・春分・穀雨…)を含まない月を閏月とする
  4. 年によって閏月の配置が変わる(約19年に7回、Metonic cycle)

これを真面目にやるには、太陽黄経と月黄経を天文学的精度で計算する必要がある(VSOP87等)。近似式で済ませると、朔が真夜中付近になる日に1日ズレて、その年の以降の旧暦日付がすべて1日ズレる、というドミノ倒しが起きる。

参考までに、広く引用されている旧暦計算アルゴリズムに QREKI.AWK(高野英明氏)がある。これはAWKで書かれた数百行のコードで、月・太陽の視黄経を級数展開で計算している。手書きで移植するのは 量的にも原理的にもLLM向きの作業ではない


解決策: 日本暦専用APIに投げる

自前計算を捨てて、AIエージェント前提で設計された日本暦APIに丸投げするほうが、精度・保守コスト・AI統合のしやすさの全てで有利になる。以降の例は Shirabe Calendar API を使う。Free枠 月10,000回 で、APIキーなしでも試せる。

3行で動くサンプル

# 指定日の暦情報を取得(六曜・暦注・干支・二十四節気まで1レスポンス)
curl https://shirabe.dev/api/v1/calendar/2026-04-15

返ってくるJSONはこんな構造:

{
  "date": "2026-04-15",
  "rokuyo": {
    "name": "大安",
    "reading": "たいあん",
    "timeSlots": { "morning": "吉", "noon": "吉", "afternoon": "吉", "evening": "吉" }
  },
  "rekichu": [
    { "name": "一粒万倍日", "type": "吉" }
  ],
  "context": {
    "wedding": { "judgment": "大吉", "score": 9,
      "note": "大安 × 一粒万倍日。結婚式に非常に良い日。" }
  },
  "summary": "令和8年4月15日(水)大安・一粒万倍日。結婚式・開業に大吉の日。"
}

ポイントは contextsummary フィールドで、単なる「大安」というラベルではなく、用途ごとの判定(8カテゴリ、1〜10のスコア付き)と1行サマリまで返してくれる。AIエージェントはこの summary をそのままユーザーへの回答に差し込める。

TypeScript

const res = await fetch(
  "https://shirabe.dev/api/v1/calendar/2026-04-15",
  { headers: { "X-API-Key": process.env.SHIRABE_API_KEY! } }
);
const data = await res.json();
console.log(data.rokuyo.name);   // "大安"
console.log(data.summary);       // "令和8年4月15日(水)大安・一粒万倍日..."

Python

import os, requests

r = requests.get(
    "https://shirabe.dev/api/v1/calendar/2026-04-15",
    headers={"X-API-Key": os.environ["SHIRABE_API_KEY"]},
    timeout=10,
)
print(r.json()["rokuyo"]["name"])

範囲取得・目的別ベスト日検索

単日だけでなく、期間で絞り込む系もある。

# 4月の大安・友引だけ一覧(最大93日)
curl "https://shirabe.dev/api/v1/calendar/range?start=2026-04-01&end=2026-04-30&filter_rokuyo=大安,友引"

# 4〜12月で「結婚式に最適な日」を上位5件、スコア順で
curl "https://shirabe.dev/api/v1/calendar/best-days?purpose=wedding&start=2026-04-01&end=2026-12-31&limit=5"

best-daysAIエージェントが「判断」までを外部化する想定で作られている。期間と用途(wedding / moving / business など8カテゴリ)を投げるだけで、スコア上位日のランキングと根拠(大安 × 天赦日など)が返る。


AIエージェントへの統合

この手のAPIは、ChatGPT GPTs / Claude Tool Use / Gemini Function Calling / LangChain / Dify のいずれからでも一発で呼べるように OpenAPI 3.1 仕様を公開しておくと楽だ。Shirabeの場合は https://shirabe.dev/openapi.yaml にそのまま置かれている。

ChatGPT GPTs Actions

GPT Builder の "Create new action" で Import URL にOpenAPI URLを貼るだけ。

https://shirabe.dev/openapi.yaml

Authentication は API Key(ヘッダー名 X-API-Key)を選ぶ。これだけでカスタムGPTが自動的にShirabeを呼び出せるようになる。

Claude Desktop / MCP

claude_desktop_config.json にリモートMCPサーバーを登録するだけ。

{
  "mcpServers": {
    "shirabe-calendar": { "url": "https://shirabe.dev/mcp" }
  }
}

あとは Claude に「来月の結婚式に向く日を5個教えて」と聞けば、裏で find_best_days ツールが自動で呼ばれる。

LangChain / LlamaIndex / Dify

OpenAPI Loader で同じURLを食わせるだけでツール化される。operationId がそのまま関数名になる設計のため、特別な glue コードは不要だった。


自前実装 vs API の比較

観点 自前実装 / LLM生成コード 日本暦API(Shirabe等)
旧暦計算精度 近似式では朔境界で1日ズレる 天文学的精度
暦注の網羅性 一粒万倍日など13種を実装するのは別個の大仕事 標準で13種以上
用途別吉凶判定 仕様化・実装ともに大変 context.wedding.score 等で標準提供
メンテナンス 朔の計算ライブラリのバージョン管理など発生 利用側ゼロ
AI統合 自前で Tool定義 / Function定義を書く OpenAPI 1URLで完了
初期コスト ゼロに見えるが、後ろに「事故1件の賠償」が控える Free枠 月10,000回

「無料で自前」は見かけだけ無料で、失敗時の事業リスク込みで見ると大抵は割に合わない、というのがこの手のドメインの実情だと思う。


まとめ

  • 六曜そのものの計算式は簡単。難しいのは その前段の旧暦変換 で、これには天文学的精度の朔計算が要る
  • LLMに自前実装を書かせると、朔境界の日だけピンポイントで1日ズレる サイレント事故 が起きやすい
  • 冠婚葬祭・引越し・契約日など、間違えた日に気付くのが当日というドメインで使うなら、自前実装は回避したほうが安い
  • AIエージェント前提で設計された日本暦API(OpenAPI公開、MCP対応、Free枠あり)を使えば、fetch 3行で終わる

サンプルコードは https://shirabe.dev/openapi.yamlexamples にもまとまっている。ChatGPT GPTs / Claude Tool Use / Gemini Function Calling から直接呼び出したい場合も同じURLを使えばよい。

暦注(一粒万倍日・天赦日・三隣亡など)の解説は別記事で書く予定。


関連リンク


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?