Reference:Datawhale wow agent day11
実現内容#
本節では、Zigent フレームワークを使用して、指定された Markdown ファイルの内容に基づいて自動的に試験問題を生成する出題エージェントを実装します。このエージェントは、単一選択問題、複数選択問題、空欄補充問題の 3 種類の問題形式をサポートし、生成された試験問題を Markdown ファイルとして保存できます。
設計思路#
出題エージェントの核心機能は以下の通りです:
- 指定されたディレクトリから Markdown ファイルを読み込み、知識のソースとする
- ユーザーが指定した対象群と評価目的に基づいて試験問題を生成する
- 様々な問題形式をサポート(単一選択問題、複数選択問題、空欄補充問題)
- 自動的に生成された試験問題を保存し、結果のパスを返す
コード実装#
出題アクションの定義#
QuizGenerationAction クラスを定義し、試験問題を生成します:
class QuizGenerationAction(BaseAction):
"""Markdownコンテンツから試験問題を生成する"""
def __init__(self, llm: LLM) -> None:
action_name = "GenerateQuiz"
action_desc = "Markdownコンテンツから試験問題を生成する"
params_doc = {
"content": "(Type: string): 問題を生成するためのMarkdownコンテンツ",
"question_types": "(Type: list): 生成する問題形式のリスト",
"audience": "(Type: string): 試験の対象群",
"purpose": "(Type: string): 試験の目的"
}
super().__init__(action_name, action_desc, params_doc)
self.llm = llm
def __call__(self, **kwargs):
content = kwargs.get("content", "")
question_types = kwargs.get("question_types", [])
audience = kwargs.get("audience", "")
purpose = kwargs.get("purpose", "")
prompt = f"""
あなたは試験問題を設計するためのロボットです。全てのやり取りは日本語で行います。
あなたの任務は、ユーザーが迅速に試験問題を作成、設計するのを助けることです。試験問題はMarkdown形式で提供されます。
要求:
1. 対象群:{audience}
2. 評価目的:{purpose}
3. 含める必要がある問題形式:{", ".join(question_types)}
4. 試験問題の形式要件:
"""
prompt += """
# アンケートタイトル
---
1. これは判断問題の問題文ですか?
- (x) 正しい
- ( ) 間違い
# (x)が正しい答えです
2. これは単一選択問題の問題文です
- (x) これは正しい選択肢です
- ( ) これは間違った選択肢です
# (x)が正しい答えです
3. これは複数選択問題の問題文ですか?
- [x] これは正しい選択肢1です
- [x] これは正しい選択肢2です
- [ ] これは間違った選択肢1です
- [ ] これは間違った選択肢2です
# [x]が正しい答えです
4. これは空欄補充問題の問題文ですか?
- R:= 空欄補充問題の答え
#空欄補充問題の正しい答えの形式
"""
prompt += f"\n以下の内容に基づいて試験問題を生成してください:\n{content}"
quiz_content = self.llm.run(prompt)
return {
"quiz_content": quiz_content,
"audience": audience,
"purpose": purpose,
"question_types": question_types
}
保存アクションの定義#
SaveQuizAction クラスを定義し、生成された試験問題を保存します:
class SaveQuizAction(BaseAction):
"""試験をファイルに保存し、URLを返す"""
def __init__(self) -> None:
action_name = "SaveQuiz"
action_desc = "試験コンテンツをファイルに保存し、URLを返す"
params_doc = {
"quiz_content": "(Type: string): 保存する試験コンテンツ",
"quiz_title": "(Type: string): 試験のタイトル"
}
super().__init__(action_name, action_desc, params_doc)
def __call__(self, **kwargs):
quiz_content = kwargs.get("quiz_content", "")
quiz_title = kwargs.get("quiz_title", "quiz")
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"{quiz_title}.md")
with open(output_file, 'w', encoding='utf-8') as f:
f.write(quiz_content)
return {
"file_path": output_file,
"quiz_url": f"/{output_file}"
}
出題エージェントの定義#
QuizGeneratorAgent クラスを定義し、出題プロセス全体を管理します:
class QuizGeneratorAgent(BaseAgent):
"""試験生成エージェントで、試験作成プロセスを管理する"""
def __init__(
self,
llm: LLM,
markdown_dir: str
):
name = "QuizGeneratorAgent"
role = """あなたは専門の試験生成アシスタントです。提供されたMarkdownコンテンツに基づいて、構造が整い、
内容が充実した試験問題を生成できます。あなたは対象群と評価目的に応じて適切な問題を設計するのが得意です。"""
super().__init__(
name=name,
role=role,
llm=llm,
)
self.markdown_dir = markdown_dir
self.quiz_action = QuizGenerationAction(llm)
self.save_action = SaveQuizAction()
self._add_quiz_example()
def _load_markdown_content(self) -> str:
"""ディレクトリからすべてのMarkdownファイルを読み込む"""
content = []
for root, _, files in os.walk(self.markdown_dir):
for file in files:
if file.endswith(".md"):
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
content.append(f.read())
return "\n".join(content)
def __call__(self, task: TaskPackage):
"""試験生成タスクを処理する"""
# タスクパラメータを解析
params = json.loads(task.instruction)
audience = params.get("audience", "")
purpose = params.get("purpose", "")
question_types = params.get("question_types", [])
# Markdownコンテンツを読み込む
content = self._load_markdown_content()
# 試験を生成する
quiz_result = self.quiz_action(
content=content,
question_types=question_types,
audience=audience,
purpose=purpose
)
# 試験を保存する
save_result = self.save_action(
quiz_content=quiz_result["quiz_content"],
quiz_title="generated_quiz"
)
task.answer = {
"quiz_content": quiz_result["quiz_content"],
"quiz_url": save_result["quiz_url"]
}
task.completion = "completed"
return task
使用#
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('ZISHU_API_KEY')
base_url = "http://43.200.7.56:8008/v1"
chat_model = "deepseek-chat"
llm = LLM(api_key=api_key, base_url=base_url, model_name=chat_model)
# 出題エージェントを作成
markdown_dir = "docs" # Markdownファイルを含むディレクトリを指定
agent = QuizGeneratorAgent(llm=llm, markdown_dir=markdown_dir)
# 試験パラメータを定義
quiz_params = {
"audience": "高レベル", # 対象群
"purpose": "知識の習得状況をテストする", # 評価目的
"question_types": ["単一選択問題"] # 含める必要がある問題形式
}
# 試験を生成する
task = TaskPackage(instruction=json.dumps(quiz_params))
result = agent(task)
print("生成された試験内容:")
print(result.answer["quiz_content"])
print(f"試験のパス: {result.answer['quiz_url']}")
結果:
高レベル知識習得状況テスト
-
量子力学におけるプランク定数 (h) の単位は何ですか?
- (x) ジュール・秒 (J・s)
- () ニュートン・メートル (N・m)
- () ワット・秒 (W・s)
- () クーロン・秒 (C・s)
-
生化学において、DNA の二重螺旋構造は 1953 年にどの 2 人の科学者によって提唱されましたか?
- (x) ジェームズ・ワトソンとフランシス・クリック
- ( ) ルドルフ・ウィルツとアルバート・サックス
- ( ) ロバート・フッカーとフランシス・クリック
- ( ) ジョージ・ビードルとエドワード・タトゥム
-
次の元素の中で、アルカリ金属元素に該当するのはどれですか?
- (x) ナトリウム (Na)
- () 塩素 (Cl)
- () 酸素 (O)
- () ケイ素 (Si)
-
古典力学において、物体の空間における運動状態を記述する基本方程式は:
- (x) ニュートンの第二法則 ( F = ma )
... - () 酸素 (O)
- () 水素 (H)
- () 塩素 (Cl)
- (x) ニュートンの第二法則 ( F = ma )