在学习新技术时,一份好的教程可以事半功倍。MetaGPT 的教学助手角色,可以帮助你根据简单的描述,自动生成技术教程文档。
友情链接:ACEJoy
教学助手的功能
教学助手可以根据用户提供的单句描述,生成一份技术教程文档,并支持自定义语言。
设计理念
教学助手的设计思路是:
- 使用大型语言模型 (LLM) 生成教程的大纲。
- 根据二级标题将大纲分解成多个部分。
- 针对每个部分,根据标题生成详细内容。
- 最后将标题和内容拼接起来。
使用分段的方式,可以解决 LLM 模型处理长文本的限制。
代码实现
角色定义
定义教学助手角色类,继承自 Role
基类,并重写 __init__
初始化方法。__init__
方法必须包含 name
、profile
、goal
和 constraints
参数。第一行代码使用 super().__init__(name, profile, goal, constraints)
调用父类的构造函数,初始化 Role
。使用 self.set_actions([WriteDirectory(language=language)])
添加初始动作和状态。这里,最初添加了写目录动作。此外,还可以定义自定义参数;这里添加了 language
参数,用于支持自定义语言。使用 self._set_react_mode(react_mode="by_order")
将 set_actions
中的动作执行顺序设置为顺序执行。
class TutorialAssistant(Role):
"""教学助手,输入一句话生成一份标记格式的教程文档。
Args:
name: 角色名称。
profile: 角色简介。
goal: 角色目标。
constraints: 角色的约束或要求。
language: 生成教程文档的语言。
"""
def __init__(
self,
name: str = "Stitch",
profile: str = "教学助手",
goal: str = "生成教程文档",
constraints: str = "严格遵循 Markdown 语法,布局整洁规范",
language: str = "Chinese",
):
super().__init__(name, profile, goal, constraints)
self.set_actions([WriteDirectory(language=language)])
self.topic = ""
self.main_title = ""
self.total_content = ""
self.language = language
self._set_react_mode(react_mode="by_order")
重写 react
方法
重写 react
方法。使用 await super().react()
调用 Role
基类的 react
方法。根据 __init__
方法中设置的 react_mode="by_order"
,按顺序执行 states
中的每个动作。这里重写的目的是在完成所有动作后执行最终操作,即:将拼接后的教程内容写入 markdown 文件。
async def react(self) -> Message:
msg = await super().react()
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
重写 _act
方法
_act
方法负责执行动作。使用 todo = self.rc.todo
从上下文中获取下一个要执行的动作,然后执行动作的 run
方法。这里,它首先通过 WriteDirectory
获取教程目录结构,然后对目录进行分块,为每个块生成一个 WriteContent
动作,并初始化新添加的动作。这里再次调用 await super().react()
是为了从头开始执行所有新添加的 WriteContent
动作。每个动作的结果用于生成一个 Message(content=resp, role=self.profile)
消息,该消息可以放置在上下文记忆 self.rc.memory
中。该角色不需要存储。
async def _act(self) -> Message:
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)
await self._handle_directory(resp)
return await super().react()
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 _handle_directory(self, titles: Dict) -> Message:
"""处理教程文档的目录。
Args:
titles: 包含标题和目录结构的字典,
例如 {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}
Returns:
包含目录信息的 Message。
"""
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)
动作定义
定义动作,每个动作对应一个类对象。继承自 Action
基类,并重写 __init__
初始化方法。__init__
方法包含 name
参数。第一行代码使用 super().__init__(name, *args, **kwargs)
调用父类的构造函数,初始化动作。这里,使用 args
和 kwargs
将其他参数传递给父类构造函数,例如 context
和 llm
。
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
"""
@Time : 2023/9/4 15:40:40
@Author : Stitch-z
@File : tutorial_assistant.py
@Describe : Actions of the tutorial assistant, including writing directories and document content.
"""
from typing import Dict
from metagpt.actions import Action
from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT
from metagpt.utils.common import OutputParser
class WriteDirectory(Action):
"""写教程目录的动作类。
Args:
name: 动作名称。
language: 输出语言,默认值为 "Chinese"。
"""
def __init__(self, name: str = "", language: str = "Chinese", *args, **kwargs):
super().__init__(name, *args, **kwargs)
self.language = language
重写 run
方法
run
方法是动作执行的主要函数,使用 self._aask(prompt=prompt)
方法查询 LLM 模型。
async def run(self, topic: str, *args, **kwargs) -> Dict:
"""执行动作,根据主题生成教程目录。
Args:
topic: 教程主题。
Returns:
教程目录信息,包括 {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}。
"""
prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language)
resp = await self._aask(prompt=prompt)
return OutputParser.extract_struct(resp, dict)
其他动作的编写类似:
class WriteContent(Action):
"""写教程内容的动作类。
Args:
name: 动作名称。
directory: 要写入的内容。
language: 输出语言,默认值为 "Chinese"。
"""
def __init__(self, name: str = "", directory: str = "", language: str = "Chinese", *args, **kwargs):
super().__init__(name, *args, **kwargs)
self.language = language
self.directory = directory
async def run(self, topic: str, *args, **kwargs) -> str:
"""执行动作,根据目录和主题写入文档内容。
Args:
topic: 教程主题。
Returns:
写入的教程内容。
"""
prompt = CONTENT_PROMPT.format(topic=topic, language=self.language, directory=self.directory)
return await self._aask(prompt=prompt)
角色执行结果
输入示例
- MySQL 教程
- Redis 教程
- Hive 教程
执行命令示例
提供相应的执行命令示例。
执行结果
生成的教程文档位于项目的 /data/tutorial_docx
目录中。
注意:
该角色目前不支持互联网搜索功能。内容生成依赖于 LLM 大模型训练的数据。
总结
MetaGPT 的教学助手角色可以帮助你快速生成技术教程文档,并支持自定义语言。它可以节省你大量时间和精力,让你专注于更重要的工作。
更多学习资源
- MetaGPT 文档:了解更多关于 MetaGPT 的信息。
- MetaGPT Github:查看 MetaGPT 的源代码和示例。
- MetaGPT 论文:深入了解 MetaGPT 的技术细节。