注意: この記事は英語から日本語に翻訳されました。
Note: This article has been translated from English to Japanese.
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
AIエージェントを作成して仕事を見つけてもらった
「10分で読了」
The text provided is a bullet point "·" and does not require translation.
「2025年6月2日」
申し訳ありませんが、翻訳するテキストが提供されていません。翻訳が必要なテキストを入力してください。
申し訳ありませんが、翻訳するための具体的なテキストが提供されていません。翻訳が必要なテキストを提供してください。
「リッスン」
「共有」
現在の市場で、自分に合った仕事を見つけるのは非常に難しいです!
最近、OpenAI Agents SDKを調査し、MCPエージェントとエージェンティックワークフローを構築していました。
学んだことを実践するために、実際の一般的な問題を解決してみようと思いました。
「そこで、自分に合った仕事を見つけるマルチエージェントのジョブ検索ワークフローを作りました!」
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
この記事では、このAgent Workflowの構築方法を紹介します!
「始めましょう!」
エージェントの動作原理
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
先に進む前に、Workflow Agent の動作を理解しましょう!
- まず、ユーザーがLinkedInプロフィールを提供します
- 最初のエージェントがBrightDataのMCPサーバーを使用して、プロフィール、経験、スキル、キャリアパスを分析します
- 分析に基づいて、興味と経験に合ったドメインを予測します
- 次に、BrightDataのMCPサーバーを使用してY Combinatorの求人ボードから現在の求人情報をスクレイピングします
- 最後に、プロフィールの概要とあなたに合わせたトップ求人のリストを含む詳細なレポートを生成します
これらのタスクごとに別々のエージェントを使用しました。
はい、いくつかをまとめることもできたかもしれませんが…私の実験では、それがエージェントを混乱させ、幻覚を増やすだけでした。🙃
ビデオの方が好みの場合は、こちらをご覧ください:👇🏼
使用したツール
このプロジェクトを構築するために使用した主なツールは以下の通りです。
シンプルで信頼性のあるオプションを探していたところ、これらがその仕事に完璧であることがわかりました。
```
- Nebius AI Studio
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
コストやパフォーマンスをあまり気にせずに、単一のワークフローで複数回LLMを実行するための予算に優しい方法を探していました。
That’s when I found ["ネビウス AI スタジオ"](https://studio.nebius.com/).
オープンソースのAIモデルを簡単に実行でき、私のニーズに対してパフォーマンスは素晴らしかったです。高速で手頃な価格であり、強力なモデルを多くの費用をかけずに使用したい場合に非常にうまく機能します。
# ```
2. BrightDataのMCPサーバー
「Enterキーを押すかクリックして画像を全画面表示」
"!"
LLMsの大きな制限の一つは?通常、リアルタイムデータへのライブアクセスがないことです。
MCPはLLMに外部ツールへのアクセスを提供することで、この問題を解決します。
For this project, I used "Bright Data"’s MCP (Model Context Protocol) Server.
I found it while looking for ways to give my agent internet access. With this MCPサーバー, our agent can:
- ウェブサイトを検索および閲覧する
- ロケーションブロックやCAPTCHAを回避する
- ブロックされずにデータをスクレイピングする
このプロジェクトでは、ウェブからリアルタイム情報が必要だったため、完璧に機能しました。
```
- OpenAI Agents SDK
「Enterキーを押すかクリックして画像を全画面表示」
"!"
私はすでにOpenAI Agents SDKを探索し、MCPエージェントとエージェンティックワークフローの構築方法を学んでいたので、このプロジェクトの基盤としてそれを使用することに決めました。
実際のタスクである就職活動のようなものを解決できるマルチエージェントのセットアップを作成するために、まさに必要だった非常にシンプルで柔軟なものでした。
軽量なツールで、エージェントベースのアプリを簡単に作成できます。これを使って、私は以下を行うことができました。
- LLMに指示とツールを提供する
- エージェントが互いに作業を渡せるようにする
- モデルにデータを送信する前に基本的なチェックを追加する
マルチステップまたはマルチエージェントアプリを構築する際に非常に役立ちます。
# "4. UIのためのStreamlit"
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
最後に、UIを素早くかつクリーンに構築する方法が必要でした。
As a Python dev, [ストリームリット](https://streamlit.io/) was the obvious choice.
Python開発者にとって、Streamlitはアプリケーションの直感的なUIを構築するための定番オプションです。
ほんの数行で、LinkedIn URLを入力してワークフローを実行できる完全に機能するダッシュボードができました。
全体のプロセスがとても簡単になりました。
# ジョブサーチエージェントの構築
話はこのくらいにして、Agentの構築を始めましょう!🔥
# 前提条件
このプロジェクトを実行する前に、以下を確認してください。
- Python 3.10 or higher
- A ["Bright Data"](https://brightdata.com/) account and API credentials
- ["Nebius AI Studio"](https://studio.nebius.com/) account and API key
# プロジェクト構造
プロジェクトをクリーンでモジュール化された状態に保つため、次のように整理しました。
```
フォルダ構造 👇🏼
job_finder_agent/
├── app.py # Streamlit web interface
├── job_agents.py # AI agent definitions and analysis logic
├── mcp_server.py # Bright Data MCP server management
├── requirements.txt # Python dependencies
├── assets/ # Static assets (images, GIFs)
└── .env # Environment variables (create this)
簡単に説明しますと:
- `app.py` : Streamlitアプリのエントリーポイント。ここでユーザーはツールと対話します。
- `job_agents.py` : エージェント関連のロジックとワークフローを含みます。
- `mcp_server.py` : MCP Serverを初期化します。
- assets/: UIで使用されるビジュアルやメディアを保持します。
- `.env` : APIキーのような機密データを保存します。このファイルを`.gitignore`に含めることを忘れないでください。
この構造により、後でデバッグ、拡張、または新しいエージェントをプラグインすることが非常に簡単になります。
# エージェントの作成 🤖
まず、プロジェクトに必要なエージェントを作成します。そのために、`job_agents.py` ファイルに移動しましょう。
以下に、必要なモジュールをインポートします。
```
job_agents.py 👇🏼
import os
import logging
import asyncio
from agents import (
Agent,
OpenAIChatCompletionsModel,
Runner,
set_tracing_disabled,
)
from agents.mcp import MCPServer
from openai import AsyncOpenAI
logger = logging.getLogger(name)
エージェントのワークフロー全体を開始する非同期関数 `run_analysis` を定義します。
async def run_analysis(mcp_server: MCPServer, linkedin_url: str):
logger.info(f"Starting analysis for LinkedIn URL: {linkedin_url}")
api_key = os.environ["NEBIUS_API_KEY"]
base_url = "https://api.studio.nebius.ai/v1"
client = AsyncOpenAI(base_url=base_url, api_key=api_key)
set_tracing_disabled(disabled=True)
これは私たちの最初であり、最も重要なエージェントの一つです。ユーザーのLinkedInプロファイルを分析して、関連するキャリアの洞察を抽出します。
linkedin_agent = Agent(
name="LinkedIn Profile Analyzer",
instructions=f"""You are a LinkedIn profile analyzer.
Analyze profiles for:
- Professional experience and career progression
- Education and certifications
- Core skills and expertise
- Current role and company
- Previous roles and achievements
- Industry reputation (recommendations/endorsements)
Provide a structured analysis with bullet points and a brief executive summary.
""",
mcp_servers=[mcp_server],
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
> *💡 注: このエージェントは `mcp_server` を使用します。BrightData のスクレイピングエンジンを使用して LinkedIn からデータを取得するためです。*
ユーザーのプロファイルを分析したので、次にどのドメインに最も適しているかを予測する必要があります。
ユーザーの好みのドメインを提案するJob Suggestionエージェントは、前のエージェントの分析に基づいて提案を行います。
job_suggestions_agent = Agent(
name="Job Suggestions",
instructions=f"""You are a domain classifier that identifies the primary professional domain from a LinkedIn profile.
""",
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
> *💡 ノート: ここでは、関数呼び出しを行っていないため、MCP Tools を使用していません。*
次に、Job board URL ジェネレーターを作成します。このエージェントはドメインを受け取り、Y Combinator のジョブボード用の URL を構築します。
url_generator_agent = Agent(
name="URL Generator",
instructions=f"""You are a URL generator that creates Y Combinator job board URLs based on domains.
""",
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
次に、Job Search Agentを構築します。このエージェントは生成されたURLを訪問し、実際の求人情報を抽出します。
Job_search_agent = Agent(
name="Job Finder",
instructions=f"""You are a job finder that extracts job listings from Y Combinator's job board.
""",
mcp_servers=[mcp_server],
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
> *💡 注: このエージェントは再びMCPサーバーを使用します。これは、YCの求人ボードに対してライブスクレイピングを行う必要があるためです。*
YCの求人リンクが認証リダイレクトの背後にあることがあります 🤷🏻♂️。このエージェントはそれらのURLを整理して簡素化します。
ジョブボードでクリーンアップが不要な場合、このエージェントをスキップできます。
url_parser_agent = Agent(
name="URL Parser",
instructions=f"""You are a URL parser that transforms Y Combinator authentication URLs into direct job URLs.
""",
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
最後に、すべてをまとめます。このエージェントは、ユーザーのプロフィール、ドメイン予測、ジョブ結果を要約し、クリーンなMarkdownレポートにまとめます。
summary_agent = Agent(
name="Summary Agent",
instructions=f"""You are a summary agent that creates comprehensive career analysis reports.Ensure your response is well-formatted markdown that can be directly displayed.""",
model=OpenAIChatCompletionsModel(
model="meta-llama/Llama-3.3-70B-Instruct",
openai_client=client
)
)
StreamlitやMarkdownレンダラーで見栄えの良いレポートを作成するので、簡単に共有したり、後で保存したりできます!😉
# ワークフローの作成
すべてのエージェントが準備できたので、それらを論理的な順序で接続する実際のフローを構築しましょう。
Agents SDKでは、ワークフローを作成する際に、前のエージェントの結果を入力として渡し、現在のタスクを処理するエージェントとして`starting_agent`を指定する必要があります。
コードでは次のようになります。
```
LinkedInプロフィール分析を取得する
logger.info("LinkedInプロフィール分析を実行中")
linkedin_result = await Runner.run(starting_agent=linkedin_agent, input=query)
logger.info("LinkedInプロフィール分析が完了しました")
求人提案を取得する
logger.info("求人提案を取得中")
suggestions_result = await Runner.run(starting_agent=job_suggestions_agent, input=linkedin_result.final_output)
logger.info("求人提案が完了しました")
特定の求人マッチを取得する
logger.info("求人リンクを取得中")
job_link_result = await Runner.run(starting_agent=url_generator_agent, input=suggestions_result.final_output)
logger.info("求人リンク生成が完了しました")
求人マッチを取得する
logger.info("求人マッチを取得中")
job_search_result = await Runner.run(starting_agent=Job_search_agent, input=job_link_result.final_output)
logger.info("求人検索が完了しました")
URLを解析して直接求人リンクを取得する
logger.info("求人URLを解析中")
parsed_urls_result = await Runner.run(starting_agent=url_parser_agent, input=job_search_result.final_output)
logger.info("URL解析が完了しました")
サマリーエージェントのための単一入力を作成する
logger.info("最終サマリーを生成中")
summary_input = f"""LinkedInプロフィール分析:
{linkedin_result.final_output}
求人提案:
{suggestions_result.final_output}
求人マッチ:
{parsed_urls_result.final_output}
上記の情報を分析し、包括的なキャリア分析レポートをMarkdown形式で作成してください。"""
単一の呼び出しで最終サマリーを取得する
summary_result = await Runner.run(starting_agent=summary_agent, input=summary_input)
logger.info("サマリー生成が完了しました")
return summary_result.final_output
# "MCPサーバーの初期化"
"では、MCPサーバーを初期化しましょう!"
`mcp_server.py` ファイルを作成することから始めます。このファイルは、MCP サーバーのセットアップとアクセスに関するすべての処理を行います。
まず、async ロジックを処理するための asyncio や Agents SDK からの MCPServerStdio を含む必要なモジュールをインポートします。
import os
import logging
import asyncio
from agents.mcp import MCPServerStdio
logger = logging.getLogger(name)
_mcp_server = None
次に、`initialize_mcp_server` 関数を作成し、資格情報で MCP サーバーを初期化します。
async def initialize_mcp_server():
"""Initialize MCP server."""
global _mcp_server
if _mcp_server:
return _mcp_server
try:
server = MCPServerStdio(
cache_tools_list=False,
params={
"command": "npx",
"args": ["-y", "@brightdata/mcp"],
"env": {
"API_TOKEN": os.environ["BRIGHT_DATA_API_KEY"],
"WEB_UNLOCKER_ZONE": "mcp_unlocker",
"BROWSER_AUTH": os.environ["BROWSER_AUTH"],
}
}
)
await asyncio.wait_for(server.aenter(), timeout=10)
_mcp_server = server
return server
except Exception as e:
logger.error(f"Error initializing MCP server: {e}")
return None
> *💡 注意: .env に環境変数 (BRIGHT_DATA_API_KEY, BROWSER_AUTH) を正しく設定していないと、サーバーの起動に失敗します。*
この関数は、いくつかの重要なことを行います。
- サーバーが既に起動している場合、再初期化を防ぎます。
- 新しいMCPサーバーインスタンスを起動します。
- タイムアウトを処理し、失敗を丁寧にログに記録します。
クリーンに保つために、2つのユーティリティ関数を追加します。
- サーバーの準備が整うまで待機するもの(`wait_for_initialization`)
- サーバーインスタンスを取得するもの(`get_mcp_server`)
「こちらです。」
async def wait_for_initialization():
"""Wait for MCP initialization to complete."""
return await initialize_mcp_server() is not None
def get_mcp_server():
"""Get the current MCP server instance."""
return _mcp_server
# Streamlit UI の作成
最後に、すべてを組み立てて、Streamlitを使用してこのエージェントのUIを作成します。
まず最初に、必要なモジュールをすべてインポートし、Streamlitアプリの設定を行います。
import streamlit as st
import asyncio
import os
import logging
import nest_asyncio
import base64
from dotenv import load_dotenv
from job_agents import run_analysis
from mcp_server import wait_for_initialization, get_mcp_server
nest_asyncio.apply()
load_dotenv()
logger = logging.getLogger(name)
ページ設定を行う
st.set_page_config( page_title="LinkedIn Profile Analyzer", page_icon="🔍", layout="wide" )
MCPサーバーが初期化されたら、次に asyncio
を使用して run_analysis()
関数を非同期で呼び出します。
ユーザーが「Analyze Profile」ボタンをクリックすると、この関数がトリガーされます。
# ```
セッション状態の初期化
if 'analysis_result' not in st.session_state:
st.session_state.analysis_result = ""
if 'is_analyzing' not in st.session_state:
st.session_state.is_analyzing = False
async def analyze_profile(linkedin_url: str):
try:
if not await wait_for_initialization():
st.error("MCPサーバーの初期化に失敗しました")
return
result = await run_analysis(get_mcp_server(), linkedin_url)
st.session_state.analysis_result = result
except Exception as e:
logger.error(f"LinkedInプロファイルの解析中にエラーが発生しました: {str(e)}")
st.error(f"LinkedInプロファイルの解析中にエラーが発生しました: {str(e)}")
finally:
st.session_state.is_analyzing = False
セッション状態変数を初期化して、インタラクション間でアプリのステータスを管理するようにしました。
さて、楽しい部分、つまりメインUIです 🤩! エージェントとやり取りするためのシンプルなUIを作成します。
def main():
# ```
// 画像をロードしてエンコードする
with open("./assets/bright-data-logo.png", "rb") as bright_data_file:
bright_data_base64 = base64.b64encode(bright_data_file.read()).decode()
# "埋め込み画像付きのタイトルを作成"
title_html = f"""
<div style="display: flex; align-items: center; gap: 0px; margin: 0; padding: 0;">
<h1 style="margin: 0; padding: 0;">
Job Searcher Agent with
<img src="data:image/png;base64,{bright_data_base64}" style="height: 110px; margin: 0; padding: 0;"/>
</h1>
</div>
"""
st.markdown(title_html, unsafe_allow_html=True)
st.markdown("---")
# "サイドバー"
with st.sidebar:
st.image("./assets/Nebius.png", width=150)
api_key = st.text_input("Enter your API key", type="password")
st.divider()
st.subheader("Enter LinkedIn Profile URL")
linkedin_url = st.text_input("LinkedIn URL", placeholder="https://www.linkedin.com/in/username/")
if st.button("Analyze Profile", type="primary", disabled=st.session_state.is_analyzing):
if not linkedin_url:
st.error("Please enter a LinkedIn profile URL")
return
if not api_key:
st.error("Please enter your API key")
return
st.session_state.is_analyzing = True
st.session_state.analysis_result = ""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(analyze_profile(linkedin_url))
finally:
loop.close()
# "結果セクション"
if st.session_state.analysis_result:
st.subheader("Analysis Results")
st.markdown(st.session_state.analysis_result)
# "Loading状態"
if st.session_state.is_analyzing:
st.markdown("---")
with st.spinner("Analyzing profile... This may take a few minutes."):
st.empty()
if name == "main":
main()
これで完了です!私たちは、MCPを介したブラウザ自動化とスクレイピングを備えたマルチエージェントAIシステムに接続された、完全に機能するStreamlit UIを手に入れました。
# ローカルでの実行:
すべてのセットアップが完了したので、このアプリをローカルで実行してみましょう!
まず、仮想環境を作成します。
python -m venv venv
source venv/bin/activate # On Windows, use: venv\Scripts\activate
次に、`requirements.txt` に記載されている必要なパッケージをすべてインストールします。
pip install -r requirements.txt
最後に、アプリケーションを実行します:
streamlit run app.py
Now go to [The text "http://localhost:8501" does not contain any translatable content, as it is a URL. Therefore, it remains unchanged: "http://localhost:8501".](http://localhost:8501/):
「Enterキーを押すかクリックして画像をフルサイズで表示」
"!"
私たちのAgentは、LinkedInプロフィールを分析し、まるで魔法のように仕事のマッチングを見つけます。🪄
これで完了です!🎉
私たちは、MCPサーバーを使用してジョブを見つけるマルチエージェントワークフローを構築することに成功しました。
You’ll find the Full Code Here: [GitHub リポジトリ](http://github.com/Arindam200/awesome-ai-apps/tree/main/advance_ai_agents/job_finder_agent)
この記事が役に立ったと思ったら、同僚と共有してください。
For paid collaboration, mail me at: ["arindammajumder2020@gmail.com"](https://mailto:arindammajumder2020@gmail.com/).
お読みいただきありがとうございます!
"!"
This story is published on [生成AI](https://generativeai.pub/). Connect with us on [LinkedIn](https://www.linkedin.com/company/generative-ai-publication).
Subscribe to our [ニュースレター](https://www.generativeaipub.com/) and [YouTube](https://www.youtube.com/@generativeaipub) channel to stay updated with the latest news and updates on generative AI. Let’s shape the future of AI together!
"!"