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?

私が作ったAIエージェントが仕事を見つけてくれる

Posted at

注意: この記事は英語から日本語に翻訳されました。
Note: This article has been translated from English to Japanese.


"エンターキーを押すか、クリックして画像をフルサイズで表示します"

テキストの翻訳: "!"

日本語訳: "!"

"私が作成したAIエージェントが私のために求人を見つけてくれます"

"アリンダム・マジュムダー"

"アリンダム・マジュムダー"

"10分間の読み物"

テキストが提供されていません。翻訳するテキストを提供してください。

"2025年6月2日"

翻訳するテキストが提供されていません。テキストを提供していただけますか?

"1"の日本語訳:「1」

"聞く"

"シェア"

"この現在の市場では、自分に合った仕事を見つけることは超難しいです!"

"最近、私はOpenAI Agents SDKを探索し、MCPエージェントとエージェントワークフローを構築していました。"

"私の学びを実装するために、なぜ実際の、一般的な問題を解決しないのかと思いました。"

「だから、私はこのマルチエージェントジョブサーチワークフローを構築しました。それは私に合った仕事を見つけるのです!」

"エンターキーを押すか、クリックして画像を全画面で表示してください"

テキストの翻訳: "!"

日本語訳: "!"

この記事では、このエージェントワークフローを構築する方法をご紹介します!

"始めましょう!"

"エージェントの動作方法"

"エンターキーを押すか、クリックして画像をフルサイズで表示してください"

テキストを翻訳:"!"

日本語訳: "!"

"進む前に、私たちのワークフローエージェントがどのように機能するかを理解しましょう!"

"- 最初に、ユーザーは自分のLinkedInプロフィールを提供します

  • 最初のエージェントは、BrightDataのMCPサーバーを使用して、あなたのプロフィール、経験、スキル、キャリアパスを分析します。
  • 分析に基づいて、あなたの興味と経験に一致するドメインを予測します
  • 次に、BrightDataのMCPサーバーを使用して、Y Combinatorの求人掲示板から現在の空席をスクレイピングします
  • 最後に、あなたのプロフィールの要約とあなたに合わせたトップの求人マッチのリストを含む詳細なレポートを生成します。"

"これらのタスクごとに別々のエージェントを使用しました。"

「はい、私はいくつかをまとめることができたかもしれません...しかし、私の実験からすると、それはただエージェントを混乱させ、幻覚を増加させるだけでした。🙃」

"> もしビデオの方が好きなら、これを見ることができます:👇🏼"

私が使用したツール

"このプロジェクトを構築するために使用した主なツールは次のとおりです。"

「私はシンプルで信頼性の高いオプションを探していましたが、これらが仕事に最適だとわかりました。」

"1. ネビウス AI スタジオ"

"エンターキーを押すか、クリックして画像をフルサイズで表示します"

テキストの翻訳: "!"

日本語の翻訳: "!"

"私は、コストやパフォーマンスにあまり心配せずに、単一のワークフローでLLMsを複数回実行するための予算に優しい方法を探していました。"

That’s when I found "Nebius AI スタジオ".

"これにより、オープンソースのAIモデルを簡単に実行でき、私が必要とするものに対するパフォーマンスは素晴らしかったです。それは速く、手頃な価格で、多くの費用をかけずに強力なモデルを使用したい場合に非常にうまく機能します。"

"2. ブライトデータのMCPサーバー"

"エンターキーを押すか、クリックして画像を全画面で表示します"

テキストの翻訳: "!"

日本語訳: "!"

"LLMの一つの大きな制限は何かと言えば、通常、リアルタイムデータへのライブアクセスがないことです。"

"MCPは、LLMに外部ツールへのアクセスを可能にすることで、この問題を解決します。"

For this project, I used "ブライトデータ"’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を回避する
  • ブロックされることなくデータをスクレイピングする"

"このプロジェクトには完全に適しており、ウェブからリアルタイム情報が必要でした。"

"3. OpenAIエージェントSDK"

"エンターキーを押すか、クリックして画像をフルサイズで表示します"

テキストを翻訳する: "!"

日本語訳: "!"

「既にOpenAI Agents SDKを探索し、MCPエージェントとエージェントワークフローの構築方法を学んでいたので、このプロジェクトの基盤としてそれを使用することにしました。」

「それは非常にシンプルで柔軟性があり、まさに私が求めていたもので、実際に実世界のタスク、例えば就職活動のようなものを解決できるマルチエージェントのセットアップを作成するのに必要なものでした。」

"これは軽量なツールで、エージェントベースのアプリを簡単に作成するのに役立ちます。これを使用すると、私は以下のことが可能になります:"

"- 私のLLMに指示とツールを提供する

  • エージェントが互いに作業を渡すことを許可する
  • モデルにデータを送信する前に基本的なチェックを追加する"

"マルチステップまたはマルチエージェントのアプリを構築している場合、これは非常に便利です。"

"4. UIのためのStreamlit"

"エンターキーを押すか、クリックして画像を全画面で表示してください"

テキストの翻訳: "!"

日本語訳: "!"

"最後に、UIを素早くクリーンに構築する方法が必要でした。"

As a Python dev, "Streamlit" was the obvious choice.

"Python開発者にとって、Streamlitは彼らのアプリケーションの直感的なUIを構築するための主要な選択肢です。"

「わずか数行で、LinkedInのURLを入力し、ワークフローを実行できる完全機能のダッシュボードを作成しました。」

"それは全体のプロセスを本当に簡単にしました。"

"ジョブサーチエージェントの構築"

"十分に話し合った、それではエージェントの構築を始めましょう!🔥"

"前提条件"

このプロジェクトを実行する前に、以下のことを確認してください:

"プロジェクト構造"

"物事をきれいでモジュラーに保つために、私たちはこのようにプロジェクトを整理しました:"


# "フォルダー構造 👇🏼

```"

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サーバーを初期化します。
- 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)


日本語訳:
"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を使用しています。なぜなら、LinkedInからのデータをBrightDataのスクレイピングエンジンを使用して取り込むからです。*"

"プロファイルを分析したところで、ユーザーが最も適合するドメインを予測する必要があります。"

"ユーザーの優先ドメインを以前のエージェントの分析に基づいて提案するジョブ提案エージェント"

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を使用していません。なぜなら、それが機能呼び出しを行っていないからです。*"

"次に、ジョブボード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
)
)


次に、ジョブサーチエージェントの構築に取り組みます。このエージェントは生成された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
)
)


"> *💡 注意: このエージェントは、YCのジョブボードへのリアルタイムスクレイピングコールを行う必要があるため、再度MCPサーバーを使用します。*"

"時々、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
)
)


"最後に、すべてをまとめます。このエージェントは、ユーザーのプロファイル、ドメイン予測、およびジョブ結果をまとめてクリーンなマークダウンレポートにまとめます。"

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やマークダウンレンダラーでクリーンに見えるレポートを作成し、簡単に共有したり、後で保存したりできます!😉"

# "ワークフローの作成"

"すべてのエージェントが準備完了したので、それらを論理的な順序で接続する実際のフローを構築しましょう。"

"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}  
    上記の情報を分析し、マークダウン形式で包括的なキャリア分析レポートを作成してください。"""  
    # 単一の呼び出しで最終サマリーを取得  

    summary_result = await Runner.run(starting_agent=summary_agent, input=summary_input)  
    logger.info("サマリー生成が完了しました")  
    return summary_result.final_output

# "MCPサーバーの初期化"

"さあ、私たちのMCPサーバーを初期化しましょう!"

まず、MCPサーバーの設定とアクセスに関連するすべての事項を処理する`mcp_server.py`ファイルを作成することから始めます。

"まず、非同期ロジックの処理に必要な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


"> *💡 注意: 環境変数(BRIGHT_DATA_API_KEY、BROWSER_AUTH)が正しく.envに設定されていることを確認してください。そうでない場合、サーバーの起動に失敗します。*"

この関数はいくつかの重要なことを行います:

"- サーバーが既に稼働している場合は再初期化を防ぎます。
- 新しい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の作成"

"最後に、全てを組み立てて、このエージェントのためのUIをStreamlitを使用して作成します。"

まず最初に、必要なすべてのモジュールをインポートし、私たちの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プロフィールアナライザー",
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

非同期でプロファイルを分析する(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)  
# "ローディング状態"

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 ["http://localhost:8501" 

(このテキストはURLであり、翻訳の必要はありません。)](http://localhost:8501/):

"エンターキーを押すか、クリックして画像をフルサイズで表示してください"

テキストの翻訳: "!" 

日本語訳: "!"

"弊社のエージェントは、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!

テキストの翻訳: "!" 

日本語訳: "!"
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?