Merge pull request #368 from zhouzinimg/ga_game

ga_game add reflection part
This commit is contained in:
better629 2023-10-01 08:46:22 +08:00 committed by GitHub
commit bd11e48d7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 205 additions and 16 deletions

View file

@ -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"
]

View file

@ -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: !<INPUT>! -- 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"!<INPUT {count}>!", i)
if "<commentblockmarker>###</commentblockmarker>" in prompt:
prompt = prompt.split(
"<commentblockmarker>###</commentblockmarker>")[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

View file

@ -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]]]
'''
"""

View file

@ -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)

View file

@ -1,3 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc : base class of planning

View file

@ -1,3 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc : reflection module

View file

@ -1,3 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc : base class of reflection

View file

@ -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: