Skip to content

Commit

Permalink
Merge pull request #2 from Stitch-z/docs-roles
Browse files Browse the repository at this point in the history
docs: Update tutorial assistant documentation.
  • Loading branch information
Stitch-z authored Dec 2, 2023
2 parents a20cda9 + f71d3e5 commit f1738c2
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 84 deletions.
71 changes: 20 additions & 51 deletions src/guide/use_cases/agent/tutorial_assistant.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
# Tutorial Assistant: Generate technology tutorial

### Role Introduction
## Role Introduction

#### Function Description
### Function Description

Generate a technical tutorial document based on a single sentence input, with support for custom languages.

#### Design Concept
### Design Concept

The design approach involves using the `LLM` (Large Language Model) to initially generate the tutorial's outline. Then, the outline is divided into sections based on secondary headings. For each section, detailed content is generated according to the headings. Finally, the titles and content are concatenated. The use of sections addresses the limitation of long text in the `LLM` model.

#### Source Code
### Source Code

[GitHub Source Code](https://github.com/geekan/MetaGPT/blob/main/metagpt/roles/tutorial_assistant.py)


## Role Definition

### Role Definition

1. Define a role class, inherit from the `Role` base class, and override the `__init__` initialization method. The `__init__` method must include `name`, `profile`, `goal`, `constraints` parameters. The first line of code uses `super().__init__(name, profile, goal, constraints)` to call the constructor of the parent class, initializing the `Role`. Use `self._init_actions([WriteDirectory(language=language)])` to add initial `action` and `states`, here adding the action to write the directory. Custom parameters can also be added; here, the `language` parameter is added to support custom languages.
1. Define the role class, inheriting from the `Role` base class, and override the `__init__` initialization method. The `__init__` method must include the parameters `name`, `profile`, `goal`, and `constraints`. The first line of code uses `super().__init__(name, profile, goal, constraints)` to call the constructor of the parent class, initializing the `Role`. Use `self._init_actions([WriteDirectory(language=language)])` to add initial `action` and `states`. Here, the write directory action is added initially. Additionally, custom parameters can be defined; here, the `language` parameter is added to support custom languages. Use `self._set_react_mode(react_mode="by_order")` to set the execution order of actions in `_init_actions` to sequential.

```python
class TutorialAssistant(Role):
Expand Down Expand Up @@ -46,58 +45,31 @@ The design approach involves using the `LLM` (Large Language Model) to initially
self.main_title = ""
self.total_content = ""
self.language = language
self._set_react_mode(react_mode="by_order")
```

2. Override the `_react` method. The `_react` method loops through think and action operations. When there is no next action to be done (`todo` is None), the loop ends. After executing all actions, the final operation can be performed, here writing the concatenated tutorial content into a markdown file.
2. Override the `react` method. Use `await super().react()` to call the `react` method of the `Role` base class. According to the `react_mode="by_order"` set in the `__init__` method, execute each `action` in `states` in order. The purpose of overriding here is to perform final operations after completing all actions, i.e., writing the concatenated tutorial content into a `markdown` file.

```python
async def _react(self) -> Message:
"""Execute the assistant's think and actions.
Returns:
A message containing the final result of the assistant's actions.
"""
while True:
await self._think()
if self._rc.todo is None:
break
msg = await self._act()
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
```

3. Override the `_think `method. The `_think` method is used to consider the next execution step. If `todo` is empty, indicating no next operation to be executed, set `state` to `0`. If executing the next step would exceed the length of the initialized `states`, indicating that it is currently the last step, set `todo` to None to exit the loop in the `_react` method and stop executing think and action; otherwise, increment `state` by `1`.

```python
async def _think(self) -> None:
"""Determine the next action to be taken by the role."""
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
```

4. Override the `_act` method. The `_act` method is the execution of an `action`. Use `todo = self._rc.todo` to get the next `action` to be executed from the context, then execute the `run` method of the `action`. Here, first get the directory structure of the tutorial through `WriteDirectory`, then chunk the directory. For each chunk, generate a `WriteContent` action, then initialize the newly added `action`. The result of each `action` is used to generate a message `Message(content=resp, role=self.profile)`, which can be placed in the context memory `self._rc.memory`. This role does not need to be stored.
3. Override the `_act` method. The `_act` method is responsible for executing the `action`. Use `todo = self._rc.todo` to get the next `action` to be executed from the context, and then execute the `run` method of the `action`. Here, it first obtains the tutorial directory structure through `WriteDirectory`, then chunks the directory, generates a `WriteContent` action for each chunk, and initializes the newly added action. Here, calling `await super().react()` again is to execute all the newly added `WriteContent` actions from the beginning. The result of each action is used to generate a message `Message(content=resp, role=self.profile)`, which can be placed in the context memory `self._rc.memory`. This role does not need to be stored.

```python
async def _act(self) -> Message:
"""Perform an action as determined by the role.
Returns:
A message containing the result of the action.
"""
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)
await self._handle_directory(resp)
return await super().react()
resp = await todo.run(topic=self.topic)
logger.info(resp)
if self.total_content != "":
Expand All @@ -111,7 +83,7 @@ The design approach involves using the `LLM` (Large Language Model) to initially
Args:
titles: A dictionary containing the titles and directory structure,
such as {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}
Returns:
A message containing information about the directory.
"""
Expand All @@ -126,13 +98,10 @@ The design approach involves using the `LLM` (Large Language Model) to initially
for second_dir in first_dir[key]:
directory += f" - {second_dir}\n"
self._init_actions(actions)
self._rc.todo = None
return Message(content=directory)
```



### Action Definition
## Action Definition

1. Define an `action`, where each `action` corresponds to a `class` object. Inherit from the `Action` base class and override the `__init__` initialization method. The `__init__` method includes the `name` parameter. The first line of code uses `super().__init__(name, *args, **kwargs)` to call the constructor of the parent class, initializing the `action`. Here, use `args` and `kwargs` to pass other parameters to the parent class constructor, such as `context` and `llm`.

Expand Down Expand Up @@ -215,19 +184,19 @@ The design approach involves using the `LLM` (Large Language Model) to initially



### Role Execution Results
## Role Execution Results

#### Input Examples
### Input Examples

- `MySQL` Tutorial
- `Redis` Tutorial
- `Hive` Tutorial

#### Execution Command Examples
### Execution Command Examples

Provide corresponding execution command examples.

#### Execution Results
### Execution Results

The generated tutorial documents are located in the project's `/data/tutorial_docx` directory. Screenshots are provided below:

Expand All @@ -237,7 +206,7 @@ The generated tutorial documents are located in the project's `/data/tutorial_do



### Note
## Note

This role currently does not support internet search capabilities. Content generation relies on data trained by the `LLM` large model.

41 changes: 8 additions & 33 deletions src/zhcn/guide/use_cases/agent/tutorial_assistant.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

## 角色定义

1. 定义角色类,继承 `Role` 基类,重写 `__init__` 初始化方法。`__init__` 方法必须包含`name``profile``goal``constraints` 参数。第一行代码使用`super().__init__(name, profile, goal, constraints)` 调用父类的构造函数,实现 `Role` 的初始化。使用 `self._init_actions([WriteDirectory(language=language)])` 添加初始的 `action``states`,这里先添加写目录的 `action`。同时,也可以自定义参数,这里加了 `language` 参数支持自定义语言。
1. 定义角色类,继承 `Role` 基类,重写 `__init__` 初始化方法。`__init__` 方法必须包含`name``profile``goal``constraints` 参数。第一行代码使用`super().__init__(name, profile, goal, constraints)` 调用父类的构造函数,实现 `Role` 的初始化。使用 `self._init_actions([WriteDirectory(language=language)])` 添加初始的 `action``states`,这里先添加写目录的 `action`。同时,也可以自定义参数,这里加了 `language` 参数支持自定义语言。使用`self._set_react_mode(react_mode="by_order")``_init_actions``action` 执行顺序设置为顺序。

```python
class TutorialAssistant(Role):
Expand Down Expand Up @@ -46,43 +46,20 @@
self.main_title = ""
self.total_content = ""
self.language = language
self._set_react_mode(react_mode="by_order")
```

2. 重写 `_react` 方法。`_react` 方法循环执行 `think``action` 操作,当没有下一步 `action``todo` 时就结束循环。执行完所有的 `action` 后可以做最后的操作,这里是把拼接完的教程内容写成markdown文件
2. 重写 `react` 方法。使用 `await super().react()` 调用 `Role` 基类的 `react` 方法,根据 `__init__` 初始化方法设置的 `react_mode="by_order"` 按顺序执行 `states` 的每一个 `action` 。这里重写的目的是为了执行完所有的 `action` 后可以做最后的操作,即把拼接完的教程内容写成 `markdown` 文件

```python
async def _react(self) -> Message:
"""Execute the assistant's think and actions.
Returns:
A message containing the final result of the assistant's actions.
"""
while True:
await self._think()
if self._rc.todo is None:
break
msg = await self._act()
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
```

3. 重写 `_think `方法,`_think` 方法是思考下一步的执行。如果 `todo` 为空,即没有下一步操作要执行,则设置 `state``0` 。如果执行下一步后 `state` 会超出了初始化的 `states` 长度,说明目前已经是最后一步了,将 `todo` 设为空使得在 `_react` 方法会跳出循环,不再执行 `think``action`,否则 `state``1` 记录。

```python
async def _think(self) -> None:
"""Determine the next action to be taken by the role."""
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
```

4. 重写 `_act` 方法,`_act` 方法是执行 `action`。使用 `todo = self._rc.todo` 从上下文获取下一步要执行的 `action`,再执行 `action``run` 方法。这里是先通过 `WriteDirectory` 获取教程的目录结构,再分块目录,每块生成一个 `WriteContent``action`,再初始化新添加的 `action`。每个 action 执行完的结果生成消息 `Message(content=resp, role=self.profile)`,可以将其放入上下文内存 `self._rc.memory`,该角色不需要存入。
3. 重写 `_act` 方法,`_act` 方法是执行 `action`。使用 `todo = self._rc.todo` 从上下文获取下一步要执行的 `action`,再执行 `action``run` 方法。这里是先通过 `WriteDirectory` 获取教程的目录结构,再分块目录,每块生成一个 `WriteContent``action`,再初始化新添加的 `action`。这里再次调用 `await super().react()` 是为了从头执行新添加的所有 `WriteContent` `action`。每个 action 执行完的结果生成消息 `Message(content=resp, role=self.profile)`,可以将其放入上下文内存 `self._rc.memory`,该角色不需要存入。

```python
async def _act(self) -> Message:
Expand All @@ -97,7 +74,8 @@
self.topic = msg.content
resp = await todo.run(topic=self.topic)
logger.info(resp)
return await self._handle_directory(resp)
await self._handle_directory(resp)
return await super().react()
resp = await todo.run(topic=self.topic)
logger.info(resp)
if self.total_content != "":
Expand Down Expand Up @@ -126,12 +104,9 @@
for second_dir in first_dir[key]:
directory += f" - {second_dir}\n"
self._init_actions(actions)
self._rc.todo = None
return Message(content=directory)
```



## Action定义

1. 定义 `action`,每个 `action` 对应一个 `class` 对象,继承 `Action` 基类,重写 `__init__` 初始化方法。。`__init__` 方法包含 `name` 参数。第一行代码使用 `super().__init__(name, *args, **kwargs)` 调用父类的构造函数,实现 `action` 的初始化。这里使用 `args``kwargs` 将其他参数传递给父类的构造函数,比如 `context``llm`
Expand Down

0 comments on commit f1738c2

Please sign in to comment.