Reference:Datawhale wow agent day10
本節では、Zigent フレームワークを使用して、チュートリアルを作成するエージェントを実装します。このエージェントの主な機能は、チュートリアルのテーマを入力し、その後自動的に完全なチュートリアルコンテンツを生成することです。 設計思想:
まず、LLM 大モデルを使用してチュートリアルの目次を生成し、次に目次を二次タイトルに従って分割し、各ブロックの目次に従って詳細な内容を生成し、最後にタイトルと内容を結合します。この分割設計は、LLM 大モデルの長文制限の問題を解決します。
チュートリアルの目次を生成する Action クラスの定義#
WriteDirectoryAction クラスを定義し、BaseAction から継承します。このクラスの主な機能は、チュートリアルの目次構造を生成することです。具体的には、与えられたテーマと言語に基づいて特定の形式に従った目次を生成するために、大言語モデル(LLM)を呼び出します。
class WriteDirectoryAction(BaseAction):
"""チュートリアル目次構造生成アクション"""
def __init__(self) -> None:
action_name = "WriteDirectory"
action_desc = "チュートリアル目次構造を生成"
params_doc = {
"topic": "(タイプ: 文字列): チュートリアルのテーマ名",
"language": "(タイプ: 文字列): 出力言語 (デフォルト: 'Chinese')"
}
super().__init__(action_name, action_desc, params_doc)
def __call__(self, **kwargs):
topic = kwargs.get("topic", "")
language = kwargs.get("language", "Chinese")
directory_prompt = f"""
テーマ"{topic}"のチュートリアル目次構造を生成してください。要求:
1. 出力言語は{language}でなければなりません
2. 以下の辞書形式に厳密に従って出力してください: {{"title": "xxx", "directory": [{{"章1": ["小節1", "小節2"]}}, {{"章2": ["小節3", "小節4"]}}]}}
3. 目次の階層は合理的で、主目次と子目次を含む必要があります
4. 各目次タイトルは実際の意味を持つ必要があります
5. 不要な空白や改行を含めないでください
"""
# LLMを呼び出して目次を生成
directory_data = llm.run(directory_prompt)
try:
directory_data = json.loads(directory_data)
except:
directory_data = {"title": topic, "directory": []}
return {
"topic": topic,
"language": language,
"directory_data": directory_data
}
チュートリアル内容を生成する Action クラスの定義#
WriteContentAction クラスは、チュートリアル内容を生成するために使用されます。その__call__メソッドは、タイトル、章、言語、目次データを受け取り、内容のプロンプトを構築し、最後に LLM を呼び出して対応する内容を生成します。
class WriteContentAction(BaseAction):
"""チュートリアル内容生成アクション"""
def __init__(self) -> None:
action_name = "WriteContent"
action_desc = "目次構造に基づいて詳細なチュートリアル内容を生成"
params_doc = {
"title": "(タイプ: 文字列): セクションタイトル",
"chapter": "(タイプ: 文字列): 章タイトル",
"directory_data": "(タイプ: dict): 完全な目次構造",
"language": "(タイプ: 文字列): 出力言語 (デフォルト: 'Chinese')"
}
super().__init__(action_name, action_desc, params_doc)
def __call__(self, **kwargs):
title = kwargs.get("title", "")
chapter = kwargs.get("chapter", "")
language = kwargs.get("language", "Chinese")
directory_data = kwargs.get("directory_data", {})
content_prompt = f"""
チュートリアル章の詳細な内容を生成してください:
チュートリアルタイトル: {directory_data.get('title', '')}
章: {chapter}
小節: {title}
要求:
1. 内容は詳細かつ正確であること
2. コード例が必要な場合は、標準規範に従って提供してください
3. Markdown形式を使用してください
4. 出力言語は{language}でなければなりません
5. 内容の長さは適度で、通常500〜1000字の範囲であること
"""
# LLMを呼び出して内容を生成
content = llm.run(content_prompt)
return content
チュートリアル作成エージェントの定義#
TutorialAssistant クラスを定義し、BaseAgent から継承します。チュートリアル内容を生成するために使用されます。その主な機能には、目次と内容生成のアクション(WriteDirectoryAction と WriteContentAction)の初期化、_generate_tutorial メソッドが目次データに基づいて目次と各章の詳細な内容を生成すること、_add_tutorial_example メソッドがアシスタントに例題を追加し、Python チュートリアルの目次と内容を生成する方法を示すことが含まれます。最終的に__call__メソッドを呼び出してチュートリアル生成タスクを処理します。タスクからテーマを抽出し、目次構造を生成し、次に完全なチュートリアル内容を生成し、結果をローカルに保存します。
class TutorialAssistant(BaseAgent):
"""目次と内容の作成を管理するチュートリアル生成アシスタント"""
def __init__(
self,
llm: LLM,
language: str = "Chinese"
):
name = "TutorialAssistant"
role = """あなたはプロのチュートリアルライターです。さまざまなトピックに関する構造化された、包括的なチュートリアルを作成できます。内容を論理的に整理し、複雑な概念を明確に説明するのが得意です。"""
super().__init__(
name=name,
role=role,
llm=llm,
)
self.language = language
self.directory_action = WriteDirectoryAction()
self.content_action = WriteContentAction()
# チュートリアルアシスタントの例を追加
self._add_tutorial_example()
def _generate_tutorial(self, directory_data: Dict) -> str:
"""目次構造に基づいて完全なチュートリアル内容を生成"""
full_content = []
title = directory_data["title"]
full_content.append(f"# {title}\n")
# 目次を生成
full_content.append("## 目次\n")
for idx, chapter in enumerate(directory_data["directory"], 1):
for chapter_title, sections in chapter.items():
full_content.append(f"{idx}. {chapter_title}")
for section_idx, section in enumerate(sections, 1):
full_content.append(f" {idx}.{section_idx}. {section}")
full_content.append("\n---\n")
# 各セクションの内容を生成
for chapter in directory_data["directory"]:
for chapter_title, sections in chapter.items():
for section in sections:
content = self.content_action(
title=section,
chapter=chapter_title,
directory_data=directory_data,
language=self.language
)
full_content.append(content)
full_content.append("\n---\n")
return "\n".join(full_content)
def __call__(self, task: TaskPackage):
"""チュートリアル生成タスクを処理"""
# タスクからテーマを抽出
topic = task.instruction.split("Create a ")[-1].split(" tutorial")[0]
if not topic:
topic = task.instruction
# 目次構造を生成
directory_result = self.directory_action(
topic=topic,
language=self.language
)
print(directory_result)
# 完全なチュートリアルを生成
tutorial_content = self._generate_tutorial(directory_result["directory_data"])
# 結果を保存
task.answer = tutorial_content
task.completion = "completed"
return task
def _add_tutorial_example(self):
"""チュートリアルアシスタントのための例を追加"""
exp_task = "初心者向けのPythonチュートリアルを作成する"
exp_task_pack = TaskPackage(instruction=exp_task)
topic = "Python基礎チュートリアル"
act_1 = AgentAct(
name=ThinkAct.action_name,
params={INNER_ACT_KEY: """まず、Pythonチュートリアルの目次構造を作成し、次に各セクションの詳細な内容を生成します。"""}
)
obs_1 = "OK。目次構造から始めます。"
act_2 = AgentAct(
name=self.directory_action.action_name,
params={
"topic": topic,
"language": self.language
}
)
obs_2 = """{"title": "Python基礎チュートリアル", "directory": [
{"第1章:Python紹介": ["1.1 Pythonとは", "1.2 環境構築"]},
{"第2章:基本文法": ["2.1 変数とデータ型", "2.2 制御フロー"]}
]}"""
act_3 = AgentAct(
name=self.content_action.action_name,
params={
"title": "Pythonとは",
"chapter": "第1章:Python紹介",
"directory_data": json.loads(obs_2),
"language": self.language
}
)
obs_3 = """# 第1章:Python紹介\n## Pythonとは\n\nPythonは高級プログラミング言語です..."""
act_4 = AgentAct(
name=FinishAct.action_name,
params={INNER_ACT_KEY: "チュートリアルの構造と内容が正常に生成されました。"}
)
obs_4 = "チュートリアル生成タスクが正常に完了しました。"
exp_act_obs = [(act_1, obs_1), (act_2, obs_2), (act_3, obs_3), (act_4, obs_4)]
self.prompt_gen.add_example(
task=exp_task_pack,
action_chain=exp_act_obs
)
インタラクティブな操作でチュートリアル作成エージェントを呼び出す#
メインプログラムでは、TutorialAssistant インスタンスを作成し、その__call__メソッドを呼び出してインタラクティブにチュートリアルを生成する機能を実現します。ユーザーは作成したいチュートリアルのテーマを入力し、その後 TutorialAssistant を呼び出して対応するチュートリアル内容を生成し、結果をローカルファイルに保存します。
if __name__ == "__main__":
assistant = TutorialAssistant(llm=llm)
# インタラクティブにチュートリアルを生成
FLAG_CONTINUE = True
while FLAG_CONTINUE:
input_text = input("どのチュートリアルを作成したいですか?\n")
task = TaskPackage(instruction=input_text)
result = assistant(task)
print("\n生成されたチュートリアル:\n")
print(result.answer)
# 出力ディレクトリを作成
output_dir = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
os.makedirs(output_dir, exist_ok=True)
# ファイルを保存
output_file = os.path.join(output_dir, f"{input_text}.md")
with open(output_file, 'w', encoding='utf-8') as f:
f.write(result.answer)
if input("\n別のチュートリアルを作成しますか? (y/n): ").lower() != "y":
FLAG_CONTINUE = False
テスト結果#
基礎トポロジーのチュートリアルを生成:
長すぎるので完全には掲載しません。