diff --git a/examples/st_game/reflect/__init__.py b/examples/st_game/reflect/__init__.py new file mode 100644 index 000000000..6acb42c9e --- /dev/null +++ b/examples/st_game/reflect/__init__.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : reflection module + +from metagpt.reflect import agent_reflect +from metagpt.reflect import ga_prompt_generator +__all__ = [ + "agent_reflect", + "LongTermMemory", + "ga_po" + "ga_prompt_generator" +] diff --git a/examples/st_game/reflect/ga_prompt_generator.py b/examples/st_game/reflect/ga_prompt_generator.py new file mode 100644 index 000000000..6b7bdbad7 --- /dev/null +++ b/examples/st_game/reflect/ga_prompt_generator.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# author: didi +# Date:9.25 + +import openai +from metagpt.llm import DEFAULT_LLM +# 直接调用Prompt生成 +# ga的prompt构建格式和metagpt完全不同。没有办法融合。 + + +# 特殊指令加入Prompt生成 + + +async def final_response(prompt, special_instruction, example_output=None): + """ + 通过将特殊指令加入Prompt生成最终的响应。 + + 参数: + - prompt:要生成响应的提示文本。 + - special_instruction:要加入Prompt的特殊指令。 + - example_output(可选):示例输出的JSON字符串。 + + 返回: + 生成的最终响应。 + + """ + prompt = '"""\n' + prompt + '\n"""\n' + prompt += f"Output the response to the prompt above in json. {special_instruction}\n" + if example_output: + prompt += "Example output json:\n" + prompt += '{"output": "' + str(example_output) + '"}' + return await DEFAULT_LLM.aask(prompt) + +# prompt填充模板 + + +def prompt_generate(curr_input, prompt_lib_file): + """ + Takes in the current input (e.g. comment that you want to classifiy) and + the path to a prompt file. The prompt file contains the raw str prompt that + will be used, which contains the following substr: !! -- this + function replaces this substr with the actual curr_input to produce the + final promopt that will be sent to the GPT3 server. + ARGS: + curr_input: the input we want to feed in (IF THERE ARE MORE THAN ONE + INPUT, THIS CAN BE A LIST.) + prompt_lib_file: the path to the promopt file. + RETURNS: + a str prompt that will be sent to OpenAI's GPT server. + """ + if type(curr_input) is type("string"): + curr_input = [curr_input] + curr_input = [str(i) for i in curr_input] + + f = open(prompt_lib_file, "r") + prompt = f.read() + f.close() + for count, i in enumerate(curr_input): + prompt = prompt.replace(f"!!", i) + if "###" in prompt: + prompt = prompt.split( + "###")[1] + return prompt.strip() + +# 使用OpenAI embedding库进行存储 + + +def embedding(query): + """ + Generates an embedding for the given query. + + Args: + query (str): The text query to be embedded. + + Returns: + str: The embedding key generated for the query. + """ + embedding_result = openai.Embedding.create( + model="text-embedding-ada-002", + input=query + ) + embedding_key = embedding_result['data'][0]["embedding"] + return embedding_key diff --git a/examples/st_game/reflect/st_reflect.py b/examples/st_game/reflect/st_reflect.py index 97efe0dc6..1b22ce99f 100644 --- a/examples/st_game/reflect/st_reflect.py +++ b/examples/st_game/reflect/st_reflect.py @@ -1,3 +1,97 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Desc : st's reflection execution + +import asyncio +import json +from metagpt.logs import logger +import time +from ga_prompt_generator import final_response +''' +等待Agent和memory更新,保留相关引用但可以忽略。 +''' +from ..memory.associative_memory import MemoryBasic + +import json +import time + + +async def agent_reflect(memories_list): + """ + 代理反思函数:生成关注点并生成洞察和证据 + + """ + A = await generate_focus_point(memories_list) + + for i in A: + B = await generate_insights_and_evidence(memories_list, question=i) + + +async def generate_focus_point(memories_list: list[MemoryBasic], n=3): + """ + 生成关注点函数:根据记忆列表生成关注点 + """ + wait_sorted_mem = [[i.accessed_time, i] for i in memories_list] + sorted_memories = sorted(wait_sorted_mem, key=lambda x: x[0]) + memorys = [i for created, i in sorted_memories] + statements = '' + for i in memorys: + statements += i.description + "\n" + prompt = ''' + {statements} + Given only the information above, what are {num_question} most salient high-level questions we can answer about the subjects grounded in the statements? + ''' + example_output = '["What should Jane do for lunch", "Does Jane like strawberry", "Who is Jane"]' + out = await final_response(prompt.format(statements=statements, num_question=n), + "Output must be a list of str.", example_output) + try: + poi_dict = json.loads(out) + return poi_dict['output'] + except ValueError: + print(out) + logger.error('无法返回正常结果') + return out + + +async def generate_insights_and_evidence(memories_list: list[MemoryBasic], question: str, n=5): + """ + 生成洞察和证据函数:根据问题生成洞察和证据 + """ + memories_list = await agent_retrieve(agent, question, 50, 10) + statements = "" + for count, mem in enumerate(memories_list): + statements += f'{str(count)}. {mem.description}\n' + prompt = ''' + Input: + {statements} + + What {n} high-level insights can you infer from the above statements? + You should return a list of list[str,list]. The first element is the insight you have found. The second element is the + ''' + + ret = final_response(prompt.format( + question=question, statements=statements, n=n), "['insightA',[1,2,3]]") + try: + insight_list = json.loads(ret) + for insight, index in insight_list: + agent.memory_list.append(MemoryBasic( + time.time(), None, insight, None, None)) + return insight_list + except: + logger.error('我们无法获得想要的返回。') + return ret + + +""" if __name__ == "__main__": + # 例子,构建John Agent,实现retrive + John_iss = "John Lin is a pharmacy shopkeeper at the Willow Market and Pharmacy who loves to help people. He is always looking for ways to make the process of getting medication easier for his customers; John Lin is living with his wife, Mei Lin, who is a college professor, and son, Eddy Lin, who is a student studying music theory; John Lin loves his family very much; John Lin has known the old couple next-door, Sam Moore and Jennifer Moore, for a few years; John Lin thinks Sam Moore is a kind and nice man; John Lin knows his neighbor, Yuriko Yamamoto, well; John Lin knows of his neighbors, Tamara Taylor and Carmen Ortiz, but has not met them before; John Lin and Tom Moreno are colleagues at The Willows Market and Pharmacy; John Lin and Tom Moreno are friends and like to discuss local politics together; John Lin knows the Moreno family somewhat well — the husband Tom Moreno and the wife Jane Moreno." + John = AgentMemory( + "John", John_iss, memory_path="agent_memories/John_memory.json") + + # John的相关信息:{'Had a friendly chat with Yuriko about her garden.': 2.4992317730827667, 'Helped Mrs. Moore carry groceries into her house.': 1.957656720441911, 'Discussed local politics with Tom Moreno.': 1.9458268038234035} + asyncio.run(agent_reflect(John)) + ''' + 这里是输出,list形式,返回给记忆。 + [['The pharmacy is a friendly and helpful community.', [0, 2, 9, 12]], ['The pharmacy is a place where people come for more than just medication.', [3, 5, 13, 14]], ['The pharmacy is a place where people come for advice and conversation.', [0, 2, 6, 9, 12]], ['The pharmacy is a place where people come for assistance with daily tasks.', [3, 5, 13, 14]], ['The pharmacy is a place where people come for political discussions.', [1]]] + ''' + """ diff --git a/examples/st_game/roles/st_role.py b/examples/st_game/roles/st_role.py index ff807b95b..0c70c1d80 100644 --- a/examples/st_game/roles/st_role.py +++ b/examples/st_game/roles/st_role.py @@ -27,6 +27,8 @@ from ..memory.retrieve import agent_retrieve from ..memory.scratch import Scratch from ..utils.utils import get_embedding, generate_poig_score +from ..reflect.st_reflect import agent_reflect + class STRoleContext(RoleContext): env: 'MazeEnvironment' = Field(default=None) diff --git a/metagpt/plan/plan.py b/metagpt/plan/plan.py deleted file mode 100644 index caa19416b..000000000 --- a/metagpt/plan/plan.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @Desc : base class of planning diff --git a/metagpt/reflect/__init__.py b/metagpt/reflect/__init__.py deleted file mode 100644 index da486a131..000000000 --- a/metagpt/reflect/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @Desc : reflection module diff --git a/metagpt/reflect/reflect.py b/metagpt/reflect/reflect.py deleted file mode 100644 index 5abf6e5d8..000000000 --- a/metagpt/reflect/reflect.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @Desc : base class of reflection diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index b1ae51cf5..fef31dc61 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -18,6 +18,7 @@ from metagpt.llm import LLM from metagpt.logs import logger from metagpt.memory import Memory, LongTermMemory from metagpt.schema import Message +from metagpt.reflect import agent_reflect PREFIX_TEMPLATE = """You are a {profile}, named {name}, your goal is {goal}, and the constraint is {constraints}. """ @@ -78,7 +79,8 @@ class RoleContext(BaseModel): def check(self, role_id: str): if hasattr(CONFIG, "long_term_memory") and CONFIG.long_term_memory: self.long_term_memory.recover_memory(role_id, self) - self.memory = self.long_term_memory # use memory to act as long_term_memory for unify operation + # use memory to act as long_term_memory for unify operation + self.memory = self.long_term_memory @property def important_memory(self) -> list[Message]: @@ -95,7 +97,8 @@ class Role: def __init__(self, name="", profile="", goal="", constraints="", desc=""): self._llm = LLM() - self._setting = RoleSetting(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc) + self._setting = RoleSetting( + name=name, profile=profile, goal=goal, constraints=constraints, desc=desc) self._states = [] self._actions = [] self._role_id = str(self._setting) @@ -169,9 +172,10 @@ class Role: # logger.info(response) if isinstance(response, ActionOutput): msg = Message(content=response.content, instruct_content=response.instruct_content, - role=self.profile, cause_by=type(self._rc.todo)) + role=self.profile, cause_by=type(self._rc.todo)) else: - msg = Message(content=response, role=self.profile, cause_by=type(self._rc.todo)) + msg = Message(content=response, role=self.profile, + cause_by=type(self._rc.todo)) self._rc.memory.add(msg) # logger.debug(f"{response}") @@ -184,8 +188,9 @@ class Role: 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 + + # remember recent exact or similar memories + self._rc.news = self._rc.memory.remember(observed) for i in env_msgs: self.recv(i) @@ -205,7 +210,8 @@ class Role: async def _react(self) -> Message: """Think first, then act""" await self._think() - logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}") + logger.debug( + f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}") return await self._act() def recv(self, message: Message) -> None: