単一アクション単一エージェント#
既存のエージェントを使用する#
# どの役割でもインポート可能で、初期化し、開始メッセージで実行し、完了!
from metagpt.roles.product_manager import ProductManager
prompt = f"""
# 役割:ソフトウェア開発チーム
## 背景 :
私はソフトウェア開発チームです。
今、html、js、vue3、element-plusを使って問題解決プログラムを開発します。
問題解決は、人々が問題に関連する知識をより深く理解するのに役立ちます。
## プロフィール:
- 著者: 黎伟
- バージョン: 0.1
- 言語: 中国語
- 説明: 私はソフトウェア開発チームです。
## 目標:
- html、js、vue3、element-plusを使って問題解決プログラムの開発要件文書を作成します。
## 制約:
1. 最終的に納品されるプログラムはhtml単一ファイルであり、他のファイルは不要です。
2. 問題の形式は、少なくとも2つの判断問題、2つの選択問題、2つの空欄問題を含む必要があります。
3. 問題の内容は人工知能のエージェントの基本理論に関連している必要があります。
4. 問題解決プログラムは、少なくとも10のサンプル問題を提供する必要があります。
5. 問題はリスト形式でhtmlファイルのscript部分に書き込む必要があります。
6. vue3、element-plusはcdn形式でhtmlのheader部分にインポートします。
## スキル:
1. 強力なjs言語開発能力を持つ
2. vue3、element-plusの使用に精通している
3. 人工知能のエージェントの基本理論を良く理解している
4. タイポグラフィの美学を持ち、番号、インデント、区切り線、改行などを利用して情報のレイアウトを美化することができる
上記の要件に基づいて、問題解決プログラムの開発要件文書を完成させてください。
"""
async def main():
role = ProductManager()
result = await role.run(prompt)
await main()
カスタマイズエージェント#
実際の使用の観点から考えると、エージェントが私たちに役立つためには、どのような基本要素を備えている必要があるのでしょうか?MetaGPT の観点から見ると、エージェントが特定のアクションを実行できる場合(LLM 駆動であろうと他の方法であろうと)、それは一定の用途を持つことになります。簡単に言えば、エージェントがどのような行動を持つべきかを定義し、その能力をエージェントに装備すれば、私たちはシンプルで使いやすいエージェントを持つことになります!
自然言語でコードを書きたいと考え、エージェントにそれを行わせたいとしましょう。このエージェントを SimpleCoder と呼び、機能させるために 2 つのステップが必要です:
- コードを書くアクションを定義する
- エージェントにこのアクションを装備する
アクションの定義
MetaGPT では、Action クラスはアクションの論理的抽象です。ユーザーは self._aask 関数を呼び出すことで LLM にこのアクションの能力を与えることができ、この関数は基盤で LLM API を呼び出します。
from metagpt.actions import Action
class SimpleWriteCode(Action):
PROMPT_TEMPLATE: str = """
Write a python function that can {instruction} and provide two runnnable test cases.
Return ```python your_code_here ```with NO other texts,
your code:
"""
name: str = "SimpleWriteCode"
async def run(self, instruction: str):
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
rsp = await self._aask(prompt)
code_text = SimpleWriteCode.parse_code(rsp)
return code_text
@staticmethod
def parse_code(rsp):
pattern = r"```python(.*)```"
match = re.search(pattern, rsp, re.DOTALL)
code_text = match.group(1) if match else rsp
return code_text
役割の定義#
MetaGPT では、Role クラスはエージェントの論理的抽象です。Role は特定の Action を実行し、記憶を持ち、思考し、さまざまな戦略を用いて行動します。基本的に、これらのコンポーネントをすべて結びつける凝縮された実体として機能します。現在、特定のアクションを実行するエージェントにのみ焦点を当て、最もシンプルな Role を定義する方法を見てみましょう。
この例では、私たちは SimpleCoder を作成し、人間の自然言語の説明に基づいてコードを書くことができます。手順は次のとおりです:
- 名前とプロファイルを指定します。
- self._init_action 関数を使用して、期待されるアクション SimpleWriteCode を装備します。
- _act 関数をオーバーライドし、エージェントの具体的な行動ロジックを含めます。私たちは書き込み、エージェントは最新の記憶から人間の指示を取得し、装備されたアクションを実行し、MetaGPT はそれをバックグラウンドでタスクとして処理し、最終的に完全なメッセージを返します。
import re
import os
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
class SimpleCoder(Role):
name: str = "Alice"
profile: str = "SimpleCoder"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([SimpleWriteCode])
async def _act(self) -> Message:
logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
todo = self.rc.todo # todo will be SimpleWriteCode()
msg = self.get_memories(k=1)[0] # find the most recent messages
code_text = await todo.run(msg.content)
msg = Message(content=code_text, role=self.profile, cause_by=type(todo))
return msg
あなたの役割を実行する#
今、私たちはエージェントを働かせることができます。単に初期化し、開始メッセージを使用して実行するだけです。
async def main():
msg = "write a function that calculates the sum of a list"
role = SimpleCoder()
logger.info(msg)
result = await role.run(msg)
logger.info(result)
return result
rtn = await main()
結果#
2025-01-26 18:14:18.317 | INFO | __main__:main:4 - write a function that calculates the sum of a list
2025-01-26 18:14:18.319 | INFO | __main__:_act:16 - Alice(SimpleCoder): to do SimpleWriteCode(SimpleWriteCode)
```python
def sum_list(numbers):
return sum(numbers)
# テストケース
test_case_1 = [1, 2, 3, 4, 5]
test_case_2 = [10, 20, 30, 40, 50]
# テストケース1
print(sum_list(test_case_1)) # 期待される出力: 15
# テストケース2
print(sum_list(test_case_2)) # 期待される出力: 150
2025-01-26 18:14:25.372 | WARNING | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.
2025-01-26 18:14:25.374 | INFO | __main__:main:6 - SimpleCoder:
def sum_list(numbers):
return sum(numbers)
# テストケース
test_case_1 = [1, 2, 3, 4, 5]
test_case_2 = [10, 20, 30, 40, 50]
# テストケース1
print(sum_list(test_case_1)) # 期待される出力: 15
# テストケース2
print(sum_list(test_case_2)) # 期待される出力: 150
多アクションエージェント#
複数のアクションを持つエージェント#
私たちは、エージェントが 1 つのアクションを実行できることに気づきましたが、もしそれだけなら、実際にはエージェントは必要ありません。アクション自体を直接実行することで、同じ結果を得ることができます。エージェントの力、または Role 抽象の驚くべき点は、アクションの組み合わせ(および他のコンポーネント、たとえば記憶ですが、それは後の部分に留めておきます)にあります。アクションを接続することで、エージェントがより複雑なタスクを完了できるワークフローを構築できます。
今、私たちは自然言語でコードを書くことを望んでいるだけでなく、生成されたコードを即座に実行したいと考えています。複数のアクションを持つエージェントは、私たちのニーズを満たすことができます。これを RunnableCoder と呼び、コードを書いて即座に実行する Role です。私たちは 2 つのアクションが必要です:SimpleWriteCode と SimpleRunCode。
アクションの定義#
まず、SimpleWriteCode を定義します。上で作成したものを再利用します。
次に、SimpleRunCode を定義します。前述のように、概念的には、アクションは LLM を利用することも、LLM なしで実行することもできます。SimpleRunCode の場合、LLM は関与しません。私たちは単にサブプロセスを起動してコードを実行し、結果を取得します。私たちが示したいのは、アクションのロジックの構造に制限を設けていないことです。ユーザーは必要に応じてロジックを完全に柔軟に設計できます。
# SimpleWriteCode このクラスは前のセクションとまったく同じです
from metagpt.actions import Action
class SimpleWriteCode(Action):
PROMPT_TEMPLATE: str = """
Write a python function that can {instruction} and provide two runnnable test cases.
Return ```python your_code_here ```with NO other texts,
your code:
"""
name: str = "SimpleWriteCode"
async def run(self, instruction: str):
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
rsp = await self._aask(prompt)
code_text = SimpleWriteCode.parse_code(rsp)
return code_text
@staticmethod
def parse_code(rsp):
pattern = r"```python(.*)```"
match = re.search(pattern, rsp, re.DOTALL)
code_text = match.group(1) if match else rsp
return code_text
# 本節ではSimpleRunCodeというクラスを追加します
class SimpleRunCode(Action):
name: str = "SimpleRunCode"
async def run(self, code_text: str):
result = subprocess.run(["python", "-c", code_text], capture_output=True, text=True)
code_result = result.stdout
logger.info(f"{code_result=}")
return code_result
役割の定義#
単一アクションのエージェントを定義するのと大きな違いはありません!マッピングしてみましょう:
- self.set_actions を使用してすべてのアクションを初期化します
- Role がどのアクションを選択するかを指定します。react_mode を "by_order" に設定します。これは、Role が self.set_actions で指定された順序で実行できるアクションを実行することを意味します。この場合、Role が_act を実行すると、self.rc.todo は最初に SimpleWriteCode、次に SimpleRunCode になります。
- _act 関数をオーバーライドします。Role は前のラウンドの人間の入力またはアクションの出力からメッセージを取得し、現在のアクション(self.rc.todo)に適切な Message 内容を提供し、最後に現在のアクションの出力で構成された Message を返します。
import re
import os
import subprocess
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
class RunnableCoder(Role):
name: str = "Alice"
profile: str = "RunnableCoder"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([SimpleWriteCode, SimpleRunCode])
self._set_react_mode(react_mode="by_order")
async def _act(self) -> Message:
logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
# By choosing the Action by order under the hood
# todo will be first SimpleWriteCode() then SimpleRunCode()
todo = self.rc.todo
msg = self.get_memories(k=1)[0] # find the most k recent messages
result = await todo.run(msg.content)
msg = Message(content=result, role=self.profile, cause_by=type(todo))
self.rc.memory.add(msg)
return msg
async def main():
msg = "write a function that calculates the sum of a list"
role = RunnableCoder()
logger.info(msg)
result = await role.run(msg)
logger.info(result)
return result
rtn = await main()
print(rtn)
結果#
def calculate_sum(numbers):
return sum(numbers)
# テストケース
def test_case_1():
assert calculate_sum([1, 2, 3, 4, 5]) == 15
def test_case_2():
assert calculate_sum([10, -5, 0, 7, 3]) == 15
技術チュートリアルエージェント#
役割紹介#
機能説明
1 文を入力すると、技術的なチュートリアル文書を生成し、カスタム言語をサポートします。
設計思考
まず、LLM 大モデルを使用してチュートリアルの目次を生成し、次に目次を二次タイトルに従ってブロックに分割し、各ブロックの目次に基づいて詳細な内容を生成し、最後にタイトルと内容を結合します。ブロック設計は、LLM 大モデルの長文制限の問題を解決します。
原文リンク:
https://docs.deepwisdom.ai/v0.8/zh/guide/use_cases/agent/tutorial_assistant.html
WriteDirectory アクションの作成
ユーザーの要求に基づいて記事のアウトラインを生成するコードを実装します。
from metagpt.actions import Action
from typing import Dict, Union
import ast
def extract_struct(text: str, data_type: Union[type(list), type(dict)]) -> Union[list, dict]:
"""指定されたタイプの構造(辞書またはリスト)を与えられたテキストから抽出し、解析します。
テキストにはリストまたは辞書のみが含まれ、ネストされた構造を持つ場合があります。
Args:
text: 構造を含むテキスト(辞書またはリスト)。
data_type: 抽出するデータタイプ、"list"または"dict"。
Returns:
- 抽出と解析が成功した場合、対応するデータ構造(リストまたは辞書)を返します。
- 抽出が失敗した場合や解析中にエラーが発生した場合は、例外をスローします。
例:
>>> text = 'xxx [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}] xxx'
>>> result_list = OutputParser.extract_struct(text, "list")
>>> print(result_list)
>>> # 出力: [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}]
>>> text = 'xxx {"x": 1, "y": {"a": 2, "b": {"c": 3}}} xxx'
>>> result_dict = OutputParser.extract_struct(text, "dict")
>>> print(result_dict)
>>> # 出力: {"x": 1, "y": {"a": 2, "b": {"c": 3}}}
"""
# 最初の "[" または "{" と最後の "]" または "}" を見つける
start_index = text.find("[" if data_type is list else "{")
end_index = text.rfind("]" if data_type is list else "}")
if start_index != -1 and end_index != -1:
# 構造部分を抽出する
structure_text = text[start_index : end_index + 1]
try:
# テキストをPythonデータ型に変換しようとする
result = ast.literal_eval(structure_text)
# 結果が指定されたデータタイプと一致することを確認する
if isinstance(result, list) or isinstance(result, dict):
return result
raise ValueError(f"The extracted structure is not a {data_type}.")
except (ValueError, SyntaxError) as e:
raise Exception(f"Error while extracting and parsing the {data_type}: {e}")
else:
logger.error(f"No {data_type} found in the text.")
return [] if data_type is list else {}
class WriteDirectory(Action):
"""チュートリアルディレクトリを書くためのアクションクラス。
Args:
name: アクションの名前。
language: 出力する言語、デフォルトは"Chinese"。
チュートリアルディレクトリを書くためのアクションクラス。
パラメータ:
name:アクションの名前。
language:出力の言語、デフォルトは"Chinese"。
"""
name: str = "WriteDirectory"
language: str = "Chinese"
async def run(self, topic: str, *args, **kwargs) -> Dict:
"""トピックに従ってチュートリアルディレクトリを生成するアクションを実行します。
Args:
topic: チュートリアルのトピック。
Returns:
チュートリアルディレクトリ情報、{"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}を含む。
トピックに従ってチュートリアルディレクトリを生成するアクションを実行します。
パラメータ:
topic:チュートリアルのトピック。
戻り値:
チュートリアルディレクトリ情報、{"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}を含む。
"""
COMMON_PROMPT = """
あなたは今、インターネットの分野で経験豊富な技術専門家です。
私たちはあなたに「{topic}」というトピックの技術チュートリアルを書いてもらいたいです。
"""
DIRECTORY_PROMPT = COMMON_PROMPT + """
このチュートリアルの具体的な目次を提供してください。以下の要件に厳密に従ってください:
1. 出力は指定された言語、{language}に厳密に従う必要があります。
2. 回答は厳密に辞書形式で、{{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}, {{"dir 2": ["sub dir 3", "sub dir 4"]}}]}}のようにしてください。
3. 目次はできるだけ具体的かつ十分であるべきで、一次および二次の目次を含むべきです。二次の目次は配列にします。
4. 余分なスペースや改行がないようにしてください。
5. 各目次のタイトルには実用的な意味があるべきです。
"""
prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language)
resp = await self._aask(prompt=prompt)
return extract_struct(resp, dict)
テストしてみましょう:
text = 'xxx [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}] xxx'
result_list = extract_struct(text, list)
print(result_list)
# 出力: [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}]
text = 'xxx {"x": 1, "y": {"a": 2, "b": {"c": 3}}} xxx'
result_dict = extract_struct(text, dict)
print(result_dict)
# 出力: {"x": 1, "y": {"a": 2, "b": {"c": 3}}}
from metagpt.const import TUTORIAL_PATH
TUTORIAL_PATH
# WindowsPath('d:/Code/agent_from_scratch/data/tutorial_docx')
from datetime import datetime
from typing import Dict
import asyncio
from metagpt.actions.write_tutorial import WriteDirectory, WriteContent
from metagpt.const import TUTORIAL_PATH
from metagpt.logs import logger
from metagpt.roles.role import Role, RoleReactMode
from metagpt.schema import Message
from metagpt.utils.file import File
from typing import Dict
from metagpt.actions import Action
class WriteDirectory(Action):
"""チュートリアルディレクトリを書くためのアクションクラス。
Args:
name: アクションの名前。
language: 出力する言語、デフォルトは"Chinese"。
"""
name: str = "WriteDirectory"
language: str = "Chinese"
async def run(self, topic: str, *args, **kwargs) -> Dict:
"""トピックに従ってチュートリアルディレクトリを生成するアクションを実行します。
Args:
topic: チュートリアルのトピック。
Returns:
チュートリアルディレクトリ情報、{"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}を含む。
"""
COMMON_PROMPT = """
あなたは今、インターネットの分野で経験豊富な技術専門家です。
私たちはあなたに「{topic}」というトピックの技術チュートリアルを書いてもらいたいです。
"""
DIRECTORY_PROMPT = COMMON_PROMPT + """
このチュートリアルの具体的な目次を提供してください。以下の要件に厳密に従ってください:
1. 出力は指定された言語、{language}に厳密に従う必要があります。
2. 回答は厳密に辞書形式で、{{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}, {{"dir 2": ["sub dir 3", "sub dir 4"]}}]}}のようにしてください。
3. 目次はできるだけ具体的かつ十分であるべきで、一次および二次の目次を含むべきです。二次の目次は配列にします。
4. 余分なスペースや改行がないようにしてください。
5. 各目次のタイトルには実用的な意味があるべきです。
"""
prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language)
resp = await self._aask(prompt=prompt)
return extract_struct(resp, dict)
class WriteContent(Action):
"""チュートリアルコンテンツを書くためのアクションクラス。
Args:
name: アクションの名前。
directory: 書くコンテンツ。
language: 出力する言語、デフォルトは"Chinese"。
"""
name: str = "WriteContent"
directory: dict = dict()
language: str = "Chinese"
async def run(self, topic: str, *args, **kwargs) -> str:
"""ディレクトリとトピックに従って文書コンテンツを書くアクションを実行します。
Args:
topic: チュートリアルのトピック。
Returns:
書かれたチュートリアルコンテンツ。
"""
COMMON_PROMPT = """
あなたは今、インターネットの分野で経験豊富な技術専門家です。
私たちはあなたに「{topic}」というトピックの技術チュートリアルを書いてもらいたいです。
"""
CONTENT_PROMPT = COMMON_PROMPT + """
今、私はあなたにトピックのモジュールディレクトリタイトルを与えます。
このタイトルの詳細な原則コンテンツを詳細に出力してください。
コード例がある場合は、標準のコード仕様に従って提供してください。
コード例がない場合は、必要ありません。
トピックのモジュールディレクトリタイトルは次のとおりです:
{directory}
出力を次の要件に厳密に制限してください:
1. レイアウトのためにMarkdown構文形式に従ってください。
2. コード例がある場合は、標準の構文仕様に従い、ドキュメント注釈を付け、コードブロックで表示してください。
3. 出力は指定された言語、{language}に厳密に従う必要があります。
4. 冗長な出力は避けてください。結論の発言を含めないでください。
5. トピック"{topic}"を出力しないという厳格な要件。
"""
prompt = CONTENT_PROMPT.format(
topic=topic, language=self.language, directory=self.directory)
return await self._aask(prompt=prompt)
class TutorialAssistant(Role):
"""チュートリアルアシスタント、1文を入力してマークアップ形式のチュートリアル文書を生成します。
Args:
name: 役割の名前。
profile: 役割のプロフィール説明。
goal: 役割の目標。
constraints: 役割の制約または要件。
language: チュートリアル文書が生成される言語。
"""
name: str = "Stitch"
profile: str = "Tutorial Assistant"
goal: str = "Generate tutorial documents"
constraints: str = "Strictly follow Markdown's syntax, with neat and standardized layout"
language: str = "Chinese"
topic: str = ""
main_title: str = ""
total_content: str = ""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([WriteDirectory(language=self.language)])
self._set_react_mode(react_mode=RoleReactMode.REACT.value)
async def _think(self) -> None:
"""役割が取るべき次のアクションを決定します。"""
logger.info(self.rc.state)
logger.info(self,)
if self.rc.todo is None:
self._set_state(0)
return
if self.rc.state + 1 < len(self.states):
self._set_state(self.rc.state + 1)
else:
self.rc.todo = None
async def _handle_directory(self, titles: Dict) -> Message:
"""チュートリアル文書のディレクトリを処理します。
Args:
titles: タイトルとディレクトリ構造を含む辞書、
たとえば{"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}
Returns:
ディレクトリに関する情報を含むメッセージ。
"""
self.main_title = titles.get("title")
directory = f"{self.main_title}\n"
self.total_content += f"# {self.main_title}"
actions = list()
for first_dir in titles.get("directory"):
actions.append(WriteContent(
language=self.language, directory=first_dir))
key = list(first_dir.keys())[0]
directory += f"- {key}\n"
for second_dir in first_dir[key]:
directory += f" - {second_dir}\n"
self.set_actions(actions)
self.rc.todo = None
return Message(content=directory)
async def _act(self) -> Message:
"""役割によって決定されたアクションを実行します。
Returns:
アクションの結果を含むメッセージ。
"""
todo = self.rc.todo
if type(todo) is WriteDirectory:
msg = self.rc.memory.get(k=1)[0]
self.topic = msg.content
resp = await todo.run(topic=self.topic)
logger.info(resp)
return await self._handle_directory(resp)
resp = await todo.run(topic=self.topic)
logger.info(resp)
if self.total_content != "":
self.total_content += "\n\n\n"
self.total_content += resp
return Message(content=resp, role=self.profile)
async def _react(self) -> Message:
"""アシスタントの思考とアクションを実行します。
Returns:
アシスタントのアクションの最終結果を含むメッセージ。
"""
while True:
await self._think()
if self.rc.todo is None:
break
msg = await self._act()
root_path = TUTORIAL_PATH / datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
await File.write(root_path, f"{self.main_title}.md", self.total_content.encode('utf-8'))
return msg
async def main():
msg = "AI Agent開発教程"
role = TutorialAssistant()
logger.info(msg)
result = await role.run(msg)
logger.info(result)
return role.total_content
rtn = await main()
print(rtn)出力生成的 AI Agent 開発教程
結果#
太長了仅展示部分。
订阅智能体标题#
输入pip show metagpt
查看版本
类似:
Version: 0.8.0
Summary: The Multi-Agent Framework
Home-page: https://github.com/geekan/MetaGPT
Author: Alexander Wu
Author-email: [email protected]
License: MIT
Location: c:\users\liwei\appdata\roaming\python\python39\site-packages
Requires: aiofiles, aiohttp, aioredis, anthropic, anytree, beautifulsoup4, channels, dashscope, faiss-cpu, fire, gitignore-parser, gitpython, google-generativeai, imap-tools, ipykernel, ipython, ipywidgets, jieba, lancedb, libcst, loguru, meilisearch, nbclient, nbformat, networkx, numpy, openai, openpyxl, pandas, Pillow, playwright, pydantic, python-docx, PyYAML, qdrant-client, qianfan, rank-bm25, rich, scikit-learn, semantic-kernel, setuptools, socksio, ta, tenacity, tiktoken, tqdm, typer, typing-extensions, typing-inspect, websocket-client, websockets, wrapt, zhipuai
我们先来完成网页爬取的功能,我们教程直接爬取当天不分国家语言和编程语言的热门仓库进行分析,如果有特殊要求,爬取加上筛选条件条件后网页即可。我们先打开https://github.com/trending 网页,观察网页内容,找到我们需要的内容对应的 html 元素,。
如果熟悉爬虫的就可以直接写爬取和解析脚本了,如果不熟悉的也没关系,我们可以 用 ChatGPT 辅助开发: 首先我们将 trending 页面保存到 本地 github-trending-raw.html
格式化后发现内容非常多,大概 600 多 k,还有一些 svg 源码,因为一般用 CSS 足以定位 html 里的元素,所以我们可以对 html 内容进行瘦身,可以使用以下的脚本:
from bs4 import BeautifulSoup
with open("github-trending-raw.html") as f:
html = f.read()
soup = BeautifulSoup(html, "html.parser")
for i in soup.find_all(True):
for name in list(i.attrs):
if i[name] and name not in ["class"]:
del i[name]
for i in soup.find_all(["svg", "img", "video", "audio"]):
i.decompose()
with open("github-trending-slim.html", "w") as f:
f.write(str(soup))
经过以上的脚本处理之后,大概还有 100 多 k。对于爬虫来说,重要的是 Html 的结构,处理后的 Html 文件其实有大量的信息是重复的,如果我们要让 GPT 协助我们写爬虫脚本,只需要截取部分信息就可以了。
接下来解析一下 html 文件
import aiohttp
import asyncio
from bs4 import BeautifulSoup
def fetch_html(url):
with open(url, encoding="utf-8") as f:
html = f.read()
return html
async def parse_github_trending(html):
soup = BeautifulSoup(html, 'html.parser')
repositories = []
for article in soup.select('article.Box-row'):
repo_info = {}
repo_info['name'] = article.select_one('h2 a').text.strip()
repo_info['url'] = article.select_one('h2 a')['href'].strip()
# Description
description_element = article.select_one('p')
repo_info['description'] = description_element.text.strip() if description_element else None
# Language
language_element = article.select_one('span[itemprop="programmingLanguage"]')
repo_info['language'] = language_element.text.strip() if language_element else None
# Stars and Forks
stars_element = article.select('a.Link--muted')[0]
forks_element = article.select('a.Link--muted')[1]
repo_info['stars'] = stars_element.text.strip()
repo_info['forks'] = forks_element.text.strip()
# Today's Stars
today_stars_element = article.select_one('span.d-inline-block.float-sm-right')
repo_info['today_stars'] = today_stars_element.text.strip() if today_stars_element else None
repositories.append(repo_info)
return repositories
async def main():
url = 'github-trending-raw.html'
html = fetch_html(url)
repositories = await parse_github_trending(html)
for repo in repositories:
print(f"Name: {repo['name']}")
print(f"URL: https://github.com{repo['url']}")
print(f"Description: {repo['description']}")
print(f"Language: {repo['language']}")
print(f"Stars: {repo['stars']}")
print(f"Forks: {repo['forks']}")
print(f"Today's Stars: {repo['today_stars']}")
print()
await main()
上面这些代码的作用是解析一下 github-trending 的数据。由于 github-trending 网站打开比较慢。我们可以先把 github 的 trending 页面保存到本地,再进行解读。
继续导入其他相关的库#
import os
from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Optional
from aiocron import crontab
from pydantic import BaseModel, Field
from pytz import BaseTzInfo
from metagpt.actions.action import Action
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
# fix SubscriptionRunner not fully defined
from metagpt.environment import Environment as _ # noqa: F401
订阅模块#
可以 from metagpt.subscription import SubscriptionRunner 导入,这里贴上代码供参考
class SubscriptionRunner(BaseModel):
"""A simple wrapper to manage subscription tasks for different roles using asyncio.
Example:
>>> import asyncio
>>> from metagpt.subscription import SubscriptionRunner
>>> from metagpt.roles import Searcher
>>> from metagpt.schema import Message
>>> async def trigger():
... while True:
... yield Message("the latest news about OpenAI")
... await asyncio.sleep(3600 * 24)
>>> async def callback(msg: Message):
... print(msg.content)
>>> async def main():
... pb = SubscriptionRunner()
... await pb.subscribe(Searcher(), trigger(), callback)
... await pb.run()
>>> asyncio.run(main())
"""
tasks: Dict[Role, asyncio.Task] = Field(default_factory=dict)
class Config:
arbitrary_types_allowed = True
async def subscribe(
self,
role: Role,
trigger: AsyncGenerator[Message, None],
callback: Callable[
[
Message,
],
Awaitable[None],
],
):
"""Subscribes a role to a trigger and sets up a callback to be called with the role's response.
Args:
role: The role to subscribe.
trigger: An asynchronous generator that yields Messages to be processed by the role.
callback: An asynchronous function to be called with the response from the role.
"""
loop = asyncio.get_running_loop()
async def _start_role():
async for msg in trigger:
resp = await role.run(msg)
await callback(resp)
self.tasks[role] = loop.create_task(_start_role(), name=f"Subscription-{role}")
async def unsubscribe(self, role: Role):
"""Unsubscribes a role from its trigger and cancels the associated task.
Args:
role: The role to unsubscribe.
"""
task = self.tasks.pop(role)
task.cancel()
async def run(self, raise_exception: bool = True):
"""Runs all subscribed tasks and handles their completion or exception.
Args:
raise_exception: _description_. Defaults to True.
Raises:
task.exception: _description_
"""
i=0
while True:
i+=1
for role, task in self.tasks.items():
i=0
if task.done():
if task.exception():
if raise_exception:
raise task.exception()
logger.opt(exception=task.exception()).error(
f"Task {task.get_name()} run error"
)
else:
logger.warning(
f"Task {task.get_name()} has completed. "
"If this is unexpected behavior, please check the trigger function."
)
self.tasks.pop(role)
break
else:
await asyncio.sleep(1)
if i>0:
break
Actions 的实现#
TRENDING_ANALYSIS_PROMPT = """# Requirements
You are a GitHub Trending Analyst, aiming to provide users with insightful and personalized recommendations based on the latest
GitHub Trends. Based on the context, fill in the following missing information, generate engaging and informative titles,
ensuring users discover repositories aligned with their interests.
# The title about Today's GitHub Trending
## Today's Trends: Uncover the Hottest GitHub Projects Today! Explore the trending programming languages and discover key domains capturing developers' attention. From ** to **, witness the top projects like never before.
## The Trends Categories: Dive into Today's GitHub Trending Domains! Explore featured projects in domains such as ** and **. Get a quick overview of each project, including programming languages, stars, and more.
## Highlights of the List: Spotlight noteworthy projects on GitHub Trending, including new tools, innovative projects, and rapidly gaining popularity, focusing on delivering distinctive and attention-grabbing content for users.
---
# Format Example
# [Title]
## Today's Trends
Today, ** and ** continue to dominate as the most popular programming languages. Key areas of interest include **, ** and **.
The top popular projects are Project1 and Project2.
## The Trends Categories
1. Generative AI
- [Project1](https://github/xx/project1): [detail of the project, such as star total and today, language, ...]
- [Project2](https://github/xx/project2): ...
...
## Highlights of the List
1. [Project1](https://github/xx/project1): [provide specific reasons why this project is recommended].
...
---
# Github Trending
{trending}
"""
class CrawlOSSTrending(Action):
async def run(self, url: str = "https://github.com/trending"):
async with aiohttp.ClientSession() as client:
async with client.get(url, proxy=CONFIG.global_proxy) as response:
response.raise_for_status()
html = await response.text()
soup = BeautifulSoup(html, "html.parser")
repositories = []
for article in soup.select("article.Box-row"):
repo_info = {}
repo_info["name"] = (
article.select_one("h2 a")
.text.strip()
.replace("\n", "")
.replace(" ", "")
)
repo_info["url"] = (
"https://github.com" + article.select_one("h2 a")["href"].strip()
)
# Description
description_element = article.select_one("p")
repo_info["description"] = (
description_element.text.strip() if description_element else None
)
# Language
language_element = article.select_one(
'span[itemprop="programmingLanguage"]'
)
repo_info["language"] = (
language_element.text.strip() if language_element else None
)
# Stars and Forks
stars_element = article.select("a.Link--muted")[0]
forks_element = article.select("a.Link--muted")[1]
repo_info["stars"] = stars_element.text.strip()
repo_info["forks"] = forks_element.text.strip()
# Today's Stars
today_stars_element = article.select_one(
"span.d-inline-block.float-sm-right"
)
repo_info["today_stars"] = (
today_stars_element.text.strip() if today_stars_element else None
)
repositories.append(repo_info)
return repositories
class AnalysisOSSTrending(Action):
async def run(self, trending: Any):
return await self._aask(TRENDING_ANALYSIS_PROMPT.format(trending=trending))
Role 实现#
# Role实现
# 对于V0.7 以上的版本,需要把老版本的
# self._init_actions 改为self.set_actions
class OssWatcher(Role):
def __init__(
self,
name="Codey",
profile="OssWatcher",
goal="Generate an insightful GitHub Trending analysis report.",
constraints="Only analyze based on the provided GitHub Trending data.",
):
super().__init__(name=name, profile=profile, goal=goal, constraints=constraints)
self.set_actions([CrawlOSSTrending, AnalysisOSSTrending])
self._set_react_mode(react_mode="by_order")
async def _act(self) -> Message:
logger.info(f"{self._setting}: ready to {self.rc.todo}")
# By choosing the Action by order under the hood
# todo will be first SimpleWriteCode() then SimpleRunCode()
todo = self.rc.todo
msg = self.get_memories(k=1)[0] # find the most k recent messages
result = await todo.run(msg.content)
msg = Message(content=str(result), role=self.profile, cause_by=type(todo))
self.rc.memory.add(msg)
return msg
async def wxpusher_callback(msg: Message):
print(msg.content)
async def trigger():
# 这里设置了只触发五次,也可以用while True 永远执行下去
for i in range(5):
yield Message("the latest news about OpenAI")
await asyncio.sleep(5)
# 每隔五秒钟执行一次。
# 也可以设置为每隔3600 * 24 秒执行一次
运行入口#
# 运行入口,
async def main():
callbacks = []
if not callbacks:
async def _print(msg: Message):
print(msg.content)
callbacks.append(_print)
# callback
async def callback(msg):
await asyncio.gather(*(call(msg) for call in callbacks))
runner = SubscriptionRunner()
await runner.subscribe(OssWatcher(), trigger(), callback)
await runner.run()
await main()
It seems like you've provided a list of GitHub repositories related to various topics. However, there's no clear indication whether these are open-source projects or not. If they're open-source projects, you can use the following Python code to check their star count:
import requests
def get_repo_info(repo_name):
url = f"https://github.com/search?q=stars:{repo_name}&type=Repositories"
response = requests.get(url)
if response.status_code == 200:
repos = []
for repo in response.json():
if "name" in repo and "url" in repo:
name = repo["name"]
url = repo["url"]
stars = int(repo["stargazers_count"])
forks = int(repo["forks_count"]) # Assuming the number of forks is available
repos.append({"name": name, "url": url, "stars": stars, "forks": forks})
return repos
else:
print("Failed to fetch data. Status code:", response.status_code)
repos = get_repo_info('language')
for repo in repos:
if 'language' not in repo or repo['language'] == None:
del repo['language']
print(repos)
This script will fetch the repositories with a specific name (in this case, "language") and print out their star count. If there's no specific language mentioned, it'll assume all languages are open-source.
Please note that GitHub API might not always return accurate data due to various reasons like missing or incorrect information in the repository details. Also, some repositories may have a different number of stars depending on when they were last updated.
It seems like you've provided a list of GitHub repositories related to various topics. However, there's no clear indication whether these are open-source projects or not. If they're open-source projects, you can use the following Python code to check their star count:
import requests
def get_repo_info(repo_name):
url = f"https://github.com/search?q=stars:{repo_name}&type=Repositories"
response = requests.get(url)
if response.status_code == 200:
repos = []
for repo in response.json():
if "name" in repo and "url" in repo:
name = repo["name"]
url = repo["url"]
stars = int(repo["stargazers_count"])
forks = int(repo["forks_count"]) # Assuming the number of forks is available
repos.append({"name": name, "url": url, "stars": stars, "forks": forks})
return repos
else:
print("Failed to fetch data. Status code:", response.status_code)
repos = get_repo_info('language')
for repo in repos:
if 'language' not in repo or repo['language'] == None:
del repo['language']
print(repos)
This script will fetch the repositories with a specific name (in this case, "language") and print out their star count. If there's no specific language mentioned, it'll assume all languages are open-source.
Please note that GitHub API might not always return accurate data due to various reasons like missing or incorrect information in the repository details. Also, some repositories may have a different number of stars depending on when they were last updated.
由于我设置的是每 5 秒钟触发一次,执行 5 次后停止。可以根据自己的需求设置执行频率。
我不能运行,关于代理的问题除了错误。
単一アクション多エージェント#
MetaGPT の核心的な利点は、エージェントチームを簡単かつ柔軟に開発できることです。
私たちは、チームを構築して機能させるために 3 つのステップが必要です:
-
各役割が実行できる期待されるアクションを定義する
-
標準作業手順(SOP)に基づいて各役割がそれを遵守することを保証します。各役割が上流の対応する出力結果を観察し、下流に自分の出力結果を発表することで、これを実現できます。
-
すべての役割を初期化し、環境を持つエージェントチームを作成し、相互に通信できるようにします。
内容は次の通りです:
https://docs.deepwisdom.ai/v0.8/zh/guide/tutorials/multi_agent_101.html
アクションの定義#
私たちは、3 つの役割を持つ Role を定義できます:
SimpleCoder は SimpleWriteCode アクションを持ち、ユーザーの指示を受け取り、主要なコードを作成します。
SimpleTester は SimpleWriteTest アクションを持ち、SimpleWriteCode の出力から主コードを取得し、テストスイートを提供します。
SimpleReviewer は SimpleWriteReview アクションを持ち、SimpleWriteTest の出力からテストケースをレビューし、カバレッジと品質を確認します。
import re
from metagpt.actions import Action, UserRequirement
# コードを書くアクションを構築します
def parse_code(rsp):
pattern = r"```python(.*)```"
match = re.search(pattern, rsp, re.DOTALL)
code_text = match.group(1) if match else rsp
return code_text
class SimpleWriteCode(Action):
PROMPT_TEMPLATE: str = """
Write a python function that can {instruction}.
Return ```python your_code_here ```with NO other texts,
your code:
"""
name: str = "SimpleWriteCode"
async def run(self, instruction: str):
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
rsp = await self._aask(prompt)
code_text = parse_code(rsp)
return code_text
# テストケースを書くアクションを構築します
class SimpleWriteTest(Action):
PROMPT_TEMPLATE: str = """
Context: {context}
Write {k} unit tests using pytest for the given function, assuming you have imported it.
Return ```python your_code_here ```with NO other texts,
your code:
"""
name: str = "SimpleWriteTest"
async def run(self, context: str, k: int = 3):
prompt = self.PROMPT_TEMPLATE.format(context=context, k=k)
rsp = await self._aask(prompt)
code_text = parse_code(rsp)
return code_text
# コードをレビューするアクションを構築します
class SimpleWriteReview(Action):
PROMPT_TEMPLATE: str = """
Context: {context}
Review the test cases and provide one critical comments:
"""
name: str = "SimpleWriteReview"
async def run(self, context: str):
prompt = self.PROMPT_TEMPLATE.format(context=context)
rsp = await self._aask(prompt)
return rsp
役割の定義#
多くの多エージェントシナリオでは、Role を定義するのに数行のコードしか必要ありません。SimpleCoder の場合、私たちは 2 つのことをしました:
-
set_actions を使用して Role に適切なアクションを装備します。これは単一エージェントの設定と同じです。
-
多エージェント操作ロジック:私たちは Role がユーザーまたは他のエージェントからの重要な上流メッセージを観察するようにします。私たちの SOP を思い出してください。SimpleCoder はユーザーの指示を受け取ります。これは MetaGPT の UserRequirement によって引き起こされる Message です。したがって、self._watch ([UserRequirement]) を追加します。
class SimpleCoder(Role):
name: str = "Alice"
profile: str = "SimpleCoder"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._watch([UserRequirement])
self.set_actions([SimpleWriteCode])
上記と同様に、SimpleTester の場合:
-
set_actions を使用して SimpleTester に SimpleWriteTest アクションを装備します。
-
Role が他のエージェントからの重要な上流メッセージを観察するようにします。私たちの SOP を思い出してください。SimpleTester は SimpleCoder から主コードを取得します。これは SimpleWriteCode によって引き起こされる Message です。したがって、self._watch ([SimpleWriteCode]) を追加します。
-
_act 関数をオーバーライドします。私たちは、SimpleTester がすべての記憶をテストケースを書くためのコンテキストとして使用し、5 つのテストケースを希望することを望みます。
class SimpleTester(Role):
name: str = "Bob"
profile: str = "SimpleTester"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([SimpleWriteTest])
self._watch([SimpleWriteCode])
# self._watch([SimpleWriteCode, SimpleWriteReview]) # feel free to try this too
async def _act(self) -> Message:
logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
todo = self.rc.todo
# context = self.get_memories(k=1)[0].content # use the most recent memory as context
context = self.get_memories() # use all memories as context
code_text = await todo.run(context, k=5) # specify arguments
msg = Message(content=code_text, role=self.profile, cause_by=type(todo))
return msg
# 同様のプロセスでSimpleReviewerを定義します:
class SimpleReviewer(Role):
name: str = "Charlie"
profile: str = "SimpleReviewer"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([SimpleWriteReview])
self._watch([SimpleWriteTest])
チームを作成し、役割を追加します
今、私たちは 3 つの役割を定義しました。彼らを一緒に置く時が来ました。すべての役割を初期化し、チームを設定し、彼らを雇います。
チームを実行すると、彼らの間の協力を見ることができます!
import asyncio
from metagpt.team import Team
async def main(
idea: str = "write a function that calculates the product of a list",
investment: float = 3.0,
n_round: int = 5,
):
logger.info(idea)
team = Team()
team.hire(
[
SimpleCoder(),
SimpleTester(),
SimpleReviewer(),
]
)
team.invest(investment=investment)
team.run_project(idea)
await team.run(n_round=n_round)
await main()
結果#
2025-01-26 19:37:59.576 | INFO | main:main:9 - write a function that calculates the product of a list
2025-01-26 19:38:00.116 | INFO | metagpt.team:invest:90 - Investment: $3.0.
2025-01-26 19:38:00.119 | INFO | metagpt.roles.role:_act:391 - Alice(SimpleCoder): to do SimpleWriteCode(SimpleWriteCode)
def product_of_list(lst):
result = 1
for num in lst:
result *= num
return result
2025-01-26 19:38:03.794 | WARNING | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.
2025-01-26 19:38:03.798 | INFO | main:_act:15 - Bob(SimpleTester): to do SimpleWriteTest(SimpleWriteTest)
def test_product_of_list_empty():
assert product_of_list([]) == 1
def test_product_of_list_single_element():
assert product_of_list([5]) == 5
def test_product_of_list_positive_numbers():
assert product_of_list([1, 2, 3, 4]) == 24
def test_product_of_list_negative_numbers():
assert product_of_list([-1, -2, -3, -4]) == -24
def test_product_of_list_mixed_numbers():
assert product_of_list([-1, 2, -3, 4]) ==
2025-01-26 19:38:09.372 | WARNING | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.
2025-01-26 19:38:09.376 | INFO | metagpt.roles.role:_act:391 - Charlie(SimpleReviewer): to do SimpleWriteReview(SimpleWriteReview)
24
Critical Comment:
While the test cases cover a range of scenarios, including empty lists, single elements, positive numbers, negative numbers, and mixed numbers, there is a potential oversight in the test suite. The function product_of_list
does not handle the case where the list contains zero. Since multiplying any number by zero results in zero, it would be beneficial to include a test case that verifies the function's behavior when zero is present in the list. This would ensure that the function is robust and can handle all possible integer values within the
2025-01-26 19:38:14.253 | WARNING | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.
list.
単一アクション多エージェントの例:ディベート#
これは、複数のエージェントを設計し、相互作用を促進する方法を示す例です。バイデンとトランプを代表するエージェントが協力してどのようになるかをシミュレートします。このような組み合わせは生き生きとした交流を生む可能性があり、この実験を「バイデン - トランプディベート」と呼びます。
インスタンスコードの詳細は次のリンクを参照してください:
https://github.com/geekan/MetaGPT/blob/main/examples/debate.py
ディベートは 3 つのステップで設定されます:
- 発言行動を持つディベーター役割を定義します。
- ディベーター間の通信を処理します。つまり、バイデンがトランプの発言を聞き、逆もまた然りです。
- 2 つのディベーターインスタンス、バイデンとトランプを初期化し、環境を持つチームを作成し、相互に交流できるようにします。
モジュールの挿入
import asyncio
import platform
from typing import Any
import fire
from metagpt.actions import Action, UserRequirement
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.team import Team
import asyncio
from metagpt.actions import Action,UserRequirement
from metagpt.schema import Message
アクションの定義
まず、SpeakAloud というアクションを定義します。これは、ディベートシーンのためのものです。
class SpeakAloud(Action):
"""アクション:ディベートで大声で話す(口論)"""
PROMPT_TEMPLATE: str = """
## 背景
あなたは{name}です。あなたは{opponent_name}とのディベートにいます。
## ディベートの履歴
前のラウンド:
{context}
## あなたのターン
今、あなたのターンです。相手の最新の主張に密接に応じ、自分の立場を述べ、主張を守り、相手の主張を攻撃してください。
強力で感情的な応答を80語で作成し、{name}の修辞と視点で主張します。
"""
def __init__(self, name="SpeakAloud", context=None, llm=None):
super().__init__(name, context, llm)
async def run(self, context: str, name: str, opponent_name: str):
prompt = self.PROMPT_TEMPLATE.format(context=context, name=name, opponent_name=opponent_name)
rsp = await self._aask(prompt)
return rsp
役割の定義
汎用的な Role を定義し、Debator と呼びます。
set_actions を使用して、私たちの Role に先ほど定義した SpeakAloud アクションを持たせます。また、_watch を使用して、各ディベーターが相手からの SpeakAloud メッセージとユーザーからの UserRequirement(人間の指示)に注意を払うようにします。
class Debator(Role):
def __init__(
self,
name: str,
profile: str,
opponent_name: str,
**kwargs,
):
super().__init__(name=name, profile=profile, **kwargs)
self.set_actions([SpeakAloud])
self._watch([UserRequirement, SpeakAloud])
self.name = name
self.opponent_name = opponent_name
次に、_observe 関数をオーバーライドすることで、各ディベーターが相手の主張を聞くことができるようにします。このステップは重要です。なぜなら、環境にはトランプとバイデンからの「SpeakAloud メッセージ」があるからです。私たちはトランプが自分の前のラウンドの「SpeakAloud メッセージ」を処理することを望んでいません。代わりに、バイデンからのメッセージを処理する必要があります。
async def _observe(self) -> int:
await super()._observe()
# 自分に送られたメッセージを受け入れ、前のラウンドからの自分のメッセージを無視します
self.rc.news = [msg for msg in self.rc.news if msg.send_to == self.name]
return len(self.rc.news)
最後に、各ディベーターが相手に反論を送信できるようにします。ここでは、メッセージ履歴からコンテキストを構築し、Debator が持っている SpeakAloud アクションを実行し、反論の内容を使用して新しいメッセージを作成します。各 Debator がメッセージを相手に送信することを定義することに注意してください。
async def _act(self) -> Message:
logger.info(f"{self._setting}: ready to {self.rc.todo}")
todo = self.rc.todo # SpeakAloudのインスタンス
memories = self.get_memories()
context = "\n".join(f"{msg.sent_from}: {msg.content}" for msg in memories)
rsp = await todo.run(context=context, name=self.name, opponent_name=self.opponent_name)
msg = Message(
content=rsp,
role=self.profile,
cause_by=todo,
sent_from=self.name,
send_to=self.opponent_name,
)
self.rc.memory.add(msg)
return msg
チームを作成し、役割を追加します
チームを構築し、役割アクションを組み合わせます。私たちは、指示(UserRequirement)をバイデンに送信し、彼が最初に開始するようにします。トランプに最初に発言させたい場合は、send_to を "Trump" に設定します。
チームを実行すると、彼らの間の対話を見ることができます!
async def debate(idea: str, investment: float = 3.0, n_round: int = 5):
"""バイデン-トランプディベートを実行し、彼らの友好的な対話を観察します :) """
Biden = Debator(name="Biden", profile="Democrat", opponent_name="Trump")
Trump = Debator(name="Trump", profile="Republican", opponent_name="Biden")
team = Team()
team.hire([Biden, Trump])
team.invest(investment)
team.run_project(idea, send_to="Biden") # ディベートのトピックをバイデンに送信し、彼が最初に発言するようにします
await team.run(n_round=n_round)
import typer
app = typer.Typer()
@app.command()
def main(
idea: str = typer.Argument(..., help="Economic Policy: Discuss strategies and plans related to taxation, employment, fiscal budgeting, and economic growth."),
investment: float = typer.Option(default=3.0, help="Dollar amount to invest in the AI company."),
n_round: int = typer.Option(default=5, help="Number of rounds for the simulation."),
):
"""
:param idea: Debate topic, such as "Topic: The U.S. should commit more in climate change fighting"
or "Trump: Climate change is a hoax"
:param investment: contribute a certain dollar amount to watch the debate
:param n_round: maximum rounds of the debate
:return:
"""
if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(debate(idea, investment, n_round))
if __name__ == '__main__':
app()# run as python debate.py --idea="TOPIC" --investment=3.0 --n_round=5
しかし、上記のチュートリアルコードは実行できません。以下のコードを実行してください:
import asyncio
import platform
from typing import Any
import fire
from metagpt.actions import Action, UserRequirement
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.team import Team
import asyncio
from metagpt.actions import Action,UserRequirement
from metagpt.schema import Message
class SpeakAloud(Action):
"""アクション:ディベートで大声で話す(口論)"""
PROMPT_TEMPLATE: str = """
## 背景
あなたは{name}です。あなたは{opponent_name}とのディベートにいます。
## ディベートの履歴
前のラウンド:
{context}
## あなたのターン
今、あなたのターンです。相手の最新の主張に密接に応じ、自分の立場を述べ、主張を守り、相手の主張を攻撃してください。
強力で感情的な応答を80語で作成し、{name}の修辞と視点で主張します。
"""
def __init__(self, name="SpeakAloud", context=None, llm=None):
super().__init__(name=name, context=context, llm=llm)
async def run(self, context: str, name: str, opponent_name: str):
prompt = self.PROMPT_TEMPLATE.format(context=context, name=name, opponent_name=opponent_name)
rsp = await self._aask(prompt)
return rsp
class Debator(Role):
def __init__(
self,
**kwargs,
):
super().__init__(**kwargs)
self.set_actions([SpeakAloud])
self._watch([UserRequirement, SpeakAloud])
async def _observe(self) -> int:
await super()._observe()
# 自分に送られたメッセージを受け入れ、前のラウンドからの自分のメッセージを無視します
self.rc.news = [msg for msg in self.rc.news if self.name in msg.send_to]
return len(self.rc.news)
async def _act(self) -> Message:
logger.info(f"{self._setting}: ready to {self.rc.todo}")
todo = self.rc.todo # SpeakAloudのインスタンス
memories = self.get_memories()
context = "\n".join(f"{msg.sent_from}: {msg.content}" for msg in memories)
rsp = await todo.run(context=context, name=self.name, opponent_name=self.opponent_name)
msg = Message(
content=rsp,
role=self.profile,
cause_by=todo,
sent_from=self.name,
send_to=self.opponent_name,
)
return msg
async def debate(idea: str, investment: float = 3.0, n_round: int = 5):
"""バイデン-トランプディベートを実行し、彼らの友好的な対話を観察します :) """
Biden = Debator(name="Biden", profile="Democrat", opponent_name="Trump")
Trump = Debator(name="Trump", profile="Republican", opponent_name="Biden")
team = Team()
team.hire([Biden, Trump])
team.invest(investment)
team.run_project(idea, send_to="Biden") # ディベートのトピックをバイデンに送信し、彼が最初に発言するようにします
await team.run(n_round=n_round)
import typer
app = typer.Typer()
@app.command()
def main(
idea: str = typer.Argument(..., help="Economic Policy: Discuss strategies and plans related to taxation, employment, fiscal budgeting, and economic growth."),
investment: float = typer.Option(default=3.0, help="Dollar amount to invest in the AI company."),
n_round: int = typer.Option(default=5, help="Number of rounds for the simulation."),
):
"""
:param idea: