mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-15 11:02:36 +02:00
feat: merge teacher
This commit is contained in:
commit
01c487fb6a
32 changed files with 1578 additions and 38 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -163,4 +163,9 @@ workspace/*
|
|||
*.mmd
|
||||
tmp
|
||||
output.wav
|
||||
*.bak
|
||||
|
||||
# output folder
|
||||
output
|
||||
tmp.png
|
||||
|
||||
|
|
|
|||
40
config/pattern/template.yaml
Normal file
40
config/pattern/template.yaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Pattern Configuration Template
|
||||
# Created By: mashenquan, 2023-8-7
|
||||
# File Name: template.yaml
|
||||
# This template defines a set of structural standards for generating roles and action flows based on configurations.
|
||||
# For more about UML 2.0 activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
|
||||
|
||||
# project settings
|
||||
startup:
|
||||
requirement: "TeachingPlanRequirement" # Defines project initial requirement action
|
||||
role: "Teacher" # Defines project role
|
||||
investment: 3.0 # Defines the max project investment
|
||||
n_round: 1 # Defines the max project round count
|
||||
|
||||
# roles settings
|
||||
roles: # A project can involve multiple roles.
|
||||
- role_type: "fork" # `fork` type role corresponds to the functional positioning of the `fork` node in UML 2.0 activity diagrams.
|
||||
name: "Lily"
|
||||
profile: "{teaching_language} Teacher"
|
||||
goal: "writing a {language} teaching plan part by part"
|
||||
constraints: "writing in {language}"
|
||||
role: "You are a {teaching_language} Teacher, named Lily, your goal is ..."
|
||||
desc: ""
|
||||
output_filename: "teaching_plan_demo.md"
|
||||
requirement: ["TeachingPlanRequirement"]
|
||||
templates: # The template provides a convenient way to generate prompts. After each action selects its respective template, you only need to provide the corresponding variable values. Variable replacement is automatically handled by the framework.
|
||||
- "Do ..."
|
||||
- "Do ..."
|
||||
# role's action settings
|
||||
actions: # A role can have multiple actions.
|
||||
- name: ""
|
||||
topic: "Title"
|
||||
language: "Chinese"
|
||||
statements: # When replacing template variables, multiple statements will be joined into a single string using line breaks.
|
||||
- "Statement: Find and return ..."
|
||||
template_ix: 0
|
||||
rsp_begin_tag: "[..._BEGIN]" # When asking, request the LLM to include the tag in the response. It's optional.
|
||||
rsp_end_tag: "[..._END]" # When asking, request the LLM to include the tag in the response. It's optional.
|
||||
|
||||
|
||||
|
||||
126
config/pattern/write_teaching_plan.yaml
Normal file
126
config/pattern/write_teaching_plan.yaml
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# The `fork` role demo implements the flow of the code in `examples/write_teaching_plan.py`.
|
||||
|
||||
# project settings
|
||||
startup:
|
||||
requirement: "TeachingPlanRequirement" # Defines project initial requirement action
|
||||
role: "Teacher"
|
||||
investment: 3.0
|
||||
n_round: 1
|
||||
|
||||
# roles settings
|
||||
roles: # A project can involve multiple roles.
|
||||
- role_type: "fork" # `fork` type role corresponds to the functional positioning of the `fork` node in UML 2.0 activity diagrams.
|
||||
name: "Lily"
|
||||
profile: "{teaching_language} Teacher"
|
||||
goal: "writing a {language} teaching plan part by part"
|
||||
constraints: "writing in {language}"
|
||||
role: "You are a {teaching_language} Teacher, named Lily, your goal is writing a {teaching_language} teaching plan part by part, and the constraint is writing in {language}."
|
||||
desc: ""
|
||||
output_filename: "teaching_plan_demo"
|
||||
requirement: ["TeachingPlanRequirement"]
|
||||
templates: # The template provides a convenient way to generate prompts. After each action selects its respective template, you only need to provide the corresponding variable values. Variable replacement is automatically handled by the framework.
|
||||
- "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\n{statements}\nConstraint: Writing in {language}.\nAnswer options: Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END]"
|
||||
- "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: {role}\nStatement: Write the \"{topic}\" part of teaching plan, WITHOUT ANY content unrelated to \"{topic}\"!!\n{statements}\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in {language}.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END]"
|
||||
actions: # 一个role可以有多个action
|
||||
- name: ""
|
||||
topic: "Title"
|
||||
language: "Chinese"
|
||||
statements: # When replacing template variables, multiple statements will be joined into a single string using line breaks.
|
||||
- "Statement: Find and return the title of the lesson only with \"# \" string prefixed, without anything else."
|
||||
template_ix: 0
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Teaching Hours"
|
||||
language: "Chinese"
|
||||
statements: []
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]" # When asking, request the LLM to include the tag in the response. It's optional.
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]" # When asking, request the LLM to include the tag in the response. It's optional.
|
||||
- name: ""
|
||||
topic: "Teaching Objectives"
|
||||
language: "Chinese"
|
||||
statements: []
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Teaching Content"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: \"Teaching Content\" must include vocabulary, analysis, and examples of various grammar structures that appear in the textbook, as well as the listening materials and key points."
|
||||
- "Statement: \"Teaching Content\" must include more examples."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Teaching Methods and Strategies"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: \"Teaching Methods and Strategies\" must include teaching focus, difficulties, materials, procedures, in detail."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Learning Activities"
|
||||
language: "Chinese"
|
||||
statements: []
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Teaching Time Allocation"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: \"Teaching Time Allocation\" must include how much time is allocated to each part of the textbook content."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Assessment and Feedback"
|
||||
language: "Chinese"
|
||||
statements: []
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Teaching Summary and Improvement"
|
||||
language: "Chinese"
|
||||
statements: []
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Vocabulary Cloze"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create vocabulary cloze. The cloze should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers. The key-related vocabulary and phrases in the textbook content must all be included in the exercises."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Choice Questions"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create choice questions. 10 questions."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Grammar Questions"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create grammar questions. 10 questions."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
- name: ""
|
||||
topic: "Translation Questions"
|
||||
language: "Chinese"
|
||||
statements:
|
||||
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create translation questions. The translation should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers."
|
||||
template_ix: 1
|
||||
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
|
||||
rsp_end_tag: "[TEACHING_PLAN_END]"
|
||||
|
||||
|
||||
126
examples/fork_meta_role_write_teaching_plan.py
Normal file
126
examples/fork_meta_role_write_teaching_plan.py
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/7
|
||||
@Author : mashenquan
|
||||
@File : fork_meta_role.py
|
||||
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to possess the
|
||||
ability to construct flows freely by concatenating symbols. Simultaneously, I am also striving to make
|
||||
these symbols configurable and standardized, making the process of building flow structures more
|
||||
convenient. This is a fork meta-role demo that implements the functionality of
|
||||
`examples/write_teaching_plan.py`.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
import aiofiles
|
||||
import fire
|
||||
import yaml
|
||||
|
||||
from metagpt.actions.meta_action import MetaAction
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory
|
||||
from metagpt.roles.uml_meta_role_options import ProjectConfig
|
||||
from metagpt.software_company import SoftwareCompany
|
||||
|
||||
|
||||
async def startup(lesson_file: str, investment: float = 3.0, n_round: int = 1, *args, **kwargs):
|
||||
"""Run a startup. Be a teacher in education industry."""
|
||||
|
||||
demo_lesson = """
|
||||
UNIT 1 Making New Friends
|
||||
TOPIC 1 Welcome to China!
|
||||
Section A
|
||||
|
||||
1a Listen and number the following names.
|
||||
Jane Mari Kangkang Michael
|
||||
Look, listen and understand. Then practice the conversation.
|
||||
Work in groups. Introduce yourself using
|
||||
I ’m ... Then practice 1a
|
||||
with your own hometown or the following places.
|
||||
|
||||
1b Listen and number the following names
|
||||
Jane Michael Maria Kangkang
|
||||
1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.
|
||||
China the USA the UK Hong Kong Beijing
|
||||
|
||||
2a Look, listen and understand. Then practice the conversation
|
||||
Hello!
|
||||
Hello!
|
||||
Hello!
|
||||
Hello! Are you Maria?
|
||||
No, I’m not. I’m Jane.
|
||||
Oh, nice to meet you, Jane
|
||||
Nice to meet you, too.
|
||||
Hi, Maria!
|
||||
Hi, Kangkang!
|
||||
Welcome to China!
|
||||
Thanks.
|
||||
|
||||
2b Work in groups. Make up a conversation with your own name and the
|
||||
following structures.
|
||||
A: Hello! / Good morning! / Hi! I’m ... Are you ... ?
|
||||
B: ...
|
||||
|
||||
3a Listen, say and trace
|
||||
Aa Bb Cc Dd Ee Ff Gg
|
||||
|
||||
3b Listen and number the following letters. Then circle the letters with the same sound as Bb.
|
||||
Aa Bb Cc Dd Ee Ff Gg
|
||||
|
||||
3c Match the big letters with the small ones. Then write them on the lines.
|
||||
"""
|
||||
|
||||
lesson = ""
|
||||
if lesson_file and Path(lesson_file).exists():
|
||||
async with aiofiles.open(lesson_file, mode="r", encoding="utf-8") as reader:
|
||||
lesson = await reader.read()
|
||||
logger.info(f"Course content: {lesson}")
|
||||
if not lesson:
|
||||
logger.info("No course content provided, using the demo course.")
|
||||
lesson = demo_lesson
|
||||
|
||||
yaml_filename = kwargs["config"]
|
||||
kwargs["lesson"] = lesson
|
||||
|
||||
with open(yaml_filename, "r") as reader:
|
||||
configs = yaml.safe_load(reader)
|
||||
|
||||
startup_config = ProjectConfig(**configs)
|
||||
company = SoftwareCompany()
|
||||
roles = UMLMetaRoleFactory.create_roles(role_configs=startup_config.roles,
|
||||
options=company.options,
|
||||
cost_manager=company.cost_manager,
|
||||
**kwargs)
|
||||
company.hire(roles)
|
||||
company.invest(startup_config.startup.investment)
|
||||
company.start_project(lesson, role=startup_config.startup.role,
|
||||
cause_by=MetaAction.get_action_type(startup_config.startup.requirement))
|
||||
await company.run(n_round=startup_config.startup.n_round)
|
||||
|
||||
|
||||
def main(idea: str, investment: float = 3.0, n_round: int = 5, *args, **kwargs):
|
||||
"""
|
||||
We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
|
||||
:param idea: lesson filename.
|
||||
:param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
|
||||
:param n_round: Reserved.
|
||||
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
|
||||
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
|
||||
:return:
|
||||
"""
|
||||
asyncio.run(startup(idea, investment, n_round, *args, **kwargs))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""
|
||||
Formats:
|
||||
```
|
||||
python write_teaching_plan.py lesson_filename --teaching_language=<the language you are teaching> --language=<your native language>
|
||||
```
|
||||
If `lesson_filename` is not available, a demo lesson content will be used.
|
||||
"""
|
||||
fire.Fire(main)
|
||||
|
|
@ -4,9 +4,12 @@
|
|||
@Time : 2023/5/6 14:13
|
||||
@Author : alexanderwu
|
||||
@File : llm_hello_world.py
|
||||
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from metagpt.llm import LLM, Claude
|
||||
from metagpt.logs import logger
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@
|
|||
@Time : 2023/5/7 18:32
|
||||
@Author : alexanderwu
|
||||
@File : search_google.py
|
||||
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from metagpt.roles import Searcher
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@File : search_kb.py
|
||||
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from metagpt.const import DATA_PATH
|
||||
from metagpt.document_store import FaissStore
|
||||
from metagpt.logs import logger
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from metagpt.roles import Searcher
|
||||
from metagpt.tools import SearchEngineType
|
||||
|
||||
|
|
|
|||
107
examples/write_teaching_plan.py
Normal file
107
examples/write_teaching_plan.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023-07-27
|
||||
@Author : mashenquan
|
||||
@File : write_teaching_plan.py
|
||||
@Desc: Write teaching plan demo
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
import aiofiles
|
||||
import fire
|
||||
from metagpt.logs import logger
|
||||
from metagpt.actions.write_teaching_plan import TeachingPlanRequirement
|
||||
from metagpt.roles.teacher import Teacher
|
||||
from metagpt.software_company import SoftwareCompany
|
||||
|
||||
|
||||
async def startup(lesson_file: str, investment: float = 3.0, n_round: int = 1, *args, **kwargs):
|
||||
"""Run a startup. Be a teacher in education industry."""
|
||||
|
||||
demo_lesson = """
|
||||
UNIT 1 Making New Friends
|
||||
TOPIC 1 Welcome to China!
|
||||
Section A
|
||||
|
||||
1a Listen and number the following names.
|
||||
Jane Mari Kangkang Michael
|
||||
Look, listen and understand. Then practice the conversation.
|
||||
Work in groups. Introduce yourself using
|
||||
I ’m ... Then practice 1a
|
||||
with your own hometown or the following places.
|
||||
|
||||
1b Listen and number the following names
|
||||
Jane Michael Maria Kangkang
|
||||
1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.
|
||||
China the USA the UK Hong Kong Beijing
|
||||
|
||||
2a Look, listen and understand. Then practice the conversation
|
||||
Hello!
|
||||
Hello!
|
||||
Hello!
|
||||
Hello! Are you Maria?
|
||||
No, I’m not. I’m Jane.
|
||||
Oh, nice to meet you, Jane
|
||||
Nice to meet you, too.
|
||||
Hi, Maria!
|
||||
Hi, Kangkang!
|
||||
Welcome to China!
|
||||
Thanks.
|
||||
|
||||
2b Work in groups. Make up a conversation with your own name and the
|
||||
following structures.
|
||||
A: Hello! / Good morning! / Hi! I’m ... Are you ... ?
|
||||
B: ...
|
||||
|
||||
3a Listen, say and trace
|
||||
Aa Bb Cc Dd Ee Ff Gg
|
||||
|
||||
3b Listen and number the following letters. Then circle the letters with the same sound as Bb.
|
||||
Aa Bb Cc Dd Ee Ff Gg
|
||||
|
||||
3c Match the big letters with the small ones. Then write them on the lines.
|
||||
"""
|
||||
|
||||
lesson = ""
|
||||
if lesson_file and Path(lesson_file).exists():
|
||||
async with aiofiles.open(lesson_file, mode="r", encoding="utf-8") as reader:
|
||||
lesson = await reader.read()
|
||||
logger.info(f"Course content: {lesson}")
|
||||
if not lesson:
|
||||
logger.info("No course content provided, using the demo course.")
|
||||
lesson = demo_lesson
|
||||
|
||||
company = SoftwareCompany()
|
||||
company.hire([Teacher(options=company.options, cost_manager=company.cost_manager, *args, **kwargs)])
|
||||
company.invest(investment)
|
||||
company.start_project(lesson, role="Teacher", cause_by=TeachingPlanRequirement)
|
||||
await company.run(n_round=1)
|
||||
|
||||
|
||||
def main(idea: str, investment: float = 3.0, n_round: int = 5, *args, **kwargs):
|
||||
"""
|
||||
We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
|
||||
:param idea: lesson filename.
|
||||
:param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
|
||||
:param n_round: Reserved.
|
||||
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
|
||||
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
|
||||
:return:
|
||||
"""
|
||||
asyncio.run(startup(idea, investment, n_round, *args, **kwargs))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""
|
||||
Formats:
|
||||
```
|
||||
python write_teaching_plan.py lesson_filename --teaching_language=<the language you are teaching> --language=<your native language>
|
||||
```
|
||||
If `lesson_filename` is not available, a demo lesson content will be used.
|
||||
"""
|
||||
fire.Fire(main)
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
@Time : 2023/5/11 19:26
|
||||
@Author : alexanderwu
|
||||
@File : design_api.py
|
||||
@Modified By: mashenquan, 2023-8-9, align `run` parameters with the parent :class:`Action` class.
|
||||
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
|
||||
"""
|
||||
import shutil
|
||||
|
|
@ -136,7 +137,7 @@ class WriteDesign(Action):
|
|||
self._save_prd(docs_path, resources_path, context[-1].content)
|
||||
self._save_system_design(docs_path, resources_path, content)
|
||||
|
||||
async def run(self, context):
|
||||
async def run(self, context, **kwargs):
|
||||
prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE)
|
||||
# system_design = await self._aask(prompt)
|
||||
system_design = await self._aask_v1(prompt, "system_design", OUTPUT_MAPPING)
|
||||
|
|
|
|||
64
metagpt/actions/meta_action.py
Normal file
64
metagpt/actions/meta_action.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/7
|
||||
@Author : mashenquan
|
||||
@File : meta_action.py
|
||||
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
|
||||
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
|
||||
make these symbols configurable and standardized, making the process of building flows more convenient.
|
||||
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
|
||||
This file defines a meta action capable of generating arbitrary actions at runtime based on a
|
||||
configuration file.
|
||||
"""
|
||||
|
||||
from typing import Type
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles.uml_meta_role_options import MetaActionOptions
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class MetaAction(Action):
|
||||
def __init__(self, options, action_options: MetaActionOptions, llm=None, **kwargs):
|
||||
super(MetaAction, self).__init__(options=options,
|
||||
name=action_options.name,
|
||||
context=kwargs.get("context"),
|
||||
llm=llm)
|
||||
self.prompt = action_options.format_prompt(**kwargs)
|
||||
self.action_options = action_options
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
"""Return `topic` value when str()"""
|
||||
return self.action_options.topic
|
||||
|
||||
def __repr__(self):
|
||||
"""Show `topic` value when debug"""
|
||||
return self.action_options.topic
|
||||
|
||||
async def run(self, messages, *args, **kwargs):
|
||||
if len(messages) < 1 or not isinstance(messages[0], Message):
|
||||
raise ValueError("Invalid args, a tuple of List[Message] is expected")
|
||||
|
||||
logger.debug(self.prompt)
|
||||
rsp = await self._aask(prompt=self.prompt)
|
||||
logger.debug(rsp)
|
||||
self._set_result(rsp)
|
||||
return self.rsp
|
||||
|
||||
def _set_result(self, rsp):
|
||||
if self.action_options.rsp_begin_tag and self.action_options.rsp_begin_tag in rsp:
|
||||
ix = rsp.index(self.action_options.rsp_begin_tag)
|
||||
rsp = rsp[ix + len(self.action_options.rsp_begin_tag):]
|
||||
if self.action_options.rsp_end_tag and self.action_options.rsp_end_tag in rsp:
|
||||
ix = rsp.index(self.action_options.rsp_end_tag)
|
||||
rsp = rsp[0:ix]
|
||||
self.rsp = rsp.strip()
|
||||
|
||||
@staticmethod
|
||||
def get_action_type(topic: str):
|
||||
"""Create a runtime :class:`Action` subclass"""
|
||||
action_type: Type["Action"] = type(topic, (Action,), {"name": topic})
|
||||
return action_type
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
@Time : 2023/5/11 19:12
|
||||
@Author : alexanderwu
|
||||
@File : project_management.py
|
||||
@Modified By: mashenquan, 2023-8-9, align `run` parameters with the parent :class:`Action` class.
|
||||
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
|
||||
"""
|
||||
from typing import List, Tuple
|
||||
|
|
@ -116,7 +117,7 @@ class WriteTasks(Action):
|
|||
requirements_path = WORKSPACE_ROOT / ws_name / 'requirements.txt'
|
||||
requirements_path.write_text(rsp.instruct_content.dict().get("Required Python third-party packages").strip('"\n'))
|
||||
|
||||
async def run(self, context):
|
||||
async def run(self, context, **kwargs):
|
||||
prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE)
|
||||
rsp = await self._aask_v1(prompt, "task", OUTPUT_MAPPING)
|
||||
self._save(context, rsp)
|
||||
|
|
|
|||
159
metagpt/actions/write_teaching_plan.py
Normal file
159
metagpt/actions/write_teaching_plan.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/7/27
|
||||
@Author : mashenquan
|
||||
@File : write_teaching_plan.py
|
||||
"""
|
||||
from metagpt.logs import logger
|
||||
from metagpt.actions import Action
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class TeachingPlanRequirement(Action):
|
||||
"""Teaching Plan Requirement without any implementation details"""
|
||||
|
||||
async def run(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class WriteTeachingPlanPart(Action):
|
||||
"""Write Teaching Plan Part"""
|
||||
|
||||
def __init__(self, options, name: str = "", context=None, llm=None, topic: str = "", language: str = "Chinese"):
|
||||
"""
|
||||
|
||||
:param name: action name
|
||||
:param context: context
|
||||
:param llm: object of :class:`LLM`
|
||||
:param topic: topic part of teaching plan
|
||||
:param language: A human language, such as Chinese, English, French, etc.
|
||||
"""
|
||||
super().__init__(options, name, context, llm)
|
||||
self.topic = topic
|
||||
self.language = language
|
||||
self.rsp = None
|
||||
|
||||
async def run(self, messages, *args, **kwargs):
|
||||
if len(messages) < 1 or not isinstance(messages[0], Message):
|
||||
raise ValueError("Invalid args, a tuple of List[Message] is expected")
|
||||
|
||||
statement_patterns = self.TOPIC_STATEMENTS.get(self.topic, [])
|
||||
statements = []
|
||||
from metagpt.roles import Role
|
||||
for p in statement_patterns:
|
||||
s = Role.format_value(p, kwargs)
|
||||
statements.append(s)
|
||||
formatter = self.PROMPT_TITLE_TEMPLATE if self.topic == self.COURSE_TITLE else self.PROMPT_TEMPLATE
|
||||
prompt = formatter.format(formation=self.FORMATION,
|
||||
role=self.prefix,
|
||||
statements="\n".join(statements),
|
||||
lesson=messages[0].content,
|
||||
topic=self.topic,
|
||||
language=self.language)
|
||||
|
||||
logger.debug(prompt)
|
||||
rsp = await self._aask(prompt=prompt)
|
||||
logger.debug(rsp)
|
||||
self._set_result(rsp)
|
||||
return self.rsp
|
||||
|
||||
def _set_result(self, rsp):
|
||||
if self.DATA_BEGIN_TAG in rsp:
|
||||
ix = rsp.index(self.DATA_BEGIN_TAG)
|
||||
rsp = rsp[ix + len(self.DATA_BEGIN_TAG):]
|
||||
if self.DATA_END_TAG in rsp:
|
||||
ix = rsp.index(self.DATA_END_TAG)
|
||||
rsp = rsp[0:ix]
|
||||
self.rsp = rsp.strip()
|
||||
if self.topic != self.COURSE_TITLE:
|
||||
return
|
||||
if '#' not in self.rsp or self.rsp.index('#') != 0:
|
||||
self.rsp = "# " + self.rsp
|
||||
|
||||
def __str__(self):
|
||||
"""Return `topic` value when str()"""
|
||||
return self.topic
|
||||
|
||||
def __repr__(self):
|
||||
"""Show `topic` value when debug"""
|
||||
return self.topic
|
||||
|
||||
FORMATION = "\"Capacity and role\" defines the role you are currently playing;\n" \
|
||||
"\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n" \
|
||||
"\t\"Statement\" defines the work detail you need to complete at this stage;\n" \
|
||||
"\t\"Answer options\" defines the format requirements for your responses;\n" \
|
||||
"\t\"Constraint\" defines the conditions that your responses must comply with."
|
||||
|
||||
COURSE_TITLE = "Title"
|
||||
TOPICS = [
|
||||
COURSE_TITLE, "Teaching Hours", "Teaching Objectives", "Teaching Content",
|
||||
"Teaching Methods and Strategies", "Learning Activities",
|
||||
"Teaching Time Allocation", "Assessment and Feedback", "Teaching Summary and Improvement",
|
||||
"Vocabulary Cloze", "Choice Questions", "Grammar Questions", "Translation Questions"
|
||||
]
|
||||
|
||||
TOPIC_STATEMENTS = {
|
||||
COURSE_TITLE: ["Statement: Find and return the title of the lesson only in markdown first-level header format, "
|
||||
"without anything else."],
|
||||
"Teaching Content": [
|
||||
"Statement: \"Teaching Content\" must include vocabulary, analysis, and examples of various grammar "
|
||||
"structures that appear in the textbook, as well as the listening materials and key points.",
|
||||
"Statement: \"Teaching Content\" must include more examples."],
|
||||
"Teaching Time Allocation": [
|
||||
"Statement: \"Teaching Time Allocation\" must include how much time is allocated to each "
|
||||
"part of the textbook content."],
|
||||
"Teaching Methods and Strategies": [
|
||||
"Statement: \"Teaching Methods and Strategies\" must include teaching focus, difficulties, materials, "
|
||||
"procedures, in detail."
|
||||
],
|
||||
"Vocabulary Cloze": [
|
||||
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
|
||||
"create vocabulary cloze. The cloze should include 10 {language} questions with {teaching_language} "
|
||||
"answers, and it should also include 10 {teaching_language} questions with {language} answers. "
|
||||
"The key-related vocabulary and phrases in the textbook content must all be included in the exercises.",
|
||||
],
|
||||
"Grammar Questions": [
|
||||
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
|
||||
"create grammar questions. 10 questions."],
|
||||
"Choice Questions": [
|
||||
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
|
||||
"create choice questions. 10 questions."],
|
||||
"Translation Questions": [
|
||||
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
|
||||
"create translation questions. The translation should include 10 {language} questions with "
|
||||
"{teaching_language} answers, and it should also include 10 {teaching_language} questions with "
|
||||
"{language} answers."
|
||||
]
|
||||
}
|
||||
|
||||
# Teaching plan title
|
||||
PROMPT_TITLE_TEMPLATE = "Do not refer to the context of the previous conversation records, " \
|
||||
"start the conversation anew.\n\n" \
|
||||
"Formation: {formation}\n\n" \
|
||||
"{statements}\n" \
|
||||
"Constraint: Writing in {language}.\n" \
|
||||
"Answer options: Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" " \
|
||||
"and \"[TEACHING_PLAN_END]\" tags.\n" \
|
||||
"[LESSON_BEGIN]\n" \
|
||||
"{lesson}\n" \
|
||||
"[LESSON_END]"
|
||||
|
||||
# Teaching plan parts:
|
||||
PROMPT_TEMPLATE = "Do not refer to the context of the previous conversation records, " \
|
||||
"start the conversation anew.\n\n" \
|
||||
"Formation: {formation}\n\n" \
|
||||
"Capacity and role: {role}\n" \
|
||||
"Statement: Write the \"{topic}\" part of teaching plan, " \
|
||||
"WITHOUT ANY content unrelated to \"{topic}\"!!\n" \
|
||||
"{statements}\n" \
|
||||
"Answer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" " \
|
||||
"and \"[TEACHING_PLAN_END]\" tags.\n" \
|
||||
"Answer options: Using proper markdown format from second-level header format.\n" \
|
||||
"Constraint: Writing in {language}.\n" \
|
||||
"[LESSON_BEGIN]\n" \
|
||||
"{lesson}\n" \
|
||||
"[LESSON_END]"
|
||||
|
||||
DATA_BEGIN_TAG = "[TEACHING_PLAN_BEGIN]"
|
||||
DATA_END_TAG = "[TEACHING_PLAN_END]"
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
@Time : 2023/5/20 12:15
|
||||
@Author : alexanderwu
|
||||
@File : memory.py
|
||||
@Modified By: mashenquan, 2023-8-7. Modified get_by_actions() to support for dynamically generated Action classes
|
||||
at runtime.
|
||||
"""
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, Type
|
||||
|
|
@ -80,8 +82,12 @@ class Memory:
|
|||
def get_by_actions(self, actions: Iterable[Type[Action]]) -> list[Message]:
|
||||
"""Return all messages triggered by specified Actions"""
|
||||
rsp = []
|
||||
# Using the `type(obj).__name__` approach to support the runtime creation of requirement classes.
|
||||
# See `MetaAction.get_action_type()` for more.
|
||||
class_names = {type(k).__name__: k for k in self.index.keys()}
|
||||
for action in actions:
|
||||
if action not in self.index:
|
||||
if type(action).__name__ not in class_names:
|
||||
continue
|
||||
rsp += self.index[action]
|
||||
key = class_names[type(action).__name__]
|
||||
rsp += self.index[key]
|
||||
return rsp
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
"""
|
||||
import asyncio
|
||||
import time
|
||||
from typing import NamedTuple, Dict
|
||||
|
||||
from typing import NamedTuple
|
||||
import traceback
|
||||
import openai
|
||||
from openai.error import APIConnectionError
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -150,7 +151,15 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter):
|
|||
self.rpm = int(self._options.get("RPM", 10))
|
||||
|
||||
async def _achat_completion_stream(self, messages: list[dict]) -> str:
|
||||
response = await openai.ChatCompletion.acreate(**self._cons_kwargs(messages), stream=True)
|
||||
try:
|
||||
response = await openai.ChatCompletion.acreate(
|
||||
**self._cons_kwargs(messages),
|
||||
stream=True
|
||||
)
|
||||
except Exception as e:
|
||||
error_str = traceback.format_exc()
|
||||
logger.error(f"Exception:{e}, stack:{error_str}")
|
||||
raise e
|
||||
|
||||
# create variables to collect the stream of chunks
|
||||
collected_chunks = []
|
||||
|
|
|
|||
130
metagpt/roles/fork_meta_role.py
Normal file
130
metagpt/roles/fork_meta_role.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/7
|
||||
@Author : mashenquan
|
||||
@File : fork_meta_role.py
|
||||
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
|
||||
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
|
||||
make these symbols configurable and standardized, making the process of building flows more convenient.
|
||||
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
|
||||
This file defines a `fork` style meta role capable of generating arbitrary roles at runtime based on a
|
||||
configuration file.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import aiofiles
|
||||
|
||||
from metagpt.actions.meta_action import MetaAction
|
||||
from metagpt.const import WORKSPACE_ROOT
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.roles.uml_meta_role_options import MetaActionOptions, UMLMetaRoleOptions
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class ForkMetaRole(Role):
|
||||
"""A `fork` style meta role capable of generating arbitrary roles at runtime based on a configuration file"""
|
||||
def __init__(self, options, cost_manager, role_options, **kwargs):
|
||||
"""Initialize a `fork` style meta role
|
||||
|
||||
:param options: System configuration
|
||||
:param cost_manager: Cost manager
|
||||
:param role_options: pattern yaml file data
|
||||
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
|
||||
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
|
||||
"""
|
||||
opts = UMLMetaRoleOptions(**role_options)
|
||||
global_variables = {
|
||||
"name": Role.format_value(opts.name, kwargs),
|
||||
"profile": Role.format_value(opts.profile, kwargs),
|
||||
"goal": Role.format_value(opts.goal, kwargs),
|
||||
"constraints": Role.format_value(opts.constraints, kwargs),
|
||||
"desc": Role.format_value(opts.desc, kwargs),
|
||||
"role": Role.format_value(opts.role, kwargs)
|
||||
}
|
||||
for k, v in kwargs.items():
|
||||
if k not in global_variables:
|
||||
global_variables[k] = v
|
||||
|
||||
super(ForkMetaRole, self).__init__(
|
||||
options=options,
|
||||
cost_manager=cost_manager,
|
||||
name=global_variables["name"],
|
||||
profile=global_variables["profile"],
|
||||
goal=global_variables["goal"],
|
||||
constraints=global_variables["constraints"],
|
||||
desc=global_variables["desc"],
|
||||
**kwargs
|
||||
)
|
||||
actions = []
|
||||
for m in opts.actions:
|
||||
for k, v in m.items():
|
||||
v = Role.format_value(v, kwargs)
|
||||
m[k] = v
|
||||
for k, v in global_variables.items():
|
||||
if k not in m:
|
||||
m[k] = v
|
||||
|
||||
o = MetaActionOptions(**m)
|
||||
o.set_default_template(opts.templates[o.template_ix])
|
||||
|
||||
act = MetaAction(options=options, action_options=o, llm=self._llm, **m)
|
||||
actions.append(act)
|
||||
self._init_actions(actions)
|
||||
requirement_types = set()
|
||||
for v in opts.requirement:
|
||||
requirement_types.add(MetaAction.get_action_type(v))
|
||||
self._watch(requirement_types)
|
||||
|
||||
async def _think(self) -> None:
|
||||
"""Everything will be done part by part."""
|
||||
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
|
||||
|
||||
async def _react(self) -> Message:
|
||||
ret = Message(content="")
|
||||
while True:
|
||||
await self._think()
|
||||
if self._rc.todo is None:
|
||||
break
|
||||
logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}")
|
||||
msg = await self._act()
|
||||
if ret.content != '':
|
||||
ret.content += "\n\n\n"
|
||||
ret.content += msg.content
|
||||
logger.info(ret.content)
|
||||
await self.save(ret.content)
|
||||
return ret
|
||||
|
||||
async def save(self, content):
|
||||
"""Save teaching plan"""
|
||||
output_filename = self.options.get("output_filename")
|
||||
if not output_filename:
|
||||
return
|
||||
filename = ForkMetaRole.new_file_name(output_filename)
|
||||
pathname = WORKSPACE_ROOT / "teaching_plan"
|
||||
pathname.mkdir(exist_ok=True)
|
||||
pathname = pathname / filename
|
||||
try:
|
||||
async with aiofiles.open(str(pathname), mode='w', encoding='utf-8') as writer:
|
||||
await writer.write(content)
|
||||
except Exception as e:
|
||||
logger.error(f'Save failed:{e}')
|
||||
logger.info(f"Save to:{pathname}")
|
||||
|
||||
@staticmethod
|
||||
def new_file_name(lesson_title, ext=".md"):
|
||||
"""Create a related file name based on `lesson_title` and `ext`."""
|
||||
# Define the special characters that need to be replaced.
|
||||
illegal_chars = r'[#@$%!*&\\/:*?"<>|\n\t \']'
|
||||
# Replace the special characters with underscores.
|
||||
filename = re.sub(illegal_chars, '_', lesson_title) + ext
|
||||
return re.sub(r'_+', '_', filename)
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
@Time : 2023/5/11 14:42
|
||||
@Author : alexanderwu
|
||||
@File : role.py
|
||||
@Modified By: mashenquan, 2023-8-7, :class:`Role` + properties.
|
||||
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation;
|
||||
Change cost control from global to company level.
|
||||
"""
|
||||
|
|
@ -47,7 +48,7 @@ ROLE_TEMPLATE = """Your response should be based on the previous conversation hi
|
|||
|
||||
|
||||
class RoleSetting(BaseModel):
|
||||
"""角色设定"""
|
||||
"""Role properties"""
|
||||
name: str
|
||||
profile: str
|
||||
goal: str
|
||||
|
|
@ -62,7 +63,7 @@ class RoleSetting(BaseModel):
|
|||
|
||||
|
||||
class RoleContext(BaseModel):
|
||||
"""角色运行时上下文"""
|
||||
"""Runtime role context"""
|
||||
env: 'Environment' = Field(default=None)
|
||||
memory: Memory = Field(default_factory=Memory)
|
||||
long_term_memory: LongTermMemory = Field(default_factory=LongTermMemory)
|
||||
|
|
@ -82,7 +83,7 @@ class RoleContext(BaseModel):
|
|||
|
||||
@property
|
||||
def important_memory(self) -> list[Message]:
|
||||
"""获得关注动作对应的信息"""
|
||||
"""Retrieve information corresponding to the attention action."""
|
||||
return self.memory.get_by_actions(self.watch)
|
||||
|
||||
@property
|
||||
|
|
@ -91,17 +92,25 @@ class RoleContext(BaseModel):
|
|||
|
||||
|
||||
class Role:
|
||||
"""角色/代理"""
|
||||
"""Role/Proxy"""
|
||||
|
||||
def __init__(self, options, cost_manager, name="", profile="", goal="", constraints="", desc="", *args, **kwargs):
|
||||
self._options = Role.supply_options(options=kwargs, default_options=options)
|
||||
|
||||
name = Role.format_value(name, self._options)
|
||||
profile = Role.format_value(profile, self._options)
|
||||
goal = Role.format_value(goal, self._options)
|
||||
constraints = Role.format_value(constraints, self._options)
|
||||
desc = Role.format_value(desc, self._options)
|
||||
|
||||
def __init__(self, options, cost_manager, name="", profile="", goal="", constraints="", desc=""):
|
||||
self._options = options if options else {}
|
||||
self._cost_manager = cost_manager
|
||||
self._llm = LLM(options=self._options, cost_manager=cost_manager)
|
||||
|
||||
self._setting = RoleSetting(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc)
|
||||
self._states = []
|
||||
self._actions = []
|
||||
self._role_id = str(self._setting)
|
||||
self._rc = RoleContext(options=options)
|
||||
self._rc = RoleContext(options=self._options)
|
||||
|
||||
def _reset(self):
|
||||
self._states = []
|
||||
|
|
@ -139,6 +148,31 @@ class Role:
|
|||
"""获取角色描述(职位)"""
|
||||
return self._setting.profile
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return role `name`, read only"""
|
||||
return self._setting.name
|
||||
|
||||
@property
|
||||
def desc(self):
|
||||
"""Return role `desc`, read only"""
|
||||
return self._setting.desc
|
||||
|
||||
@property
|
||||
def goal(self):
|
||||
"""Return role `goal`, read only"""
|
||||
return self._setting.goal
|
||||
|
||||
@property
|
||||
def constraints(self):
|
||||
"""Return role `constraints`, read only"""
|
||||
return self._setting.constraints
|
||||
|
||||
@property
|
||||
def action_count(self):
|
||||
"""Return number of action"""
|
||||
return len(self._actions)
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
return self._options
|
||||
|
|
@ -175,7 +209,8 @@ class Role:
|
|||
# history=self.history)
|
||||
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
response = await self._rc.todo.run(self._rc.important_memory)
|
||||
requirement = self._rc.important_memory
|
||||
response = await self._rc.todo.run(requirement, **self._options)
|
||||
# logger.info(response)
|
||||
if isinstance(response, ActionOutput):
|
||||
msg = Message(content=response.content, instruct_content=response.instruct_content,
|
||||
|
|
@ -192,9 +227,9 @@ class Role:
|
|||
if not self._rc.env:
|
||||
return 0
|
||||
env_msgs = self._rc.env.memory.get()
|
||||
|
||||
|
||||
observed = self._rc.env.memory.get_by_actions(self._rc.watch)
|
||||
|
||||
|
||||
self._rc.news = self._rc.memory.remember(observed) # remember recent exact or similar memories
|
||||
|
||||
for i in env_msgs:
|
||||
|
|
@ -251,3 +286,30 @@ class Role:
|
|||
# 将回复发布到环境,等待下一个订阅者处理
|
||||
self._publish_message(rsp)
|
||||
return rsp
|
||||
|
||||
@staticmethod
|
||||
def supply_options(options, default_options=None):
|
||||
"""Supply missing options"""
|
||||
ret = default_options.copy() if default_options else {}
|
||||
if not options:
|
||||
return ret
|
||||
ret.update(options)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def format_value(value, opts, default_opts=None):
|
||||
"""Fill parameters inside `value` with `options`."""
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
if "{" not in value:
|
||||
return value
|
||||
|
||||
merged_opts = Role.supply_options(opts, default_opts)
|
||||
try:
|
||||
return value.format(**merged_opts)
|
||||
except KeyError as e:
|
||||
logger.warning(f"Parameter is missing:{e}")
|
||||
|
||||
for k, v in merged_opts.items():
|
||||
value = value.replace("{" + f"{k}" + "}", str(v))
|
||||
return value
|
||||
|
|
|
|||
97
metagpt/roles/teacher.py
Normal file
97
metagpt/roles/teacher.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/7/27
|
||||
@Author : mashenquan
|
||||
@File : teacher.py
|
||||
"""
|
||||
|
||||
|
||||
import aiofiles
|
||||
|
||||
from metagpt.actions.write_teaching_plan import WriteTeachingPlanPart, TeachingPlanRequirement
|
||||
from metagpt.const import WORKSPACE_ROOT
|
||||
from metagpt.roles import Role
|
||||
from metagpt.schema import Message
|
||||
from metagpt.logs import logger
|
||||
import re
|
||||
|
||||
|
||||
class Teacher(Role):
|
||||
"""Support configurable teacher roles,
|
||||
with native and teaching languages being replaceable through configurations."""
|
||||
def __init__(self, options, name='Lily', profile='{teaching_language} Teacher',
|
||||
goal='writing a {language} teaching plan part by part',
|
||||
constraints='writing in {language}', desc="", *args, **kwargs):
|
||||
super().__init__(options=options, name=name, profile=profile, goal=goal, constraints=constraints, desc=desc, *args, **kwargs)
|
||||
actions = []
|
||||
for topic in WriteTeachingPlanPart.TOPICS:
|
||||
act = WriteTeachingPlanPart(options=options, topic=topic, llm=self._llm)
|
||||
actions.append(act)
|
||||
self._init_actions(actions)
|
||||
self._watch({TeachingPlanRequirement})
|
||||
|
||||
async def _think(self) -> None:
|
||||
"""Everything will be done part by part."""
|
||||
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
|
||||
|
||||
async def _react(self) -> Message:
|
||||
ret = Message(content="")
|
||||
while True:
|
||||
await self._think()
|
||||
if self._rc.todo is None:
|
||||
break
|
||||
logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}")
|
||||
msg = await self._act()
|
||||
if ret.content != '':
|
||||
ret.content += "\n\n\n"
|
||||
ret.content += msg.content
|
||||
logger.info(ret.content)
|
||||
await self.save(ret.content)
|
||||
return ret
|
||||
|
||||
async def save(self, content):
|
||||
"""Save teaching plan"""
|
||||
filename = Teacher.new_file_name(self.course_title)
|
||||
pathname = WORKSPACE_ROOT / "teaching_plan"
|
||||
pathname.mkdir(exist_ok=True)
|
||||
pathname = pathname / filename
|
||||
try:
|
||||
async with aiofiles.open(str(pathname), mode='w', encoding='utf-8') as writer:
|
||||
await writer.write(content)
|
||||
except Exception as e:
|
||||
logger.error(f'Save failed:{e}')
|
||||
logger.info(f"Save to:{pathname}")
|
||||
|
||||
@staticmethod
|
||||
def new_file_name(lesson_title, ext=".md"):
|
||||
"""Create a related file name based on `lesson_title` and `ext`."""
|
||||
# Define the special characters that need to be replaced.
|
||||
illegal_chars = r'[#@$%!*&\\/:*?"<>|\n\t \']'
|
||||
# Replace the special characters with underscores.
|
||||
filename = re.sub(illegal_chars, '_', lesson_title) + ext
|
||||
return re.sub(r'_+', '_', filename)
|
||||
|
||||
@property
|
||||
def course_title(self):
|
||||
"""Return course title of teaching plan"""
|
||||
default_title = "teaching_plan"
|
||||
for act in self._actions:
|
||||
if act.topic != WriteTeachingPlanPart.COURSE_TITLE:
|
||||
continue
|
||||
if act.rsp is None:
|
||||
return default_title
|
||||
title = act.rsp.lstrip("# \n")
|
||||
if '\n' in title:
|
||||
ix = title.index('\n')
|
||||
title = title[0: ix]
|
||||
return title
|
||||
|
||||
return default_title
|
||||
43
metagpt/roles/uml_meta_role_factory.py
Normal file
43
metagpt/roles/uml_meta_role_factory.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/7
|
||||
@Author : mashenquan
|
||||
@File : uml_meta_role_factory.py
|
||||
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
|
||||
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
|
||||
make these symbols configurable and standardized, making the process of building flows more convenient.
|
||||
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
|
||||
"""
|
||||
|
||||
from metagpt.roles.fork_meta_role import ForkMetaRole
|
||||
from metagpt.roles.uml_meta_role_options import UMLMetaRoleOptions
|
||||
|
||||
|
||||
class UMLMetaRoleFactory:
|
||||
"""Factory of UML activity role classes"""
|
||||
|
||||
@classmethod
|
||||
def create_roles(cls, role_configs, **kwargs):
|
||||
"""Generate the flow of the project based on the configuration in the format of config/pattern/template.yaml.
|
||||
|
||||
:param role_configs: `roles` field of template.yaml
|
||||
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
|
||||
|
||||
"""
|
||||
roles = []
|
||||
for m in role_configs:
|
||||
opt = UMLMetaRoleOptions(**m)
|
||||
constructor = cls.CONSTRUCTORS.get(opt.role_type)
|
||||
if constructor is None:
|
||||
raise NotImplementedError(
|
||||
f"{opt.role_type} is not implemented"
|
||||
)
|
||||
r = constructor(role_options=m, **kwargs)
|
||||
roles.append(r)
|
||||
return roles
|
||||
|
||||
CONSTRUCTORS = {
|
||||
"fork": ForkMetaRole,
|
||||
# TODO: add more activity node constructor here..
|
||||
}
|
||||
69
metagpt/roles/uml_meta_role_options.py
Normal file
69
metagpt/roles/uml_meta_role_options.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/7
|
||||
@Author : mashenquan
|
||||
@File : uml_meta_role_options.py
|
||||
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
|
||||
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
|
||||
make these symbols configurable and standardized, making the process of building flows more convenient.
|
||||
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
|
||||
"""
|
||||
|
||||
from typing import List, Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# `startup` field of config/pattern/template.yaml
|
||||
class StartupConfig(BaseModel):
|
||||
requirement: str
|
||||
role: str
|
||||
investment: float = 3.0
|
||||
n_round: int = 3
|
||||
|
||||
|
||||
# config/pattern/template.yaml
|
||||
class ProjectConfig(BaseModel):
|
||||
startup: StartupConfig
|
||||
roles: List[Dict]
|
||||
|
||||
|
||||
# element of `actions` field of config/pattern/template.yaml
|
||||
class MetaActionOptions(BaseModel):
|
||||
topic: str
|
||||
name: str = ""
|
||||
language: str = "Chinese"
|
||||
template_ix: int = 0
|
||||
statements: List[str] = []
|
||||
template: str = ""
|
||||
rsp_begin_tag: str = ""
|
||||
rsp_end_tag: str = ""
|
||||
|
||||
def set_default_template(self, v):
|
||||
if not self.template:
|
||||
self.template = v
|
||||
|
||||
def format_prompt(self, **kwargs):
|
||||
statements = "\n".join(self.statements)
|
||||
opts = kwargs.copy()
|
||||
opts["statements"] = statements
|
||||
|
||||
from metagpt.roles import Role
|
||||
prompt = Role.format_value(self.template, opts)
|
||||
return prompt
|
||||
|
||||
|
||||
# element of `roles` field of config/pattern/template.yaml
|
||||
class UMLMetaRoleOptions(BaseModel):
|
||||
role_type: str
|
||||
name: str = ""
|
||||
profile: str = ""
|
||||
goal: str = ""
|
||||
role: str = ""
|
||||
constraints: str = ""
|
||||
desc: str = ""
|
||||
templates: List[str] = []
|
||||
output_filename: str = ""
|
||||
actions: List
|
||||
requirement: List
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
@Time : 2023/5/12 00:30
|
||||
@Author : alexanderwu
|
||||
@File : software_company.py
|
||||
@Modified By: mashenquan, 2023-07-27, Add `role` & `cause_by` parameters to `start_project()`.
|
||||
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation;
|
||||
Change cost control from global to company level.
|
||||
"""
|
||||
|
|
@ -49,10 +50,10 @@ class SoftwareCompany(BaseModel):
|
|||
if self.total_cost > self.max_budget:
|
||||
raise NoMoneyException(self.total_cost, f'Insufficient funds: {self.max_budget}')
|
||||
|
||||
def start_project(self, idea):
|
||||
def start_project(self, idea, role="BOSS", cause_by=BossRequirement):
|
||||
"""Start a project from publishing boss requirement."""
|
||||
self.idea = idea
|
||||
self.environment.publish_message(Message(role="BOSS", content=idea, cause_by=BossRequirement))
|
||||
self.environment.publish_message(Message(role=role, content=idea, cause_by=cause_by))
|
||||
|
||||
def _save(self):
|
||||
logger.info(self.json())
|
||||
|
|
|
|||
41
requirements-test.txt
Normal file
41
requirements-test.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
aiohttp==3.8.4
|
||||
azure-cognitiveservices-speech==1.30.0
|
||||
channels==4.0.0
|
||||
chromadb==0.3.22
|
||||
# Django==4.1.5
|
||||
# docx==0.2.4
|
||||
duckduckgo_search==2.9.4
|
||||
#faiss==1.5.3
|
||||
faiss_cpu==1.7.4
|
||||
fire==0.4.0
|
||||
# godot==0.1.1
|
||||
# google_api_python_client==2.93.0
|
||||
langchain==0.0.231
|
||||
loguru==0.6.0
|
||||
meilisearch==0.21.0
|
||||
numpy==1.24.3
|
||||
openai==0.27.8
|
||||
openpyxl
|
||||
pandas==1.4.1
|
||||
pydantic==1.10.7
|
||||
#pygame==2.1.3
|
||||
pymilvus==2.2.8
|
||||
pytest==7.2.2
|
||||
python_docx==0.8.11
|
||||
PyYAML==6.0
|
||||
# sentence_transformers==2.2.2
|
||||
setuptools==65.6.3
|
||||
tenacity==8.2.2
|
||||
tiktoken==0.3.3
|
||||
tqdm==4.64.0
|
||||
#unstructured[local-inference]
|
||||
playwright
|
||||
selenium>4
|
||||
webdriver_manager<3.9
|
||||
anthropic==0.3.6
|
||||
typing-inspect==0.8.0
|
||||
typing_extensions==4.5.0
|
||||
bs4
|
||||
aiofiles
|
||||
pytest
|
||||
pytest-asyncio
|
||||
|
|
@ -35,7 +35,9 @@ tqdm==4.64.0
|
|||
anthropic==0.3.6
|
||||
typing-inspect==0.8.0
|
||||
typing_extensions==4.5.0
|
||||
aiofiles
|
||||
libcst==1.0.1
|
||||
qdrant-client==1.4.0
|
||||
connexion[swagger-ui]
|
||||
aiohttp_jinja2
|
||||
aiohttp_jinja2
|
||||
|
||||
|
|
|
|||
51
tests/metagpt/actions/test_meta_action.py
Normal file
51
tests/metagpt/actions/test_meta_action.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/8
|
||||
@Author : mashenquan
|
||||
@File : test_meta_action.py
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.actions.meta_action import MetaAction
|
||||
from metagpt.roles.uml_meta_role_options import MetaActionOptions
|
||||
|
||||
|
||||
def test_meta_action_create():
|
||||
class Inputs(BaseModel):
|
||||
options: Dict
|
||||
kwargs: Dict
|
||||
expect_class_name: str
|
||||
expect_prompt: str
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"options": {
|
||||
"topic": "TOPIC_A",
|
||||
"name": "A",
|
||||
"language": "XX",
|
||||
"template_ix": 0,
|
||||
"statements": ["Statement A", "Statement B"],
|
||||
"template": "{statements}",
|
||||
"rsp_begin_tag": "",
|
||||
"rsp_end_tag": ""
|
||||
},
|
||||
"kwargs": {},
|
||||
"expect_class_name": "TOPIC_A",
|
||||
"expect_prompt": "\n".join(["Statement A", "Statement B"]),
|
||||
}
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
opt = MetaActionOptions(**seed.options)
|
||||
act = MetaAction(opt, **seed.kwargs)
|
||||
assert seed.expect_prompt == act.prompt
|
||||
t = MetaAction.get_action_type(seed.expect_class_name)
|
||||
assert t.__name__ == seed.expect_class_name
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_meta_action_create()
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
from tests.metagpt.roles.ui_role import UIDesign
|
||||
|
||||
llm_resp= '''
|
||||
llm_resp = '''
|
||||
# UI Design Description
|
||||
```The user interface for the snake game will be designed in a way that is simple, clean, and intuitive. The main elements of the game such as the game grid, snake, food, score, and game over message will be clearly defined and easy to understand. The game grid will be centered on the screen with the score displayed at the top. The game controls will be intuitive and easy to use. The design will be modern and minimalist with a pleasing color scheme.```
|
||||
|
||||
|
|
@ -100,6 +100,7 @@ body {
|
|||
font-size: 3em;
|
||||
'''
|
||||
|
||||
|
||||
def test_ui_design_parse_css():
|
||||
ui_design_work = UIDesign(name="UI design action")
|
||||
|
||||
|
|
@ -161,7 +162,7 @@ def test_ui_design_parse_css():
|
|||
transform: translate(-50%, -50%);
|
||||
font-size: 3em;
|
||||
'''
|
||||
assert ui_design_work.parse_css_code(context=llm_resp)==css
|
||||
assert ui_design_work.parse_css_code(context=llm_resp) == css
|
||||
|
||||
|
||||
def test_ui_design_parse_html():
|
||||
|
|
@ -185,7 +186,4 @@ def test_ui_design_parse_html():
|
|||
</body>
|
||||
</html>
|
||||
'''
|
||||
assert ui_design_work.parse_css_code(context=llm_resp)==html
|
||||
|
||||
|
||||
|
||||
assert ui_design_work.parse_css_code(context=llm_resp) == html
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
@Time : 2023/5/11 17:45
|
||||
@Author : alexanderwu
|
||||
@File : test_write_code.py
|
||||
@Modified By: mashenquan, 2023-8-1, fix-bug: `filename` of `write_code.run()` is missing.
|
||||
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
|
||||
"""
|
||||
import pytest
|
||||
|
|
@ -22,8 +23,7 @@ async def test_write_code():
|
|||
cost_manager = CostManager(**conf.runtime_options)
|
||||
llm = LLM(options=conf.runtime_options, cost_manager=cost_manager)
|
||||
write_code = WriteCode(options=conf.runtime_options, name="write_code", llm=llm)
|
||||
|
||||
code = await write_code.run(api_design, "filename")
|
||||
code = await write_code.run(context=api_design, filename="test")
|
||||
logger.info(code)
|
||||
|
||||
# 我们不能精确地预测生成的代码,但我们可以检查某些关键字
|
||||
|
|
|
|||
67
tests/metagpt/actions/test_write_teaching_plan.py
Normal file
67
tests/metagpt/actions/test_write_teaching_plan.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/7/28 17:25
|
||||
@Author : mashenquan
|
||||
@File : test_write_teaching_plan.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from langchain.llms.base import LLM
|
||||
|
||||
from metagpt.actions.write_teaching_plan import WriteTeachingPlanPart
|
||||
from metagpt.config import Config
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class MockWriteTeachingPlanPart(WriteTeachingPlanPart):
|
||||
def __init__(self, options, name: str = '', context=None, llm: LLM = None, topic="", language="Chinese"):
|
||||
super().__init__(options, name, context, llm, topic, language)
|
||||
|
||||
async def _aask(self, prompt: str, system_msgs: Optional[list[str]] = None) -> str:
|
||||
return f"{WriteTeachingPlanPart.DATA_BEGIN_TAG}\nprompt\n{WriteTeachingPlanPart.DATA_END_TAG}"
|
||||
|
||||
|
||||
async def mock_write_teaching_plan_part():
|
||||
class Inputs(BaseModel):
|
||||
input: str
|
||||
name: str
|
||||
topic: str
|
||||
language: str
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"input": "AABBCC",
|
||||
"name": "A",
|
||||
"topic": WriteTeachingPlanPart.COURSE_TITLE,
|
||||
"language": "C"
|
||||
},
|
||||
{
|
||||
"input": "DDEEFFF",
|
||||
"name": "A1",
|
||||
"topic": "B1",
|
||||
"language": "C1"
|
||||
}
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
options = Config().runtime_options
|
||||
act = MockWriteTeachingPlanPart(options=options, name=seed.name, topic=seed.topic, language=seed.language)
|
||||
await act.run([Message(content="")])
|
||||
assert act.topic == seed.topic
|
||||
assert str(act) == seed.topic
|
||||
assert act.name == seed.name
|
||||
assert act.rsp == "# prompt" if seed.topic == WriteTeachingPlanPart.COURSE_TITLE else "prompt"
|
||||
|
||||
|
||||
def test_suite():
|
||||
loop = asyncio.get_event_loop()
|
||||
task = loop.create_task(mock_write_teaching_plan_part())
|
||||
loop.run_until_complete(task)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_suite()
|
||||
94
tests/metagpt/roles/test_fork_meta_role.py
Normal file
94
tests/metagpt/roles/test_fork_meta_role.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/8
|
||||
@Author : mashenquan
|
||||
@File : test_fork_meta_role.py
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.config import Config
|
||||
from metagpt.provider.openai_api import CostManager
|
||||
from metagpt.roles.fork_meta_role import ForkMetaRole
|
||||
|
||||
|
||||
def test_creat_role():
|
||||
class Inputs(BaseModel):
|
||||
role: Dict
|
||||
action_count: int
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"role": {
|
||||
"role_type": "fork",
|
||||
"name": "Lily",
|
||||
"profile": "{teaching_language} Teacher",
|
||||
"goal": "writing a {language} teaching plan part by part",
|
||||
"constraints": "writing in {language}",
|
||||
"role": "You are a {teaching_language} Teacher, named Lily, your goal is writing a {"
|
||||
"teaching_language} teaching plan part by part, and the constraint is writing in {language}.",
|
||||
"desc": "",
|
||||
"output_filename": "teaching_plan_demo.md",
|
||||
"requirement": ["TeachingPlanRequirement"],
|
||||
"templates": [
|
||||
"Do not refer to the context of the previous conversation records, start the conversation "
|
||||
"anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"["
|
||||
"LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" "
|
||||
"defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the "
|
||||
"format requirements for your responses;\n\t\"Constraint\" defines the conditions that your "
|
||||
"responses must comply with.\n\n{statements}\nConstraint: Writing in {language}.\nAnswer options: "
|
||||
"Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\n["
|
||||
"LESSON_BEGIN]\n{lesson}\n[LESSON_END]",
|
||||
"Do not refer to the context of the previous conversation records, start the conversation "
|
||||
"anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"["
|
||||
"LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" "
|
||||
"defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the "
|
||||
"format requirements for your responses;\n\t\"Constraint\" defines the conditions that your "
|
||||
"responses must comply with.\n\nCapacity and role: {role}\nStatement: Write the \"{topic}\" part "
|
||||
"of teaching plan, WITHOUT ANY content unrelated to \"{topic}\"!!\n{statements}\nAnswer options: "
|
||||
"Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" "
|
||||
"tags.\nAnswer options: Using proper markdown format from second-level header "
|
||||
"format.\nConstraint: Writing in {language}.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END] "
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"name": "",
|
||||
"topic": "Title",
|
||||
"language": "Chinese",
|
||||
"statements": [
|
||||
"Statement: Find and return the title of the lesson only with \"# \" prefixed, without "
|
||||
"anything else."],
|
||||
"template_ix": 0},
|
||||
{
|
||||
"name": "",
|
||||
"topic": "Teaching Hours",
|
||||
"language": "Chinese",
|
||||
"statements": [],
|
||||
"template_ix": 1,
|
||||
"rsp_begin_tag": "[TEACHING_PLAN_BEGIN]",
|
||||
"rsp_end_tag": "[TEACHING_PLAN_END]"}
|
||||
]
|
||||
},
|
||||
"action_count": 2
|
||||
}
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
kwargs = {
|
||||
"teaching_language": "AA",
|
||||
"language": "BB"
|
||||
}
|
||||
runtime_options = Config().runtime_options
|
||||
cost_manager = CostManager(options=runtime_options)
|
||||
role = ForkMetaRole(runtime_options=runtime_options, cost_manager=cost_manager, role_options=seed.role, **kwargs)
|
||||
assert role.action_count == 2
|
||||
assert "{" not in role.profile
|
||||
assert "{" not in role.goal
|
||||
assert "{" not in role.constraints
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_creat_role()
|
||||
101
tests/metagpt/roles/test_teacher.py
Normal file
101
tests/metagpt/roles/test_teacher.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/7/27 13:25
|
||||
@Author : mashenquan
|
||||
@File : test_teacher.py
|
||||
"""
|
||||
|
||||
from typing import Dict, Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.config import Config
|
||||
from metagpt.provider.openai_api import CostManager
|
||||
from metagpt.roles.teacher import Teacher
|
||||
|
||||
|
||||
def test_init():
|
||||
class Inputs(BaseModel):
|
||||
name: str
|
||||
profile: str
|
||||
goal: str
|
||||
constraints: str
|
||||
desc: str
|
||||
kwargs: Optional[Dict] = None
|
||||
expect_name: str
|
||||
expect_profile: str
|
||||
expect_goal: str
|
||||
expect_constraints: str
|
||||
expect_desc: str
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"name": "Lily{language}",
|
||||
"expect_name": "LilyCN",
|
||||
"profile": "X {teaching_language}",
|
||||
"expect_profile": "X EN",
|
||||
"goal": "Do {something_big}, {language}",
|
||||
"expect_goal": "Do sleep, CN",
|
||||
"constraints": "Do in {key1}, {language}",
|
||||
"expect_constraints": "Do in HaHa, CN",
|
||||
"kwargs": {"language": "CN", "key1": "HaHa", "something_big": "sleep", "teaching_language": "EN"},
|
||||
"desc": "aaa{language}",
|
||||
"expect_desc": "aaaCN"
|
||||
},
|
||||
{
|
||||
"name": "Lily{language}",
|
||||
"expect_name": "Lily{language}",
|
||||
"profile": "X {teaching_language}",
|
||||
"expect_profile": "X {teaching_language}",
|
||||
"goal": "Do {something_big}, {language}",
|
||||
"expect_goal": "Do {something_big}, {language}",
|
||||
"constraints": "Do in {key1}, {language}",
|
||||
"expect_constraints": "Do in {key1}, {language}",
|
||||
"kwargs": {},
|
||||
"desc": "aaa{language}",
|
||||
"expect_desc": "aaa{language}"
|
||||
},
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
options = Config().runtime_options
|
||||
cost_manager = CostManager(options=options)
|
||||
teacher = Teacher(options=options, cost_manager=cost_manager, name=seed.name, profile=seed.profile,
|
||||
goal=seed.goal, constraints=seed.constraints,
|
||||
desc=seed.desc, **seed.kwargs)
|
||||
assert teacher.name == seed.expect_name
|
||||
assert teacher.desc == seed.expect_desc
|
||||
assert teacher.profile == seed.expect_profile
|
||||
assert teacher.goal == seed.expect_goal
|
||||
assert teacher.constraints == seed.expect_constraints
|
||||
assert teacher.course_title == "teaching_plan"
|
||||
|
||||
|
||||
def test_new_file_name():
|
||||
class Inputs(BaseModel):
|
||||
lesson_title: str
|
||||
ext: str
|
||||
expect: str
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"lesson_title": "# @344\n12",
|
||||
"ext": ".md",
|
||||
"expect": "_344_12.md"
|
||||
},
|
||||
{
|
||||
"lesson_title": "1#@$%!*&\\/:*?\"<>|\n\t \'1",
|
||||
"ext": ".cc",
|
||||
"expect": "1_1.cc"
|
||||
}
|
||||
]
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
result = Teacher.new_file_name(seed.lesson_title, seed.ext)
|
||||
assert result == seed.expect
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_init()
|
||||
test_new_file_name()
|
||||
61
tests/metagpt/roles/test_uml_meta_role_factory.py
Normal file
61
tests/metagpt/roles/test_uml_meta_role_factory.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/8
|
||||
@Author : mashenquan
|
||||
@File : test_uml_meta_role_factory.py
|
||||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory
|
||||
|
||||
|
||||
def test_create_roles():
|
||||
class Inputs(BaseModel):
|
||||
roles: List
|
||||
kwargs: Dict
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"role_type": "fork",
|
||||
"name": "Lily",
|
||||
"profile": "{teaching_language} Teacher",
|
||||
"goal": "writing a {language} teaching plan part by part",
|
||||
"constraints": "writing in {language}",
|
||||
"role": "You are a {teaching_language} Teacher, named Lily.",
|
||||
"desc": "",
|
||||
"output_filename": "teaching_plan_demo.md",
|
||||
"requirement": ["TeachingPlanRequirement"],
|
||||
"templates": ["Do 1 {statements}", "Do 2 {statements}"],
|
||||
"actions": [
|
||||
{
|
||||
"name": "",
|
||||
"topic": "Title",
|
||||
"language": "Chinese",
|
||||
"statements": ["statement 1", "statement 2"]}
|
||||
],
|
||||
"template_ix": 0
|
||||
}
|
||||
],
|
||||
"kwargs": {
|
||||
"teaching_language": "AA",
|
||||
"language": "BB",
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
roles = UMLMetaRoleFactory.create_roles(seed.roles, **seed.kwargs)
|
||||
assert len(roles) == 1
|
||||
assert "{" not in roles[0].profile
|
||||
assert "{" not in roles[0].goal
|
||||
assert roles[0].action_count == 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_create_roles()
|
||||
40
tests/metagpt/roles/test_uml_meta_role_options.py
Normal file
40
tests/metagpt/roles/test_uml_meta_role_options.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/8/8
|
||||
@Author : mashenquan
|
||||
@File : test_uml_meta_role_options.py
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.roles.uml_meta_role_options import MetaActionOptions
|
||||
|
||||
|
||||
def test_set_default_template():
|
||||
class Inputs(BaseModel):
|
||||
statements: List
|
||||
template: str
|
||||
expect_prompt: str
|
||||
|
||||
inputs = [
|
||||
{
|
||||
"statements": ["Statement: 1", "Statement: 2"],
|
||||
"template": "{statements}",
|
||||
"expect_prompt": "Statement: 1\nStatement: 2"
|
||||
}
|
||||
]
|
||||
|
||||
for i in inputs:
|
||||
seed = Inputs(**i)
|
||||
opt = MetaActionOptions(topic="", statements=seed.statements)
|
||||
assert opt.template == ""
|
||||
opt.set_default_template(seed.template)
|
||||
assert opt.template == seed.template
|
||||
kwargs = {}
|
||||
assert opt.format_prompt(**kwargs) == seed.expect_prompt
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_set_default_template()
|
||||
|
|
@ -4,23 +4,46 @@
|
|||
@Time : 2023/7/1 22:50
|
||||
@Author : alexanderwu
|
||||
@File : test_azure_tts.py
|
||||
@Modified By: mashenquan, 2023-8-9, add more text formatting options
|
||||
@Modified By: mashenquan, 2023-8-17, move to `tools` folder.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent.parent.parent)) # fix-bug: No module named 'metagpt'
|
||||
from metagpt.const import WORKSPACE_ROOT
|
||||
from metagpt.tools.azure_tts import AzureTTS
|
||||
from metagpt.utils.common import initialize_environment
|
||||
|
||||
|
||||
def test_azure_tts():
|
||||
azure_tts = AzureTTS("azure_tts")
|
||||
azure_tts.synthesize_speech(
|
||||
"zh-CN",
|
||||
"zh-CN-YunxiNeural",
|
||||
"Boy",
|
||||
"你好,我是卡卡",
|
||||
"output.wav")
|
||||
initialize_environment()
|
||||
|
||||
azure_tts = AzureTTS()
|
||||
text = """
|
||||
女儿看见父亲走了进来,问道:
|
||||
<mstts:express-as role="YoungAdultFemale" style="calm">
|
||||
“您来的挺快的,怎么过来的?”
|
||||
</mstts:express-as>
|
||||
父亲放下手提包,说:
|
||||
<mstts:express-as role="OlderAdultMale" style="calm">
|
||||
“Writing a binary file in Python is similar to writing a regular text file, but you'll work with bytes instead of strings.”
|
||||
</mstts:express-as>
|
||||
"""
|
||||
path = WORKSPACE_ROOT / "tts"
|
||||
path.mkdir(exist_ok=True, parents=True)
|
||||
filename = path / "girl.wav"
|
||||
result = azure_tts.synthesize_speech(
|
||||
lang="zh-CN",
|
||||
voice="zh-CN-XiaomoNeural",
|
||||
text=text,
|
||||
output_file=str(filename))
|
||||
|
||||
print(result)
|
||||
|
||||
# 运行需要先配置 SUBSCRIPTION_KEY
|
||||
# TODO: 这里如果要检验,还要额外加上对应的asr,才能确保前后生成是接近一致的,但现在还没有
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_azure_tts()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue