极客时间已完结课程限时免费阅读

22|提示语工程(四):如何让智能体学会反思过去和规划未来?

22|提示语工程(四):如何让智能体学会反思过去和规划未来?-AI大模型系统实战-极客时间
下载APP

22|提示语工程(四):如何让智能体学会反思过去和规划未来?

讲述:Tyler

时长14:37大小13.35M

你好,我是 Tyler。
上节课,我们学习了记忆流的基本原理和实现方法。记忆流的本质是通过类似“注意力”机制的方法,大幅提升智能体的回忆效率,帮助智能体存储和检索经历过的记忆事件。
但是,在智能体使用记忆流原始观测记录时,很难进行跨时空的概括或推理。比方说,让 Klaus Mueller 判断他和谁的关系最好时,他会直接选择和他互动最频繁的那个人,而忽略了他们之间的社交关系和对话质量。
带着这个问题,这节课,我们将会继续学习斯坦福这篇具身智能的社会实验论文。我们将结合记忆流的概念,为我们创造的“生命”设计“反思”和“计划”能力,让它能够像人类一样生活和“思考”。

反思能力

针对记忆流回忆能力的局限性,论文中引入了第二种类型的记忆,这种记忆的名字叫反思(reflection)。反思区别于观察事件记忆,它是一种更高层次的认知能力,包括对经验的分析、评价和总结。
反思能力是人类认知的重要组成部分,它可以帮助人类从错误中吸取教训,从而提高学习和适应能力。
为了让你的智能体拥有反思能力,反思的结论也会成为记忆流中的记忆,帮助智能体更好地理解和处理未来的事件。这样智能体在“回忆”的时候,同样会把反思的结果检索出来。
论文中的反思机制也受到了人类反思行为的启发。人类会在各种时间和情况下进行反思,比如在深夜,或是读到了一本特别的书。周期性的反思,可以帮智能体定期去总结经验,而随机性的反思呢,可以帮智能体更好地去应对突发事件。
在论文的方案中,智能体每天会反思大约两到三次。接下来我们就一探究竟,学习如何通过提示语工程让大语言模型驱动的智能体进行反思。

反思主题

首先,智能体需要确定反思主题。它会根据最近的生活经历生成一系列候选问题,并使用这些问题来检索记忆流。在论文中,智能体在反思时提取最近事件的描述,并询问大语言模型:“上述描述中的 x 个最突出的高级别洞察是什么?”
代码中的提示词模板是后面这样的。
insight_and_evidence_v1.txt
Variables:
!<INPUT 0>! -- Numbered list of event/thought statements
!<INPUT 1>! -- target persona name or "the conversation"
<commentblockmarker>###</commentblockmarker>
Input:
!<INPUT 0>!
What !<INPUT 1>! high-level insights can you infer from the above statements? (example format: insight (because of 1, 5, 3))
1.
在运行代码时,模型将生成一些候选问题,例如:“Klaus Mueller 热衷于什么话题?”或“Klaus Mueller 和 Maria Lopez 的关系是什么?”。
这些生成的问题将用作检索的查询,来收集相关的记忆事件(包括其他反思),这些记忆事件将作为反思的依据。

生成感悟

获得这些记忆事件以后,智能体就会使用语言模型分析相关的记忆事件,生成反思。后面这句陈述很接近最终生成的反思。
“Klaus Mueller 全身心投入在他对城市化的研究上(因为记忆事件 1、2、8、15)”
接下来,我们只需要在记忆流中存储这类陈述,并把它们相关的记忆事件连接起来即可。

存储反思

这样不断把反思存储在记忆流中,并建立反思与相关记忆的连接,便可以逐渐生成一颗智能体的反思树。树的叶子节点代表观察事件记忆,而非叶子节点代表反思记忆,随着节点高度的增加,它所代表的思维,也变得更加抽象和高级。
反思树的样式你可以参考文稿里的图片。
这本质上也是一种记忆聚合的方式,是重要的提示语压缩方法,你务必重点掌握

