MetaGPT 教学助手:一键生成技术教程

在学习新技术时,一份好的教程可以事半功倍。MetaGPT 的教学助手角色,可以帮助你根据简单的描述,自动生成技术教程文档。

教学助手的功能

教学助手可以根据用户提供的单句描述,生成一份技术教程文档,并支持自定义语言。

设计理念

教学助手的设计思路是:

  1. 使用大型语言模型 (LLM) 生成教程的大纲。
  2. 根据二级标题将大纲分解成多个部分。
  3. 针对每个部分,根据标题生成详细内容。
  4. 最后将标题和内容拼接起来。

使用分段的方式,可以解决 LLM 模型处理长文本的限制。

代码实现

角色定义

定义教学助手角色类,继承自 Role 基类,并重写 __init__ 初始化方法。__init__ 方法必须包含 nameprofilegoalconstraints 参数。第一行代码使用 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(
    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) 调用父类的构造函数,初始化动作。这里,使用 argskwargs 将其他参数传递给父类构造函数,例如 contextllm

#!/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 的教学助手角色可以帮助你快速生成技术教程文档,并支持自定义语言。它可以节省你大量时间和精力,让你专注于更重要的工作。

更多学习资源

0 0 投票数
Article Rating
订阅评论
提醒
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x