はじめに
こんにちは!鈴木です。
就活を進めながら企業分析・要約・管理ツールへのメモ整理を全部手動で行うのは、思った以上に時間がかかります。「自分が大切にしたい就活の軸だけメモしておけば、あとは定期的に関連記事を集めてNotionに追記してくれる仕組みを作れないか?」
本記事では、そんな仮説を検証するために、Typescriptのフレームワーク「Mastra」を使ってエージェントを自作した学びと過程を紹介します。
この記事で得られること
- Mastraを使った具体的な実装イメージ
- 就活のような身近な課題を、AIエージェントで解決するまでの思考プロセス
- Notionや外部APIと連携した、自動化ワークフローのノウハウ
自己紹介
改めまして、現在学部3年生の鈴木鴻太 (https://x.com/kota_biz_dev) と申します!
現在は数学科に所属しており、長期のWebアプリ系エンジニアインターンを1年少しほど続けています。
直近ではLLM(大規模言語モデル)の可能性に魅了されていて、「LLMという根幹技術をギークに追い求めたい!!」という想いのもと、勉強や大学院への進学を予定していたりします。
本開発の背景と目的
背景
- 次世代のSaaSの在り方として、いかにUI部分を極力削り、価値をサーバーサイド側に集約させるかが大事だと考えているので、今回のようなテーマを開発したかった
- Mastra を使ってマルチエージェントを手で組み上げる経験がほしかった
- 自分の就活で感じていた「企業分析に時間が割けない」という課題を解決したかった
目的
就活の軸(職種・業界・キーワードなど)をNotionのタブに書いておくだけで、週1回自動的に軸に合致する企業ニュースを要約し、Notionに自動追記していく仕組みを作る
解決したい課題
- 自分の軸に合った企業ニュースを探し出すだけで、あっという間に時間が過ぎてしまう
- 複数のサイトから集めた情報を、後で見返せるようにNotionにまとめ直す作業が地味に面倒
- 結果として、本当に集中したい自己分析や面接対策の時間が削られてしまう
アーキテクチャの全体像
MastraのWorkflow機能を使い、4つのエージェントがタスクを連携するパイプラインを構築しました。
- 司令塔 (Orchestrator): Notionから「就活の軸」を読み取り、調査対象となる企業候補をリストアップする
- 調査 (Research): 企業ごとにGoogle Newsを検索し、関連ニュースを収集・要約する
- 品質評価 (Policy): 要約結果が、就活の軸に合っているか、品質基準を満たしているかを評価する
- 書記 (Writer): 品質チェックを通過した情報のみを、Notionの企業ページに書き込む
以下図解↓
事前準備と主な使用ライブラリ
Mastra本体のインストール
こちらの公式ドキュメントにインストール手順あります。
Mastra公式ドキュメントのMCPサーバーと接続
AIエディタにこちらを接続すれば、モデルのMastra関連に対しての学習問題を解決できます。
Notion API キーの発行
読み込み・書き込みに必須です。
SerpAPI キーの発行
検索対象リンクの抽出を安定して行うために今回利用しました。月250トークンまでは無料です。
Gemini API キーの発行
mastraがopenAIネイティブなものをいくつか提供しているので本当はgptモデルが良かったですが、今回は無料枠があるGeminiで代用します。
その他主要なライブラリ
// package.json (一部抜粋)
{
"dependencies": {
"@notionhq/client": "...", // Notionへの読み書き
"@mozilla/readability": "...", // 記事本文抽出
"jsdom": "...", // HTML解析
"mastra": "...", // メインフレームワーク
"openai": "...", // LLM API (今回はGeminiですがSDKはOpenAI互換)
"serpapi": "...", // ニュース検索
"yaml": "...", // 就活の軸(YAML)の解析
"zod": "..." // 全体のスキーマ定義
}
}
notionテンプレートの準備
以下のような形で「00_JobSearchAxis」というタブの中に、roles(職種),industries(業界),キーワード3つを含めたテンプレートを今回用意しました。
ユーザーはこのテンプレートに沿って就活の軸を記入していくイメージになります。
写真左のように親ページをjob_searhタブとし、その直下にどんどん企業名ごとのタブと検索結果が追加されていくイメージです。
ディレクトリ構成と基本的な実装方針
実装方針について
Mastra の公式ドキュメントに従い、各ツールとエージェントを分離しました。
基本的な実装の流れとしては、
- createAgent でエージェント本体(司令塔・リサーチ・ポリシー・ライター)の振る舞いを定義
- createTool で各エージェントから呼ばれる具体処理(Notion 読み書きやニュース取得など)をモジュール化
- それらを createStep/createWorkflow で「Axis → Orchestrator → Research → Policy → Writer」というフローに組み上げる
という構成になっています。
また、各エージェント間はzodにてInputSchema で受け取り、outputSchema を満たす形で返す。という Mastra の王道パターンで統一しました。
詳細は公式ドキュメントにサンプルコード付きで記載されています。
ディレクトリ構成について
src/
├─ mastra/
│ ├─ agents/
│ │ ├─ orchestratorAgent.ts // 就活の軸から企業候補を選定する司令塔エージェント
│ │ ├─ researchAgent.ts // ニュース記事の要約をLLMに依頼するエージェント
│ │ ├─ policyAgent.ts // 要約の採否を判断するポリシーエージェント
│ │ └─ writerAgent.ts // Notionへ書き込む文などを生成するエージェント
│ ├─ tools/
│ │ ├─ axisReaderTool.ts // NotionからYAML形式の就活軸を読み取り、AxisSchema で検証
│ │ ├─ fetchHtml.ts // 記事URLをフェッチして生 HTML を取得
│ │ ├─ extractMainText.ts // Readability + JSDOM で本文・タイトルを抽出
│ │ ├─ llm.ts // Gemini API を叩いて要約やフィットスコア算出を行う
│ │ ├─ researchTool.ts // ニュース検索 → 本文抽出 → 要約までを一括で実行
│ │ ├─ policyEvaluationTool.ts // AnswerRelevancyMetric を用いたポリシー評価ロジックを実装
│ │ ├─ notionService.ts // Notion API のヘルパー(Axis ページ作成・ブロック追加など)
│ │ └─ notionWriterTool.ts // ポリシーチェック合格の要約をNotionページに追記するツール
│ └─ workflows/
│ └─ jobSearchWorkflow.ts // 上記ツール/エージェントをステップでつないだメインワークフロー
├─ tests/clis/
│ ├─ cli-orchestrate.ts // 開発時に手動実行するためのCLIコマンド
│ └─ run-workflow.ts // 週次自動実行(GitHub Actions から呼び出す想定)
└─ schemas.ts // 全体で共有する zod スキーマ定義
実装
ここからは本格的な実装をしていきます。
大きく4つのStepに分けて実装していきます。
Step1: Notion から就活の軸を取得し、司令塔エージェントに候補企業を挙げさせる
まずは、ワークフローの起点となる「就活の軸」をNotionから読み込みます。
Axis 取得ツール(axisReaderTool)で 00_JobSearchAxis ページの YAML を読み込み、Orchestrator エージェントに渡します。
以下簡潔なソースコード(処理の流れ)
// src/mastra/workflows/jobSearchWorkflow.ts
//就活軸の取得フェーズ
const WorkflowInputSchema = z.object({
rootPageId: z.string().min(1),
limit: z.number().int().positive().max(10).default(3),
recencyDays: z.number().int().positive().max(365).default(90),
});
const AxisStepOutputSchema = z.object({
rootPageId: z.string(),
limit: z.number().int().positive(),
recencyDays: z.number().int().positive(),
axis: AxisSchema,
});
const axisStep = createStep({
id: "axis-step",
description: "Notion から Axis を取得",
inputSchema: WorkflowInputSchema,
outputSchema: AxisStepOutputSchema,
async execute({ inputData, runtimeContext }) {
const axis = await axisReaderTool.execute({
context: { rootPageId: inputData.rootPageId },
runtimeContext,
});
return {
rootPageId: inputData.rootPageId,
limit: inputData.limit,
recencyDays: inputData.recencyDays,
axis,
} satisfies z.infer<typeof AxisStepOutputSchema>;
},
});
//検索候補企業推定フェーズ
const CompanyStepOutputSchema = AxisStepOutputSchema.extend({
companies: z.array(z.string().min(1)).min(1),
reasoning: z.string().optional(),
});
const orchestratorStep = createStep({
id: "orchestrator-step",
description: "Axis から企業候補を推定",
inputSchema: AxisStepOutputSchema,
outputSchema: CompanyStepOutputSchema,
async execute({ inputData }) {
const prompt = [
"You are assisting a Japanese job seeker.",
// 省略…
`Axis JSON: ${JSON.stringify(inputData.axis)}`,
'Return pure JSON of the form {"companies": [...], "reasoning": "..."}',
].join(" \n");
const generation = await orchestratorAgent.generate(prompt);
const raw = generation.text ?? "{}";
const cleaned = raw.trim().replace(/^```(?:json)?/i, "").replace(/```$/, "").trim();
const parsed = JSON.parse(cleaned);
const companies = Array.isArray(parsed.companies)
? parsed.companies.map((c) => String(c).trim()).filter(Boolean)
: [];
if (companies.length === 0) {
throw new Error("司令塔エージェントが企業候補を返しませんでした");
}
return { ...inputData, companies, reasoning: parsed.reasoning };
},
});
司令塔エージェントでは、Axis を JSON で渡し「最大5社まで JSON で返すこと」と厳密に指示。Gemini からの返信は JSON.parse しています。
Step2: 企業ごとに検索をし、Researchエージェントで要約する
司令塔がリストアップした企業名を使って、実際にニュースを収集・要約するステップです。
ここが一番の頑張りどころでした。researchToolという一つのツールに、以下の処理をまとめています。
- SerpAPIでGoogle Newsを検索
- 記事URLにアクセスし、Readability.jsで本文を抽出
- 抽出した本文をGeminiに渡し、箇条書きで要約させる
以下簡潔なソースコード(処理の流れ)
const ResearchOutputSchema = CompanyStepOutputSchema.extend({
research: z.array(
z.object({
company: z.string(),
summaries: z.array(SummarySchema),
})
),
});
const researchStep = createStep({
id: "research-step",
description: "企業ごとのニュース要約を取得",
inputSchema: CompanyStepOutputSchema,
outputSchema: ResearchOutputSchema,
async execute({ inputData, mastra, runtimeContext }) {
const research = [] as z.infer<typeof ResearchOutputSchema>["research"];
for (const company of inputData.companies) {
const result = await researchTool.execute({
context: {
request: {
company,
limit: inputData.limit,
recencyDays: inputData.recencyDays,
axis: inputData.axis,
},
},
mastra,
runtimeContext,
});
research.push(result);
}
return { ...inputData, research };
},
});
Step3: ポリシーエージェントで要約結果をポリシー評価する
Researchエージェントが集めてきた要約が、本当に自分の軸に合っているか、品質は十分かをチェックします。
ポイント
ちょうどMastraにAnswerRelevancyMetricという、回答の適合度を評価する機能が提供されていたので、これを活用しました。LLMに「この要約は、この就活の軸にどれくらい合ってる?」と問いかけ、0から1のスコアを出させています。
0~0.5まではRejectとして、0.6~1はacceptとして返却しています。
詳細な情報
さらにスコアの計算方法もチューニングできるらしく、せっかくなので触ってみました。
以下ここに書かれている計算の流れだったり公式ドキュメントの大雑把な要約になります
回答をステートメント単位に分割
LLM の出力を意味の塊(1 ステートメント)ごとに細分化します。
各ステートメントに対する判定。それぞれのステートメントが質問内容を満たしているかを、LLM が
- “yes” (十分に一致する)
- “unsure” (部分的 or 不確かな一致)
- “no” (無関係)
の 3 段階で判定します。
スコア計算
判定結果を合計してスコア化します(デフォルトの規則):
direct = "yes" の数
partial = "unsure" の数
score = ((direct + uncertaintyWeight * partial) / total_statements) * scale
今回はuncertaintyWeight(不確かな要素に与える重み)を0.6にカスタムしました。
以下簡潔なソースコード(処理の流れ)
const PolicyOutputSchema = ResearchOutputSchema.extend({
qcResults: z.array(QCDecisionSchema),
});
const DEFAULT_POLICY = {
maxChars: 200,
maxBullets: 5,
minFitScore: 0.45,
recencyDays: 90,
language: "ja",
} satisfies z.infer<typeof PolicyConfigSchema>;
const policyStep = createStep({
id: "policy-step",
description: "要約をポリシー評価",
inputSchema: ResearchOutputSchema,
outputSchema: PolicyOutputSchema,
async execute({ inputData, mastra, runtimeContext }) {
const qcResults: z.infer<typeof QCDecisionSchema>[] = [];
for (const item of inputData.research) {
const decision = await policyEvaluationTool.execute({
context: {
company: item.company,
summaries: item.summaries,
axis: inputData.axis,
policy: {
...DEFAULT_POLICY,
recencyDays: inputData.recencyDays,
},
},
mastra,
runtimeContext,
});
qcResults.push(decision);
}
return { ...inputData, qcResults };
},
});
Step4: Writer エージェントで Notion へ書き込む
最後に、品質チェックを通過した(acceptedな)要約だけを、Notionの各企業ページに追記します。
以下簡潔なソースコード(処理の流れ)
const writerStep = createStep({
id: "writer-step",
description: "受理済み要約を Notion に書き込み",
inputSchema: PolicyOutputSchema,
outputSchema: z.object({
qcResults: z.array(QCDecisionSchema),
writerResults: z.array(
z.object({
company: z.string(),
written: z.number(),
pageId: z.string(),
})
),
}),
async execute({ inputData, mastra, runtimeContext }) {
const writerResults: { company: string; written: number; pageId: string }[] = [];
for (const decision of inputData.qcResults) {
if (decision.accepted.length === 0) continue;
const result = await notionWriterTool.execute({
context: {
rootPageId: inputData.rootPageId,
company: decision.company,
summaries: decision.accepted,
axis: inputData.axis,
},
mastra,
runtimeContext,
});
writerResults.push(result);
}
return { qcResults: inputData.qcResults, writerResults };
},
});
最終結果
今回はMastra Playgroundで動作確認していきます。
Mastra Dev Server を起動した状態で、ブラウザで http://localhost:4111/workflows にアクセスすると、Mastra Playground からワークフローを実行できます。
GUI上で直感的に確認することが出来るし、ログなども追えてめちゃくちゃ便利です!
初期状態
実行
また、それぞれのStepにて、「受け取り値のInput」、「レスポンス結果のOutput」を確認することが出来ます。
レスポンス結果をそのまま次のStepの入力に渡しているものがほとんどなので今回はPutputのみを確認していきます。
axis-step
{
"rootPageId": "親のページID",
"limit": 5,
"recencyDays": 300,
"axis": {
"roles": [
"ソフトウェアエンジニア"
],
"industries": [
"Fintech",
"B2B SaaS"
],
"keywords": [
"AI",
"LLM",
"金融"
]
}
}
問題なく読み込めていることが確認できました。
orchestorator-step
{
"rootPageId": "親のページID",
"limit": 5,
"recencyDays": 300,
"axis": {
"roles": [
"ソフトウェアエンジニア"
],
"industries": [
"Fintech",
"B2B SaaS"
],
"keywords": [
"AI",
"LLM",
"金融"
]
},
"companies": [
"マネーフォワード (Money Forward)",
"freee (フリー)",
"LayerX (レイヤーエックス)",
"Sansan (サンサン)",
"LINEヤフー (LINEヤフー)"
],
"reasoning": "これらの企業は、Fintech領域または金融業務に密接に関連するB2B SaaSを提供しており、ソフトウェアエンジニアの採用を積極的に行っています。特にマネーフォワード、freee、LayerXは、会計・経費精算などのB2B SaaS製品において、AIやLLMを活用した自動化・分析機能の開発に注力しており、キーワード(AI, LLM, 金融)と産業(Fintech, B2B SaaS)の軸に強く合致します。LINEヤフーは、大規模な金融サービス(PayPayなど)とB2Bプラットフォームの両方を持ち、AI技術の応用範囲が広範です。"
}
こちらも問題なく企業候補が挙げられていることが確認できました。
research-step(検索結果が膨大だったので一部抜粋しています)
{
"research": [
{
"company": "マネーフォワード (Money Forward)",
"summaries": [
{
"url": "https://corp.moneyforward.com/news/info/20250310-mf-press-1/",
"title": "マネーフォワード、「健康経営優良法人2025(大規模法人部門)」認定のお知らせ",
"bullets": [
"健康経営優良法人認定から、社員のウェルビーイングを重視する文化を把握。(33)",
"「Talent Forward」を重点テーマとし、人材を価値を生む資産と定義。(34)",
"心身ともに健全で活き活きと働ける職場環境を目指している。(30)"
],
"wordCount": 97,
"publishedAt": "03/10/2025, 07:00 AM, +0000 UTC"
},
{
"url": "https://corp.moneyforward.com/news/release/corp/20241127-mf-press-2/",
"title": "SaaS型社宅管理システムを提供する株式会社シャトクをグループ会社化",
"bullets": [
"SaaS型社宅管理システムを提供するシャトク社をグループ会社化。(34)",
"B2B SaaSの人事労務領域を拡大し、従業員の手取り向上に貢献。(35)",
"『クラウド給与』等とのAPI連携を推進し、圧倒的なUXを実現予定。(36)",
"中堅企業向けERP顧客基盤を活用したクロスセル戦略を加速。(33)"
],
"wordCount": 138,
"publishedAt": "11/27/2024, 08:00 AM, +0000 UTC"
},
]
{
"company": "freee (フリー)",
"summaries": [
{
"url": "https://corp.freee.co.jp/news/20250825freee_borderless.html",
"title": "freeeとボーダレス・ジャパンが包括連携協定を締結 起業支援を通して社会起業家の育成を強化 | プレスリリース | フリー株式会社",
"bullets": [
"ボーダレス・ジャパンと連携し、社会問題解決を目指す起業家の育成を支援する。",
"「起業時代」の知見を活かし、起業手続きや会計の基礎知識に関する講座を提供する。",
"freee会計などのバックオフィスサービスを通じた事業立ち上げ支援に注力している。",
"ミッション「スモールビジネスを、世界の主役に。」に沿った社会貢献活動である。"
],
"wordCount": 218,
"publishedAt": "08/25/2025, 07:00 AM, +0000 UTC"
},
{
"url": "https://corp.freee.co.jp/news/20250813freee_benefit_line.html",
"title": "freee福利厚生ベネフィットサービス、LINE公式アカウントからも利用可能に | プレスリリース | フリー株式会社",
"bullets": [
"freee福利厚生サービスがLINE公式アカウントと連携し、利用者の利便性を向上させた。",
"利用頻度の高いクーポンをLINEから手軽に利用可能にするUX改善を実施している。",
"B2B SaaSのモバイル連携や外部プラットフォーム連携開発の事例として注目できる。"
],
"wordCount": 162,
"publishedAt": "08/13/2025, 07:00 AM, +0000 UTC"
},
]
},
{
"company": "LayerX (レイヤーエックス)",
"summaries": [
{
"url": "https://layerx.co.jp/news/20241004/",
"title": "LayerXと三菱UFJ銀行による業務提携契約の締結について / ニュース / 株式会社LayerX",
"bullets": [
"三菱UFJ銀行と業務提携し、法人支出管理SaaS「バクラク」の提供を拡大する。",
"法人支出管理と銀行決済、法人カード領域におけるシステム連携を推進する。",
"バクラクの利用データと銀行決済データを掛け合わせたデータ活用ビジネスを検討。",
"AIサポート機能を活用し、金融機関の顧客基盤を通じて業務効率化に貢献する。"
],
"wordCount": 188,
"publishedAt": "10/04/2024, 07:00 AM, +0000 UTC"
},
{
"url": "https://layerx.co.jp/news/20250901-3/",
"title": "LayerX、シリーズBで150億円を調達。エンジニアの採用を強化し、AIエージェント事業をさらに加速 / ニュース / 株式会社LayerX",
"bullets": [
"シリーズBで150億円を調達し、エンジニアを中心とした人材採用を最優先で強化する。",
"調達資金をAIエージェント事業「バクラク」と「Ai Workforce」の加速に充てる。",
"AIを活用した生産性向上と、世界でも競争力のある報酬体系の構築を目指す。",
"MUFGグループが出資者に名を連ね、Fintech領域での強固な連携基盤がある。"
],
"wordCount": 187,
"publishedAt": "09/02/2025, 07:00 AM, +0000 UTC"
},
]
},
{
"company": "Sansan (サンサン)",
"summaries": [
{
"url": "https://www.sustainablebrands.jp/news/1188055/",
"title": "Sansan、名刺のデータ枚数に応じて宮古市にブナを植林|サステナブル・ブランド ジャパン | Sustainable Brands Japan",
"bullets": [
"データ化した名刺枚数に応じた植樹プロジェクトを通じた社会貢献活動を確認。",
"名刺管理サービス「Sansan」の利用拡大が環境保全に直結する仕組みを構築。",
"企業のサステナビリティや地域復興支援への取り組み姿勢を把握する。",
"社内ボランティア組織が運営しており、社員参加の機会がある。"
],
"wordCount": 150,
"publishedAt": "04/01/2025, 01:23 PM, +0000 UTC"
},
]
},
{
"company": "LINEヤフー (LINEヤフー)",
"summaries": [
{
"url": "https://news.yahoo.co.jp/articles/9d661f0f4717afeb4625a60d7a4474fe32d55cb5",
"title": "「自称・LINEヤフー社員」が寄付勧誘 ネット募金装う不審メッセージ確認、同社が注意呼びかけ(ITmedia NEWS)",
"bullets": [
"PayPayやYahoo!基金を騙るフィッシング詐欺が発生している。(28)",
"金融サービスにおけるセキュリティ対策の重要性が高い。(30)",
"エンジニアとして不正検知や情報保護の強化に貢献可能。(32)"
],
"wordCount": 90,
"publishedAt": "10/07/2025, 08:18 AM, +0000 UTC"
},
]
}
]
}
各企業に対しての検索記事URL、タイトル、要約、出版日が取得できていることが確認できました。
policy-step(acceptの部分)
"company": "マネーフォワード (Money Forward)",
"accepted": [
{
"url": "https://corp.moneyforward.com/news/info/20250310-mf-press-1/",
"title": "マネーフォワード、「健康経営優良法人2025(大規模法人部門)」認定のお知らせ",
"bullets": [
"健康経営優良法人認定から、社員のウェルビーイングを重視する文化を把握。(33)",
"「Talent Forward」を重点テーマとし、人材を価値を生む資産と定義。(34)",
"心身ともに健全で活き活きと働ける職場環境を目指している。(30)"
],
"wordCount": 114,
"publishedAt": "03/10/2025, 07:00 AM, +0000 UTC",
"fitScore": 1
},
{
"url": "https://www.nikkei.com/article/DGXZQOUC066K20W5A001C2000000/",
"title": "マネーフォワード、クラウド会計ソフトにAIエージェント連携機能 - 日本経済新聞",
"bullets": [
"クラウド会計ソフトにAIエージェント連携機能(ベータ版)を提供開始。(36)",
"LLM(Claude等)と連携し、会計ソフトの自動操作を実現する。(34)",
"AIが仕訳入力や帳簿検索などを自律的に処理する仕組みを開発。(35)",
"MCPサーバーという接続基盤を開発しており、技術的挑戦度が高い。(37)"
],
"wordCount": 145,
"publishedAt": "10/06/2025, 09:01 AM, +0000 UTC",
"fitScore": 0.67
},
]
policy-step(rejectの部分)
"rejected": [
{
"summary": {
"url": "https://corp.moneyforward.com/news/release/corp/20241127-mf-press-2/",
"title": "SaaS型社宅管理システムを提供する株式会社シャトクをグループ会社化",
"bullets": [
"SaaS型社宅管理システムを提供するシャトク社をグループ会社化。(34)",
"B2B SaaSの人事労務領域を拡大し、従業員の手取り向上に貢献。(35)",
"『クラウド給与』等とのAPI連携を推進し、圧倒的なUXを実現予定。(36)",
"中堅企業向けERP顧客基盤を活用したクロスセル戦略を加速。(33)"
],
"wordCount": 143,
"publishedAt": "11/27/2024, 08:00 AM, +0000 UTC",
"fitScore": 0.43
},
"reason": "ポリシー閾値未達: Fit 0.43 < Min 0.45 / 古い記事"
}
]
このようにfitScoreで0~1のスコア判定とスコア値による可否判定が問題なくできていることを確認できました。
writer-step
"writerResults": [
{
"company": "マネーフォワード (Money Forward)",
"written": 4,
"pageId": "親ページID"
},
{
"company": "LayerX (レイヤーエックス)",
"written": 2,
"pageId": "親ページID"
}
]
このように企業と書き足した要約部分の件数を確認することが出来ます。
notion本体の確認
最後に、2社さんの部分が書き加えられているはずなのでnotionを確認しにいきます。
問題なく書き加えられていることが確認できました。
ちなみに書き加える際の条件分岐は以下のような感じです。
- 既に以前検索済み企業であれば、同タブ内に記述(上の写真参考)
- 新規企業であれば、新たにタブを作成しその中に記述(下の写真参考)
以上で全動作が正常に動いていることが確認できました!
あとは以下のようにgithub actionsに週1でこのJOBが走るように登録すれば完了です。

これで、就活の軸(職種・業界・キーワードなど)をNotionのタブに書いておくだけで、週1回自動的に軸に合致する企業ニュースを要約し、Notionに自動追記していく仕組みを構築するという目的が達成できました!!
今後の展望
以下に今後拡張したい内容をざっと書いていきます
- 企業ごとのセクションが増えすぎた際の自動整理
- ES の締切情報を別カレンダーに書き出し、メールでリマインドする機能
- ニュースの差分比較やスコアトレンドをダッシュボード化
- マルチエージェントの並列化などによる自律度の向上
- 現状だとworkflowによる直列処理が基盤となっているので、まだまだ「AIエージェントとAIワークフローのハイブリッド」って感じです。これからもっとAIの自律化範囲を広げていきたいです
まとめ
就活の軸さえ決めておけば、週に一度 エージェント が Notion へ最新情報を追記する仕組みを構築しました。UI に頼らず、 候補企業 → 検索結果要約 → Notion追記 というパイプラインで自動化できたのは大きな手応えです!
最後に、AIの進化が取り巻くこれからの時代において、働き方やSaaSの在り方は必ず変わってくると思います。
今回は手作りでマルチエージェント&ワークフローを構築しましたが、最近ではOpen AIが発表した Agent Builder のようにノーコードで同じことができるツールも増えてきました。この「土台を知ったうえで、いつ GUI に任せるか」を意識できれば、今後も柔軟に時代の変化を取り入れていけそうです。
ひとまず本稿が、Mastra やエージェント自動化にチャレンジしたい方の背中を押せれば幸いです!
「こうするともっと便利になるよ」などあれば、ぜひコメントで教えてください!





