🤖

crewAIを使ったマルチエージェントシステム開発入門

2024/07/10に公開

背景

生成AI技術のなかで注目を集めているマルチエージェントシステムについて学びはじめています。DeepLearning.aiのショートコースを受講していて知ることになったcrewAIというフレームワークを使って、初めてのマルチエージェントシステムを作ってみます。

crewAIの基本構成

crewAIでは、Agent, Task, Crewを定義してmulti-agentを構成していきます。

from crewai import Agent, Task, Crew

Taskを実行する主体であるAgentには、goalとbackstory(どのように振る舞うべきか)を記載します。

planner = Agent(
    role="Content Planner",
    goal="Plan engaging and factually accurate content on {topic}",
    backstory="You're working on planning a blog article "
              "about the topic: {topic}."
              "You collect information that helps the "
              "audience learn something "
              "and make informed decisions. "
              "Your work is the basis for "
              "the Content Writer to write an article on this topic.",
    allow_delegation=False,
	verbose=True
)

Taskの定義では、description, expected_output, Taskを担当するAgentを記載します。

plan = Task(
    description=(
        "1. Prioritize the latest trends, key players, "
            "and noteworthy news on {topic}.\n"
        "2. Identify the target audience, considering "
            "their interests and pain points.\n"
        "3. Develop a detailed content outline including "
            "an introduction, key points, and a call to action.\n"
        "4. Include SEO keywords and relevant data or sources."
    ),
    expected_output="A comprehensive content plan document "
        "with an outline, audience analysis, "
        "SEO keywords, and resources.",
    agent=planner,
)

最後に、AgentのリストとTaskのリストを含むCrewを定義します。kickoffメソッドによってタスクを実行します。

crew = Crew(
    agents=[planner, writer, editor],
    tasks=[plan, write, edit],
    verbose=2
)

result = crew.kickoff(inputs={"topic": "Artificial Intelligence"})

このように、役割と期待される振る舞いを規定したAgentと、ゴールと期待されるアウトプットを言語化したTaskとを定義し、Crewとしてまとめることで、複数のエージェントに協調してタスクを実行させることができるようになります。

crewAIのTool

Toolは、Agentが様々なアクションを実行するうえで活用することのできるスキルです。ウェブサーチやコード実行などがToolの例です。

下記のコードではTask実行の際に利用するToolを指定しています。他にも、Agentが実行できるToolを指定することもできます。もし両方定義されている場合はTask levelのTool setが優先されます。

from crewai_tools import ScrapeWebsiteTool

docs_scrape_tool = ScrapeWebsiteTool(
    website_url="https://docs.crewai.com/how-to/Creating-a-Crew-and-kick-it-off/"
)

inquiry_resolution = Task(
    description=(
        "{customer} just reached out with a super important ask:\n"
	    "{inquiry}\n\n"
        "{person} from {customer} is the one that reached out. "
		"Make sure to use everything you know "
        "to provide the best support possible."
		"You must strive to provide a complete "
        "and accurate response to the customer's inquiry."
    ),
    expected_output=(
	    "A detailed, informative response to the "
        "customer's inquiry that addresses "
        "all aspects of their question.\n"
        "The response should include references "
        "to everything you used to find the answer, "
        "including external data or solutions. "
        "Ensure the answer is complete, "
		"leaving no questions unanswered, and maintain a helpful and friendly "
		"tone throughout."
    ),
	tools=[docs_scrape_tool],
    agent=support_agent,
)

crewAIには多くのツールが用意されています。下記のようにカスタムツールを作成することもできます。

from crewai_tools import BaseTool

class SentimentAnalysisTool(BaseTool):
    name: str ="Sentiment Analysis Tool"
    description: str = ("Analyzes the sentiment of text "
         "to ensure positive and engaging communication.")
    
    def _run(self, text: str) -> str:
        # Your custom code tool goes here
        return "positive"

sentiment_analysis_tool = SentimentAnalysisTool()

つくってみる

crewAIの基本的な使い方が理解できたので、実践に移ります。指定されたトピックに関する問題を作成して、それに回答する、というシステムをつくってみます。特にマルチエージェントである必要はない要件ですが、理解を深めるために2つのAgentが問題の作成と回答をそれぞれ担うようにします。

まずはimportや設定部分。crewAIはdefaultでGPT-4oを使うので、違うものがよければ指定する必要があります。OpenAI以外のLLMを使うことも可能です。

import os
os.environ["OPENAI_MODEL_NAME"]="gpt-3.5-turbo"

from crewai import Agent, Task, Crew
from crewai.process import Process

続いてエージェントを定義します。allow_delegationというAttributeをTrueにすると、問題作成者が問題回答者に「どんな問題がいいと思う?」という問題を出したり、問題回答者が問題作成者に「どうやって解いたらいいと思う?」と聞いたりします。相談しながらTaskを実行する様子は微笑ましいですが、それぞれのAgentは1つのTaskにフォーカスしてほしいので、ここではFalseにしています。

q_maker = Agent(
    role='問題作成者',
    goal='{topic}に関する問題を作成する',
    backstory=('人を楽しませることが好きで好奇心旺盛なあなたは、'
               '子どもでも解けるが柔軟な発想が求められる問題をつくることが得意です。'),
    memory=True,
    allow_delegation=False,
    verbose=True
)
answerer = Agent(
    role='問題回答者',
    goal='問題作成者によって提供された問題に適切に回答する',
    backstory=('謎解きが好きで発想が豊かなあなたは、'
               '与えられた課題に対して柔軟な発想で正解を導き出すことが得意です。'),
    memory=True,
    allow_delegation=False,
    verbose=True
)

次にTaskを定義します。問題の作成と回答はSequentialに実行される必要があるので、回答タスクのcontextとして問題作成タスクを渡しています。また、問題作成タスクの結果の中に回答例まで含まれていたことがあったので、expected_outcomeに回答は含めないように指示をしています。このあたりはprompt engineeringの腕の見せどころですね。

q_task = Task(
    description='{topic}に関して、子どもでも解けるが柔軟な発想が求められる問題を作成する。',
    expected_output='{topic}に関して、子どもでも解けるが柔軟な発想が求められる問題1問。回答は含めない',
    agent=q_maker,
    human_input=True
)
a_task = Task(
    description='問題作成者が作った問題を解く',
    expected_output='与えられた問題とそれに対する回答とその理由',
    context=[q_task],
    agent=answerer,
    human_input=True
)

最後にCrewを定義します。processとしてSequentialを指定しない場合は、Manager Agentが自動的に作成されて全体の工程管理を行います(使っていないはずのGPT-4oに課金されていて後で気づきました)。

sample_crew = Crew(
    agents=[q_maker, answerer],
    tasks=[q_task, a_task],
    full_output=True,
    verbose=True,
    process=Process.sequential
)

crewを実行します。

topic = 'お金'
result = sample_crew.kickoff(inputs={'topic':topic})

まず、問題作成タスクの実行結果です。

 [2024-07-09 13:13:39][DEBUG]: == Working Agent: 問題作成者
 [2024-07-09 13:13:39][INFO]: == Starting Task: お金に関して、子どもでも解けるが柔軟な発想が求められる問題を作成する。


> Entering new CrewAgentExecutor chain...
This is the agent's final answer: 駄菓子屋さんで10円の飴を5つ買ったら、いくらかかるでしょうか?
Please provide feedback:

Taskの定義のところで、human_inputをTrueにしておくと、上記のようにTask完了後にfeedbackを求めてきます。「簡単すぎるので、より柔軟な発想が求められる問題にしてください」とfeedbackしたところ、少しだけひねった問題を作成してくれました。

Please provide feedback:  簡単すぎるので、より柔軟な発想が求められる問題にしてください
Final Answer:
駄菓子屋さんで10円の飴を5つ買ったら、いくらかかるでしょうか?しかし、友達がお店で買ったものを1つ分おごってくれることになりました。最終的にいくら支払う必要があるでしょうか?

> Finished chain.

続いて問題回答タスクの結果です。

 [2024-07-09 13:14:21][DEBUG]: == Working Agent: 問題回答者
 [2024-07-09 13:14:21][INFO]: == Starting Task: 問題作成者が作った問題を解く


> Entering new CrewAgentExecutor chain...
This is the agent's final answer: 10円の飴を5つ買った場合、まず5つ分で50円かかります。しかし、友達が1つ分おごってくれるので、自分が支払う必要があるのは実際に購入した4つ分の40円です。したがって、最終的に支払う必要がある金額は40円となります。
Please provide feedback:

簡単な問題だったこともありすぐに正解しました。間違っていた場合はfeedbackで「違うよ」と教えると別の回答を考えます(それでも間違うこともある)。

まとめ

crewAIを使ったマルチエージェント開発について理解し、実際に簡単なシステムをつくってみました。与えるtopicによって得られる問題や回答の質はまちまちでしたが、マルチエージェントをどのように定義して、それらがどのように相互作用しながらタスクを実行していくのか理解をすることができました。今後は、より意味のあるマルチエージェントシステムの開発や、他のマルチエージェントフレームワークとの比較なども行っていきたいと思います。

Discussion