代码实现

掌握了反思主题和生成感悟的过程,我们来看看具体代码实现。老规矩,我还是精选了代码的关键部分。
首先我们要做的就是生成反思所需的焦点记忆事件。
# 反思需要特定的焦点记忆事件。首先获取这些焦点记忆事件。
focal_points = generate_focal_points(persona, 3)
当运行 def run_reflect(persona) 函数时,它将执行一系列操作来实现反思。默认情况下,我们会获取 3 个焦点记忆事件,这些事件代表智能体应该反思的关键事件或想法。
接下来就是检索与每个焦点记忆事件相关的节点对象。
# 检索与每个焦点相关的节点对象。
# <retrieved> 包含焦点的键和关联的节点。
retrieved = new_retrieve(persona, focal_points)
这个方法我们在上节课中已经学习过,返回的这些节点对象代表了与反思主题相关的记忆事件,它们存储在 retrieved 字典中。其中每个键都是一个焦点记忆事件,对应的值是记忆节点对象。
在每次迭代中,代码都会获取与当前焦点相关的记忆节点对象,并提取它们的嵌入表示。
# 为每个焦点事件生成反思并保存到智能体的记忆中。
for focal_pt, nodes in retrieved.items():
接下来,我们要做的是生成当前焦点记忆事件的反思,并且得到这些反思的相关记忆线索,具体调用的函数是 generate_insights_and_evidence。
thoughts = generate_insights_and_evidence(persona, nodes, 5)
然后我们要为每个生成的想法设置创建时间和过期时间,确保它们的有效期。
for thought, evidence in thoughts.items():
created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
接着调用 generate_action_event_triple 函数,为当前想法生成包含主语、谓语和宾语的事件三元组,并创建关键词集合。
s, p, o = generate_action_event_triple(thought, persona)
keywords = set([s, p, o])
然后调用 generate_poig_score 函数,计算当前想法的情感辛酸程度(后续为了方便我们简称为情绪值),最低为 1 分,最高为 10 分,同时获取想法的嵌入表示。这里举个例子,比如:“我在早上吃了一顿早餐” 这句话就几乎没有什么情绪,而 “我的早餐被偷吃了,我很生气!” 这句话的情绪值就很高。
thought_poignancy = generate_poig_score(persona, "thought", thought)
thought_embedding_pair = (thought, get_embedding(thought))
最后,还需要把生成的反思结果保存起来,具体就是将生成的反思还有相关信息添加到智能体的记忆中,包括创建时间、有效期、事件三元组、关键词、情绪值、嵌入表示以及相关线索。
# Add the generated thought to the agent's memory
persona.a_mem.add_thought(created, expiration, s, p, o,
thought, keywords, thought_poignancy,
thought_embedding_pair, evidence)
通过这个过程,生成的反思结果被保存在智能体的记忆中,以便在后续对话中使用。

规划能力

如果仅仅依据当前情境信息生成行为,很可能会导致智能体出现不合理或不连贯的行为,比如智能体可能经常一天吃两次午饭。所以,除了日常的生活和偶尔的反思之外,一般而言,智能体需要在更长的时间轴上进行规划,以确保其行为序列的一致性和可信度
这时候就需要我们引入规划能力,它描述了智能体未来的行为序列,这个行为序列可以帮助它在时间维度上保持行为的一致性。一个规划包含地点、开始时间和持续时间。
智能体规划的方法可以分为两步,分别是全天规划和细节规划。

全天规划

第一步就是粗略地制定一个当天行程的计划。通常这个计划会描述智能体未来的一系列动作,包括位置、开始时间和持续时间。
具体来说,创建初始计划时,我们需要告诉大语言模型,该智能体历史描述,例如姓名、特征及其最近经历的摘要还有他们前一天的摘要来提示大语言模型,帮助 LLM 更好地生成新的规划。文稿后面是一个完整的示例提示,你可以看一下。其中包含了 Eddy Lin 的个人情况以及他在 2 月 12 日的记忆摘要。
姓名: Eddy Lin (age: 19)
性格:善良、外向、好客
Eddy Lin 是一个在  Oak Hill 大学攻读音乐理论与作曲的学生。他热衷于探索不同的音乐风格,并且一直想方设法提升知识水平。Eddy Lin 正在为他的大学班级创作。同时,他也积极上课以学习音乐理论。Eddy Lin 对他正在创作的新作品感到非常兴奋,并希望在接下来的几天里花更多的时间来创作它。
在 2 月 12 日,Eddy(1)早上 7:00 醒来并完成了例行事务,[ … ](6)在晚上 10 点左右准备入睡
今天是 2 月 13 日周三。这里是 Eddy 今天粗略的规划:(1)
之后,我们将会得到该智能体一天规划的大致构想,可以分为 5 到 8 条。
(1)在早上 8 点起床并完成晨间例事
(2)去 Oak Hill 大学然后从 10 点开始上课
[ … ]
(5)从下午 1 点到 5 点创作他的新音乐作品
(6)在下午 5.30 吃晚饭
(7)完成学校的作业然后在晚上 11 点上床睡觉
与反思一样,计划也存储在智能体的记忆流中,这允许智能体在回忆时同时参考观察、反思和计划的记忆。智能体可以根据需要随时更改其计划。

细节规划

第二步,递归地拆分计划,生成更详细的行动。
智能体先将这一粗粒度的全天规划存储在记忆流中,然后递归地将其拆解为更详细的行动。首先,按小时做拆解,例如,“从下午 1 点到 5 点创作他的新音乐作品”的计划可以像这样拆解。
下午 1:00: 从对音乐创作进行头脑风暴开始
[ … ]
下午 4:00: 短暂的休息一下并且在检查和润色他的作品之前恢复一下创造力
然后,智能体还会进一步将上述计划细化为 5 到 15 分钟的级别,示例如下。
下午 4:00: 吃点零食,比如水果、燕麦棒或者坚果
下午 4:05: 在工作地附近逛一逛
[ … ]
下午 4:50: 花几分钟时间收拾一下工作区
这个过程是可以根据需要进行调整的,以匹配所需的粒度。
总的来说,我们会根据智能体的摘要性描述(如姓名、特征和最近经历的概括)以及前一天的摘要生成一个初始的计划,然后递归地将该计划拆解为更详细的行动。

代码实现

接下来,让我们来看看规划能力的代码实现。
首先,规划会从智能体“清晨醒来”的时刻开始。我们通过调用 generate_wake_up_hour 函数创建智能体的起床时间。
def _long_term_planning(persona, new_day):
"""
Formulates the persona's daily long-term plan if it is the start of a new
day. This basically has two components: first, we create the wake-up hour,
and second, we create the hourly schedule based on it.
INPUT
new_day: Indicates whether the current time signals a "First day",
"New day", or False (for neither). This is important because we
create the personas' long term planning on the new day.
"""
# We start by creating the wake up hour for the persona.
wake_up_hour = generate_wake_up_hour(persona)
接着,检查 new_day 参数的值,以确定今天是该智能体的出生日还是普普通通的新一天。
# When it is a new day, we start by creating the daily_req of the persona.
# Note that the daily_req is a list of strings that describe the persona's
# day in broad strokes.
if new_day == "First day":
# Bootstrapping the daily plan for the start of then generation:
# if this is the start of generation (so there is no previous day's
# daily requirement, or if we are on a new day, we want to create a new
# set of daily requirements.
persona.scratch.daily_req = generate_first_daily_plan(persona,
wake_up_hour)
elif new_day == "New day":
revise_identity(persona)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TODO
# We need to create a new daily_req here...
persona.scratch.daily_req = persona.scratch.daily_req
如果 new_day 参数对应“第一天”,则调用 generate_first_daily_plan 函数生成新的每日需求列表,并将其存入 persona.scratch.daily_req,daily_req 中存储了该智能体一天中计划要完成的事项。如果是“新的一天”,则调用 revise_identity 函数切换到当前智能体的上下文,并且保持它自己原有的计划继续执行。
下一步就是规划智能体的每日时刻表,具体会基于每日计划列表(daily_req),使用 generate_hourly_schedule 函数。这个时间表是一个包含任务项和持续时间(以分钟为单位)的列表,总和为 24 小时,并将其赋给 persona.scratch.f_daily_schedule_hourly_org。
# Based on the daily_req, we create an hourly schedule for the persona,
# which is a list of todo items with a time duration (in minutes) that
# add up to 24 hours.
persona.scratch.f_daily_schedule = generate_hourly_schedule(persona,
wake_up_hour)
persona.scratch.f_daily_schedule_hourly_org = (persona.scratch
.f_daily_schedule[:])
接下来,将每日计划添加到记忆中。具体来说,我们需要把计划的各种属性存储到记忆流中,这些属性包括创建日期、到期日期、主体、动作、客体、关键词、情绪值和嵌入对。
thought = f"This is {persona.scratch.name}'s plan for {persona.scratch.curr_time.strftime('%A %B %d')}:"
for i in persona.scratch.daily_req:
thought += f" {i},"
thought = thought[:-1] + "."
created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
s, p, o = (persona.scratch.name, "plan", persona.scratch.curr_time.strftime('%A %B %d'))
keywords = set(["plan"])
thought_poignancy = 5
thought_embedding_pair = (thought, get_embedding(thought))
persona.a_mem.add_thought(created, expiration, s, p, o,
thought, keywords, thought_poignancy,
thought_embedding_pair, None)
以上便是计划模块中的核心逻辑代码。现在我们已经知道了智能体规划能力背后的秘密,不过,像人一样,生活在复杂社会环境中的智能体也需要根据外部环境的变化而不断调整规划,我们这就来学习这个方法。

临时规划

当然,智能体在必要的时候会中途改变它们的规划。这意味着智能体可以根据环境的变化或自身的目标和需求来调整规划。
我举个例子来帮你理解。如果艺术家 John Lin  正在画画,他看到画架时并不需要作出特别的反应,还是会按自己的原有规划继续行动。
但是,如果一个作为父亲的  John Lin  看到他的儿子在自家花园散步,那么情况就不一样了,他会调动自己的记忆(根据二者的关系和他们的状态构造查询,在记忆中进行检索并总结)。
如果他刚好想起儿子正在为他的班级创作音乐,很可能给出“父亲考虑向儿子询问关于他的音乐创作项目的事情”的想法。这时,John Lin 就会从这个反应发生的时间开始,重新生成智能体此刻后的规划。
前面的视角都是从单个或两个智能体的微观角度观察的,接下来我们聊聊论文里更宏观层面的发现。
如上图所示,斯坦福研究人员利用观察、记忆、反思、计划构成的思维闭环,在 AI 小镇中开展了社会实验。在实验中,他们观察到了涌现出的智能体之间的“社会行为”,例如信息传播、关系形成和协作。
具体而言,作者测量了两条特定信息在游戏中的传播情况。在模拟开始前,知道 Sam 参选市长的智能体的比例为 4%,知道 Isabella 举办情人节派对的智能体比例为 4%。在模拟结束后,这两个比例分别增长到 32% 和 48%。这表明,在没有用户干预的情况下,智能体能够将信息传播给其他智能体。
为了衡量关系的形成情况,作者还测量了智能体是否知道其他智能体的存在。在模拟开始前,智能体之间的关系密度为 0.167。在模拟结束后,关系密度增长到 0.74。这表明,在模拟过程中,智能体之间形成了新的联系。
此外,作者还在 Isabella 组织的情人节派对中研究了智能体协作的情况。在派对开始前,Isabella 邀请宾客、收集物料并寻求帮助以装修咖啡馆。在情人节这天,12 个受邀的智能体中有 5 个出现在了派对上。这表明,智能体能够在集体活动中相互协作。
至此,智能体具备了完备的记忆能力,反思能力和规划能力,并已经可以依靠这些能力形成了小型的稳定社会关系了。

