Merge pull request #628 from iorisa/fixbug/role/assistant

fixbug: 修复通用智能体role及其相关的TalkAction和SkillAction
This commit is contained in:
geekan 2023-12-25 23:14:21 +08:00 committed by GitHub
commit 59586f30d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 541 additions and 512 deletions

View file

@ -14,36 +14,45 @@ import traceback
from copy import deepcopy
from typing import Dict, Optional
from metagpt.actions import Action, ActionOutput
from metagpt.actions import Action
from metagpt.learn.skill_loader import Skill
from metagpt.logs import logger
from metagpt.schema import Message
# TOTEST
class ArgumentsParingAction(Action):
skill: Skill
ask: str
rsp: Optional[ActionOutput]
args: Optional[Dict]
rsp: Optional[Message] = None
args: Optional[Dict] = None
@property
def prompt(self):
prompt = f"{self.skill.name} function parameters description:\n"
prompt = "You are a function parser. You can convert spoken words into function parameters.\n"
prompt += "\n---\n"
prompt += f"{self.skill.name} function parameters description:\n"
for k, v in self.skill.arguments.items():
prompt += f"parameter `{k}`: {v}\n"
prompt += "\n"
prompt += "\n---\n"
prompt += "Examples:\n"
for e in self.skill.examples:
prompt += f"If want you to do `{e.ask}`, return `{e.answer}` brief and clear.\n"
prompt += f"\nNow I want you to do `{self.ask}`, return in examples format above, brief and clear."
prompt += "\n---\n"
prompt += (
f"\nRefer to the `{self.skill.name}` function description, and fill in the function parameters according "
'to the example "I want you to do xx" in the Examples section.'
f"\nNow I want you to do `{self.ask}`, return function parameters in Examples format above, brief and "
"clear."
)
return prompt
async def run(self, *args, **kwargs) -> ActionOutput:
async def run(self, with_message=None, **kwargs) -> Message:
prompt = self.prompt
rsp = await self.llm.aask(msg=prompt, system_msgs=[])
logger.debug(f"SKILL:{prompt}\n, RESULT:{rsp}")
self.args = ArgumentsParingAction.parse_arguments(skill_name=self.skill.name, txt=rsp)
self.rsp = ActionOutput(content=rsp)
self.rsp = Message(content=rsp, role="assistant", instruct_content=self.args, cause_by=self)
return self.rsp
@staticmethod
@ -72,9 +81,9 @@ class ArgumentsParingAction(Action):
class SkillAction(Action):
skill: Skill
args: Dict
rsp: str = ""
rsp: Optional[Message] = None
async def run(self, *args, **kwargs) -> str | ActionOutput | None:
async def run(self, with_message=None, **kwargs) -> Message:
"""Run action"""
options = deepcopy(kwargs)
if self.args:
@ -82,26 +91,21 @@ class SkillAction(Action):
if k in options:
options.pop(k)
try:
self.rsp = await self.find_and_call_function(self.skill.name, args=self.args, **options)
rsp = await self.find_and_call_function(self.skill.name, args=self.args, **options)
self.rsp = Message(content=rsp, role="assistant", cause_by=self)
except Exception as e:
logger.exception(f"{e}, traceback:{traceback.format_exc()}")
self.rsp = f"Error: {e}"
return ActionOutput(content=self.rsp, instruct_content=self.skill.json())
self.rsp = Message(content=f"Error: {e}", role="assistant", cause_by=self)
return self.rsp
@staticmethod
async def find_and_call_function(function_name, args, **kwargs):
async def find_and_call_function(function_name, args, **kwargs) -> str:
try:
module = importlib.import_module("metagpt.learn")
function = getattr(module, function_name)
# 调用函数并返回结果
# Invoke function and return result
result = await function(**args, **kwargs)
return result
except (ModuleNotFoundError, AttributeError):
logger.error(f"{function_name} not found")
return None
if __name__ == "__main__":
ArgumentsParingAction.parse_arguments(
skill_name="text_to_image", txt='`text_to_image(text="Draw an apple", size_type="512x512")`'
)
raise ValueError(f"{function_name} not found")

View file

@ -6,27 +6,21 @@
@File : talk_action.py
@Desc : Act as its a talk
"""
import json
from typing import Optional
from metagpt.actions import Action, ActionOutput
from metagpt.actions import Action
from metagpt.config import CONFIG
from metagpt.const import DEFAULT_LANGUAGE
from metagpt.llm import LLMType
from metagpt.logs import logger
from metagpt.schema import Message
# TOTEST
class TalkAction(Action):
def __init__(self, name: str = "", talk="", history_summary="", knowledge="", context=None, llm=None, **kwargs):
context = context or {}
context["talk"] = talk
context["history_summery"] = history_summary
context["knowledge"] = knowledge
super(TalkAction, self).__init__(name=name, context=context, llm=llm)
self._talk = talk
self._history_summary = history_summary
self._knowledge = knowledge
self._rsp = None
context: str
history_summary: str = ""
knowledge: str = ""
rsp: Optional[Message] = None
@property
def prompt(self):
@ -37,15 +31,15 @@ class TalkAction(Action):
f"maintaining the character's persona and habits. When faced with unrelated questions, playfully "
f"decline to answer without revealing your AI nature to preserve the character's image.\n\n"
)
prompt += f"Knowledge:\n{self._knowledge}\n\n" if self._knowledge else ""
prompt += f"{self._history_summary}\n\n"
prompt += f"Knowledge:\n{self.knowledge}\n\n" if self.knowledge else ""
prompt += f"{self.history_summary}\n\n"
prompt += (
"If the information is insufficient, you can search in the historical conversation or knowledge above.\n"
)
language = CONFIG.language or DEFAULT_LANGUAGE
prompt += (
f"Answer the following questions strictly in {language}, and the answers must follow the Markdown format.\n "
f"{self._talk}"
f"{self.context}"
)
logger.debug(f"PROMPT: {prompt}")
return prompt
@ -54,23 +48,23 @@ class TalkAction(Action):
def prompt_gpt4(self):
kvs = {
"{role}": CONFIG.agent_description or "",
"{history}": self._history_summary or "",
"{knowledge}": self._knowledge or "",
"{history}": self.history_summary or "",
"{knowledge}": self.knowledge or "",
"{language}": CONFIG.language or DEFAULT_LANGUAGE,
"{ask}": self._talk,
"{ask}": self.context,
}
prompt = TalkAction.__FORMATION_LOOSE__
prompt = TalkActionPrompt.FORMATION_LOOSE
for k, v in kvs.items():
prompt = prompt.replace(k, v)
logger.info(f"PROMPT: {prompt}")
return prompt
async def run_old(self, *args, **kwargs) -> ActionOutput:
prompt = self.prompt
rsp = await self.llm.aask(msg=prompt, system_msgs=[])
logger.debug(f"PROMPT:{prompt}\nRESULT:{rsp}\n")
self._rsp = ActionOutput(content=rsp)
return self._rsp
# async def run_old(self, *args, **kwargs) -> ActionOutput:
# prompt = self.prompt
# rsp = await self.llm.aask(msg=prompt, system_msgs=[])
# logger.debug(f"PROMPT:{prompt}\nRESULT:{rsp}\n")
# self._rsp = ActionOutput(content=rsp)
# return self._rsp
@property
def aask_args(self):
@ -84,22 +78,21 @@ class TalkAction(Action):
f"Answer the following questions strictly in {language}, and the answers must follow the Markdown format.",
]
format_msgs = []
if self._knowledge:
format_msgs.append({"role": "assistant", "content": self._knowledge})
if self._history_summary:
if CONFIG.LLM_TYPE == LLMType.METAGPT.value:
format_msgs.extend(json.loads(self._history_summary))
else:
format_msgs.append({"role": "assistant", "content": self._history_summary})
return self._talk, format_msgs, system_msgs
if self.knowledge:
format_msgs.append({"role": "assistant", "content": self.knowledge})
if self.history_summary:
format_msgs.append({"role": "assistant", "content": self.history_summary})
return self.context, format_msgs, system_msgs
async def run(self, *args, **kwargs) -> ActionOutput:
async def run(self, with_message=None, **kwargs) -> Message:
msg, format_msgs, system_msgs = self.aask_args
rsp = await self.llm.aask(msg=msg, format_msgs=format_msgs, system_msgs=system_msgs)
self._rsp = ActionOutput(content=rsp)
return self._rsp
self.rsp = Message(content=rsp, role="assistant", cause_by=self)
return self.rsp
__FORMATION__ = """Formation: "Capacity and role" defines the role you are currently playing;
class TalkActionPrompt:
FORMATION = """Formation: "Capacity and role" defines the role you are currently playing;
"[HISTORY_BEGIN]" and "[HISTORY_END]" tags enclose the historical conversation;
"[KNOWLEDGE_BEGIN]" and "[KNOWLEDGE_END]" tags enclose the knowledge may help for your responses;
"Statement" defines the work detail you need to complete at this stage;
@ -135,7 +128,7 @@ Statement: Unless you are a language professional, answer the following question
{ask}
"""
__FORMATION_LOOSE__ = """Formation: "Capacity and role" defines the role you are currently playing;
FORMATION_LOOSE = """Formation: "Capacity and role" defines the role you are currently playing;
"[HISTORY_BEGIN]" and "[HISTORY_END]" tags enclose the historical conversation;
"[KNOWLEDGE_BEGIN]" and "[KNOWLEDGE_END]" tags enclose the knowledge may help for your responses;
"Statement" defines the work detail you need to complete at this stage;