反思(reflect)是人格认知模块的核心部分,负责生成焦点、洞察和证据,并根据这些信息运行反思。它通过与 GPT 模型的交互来生成思想,并将这些思想保存到人格的记忆中,以便后续使用。
反思实现思路
反思的基本逻辑:
- 设置一个最大感知阈值,越大表示反思越不积极;
- 设置一个当前反思值;
- 当有感知 event 发生时,会调用评估模型给 event 打分;
- 当前反思值 - event 得分 <= 0 时,触发反思。
反思主线流程
我们看代码实现:
def reflect(persona):
"""
触发反思:根据 reflection_trigger 的返回值判断是否满足反思条件。
执行反思:如果满足条件,则调用 run_reflect 函数生成反思内容,并调用 reset_reflection_counter 重置相关计数器。
处理对话结束后的规划和备忘:当对话结束时间接近当前时间时,生成与对话相关的规划思想和备忘录,并将其添加到人格的记忆中。
触发条件:
1. persona.scratch.importance_trigger_curr(当前重要性计数器)小于等于 0。
2. 且人格的记忆中存在事件或思想(persona.a_mem.seq_event + persona.a_mem.seq_thought 非空)。
"""
if reflection_trigger(persona):
run_reflect(persona)
reset_reflection_counter(persona)
# 检查人格是否处于对话结束状态(chatting_end_time 不为 None)。
# 如果当前时间加上 10 秒等于对话结束时间,则构建对话内容字符串 all_utt,用于后续生成规划思想和备忘录。
if persona.scratch.chatting_end_time:
if persona.scratch.curr_time + datetime.timedelta(0,10) == persona.scratch.chatting_end_time:
all_utt = ""
if persona.scratch.chat:
for row in persona.scratch.chat:
all_utt += f"{row[0]}: {row[1]}\n"
# 生成规划思维
## 1. 获取对话的证据节点 ID(node_id),用于记录该思想的来源。
evidence = [persona.a_mem.get_last_chat(persona.scratch.chatting_with).node_id]
## 2. 调用模型生成规划思想。并重新组织内容格式
planning_thought = generate_planning_thought_on_convo(persona, all_utt)
planning_thought = f"For {persona.scratch.name}'s planning: {planning_thought}"
## 3. 计算思想的创建时间、过期时间、事件三元组(s, p, o)主谓宾、关键词集合。
created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
s, p, o = generate_action_event_triple(planning_thought, persona)
keywords = set([s, p, o])
## 4. 重要性评分和嵌入向量
thought_poignancy = generate_poig_score(persona, "thought", planning_thought)
thought_embedding_pair = (planning_thought, get_embedding(planning_thought))
## 5. 将生成的思想添加到记忆中。
persona.a_mem.add_thought(created, expiration, s, p, o,
planning_thought, keywords, thought_poignancy,
thought_embedding_pair, evidence)
# 生成备忘录
## 1. 调用模型生成备忘录,并格式化内容
memo_thought = generate_memo_on_convo(persona, all_utt)
memo_thought = f"{persona.scratch.name} {memo_thought}"
## 2. 计算备忘录的创建时间、过期时间、事件三元组(s, p, o)、关键词集合。
created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
s, p, o = generate_action_event_triple(memo_thought, persona)
keywords = set([s, p, o])
## 3. 调用模型完成重要性评分和嵌入向量。
thought_poignancy = generate_poig_score(persona, "thought", memo_thought)
thought_embedding_pair = (memo_thought, get_embedding(memo_thought))
## 4. 将生成的备忘录添加到记忆中。
persona.a_mem.add_thought(created, expiration, s, p, o,
memo_thought, keywords, thought_poignancy,
thought_embedding_pair, evidence)
关键变量说明:
- persona.scratch.importance_trigger_curr:
整数,当前剩余的“重要性点数”,用于判断是否触发反思。每当一个事件或思想被认为具有重要性时,这个值会减少。在感知时触发 perceive.perceive
当该值 ≤ 0 时,表示已经积累到足够的“重要性”事件,应触发反思。- persona.scratch.importance_trigger_max:
整数,最大允许的重要性点数,也就是触发一次反思后重置的值。这个值决定了人格的“反思频率”——数值越大,越不容易触发反思。- persona.a_mem.seq_event + persona.a_mem.seq_thought:
列表,人格记忆中的事件和思想列表。确保记忆中存在事件或思想,避免在空记忆时触发反思。
重要性值评估
persona.scratch.importance_trigger_curr减少的逻辑:
def perceive(persona, maze):
// 调用模型评估重要性
event_poignancy = generate_poig_score(persona,
"event",
desc_embedding_in)
// 减少比重
persona.scratch.importance_trigger_curr -= event_poignancy
评估事件重要性:
def generate_poig_score(persona, event_type, description):
# event 和 thought
if event_type == "event" or event_type == "thought":
return run_gpt_prompt_event_poignancy(persona, description)[0]
# chat 评估
elif event_type == "chat":
return run_gpt_prompt_chat_poignancy(persona,
persona.scratch.act_description)[0]
聊天事件重要性评估,调用 GPT 模型评估分数,范围在 1-10 分:
def run_gpt_prompt_chat_poignancy(persona, event_description, test_input=None, verbose=False):
# 1. 模型请求参数
gpt_param = {"engine": "text-davinci-002", "max_tokens": 15,
"temperature": 0, "top_p": 1, "stream": False,
"frequency_penalty": 0, "presence_penalty": 0, "stop": None}
# 2. 提示词模板
prompt_template = "persona/prompt_template/v3_ChatGPT/poignancy_chat_v1.txt" ########
# 3. 生成最终提示词 prompt
prompt_input = create_prompt_input(persona, event_description) ########
prompt = generate_prompt(prompt_input, prompt_template)
# 4. 设置模型输出格式
example_output = "5" ########
special_instruction = "The output should ONLY contain ONE integer value on the scale of 1 to 10." ########
# 5. 调用模型输出评分结果
output = ChatGPT_safe_generate_response(prompt, example_output, special_instruction, 3, fail_safe,
__chat_func_validate, __chat_func_clean_up, True)
if output != False:
return output, [output, prompt, gpt_param, prompt_input, fail_safe]
举个例子:
# 加入调用参数如下
persona.scratch.name = "Isabella Rodriguez"
persona.scratch.get_str_iss() = "Maria Lopez just asked her to meet at the park."
event_description = "Isabella Rodriguez: I can meet you at the park in 10 minutes. Maria Lopez: Great, I'll be there."
# 3 生成提示词
Isabella Rodriguez
Maria Lopez just asked her to meet at the park.
Isabella Rodriguez
Isabella Rodriguez: I can meet you at the park in 10 minutes. Maria Lopez: Great, I'll be there.
On a scale of 1 to 10, how important is this chat to Isabella Rodriguez's planning and decision-making?
The output should ONLY contain ONE integer value on the scale of 1 to 10.
# 5 调用模型输出结果,7 分
(7, [7, prompt, gpt_param, prompt_input, 4])
重要性评估模板
poignancy_chat_v1.txt
!<INPUT 1>!: agent name
!<INPUT 1>!: iss
!<INPUT 2>!: name
!<INPUT 3>!: event description
<commentblockmarker>###</commentblockmarker>
Here is a brief description of !<INPUT 0>!.
!<INPUT 1>!
On the scale of 1 to 10, where 1 is purely mundane (e.g., routine morning greetings) and 10 is extremely poignant (e.g., a conversation about breaking up, a fight), rate the likely poignancy of the following conversation for !<INPUT 2>!.
Conversation:
!<INPUT 3>!
Rate (return a number between 1 to 10):
| 变量名 | 描述 | 示例值 |
|---|---|---|
!<INPUT 0>! | 当前时间戳或上下文描述(用于提供时间背景) | 2023-10-01 14:30:00 |
!<INPUT 1>! | 人格名字(重复两次,可能是格式要求) | Isabella Rodriguez |
!<INPUT 2>! | 人格当前的“关注焦点”(ISS,Importance-Salient Stimuli) | "Maria Lopez just asked her to meet at the park." |
!<INPUT 3>! | 对话内容,格式为 "说话者: 内容",每条记录用换行符分隔 | Isabella Rodriguez: I can meet you at the park in 10 minutes. Maria Lopez: Great, I'll be there. |
- 提供上下文信息:包括时间、人格名字和当前关注焦点。
- 明确评分标准:定义 1~10 的评分范围,并给出示例。
- 提供完整对话内容:供模型分析。
- 要求简洁输出:仅返回一个整数评分。
生成反思内容
generate_memo_on_convo
def run_gpt_prompt_memo_on_convo(persona, all_utt, test_input=None, verbose=False):
# 1. 生成 prompt
gpt_param = {"engine": "text-davinci-002", "max_tokens": 15,
"temperature": 0, "top_p": 1, "stream": False,
"frequency_penalty": 0, "presence_penalty": 0, "stop": None}
prompt_template = "persona/prompt_template/v3_ChatGPT/memo_on_convo_v1.txt" ########
prompt_input = create_prompt_input(persona, all_utt) ########
prompt = generate_prompt(prompt_input, prompt_template)
example_output = 'Jane Doe was interesting to talk to.' ########
special_instruction = 'The output should ONLY contain a string that summarizes anything interesting that the agent may have noticed' ########
# 2. 获取失败标准,返回“...”表示失败
fail_safe = get_fail_safe() ########
# 3. 调用模型,如果失败可以内部重试 3 次
output = ChatGPT_safe_generate_response(prompt, example_output, special_instruction, 3, fail_safe,
__chat_func_validate, __chat_func_clean_up, True)
# 4. 成功,返回结果
if output != False:
return output, [output, prompt, gpt_param, prompt_input, fail_safe]
# ChatGPT Plugin ===========================================================
# 5. 内部重试 3 次依然失败后,换个更好的模型,使用更大的 token 数量继续重试,提示词模板也换了
gpt_param = {"engine": "text-davinci-003", "max_tokens": 50,
"temperature": 0, "top_p": 1, "stream": False,
"frequency_penalty": 0, "presence_penalty": 0, "stop": None}
prompt_template = "persona/prompt_template/v2/memo_on_convo_v1.txt"
prompt_input = create_prompt_input(persona, all_utt)
prompt = generate_prompt(prompt_input, prompt_template)
fail_safe = get_fail_safe()
# 6. 调用模型,这次重试 5 次
output = safe_generate_response(prompt, gpt_param, 5, fail_safe,
__func_validate, __func_clean_up)
return output, [output, prompt, gpt_param, prompt_input, fail_safe]
模板
memo_on_convo_v1.txt
Variables:
!<INPUT 0>! -- All convo utterances
!<INPUT 1>! -- persona name
!<INPUT 2>! -- persona name
!<INPUT 3>! -- persona name
<commentblockmarker>###</commentblockmarker>
[Conversation]
!<INPUT 0>!
Write down if there is anything from the conversation that !<INPUT 1>! might have found interesting from !<INPUT 2>!'s perspective, in a full sentence.
"!<INPUT 3>!
| 变量名 | 描述 | 示例值 |
|---|---|---|
!<INPUT 0>! | 包含完整对话内容的字符串,格式为 "说话者: 内容",每条记录用换行符 \n分隔 | Isabella Rodriguez: 今天天气不错,你想去哪里玩? |
!<INPUT 1>! | 需要记住对话内容的人格名字,对话人 1 | Isabella Rodriguez |
!<INPUT 2>! | 从其视角分析对话内容的人格名字,对话人 2 | Maria Lopez |
!<INPUT 3>! | 用于格式化输出的前缀人格名字 | Isabella Rodriguez |
- 提供完整的对话内容:供模型分析。
- 指定视角:要求模型从特定人格的视角出发,找出对话中值得关注的内容。
- 生成完整句子:确保输出是一个完整的句子,便于后续记忆存储和使用。
函数功能解析
以下是 reflect.py 文件中各个函数的详细功能分析:
1. reflect(persona)
- 功能: 人格反思的主函数。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
- 输出:
- 无直接输出,但会根据反思结果修改人格的记忆和状态。
2. generate_insights_and_evidence(persona, nodes, n=5)
- 功能: 生成与给定节点相关的洞察和证据。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
nodes: 一个节点列表,每个节点包含嵌入键(embedding key)。n: 生成的洞察数量,默认为 5。
- 输出:
- 一个字典,键为生成的洞察,值为与该洞察相关的节点 ID 列表。
3. generate_action_event_triple(act_desp, persona)
- 功能: 生成与动作描述相关的事件三元组(主语、谓语、宾语)。
- 输入:
act_desp: 动作描述,例如 "sleeping"。- persona: 一个 Persona 类的实例,代表当前的人格。
- 输出:
- 一个字符串,表示事件三元组,例如 "🧈🍞"。
4. generate_poig_score(persona, event_type, description)
- 功能: 计算事件、思想或聊天的重要性评分。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
event_type: 事件类型,可以是 "event"、"thought" 或 "chat"。- description: 事件的描述。
- 输出:
- 一个整数,表示重要性评分。
5. generate_planning_thought_on_convo(persona, all_utt)
- 功能: 生成与对话相关的规划思想。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
all_utt: 包含对话内容的字符串。
- 输出:
- 一个字符串,表示生成的规划思想。
6. generate_memo_on_convo(persona, all_utt)
- 功能: 生成与对话相关的备忘录。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
all_utt: 包含对话内容的字符串。
- 输出:
- 一个字符串,表示生成的备忘录。
7. run_reflect(persona)
- 功能: 运行实际的反思过程。首先需要检查出发条件是否满足,如果满足则开始 reflect 并重置相关的计数器。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
- 输出:
- 无直接输出,但会将生成的思想保存到人格的记忆中。
8. reflection_trigger(persona)
- 功能: 判断是否需要运行一次新的反思。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
- 输出:
- 一个布尔值,表示是否需要运行新的反思。
9. reset_reflection_counter(persona)
- 功能: 重置用于触发反思的计数器。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
- 输出:
- 无直接输出,但会修改人格的内部状态。
10. generate_focal_points(persona, n=3)
- 功能: 生成焦点(focal points),这些焦点用于后续的反思过程。
- 输入:
- persona: 一个 Persona 类的实例,代表当前的人格。
n: 生成的焦点数量,默认为 3。
- 输出:
- 一个包含焦点的列表,每个焦点是一个字符串。