总结

我们来做个总结吧,这节课我们学习了如何为智能体设计反思和规划的能力,并且了解了论文里社会实验中观测到的一些有价值的现象。
但是,你可能会问,这个人具身智能实验的意义是什么呢?这是很好的问题,这个实验的价值其实和最初人们在 GPT 上的尝试是一样的,都是为了观察到“涌现”的产生。多智能体的社会中会涌现出新的社会现象,就像海量的数据和神经元可以涌现智能一样。
更进一步讲,目前社会上几乎所有的风险投资资源都在向大模型领域聚拢,看似造成了巨大的社会资源浪费,但是这个过程其实也是大模型杀手级应用(killer App)的涌现过程。当年的短视频和 O2O 其实也是移动互联网热钱催生下所涌现出的新业态。
所以,基于多智能体架构的新型互联网应用生态下也会涌现出新的商业模式和社会现象,这也是这一波人工智能浪潮的魅力所在。只有认同了这个逻辑,你才能意识到这篇论文和这一轮技术革命的价值。
你可以想一下,如果你现在所有使用的互联网 App 都成为了一个智能体,在未来,他们之间通过大语言模型对话,共同为你服务时,将是一番什么景象。

思考题

通过修改配置和代码,将我们在前几节课学到的提示语方法用在人工智能小镇的智能体上。
恭喜完成我们第 22 次打卡学习,期待你在留言区和我交流互动。如果你觉得有收获,也欢迎你分享给你身边的朋友,邀 TA 一起讨论。

本文介绍了如何让智能体学会反思过去和规划未来的技术方法。通过提示语工程让大语言模型驱动的智能体进行反思,包括确定反思主题、生成感悟、存储反思等步骤。同时,文章介绍了规划能力的重要性,以及全天规划和细节规划的方法。通过代码实现展示了规划能力的核心逻辑。此外,作者还在社会实验中观察到了智能体之间的“社会行为”,包括信息传播、关系形成和协作。这些发现表明,智能体具备了完备的记忆能力、反思能力和规划能力,并已经可以依靠这些能力形成了小型的稳定社会关系。最后,文章指出基于多智能体架构的新型互联网应用生态下也会涌现出新的商业模式和社会现象,展示了这一波人工智能浪潮的潜力和魅力。

分享给需要的人,Ta购买本课程,你将得18
生成海报并分享
2023-10-09

赞 7

提建议

上一篇
国庆策划|赛程过半,检验一下你的学习成果吧!
下一篇
23|提示语工程(五):构建你自己的西部世界——AI小镇搭建实战
unpreview
 写留言

全部留言(3)

  • 最新
  • 精选
  • 顾琪瑶
    2023-10-09 来自上海
    今天这篇文章内容量还是很大的, 需要多看几遍回味回味了. 感觉agent的设计思路大于技术上的实现难度(在有可靠大模型基座的前提下) 需要考虑多agent系统如何确保各agent之间的闭环路径, 不仅仅需要强劲的prompt能力, 还需要带有"造物主"视角的设计能力
    5
  • 💖李京潼💖
    2024-05-14 来自北京
    直接从每个人的视角出发就好了,我们就是智能体,终极目标就是马斯克所说的:人活在真实世界几率,或不到十亿分之一。虚拟人间,放回推就好了
    1
  • l_j_dota_1111
    2024-02-05 来自天津
    智能体还是需要大模型作为底层支撑,因此在垂直领域的智能体还是要训练垂直领域的大模型,才能完成相关任务的规划和推理。
    1