mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-08 15:05:17 +02:00
fix bugs and make it perform better
This commit is contained in:
parent
0d1c0f89cc
commit
a7a1195a31
11 changed files with 59 additions and 32 deletions
|
|
@ -64,7 +64,7 @@ class AgentCreator(Role):
|
|||
self._init_actions([CreateAgent])
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
todo = self._rc.todo
|
||||
msg = self._rc.memory.get()[-1]
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class SimpleCoder(Role):
|
|||
self._init_actions([SimpleWriteCode])
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
todo = self._rc.todo # todo will be SimpleWriteCode()
|
||||
|
||||
msg = self.get_memories(k=1)[0] # find the most recent messages
|
||||
|
|
@ -80,7 +80,7 @@ class RunnableCoder(Role):
|
|||
self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
# By choosing the Action by order under the hood
|
||||
# todo will be first SimpleWriteCode() then SimpleRunCode()
|
||||
todo = self._rc.todo
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class SimpleTester(Role):
|
|||
self._watch([SimpleWriteCode, SimpleWriteReview]) # feel free to try this too
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
todo = self._rc.todo
|
||||
|
||||
# context = self.get_memories(k=1)[0].content # use the most recent memory as context
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class Debator(Role):
|
|||
return len(self._rc.news)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
todo = self._rc.todo # An instance of SpeakAloud
|
||||
|
||||
memories = self.get_memories()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@
|
|||
import asyncio
|
||||
|
||||
from metagpt.actions import Action, UserRequirement
|
||||
from metagpt.environment import Environment
|
||||
from metagpt.roles import Role
|
||||
from metagpt.team import Team
|
||||
|
||||
action1 = Action(name="BidenSay", instruction="Use diverse words to attack your opponent, strong and emotional.")
|
||||
action2 = Action(name="TrumpSay", instruction="Use diverse words to attack your opponent, strong and emotional.")
|
||||
biden = Role(name="Biden", profile="democrat", goal="win election", actions=[action1], watch=[action2, UserRequirement])
|
||||
trump = Role(name="Trump", profile="republican", goal="win election", actions=[action2], watch=[action1])
|
||||
team = Team(investment=10.0, env_desc="US election live broadcast", roles=[biden, trump])
|
||||
action1 = Action(name="BidenSay", instruction="发表政见,充满激情的与对手辩论")
|
||||
action2 = Action(name="TrumpSay", instruction="发表政见,充满激情的与对手辩论,MAGA!")
|
||||
biden = Role(name="拜登", profile="民主党", goal="大选获胜", actions=[action1], watch=[action2, UserRequirement])
|
||||
trump = Role(name="特朗普", profile="共和党", goal="大选获胜", actions=[action2], watch=[action1])
|
||||
env = Environment(desc="US election live broadcast")
|
||||
team = Team(investment=10.0, env=env, roles=[biden, trump])
|
||||
|
||||
asyncio.run(team.run(idea="Topic: climate change", n_round=5))
|
||||
asyncio.run(team.run(idea="主题:气候变化,用中文辩论", n_round=5))
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class Action(BaseModel):
|
|||
|
||||
def __init_with_instruction(self, instruction: str):
|
||||
"""Initialize action with instruction"""
|
||||
self.node = ActionNode(key=self.name, expected_type=str, instruction=instruction, example="")
|
||||
self.node = ActionNode(key=self.name, expected_type=str, instruction=instruction, example="", schema="raw")
|
||||
return self
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
|
|
@ -85,7 +85,8 @@ class Action(BaseModel):
|
|||
async def _run_action_node(self, *args, **kwargs):
|
||||
"""Run action node"""
|
||||
msgs = args[0]
|
||||
context = "\n".join([f"Msg {idx}: {i}" for idx, i in enumerate(reversed(msgs))])
|
||||
context = "## History Messages\n"
|
||||
context += "\n".join([f"{idx}: {i}" for idx, i in enumerate(reversed(msgs))])
|
||||
return await self.node.fill(context=context, llm=self.llm)
|
||||
|
||||
async def run(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from metagpt.utils.common import OutputParser, general_after_log
|
|||
|
||||
TAG = "CONTENT"
|
||||
|
||||
LANGUAGE_CONSTRAINT = "Language: Please use the same language as the user input."
|
||||
LANGUAGE_CONSTRAINT = "Language: Please use the same language as Human INPUT."
|
||||
FORMAT_CONSTRAINT = f"Format: output wrapped inside [{TAG}][/{TAG}] like format example, nothing else."
|
||||
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ def dict_to_markdown(d, prefix="- ", kv_sep="\n", postfix="\n"):
|
|||
class ActionNode:
|
||||
"""ActionNode is a tree of nodes."""
|
||||
|
||||
mode: str
|
||||
schema: str # raw/json/markdown, default: ""
|
||||
|
||||
# Action Context
|
||||
context: str # all the context, including all necessary info
|
||||
|
|
@ -81,6 +81,7 @@ class ActionNode:
|
|||
example: Any,
|
||||
content: str = "",
|
||||
children: dict[str, "ActionNode"] = None,
|
||||
schema: str = "",
|
||||
):
|
||||
self.key = key
|
||||
self.expected_type = expected_type
|
||||
|
|
@ -88,6 +89,7 @@ class ActionNode:
|
|||
self.example = example
|
||||
self.content = content
|
||||
self.children = children if children is not None else {}
|
||||
self.schema = schema
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
|
|
@ -222,7 +224,13 @@ class ActionNode:
|
|||
mode="children": 编译所有子节点为一个统一模板,包括instruction与example
|
||||
mode="all": NotImplemented
|
||||
mode="root": NotImplemented
|
||||
schmea: raw/json/markdown
|
||||
schema="raw": 不编译,context, lang_constaint, instruction
|
||||
schema="json":编译context, example(json), instruction(markdown), constraint, action
|
||||
schema="markdown": 编译context, example(markdown), instruction(markdown), constraint, action
|
||||
"""
|
||||
if schema == "raw":
|
||||
return context + "\n\n## Actions\n" + LANGUAGE_CONSTRAINT + "\n" + self.instruction
|
||||
|
||||
# FIXME: json instruction会带来格式问题,如:"Project name": "web_2048 # 项目名称使用下划线",
|
||||
# compile example暂时不支持markdown
|
||||
|
|
@ -283,12 +291,17 @@ class ActionNode:
|
|||
|
||||
async def simple_fill(self, schema, mode):
|
||||
prompt = self.compile(context=self.context, schema=schema, mode=mode)
|
||||
mapping = self.get_mapping(mode)
|
||||
|
||||
class_name = f"{self.key}_AN"
|
||||
content, scontent = await self._aask_v1(prompt, class_name, mapping, schema=schema)
|
||||
self.content = content
|
||||
self.instruct_content = scontent
|
||||
if schema != "raw":
|
||||
mapping = self.get_mapping(mode)
|
||||
class_name = f"{self.key}_AN"
|
||||
content, scontent = await self._aask_v1(prompt, class_name, mapping, schema=schema)
|
||||
self.content = content
|
||||
self.instruct_content = scontent
|
||||
else:
|
||||
self.content = await self.llm.aask(prompt)
|
||||
self.instruct_content = None
|
||||
|
||||
return self
|
||||
|
||||
async def fill(self, context, llm, schema="json", mode="auto", strgy="simple"):
|
||||
|
|
@ -297,6 +310,7 @@ class ActionNode:
|
|||
:param context: Everything we should know when filling node.
|
||||
:param llm: Large Language Model with pre-defined system message.
|
||||
:param schema: json/markdown, determine example and output format.
|
||||
- raw: free form text
|
||||
- json: it's easy to open source LLM with json format
|
||||
- markdown: when generating code, markdown is always better
|
||||
:param mode: auto/children/root
|
||||
|
|
@ -310,14 +324,16 @@ class ActionNode:
|
|||
"""
|
||||
self.set_llm(llm)
|
||||
self.set_context(context)
|
||||
if self.schema:
|
||||
schema = self.schema
|
||||
|
||||
if strgy == "simple":
|
||||
return await self.simple_fill(schema, mode)
|
||||
return await self.simple_fill(schema=schema, mode=mode)
|
||||
elif strgy == "complex":
|
||||
# 这里隐式假设了拥有children
|
||||
tmp = {}
|
||||
for _, i in self.children.items():
|
||||
child = await i.simple_fill(schema, mode)
|
||||
child = await i.simple_fill(schema=schema, mode=mode)
|
||||
tmp.update(child.instruct_content.dict())
|
||||
cls = self.create_children_class()
|
||||
self.instruct_content = cls(**tmp)
|
||||
|
|
|
|||
|
|
@ -96,15 +96,18 @@ class Environment(BaseModel):
|
|||
"""增加一个在当前环境的角色
|
||||
Add a role in the current environment
|
||||
"""
|
||||
role.set_env(self)
|
||||
self.roles[role.profile] = role
|
||||
role.set_env(self)
|
||||
|
||||
def add_roles(self, roles: Iterable[Role]):
|
||||
"""增加一批在当前环境的角色
|
||||
Add a batch of characters in the current environment
|
||||
"""
|
||||
for role in roles:
|
||||
self.add_role(role)
|
||||
self.roles[role.profile] = role
|
||||
|
||||
for role in roles: # setup system message with roles
|
||||
role.set_env(self)
|
||||
|
||||
def publish_message(self, message: Message, peekable: bool = True) -> bool:
|
||||
"""
|
||||
|
|
@ -153,8 +156,8 @@ class Environment(BaseModel):
|
|||
"""
|
||||
return self.roles.get(name, None)
|
||||
|
||||
def role_names(self) -> str:
|
||||
return ", ".join([f"{i.name}" for i in self.roles.values()])
|
||||
def role_names(self) -> list[str]:
|
||||
return [i.name for i in self.roles.values()]
|
||||
|
||||
@property
|
||||
def is_idle(self):
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class Researcher(Role):
|
|||
return False
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}({self._rc.todo.name})")
|
||||
todo = self._rc.todo
|
||||
msg = self._rc.memory.get(k=1)[0]
|
||||
if isinstance(msg.instruct_content, Report):
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ class Role(BaseModel):
|
|||
desc: str = ""
|
||||
is_human: bool = False
|
||||
|
||||
_llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
_llm: BaseGPTAPI = Field(default_factory=LLM) # Each role has its own LLM, use different system message
|
||||
_role_id: str = ""
|
||||
_states: list[str] = []
|
||||
_actions: list[Action] = []
|
||||
|
|
@ -259,6 +259,9 @@ class Role(BaseModel):
|
|||
def _init_action_system_message(self, action: Action):
|
||||
action.set_prefix(self._get_prefix())
|
||||
|
||||
def refresh_system_message(self):
|
||||
self._llm.system_prompt = self._get_prefix()
|
||||
|
||||
def set_recovered(self, recovered: bool = False):
|
||||
self.recovered = recovered
|
||||
|
||||
|
|
@ -340,6 +343,7 @@ class Role(BaseModel):
|
|||
self._rc.env = env
|
||||
if env:
|
||||
env.set_subscription(self, self._subscription)
|
||||
self.refresh_system_message() # add env message to system message
|
||||
|
||||
@property
|
||||
def subscription(self) -> Set:
|
||||
|
|
@ -362,7 +366,8 @@ class Role(BaseModel):
|
|||
prefix += CONSTRAINT_TEMPLATE.format(**{"constraints": self.constraints})
|
||||
|
||||
if self._rc.env and self._rc.env.desc:
|
||||
env_desc = f"You are in {self._rc.env.desc} with roles({self._rc.env.role_names()})."
|
||||
other_role_names = ", ".join(self._rc.env.role_names())
|
||||
env_desc = f"You are in {self._rc.env.desc} with roles({other_role_names})."
|
||||
prefix += env_desc
|
||||
return prefix
|
||||
|
||||
|
|
@ -402,13 +407,13 @@ class Role(BaseModel):
|
|||
return True
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
response = await self._rc.todo.run(self._rc.important_memory)
|
||||
if isinstance(response, (ActionOutput, ActionNode)):
|
||||
msg = Message(
|
||||
content=response.content,
|
||||
instruct_content=response.instruct_content,
|
||||
role=self.profile,
|
||||
role=self._setting,
|
||||
cause_by=self._rc.todo,
|
||||
sent_from=self,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class Searcher(Role):
|
|||
|
||||
async def _act_sp(self) -> Message:
|
||||
"""Performs the search action in a single process."""
|
||||
logger.info(f"{self._setting}: ready to {self._rc.todo}")
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}")
|
||||
response = await self._rc.todo.run(self._rc.memory.get(k=0))
|
||||
|
||||
if isinstance(response, (ActionOutput, ActionNode)):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue