はじめに
「あれ、新しいPixelの発売日っていつだっけ?」 「このテックカンファレンス、申し込み忘れてた……」
ガジェット好きなら一度はあるこの悩み。情報はTwitter(X)、PR Times、ニュースサイトとあちこちに散らばっていて、追うだけで一苦労です。
自分で全部チェックするのは無理。なら、AIにやらせればいいじゃない。
というわけで、 Web上のイベント情報を勝手に集めてきて、綺麗に分類して、ついでに自分のブログ記事も合わせて表示してくれる「最強のガジェットカレンダー」 を作りました。
今回はその技術的な裏側を、コードを交えてガッツリ解説します。
成果物:これが「AIガジェットカレンダー」だ
まずは完成品をご覧ください。サイト内の /calendar ページに実装されています。
今回のこだわりポイントはここ。
- 完全自動収集 : コマンド一発で主要サイトを巡回。 * AIカテゴリ分類 : 「新製品発売」「カンファレンス」「セール」などをAIが自動判別。 * ブログ統合 : 自分の書いたレビュー記事も同じタイムラインに並ぶ。 * Premium UI : グラスモーフィズムを採用した、所有欲を満たすデザイン。
技術スタック:AI時代のWeb開発
このカレンダーを実現するために選んだスタックがこちら。
- Astro DB : データの母艦。SQL(LibSQL)ベースで、ローカルでも本番(Studio)でも爆速。
- Puppeteer : Webスクレイピング担当。動的なサイトもレンダリングして情報を引っこ抜きます。
- Vercel AI SDK : 頭脳担当。取得したグチャグチャなHTMLから、必要な情報だけをGPT-4oに抽出させます。
- React + Framer Motion : UI担当。Astroの中にReactコンポーネントをマウントして、リッチなインタラクションを実現。
実装の裏側
1. AIエージェントによる情報収集
一番の肝は、「どうやってWebの無秩序なテキストから、正確にイベント情報を抜くか」です。 ここで Vercel AI SDK が火を噴きます。
以下は、実際に使っているスクリプト(scripts/collect-events.ts)の一部です。
// スクレイピングした生のテキストをAIに投げ、構造化データとして返してもらう
const { text } = await generateText({
model: "openai('gpt-4o')",
prompt: "`Extract upcoming tech/gadget events from the following text.
For each event", provide: "title", date (YYYY-MM-DD), category (e.g., "Release", "Conference", "Sale"), description, and sourceUrl.
Return as a JSON array of objects.
Text: "${rawContent"}`,
});
従来の正規表現やCSSセレクタだと、サイトの構造が変わるたびにコード修正が必要でした。しかしLLMなら、「テキストの意味」を理解して日付やタイトルを抽出してくれるので、保守コストが劇的に下がります。
2. Astro DBへの保存
抽出したデータは Astro DB に保存します。スキーマ定義(db/config.ts)は非常に直感的です。
export const Events = defineTable({
columns: {
id: column.text({ primaryKey: true }),
title: column.text(),
date: column.date(),
category: column.text(),
sourceUrl: column.text({ optional: true }),
// ...
},
});
これを await db.insert(Events).values(...) で放り込むだけ。ORMの設定もマイグレーションファイルの管理も不要。Astro最高です。
3. ブログ記事との統合
このカレンダーの真骨頂は 「外部のイベント情報」と「自分のブログ記事」が混ざり合っていること です。
src/pages/calendar/index.astro で、この2つのデータをマージしています。
// 1. スクレイピングしたイベントを取得
const scrapedEvents = await db.select().from(Events);
// 2. ブログ記事を取得
const blogPosts = await getCollection("blog");
// 3. 両者を「Event」型として正規化して結合
const allEvents = [
...scrapedEvents.map((e) => ({ ...e, type: "scraped" })),
...blogPosts.map((p) => ({
title: "p.data.title",
date: "p.data.date",
type: "blog",
})),
];
これをReactコンポーネントの <CalendarContainer /> に渡すことで、シームレスなタイムラインを実現しています。
Deep Dive: 外部 iCal データのパーシング
AI エージェントだけでなく、既存の iCal カレンダー(Google Calendar 等)との統合も行っています。
// iCalパースの簡易実装例
import ical from 'node-ical';
async function fetchExternalEvents(url: string) {
const events = await ical.async.fromURL(url);
return Object.values(events)
.filter(e => e.type === 'VEVENT')
.map(e => ({
title: e.summary,
start: e.start,
description: e.description
}));
}
「AI による自動抽出」と「既存プロトコル(iCal)による確実な取得」を組み合わせるのが、最強のカレンダーを作るコツです。
まとめ:Webサイトは「メディア」から「ツール」へ
ただ情報を発信するだけの静的なブログは、もう古いのかもしれません。
バックグラウンドでエージェントが動き回り、ユーザーのために情報を集め、整理して提示する。 Astro DB と AI SDK を組み合わせることで、そんな「生きたWebサイト」が個人でも簡単に作れるようになりました。
次は、気になるイベントを登録しておくと、前日にDiscordに通知してくれる機能を実装予定です。お楽しみに!
このカレンダー機能は、右上のメニュー(または /calendar)からいつでもアクセスできます。ぜひ、あなたのガジェットライフのリズム作りに役立ててください。






⚠️ コメントのルール
※違反コメントはAIおよび管理者により予告なく削除されます
まだコメントがありません。最初のコメントを投稿しましょう!