日報作成を Google Calendar API と Amazon Bedrock で半自動化してみた
はじめに
日報を毎日書くのは手間がかかります。書く内容の大半は、その日にやったことと明日の予定です。これらは Google Calendar にすでに入っています。もともと Google Calendar API で取得した JSON を手動で Claude のプロンプトに貼り付けて日報の下書きを作っていたのですが、これを CLI のコマンドひとつで完結させたいと思い、半自動化ツールを作りました。
本記事では、このシステムの構成と、特に Lambda + Bedrock まわりの実装について紹介します。
Amazon Bedrock とは
Amazon Bedrock は、Anthropic や Meta などが提供する基盤モデルを API 経由で利用できる AWS のフルマネージドサービスです。モデルのホスティングやスケーリングを AWS 側で管理してくれるため、アプリケーション開発者は推論の呼び出しに集中できます。
検証環境
- Node.js 22
- TypeScript 5.7
- AWS Lambda (nodejs22.x)
- Amazon Bedrock (Claude Sonnet 4.5)
- Terraform 1.5
対象読者
- 日報やレポートの作成を効率化したいエンジニア
- Google Calendar API の実用的なユースケースを知りたい方
- Amazon Bedrock を使ったアプリケーション開発に興味がある方
参考
システム全体像
構成は以下の通りです。
ローカルの CLI が Google Calendar から予定を取得し、不要なイベントをフィルタリングした上で API Gateway に POST します。Lambda が Bedrock (Claude Sonnet 4.5) を呼び出し、生成された日報 Markdown を返却します。
CLI と AWS を分離した理由は主に以下の 2 点です。
- Google OAuth のトークン管理をローカルに閉じたかった
- LLM の呼び出しを Lambda に任せることで、プロンプトやモデルの変更を CLI の再ビルドなしに反映できる
技術スタック
- CLI: TypeScript (Node.js 22), googleapis, @smithy/signature-v4
- Lambda: TypeScript, @aws-sdk/client-bedrock-runtime
- インフラ: Terraform (API Gateway HTTP API, Lambda, IAM)
Google Calendar からの予定取得とフィルタリング
CLI では Google Calendar API v3 の events.list を使い、対象日と翌日の予定を JST 基準で取得します。取得した予定はすべて LLM に渡すわけではありません。 CLI 側で以下のフィルタリングを行います。
- キャンセル済みイベントの除外
- 終日予定・複数日にまたがる予定の除外
- タイトルが空のイベントの除外
- 設定ファイル (
config.yaml) で指定したタイトルとの完全一致による除外 - 参加ステータスによるフィルタ
参加ステータスの判定ロジックは以下のように実装しています。
function computeMyStatus(
event: calendar_v3.Schema$Event
): "OWNER" | "YES" | "NO" {
if (event.organizer?.self === true || event.creator?.self === true) {
return "OWNER";
}
const selfAttendee = event.attendees?.find((a) => a.self === true);
if (selfAttendee?.responseStatus === "accepted") {
return "YES";
}
return "NO";
}
自分が主催者 (OWNER) または参加承諾済み (YES) のイベントだけを LLM に送ります。辞退したイベントや未回答のイベントは日報に含める必要がないためです。フィルタリングを CLI 側で行う狙いは、不要なデータを LLM に送らないようにすることでトークン消費を抑えることです。
Amazon Bedrock による日報生成
ここがシステムの中核です。Lambda が CLI から受け取ったデータを元にプロンプトを組み立て、Bedrock の InvokeModel API で Claude Sonnet 4.5 を呼び出します。
Lambda の入出力
CLI から Lambda への入力は以下の JSON 構造です。
{
"date": "2026-02-14",
"timezone": "Asia/Tokyo",
"calendar_today": [
{ "date": "2026-02-14", "start": "10:00", "end": "11:00", "title": "チーム定例", "myStatus": "OWNER" }
],
"calendar_tomorrow": [...],
"prev_report_markdown": "前営業日の日報 Markdown (あれば)",
"hitl_markdown": "ユーザーが書いた自由記述メモ"
}
Lambda はこの入力からプロンプトを構築し、Bedrock を呼び出して、生成された Markdown をそのまま返します。
Bedrock InvokeModel の呼び出し
Bedrock の呼び出し部分は以下の通りです。
const MODEL_ID = "jp.anthropic.claude-sonnet-4-5-20250929-v1:0";
const client = new BedrockRuntimeClient({});
const command = new InvokeModelCommand({
modelId: MODEL_ID,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 2048,
system: systemPrompt,
messages: [{ role: "user", content: userMessage }],
}),
});
const response = await client.send(command);
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
const markdown = responseBody.content?.[0]?.text;
モデル ID に jp. プレフィックスが付いているのは、クロスリージョン推論プロファイルを使用しているためです。ap-northeast-1 からリクエストしても、AWS が日本国内の利用可能なリージョン (ap-northeast-1, ap-northeast-3 など) に自動ルーティングします。
resource "aws_iam_role_policy" "lambda_bedrock" {
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = ["bedrock:InvokeModel"]
Resource = [
"arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0",
"arn:aws:bedrock:ap-northeast-1:${account_id}:inference-profile/jp.anthropic.claude-sonnet-4-5-20250929-v1:0"
]
}]
})
}
プロンプト設計
日報の出力形式を安定させるために、システムプロンプトで出力テンプレートとルールを明示的に指定しています。
あなたは日報作成アシスタントです。与えられた情報のみを使い、以下のMarkdownテンプレートに従って日報を生成してください。
## 出力ルール
- 出力はMarkdown本文のみ。前置き・解説・コードブロックは一切不要。
- 見出しは以下の5つで固定:
1. # 日報 yyyy年mm月dd日 (曜日) 9:00-18:00 (休憩 1:00)
2. ## やったこと
3. ## 明日の予定
4. ## 今後のタスク
5. ## ひとこと
- 根拠のない推測や捏造は一切行わない。
- 情報がない場合は「- なし」と記述する。
特に意識した点は 2 つあります。
-
見出し構造の固定
日報のフォーマットが毎回変わると読み手が困るため、見出し名と順序をプロンプトで固定しています。 -
捏造の防止
LLM はもっともらしい内容を生成しがちですが、日報で実際にやっていないことを書かれるのは困ります。「根拠のない推測や捏造は一切行わない」「情報がない場合はなしと記述する」というルールを明示して、カレンダーとメモにない情報は生成しないように制約しています。
API Gateway と SigV4 認証
Lambda のフロントには API Gateway HTTP API を配置しています。REST API ではなく HTTP API を選択した理由はコストです。HTTP API は REST API と比較して料金が低く、今回のようなシンプルな API には適しています。
認証には IAM 認証 (SigV4) を採用しました。CLI 側では @smithy/signature-v4 を使ってリクエストに署名しています。API キーの管理が不要で、既存の AWS IAM の仕組みに乗れる点が利点です。
インフラは Terraform で構成しています。リソースは API Gateway HTTP API, Lambda, IAM ロール・ポリシー, CloudWatch Logs の 4 種類で、最小限の構成に抑えています。
実行結果
実際に実行してみたところ、次のようになりました。
$ npm run report
日報生成: 2026-02-14
メモファイルを編集してください: /path/to/memo/memo-2026-02-14.md
編集が完了したら "yes" と入力してください (それ以外で中止):
>
memo/memo-2026-02-14.md に自由記述を行い、 yes を入力しました。
> yes
設定を読み込みました。
Google Calendar に認証中...
認証完了。
カレンダーイベントを取得中...
今日: 5 件, 明日: 3 件
フィルタ後 - 今日: 4 件, 明日: 2 件
前営業日の日報を読み込みました。
日報を生成中...
日報を保存しました: /path/to/daily/daily-report-user-2026-02-14.md
生成された日報の例は以下の通りです (予定名はダミーに置換しています) 。
# 日報 2026年02月14日 (土) 9:00-18:00 (休憩 1:00)
## やったこと
- チーム定例
- プロジェクト A 設計レビュー
- 社内勉強会 (Bedrock ハンズオン)
- 1on1
## 明日の予定
- スプリントプランニング
- プロジェクト B キックオフ
## 今後のタスク
- プロジェクト A テスト環境構築
- ドキュメント更新
## ひとこと
勉強会で Bedrock の活用事例を共有できた。
まとめ
Google Calendar API と Amazon Bedrock を組み合わせて、コマンドひとつで日報の下書きを生成する仕組みを作りました。カレンダーの予定をフィルタリングして LLM に渡し、固定テンプレートに沿った Markdown を生成させるというシンプルな構成です。API Gateway + Lambda のサーバーレス構成にしたことで、運用コストも最小限に抑えられています。
今後は、生成した日報を蓄積して期末報告の材料にするなど、日報データの活用についても考えていきたいと思います。









