亚马逊 Bedrock 的代理现在支持内存保留和代码解释预览新闻博客
亚马逊 Bedrock 的代理现已支持内存保留和代码解读预览
by Danilo Poccia 于 2024 年 7 月 10 日发布在 亚马逊 Bedrock, 亚马逊机器学习,公告,人工智能,AWS 峰会纽约,精选,生成式 AI,发布,新闻 永久链接 评论 分享
关键要点 亚马逊 Bedrock 的代理现已支持内存保留,能够跨多次交互保持用户的对话历史,从而提供个性化的用户体验。 新增的代码解读功能使代理能够安全动态生成和执行代码片段,从而应对复杂任务,如数据分析和文本处理。
通过 亚马逊 Bedrock 的代理,生成式人工智能AI应用程序可以在不同的系统和数据源之间执行多步骤任务。几个月前,我们 简化了代理的创建和配置 过程。今天,我们在预览中推出了两项新的完全托管能力:
内存保留跨多次交互
现在,代理可以保留与每位用户对话的总结,从而提供流畅、适配性强的体验,尤其是在复杂的多步骤任务如用户交互和企业自动化解决方案,诸如预定航班或处理保险索赔中尤为明显。
支持代码解读
代理现在可以在一个安全的沙盒环境中动态生成和运行代码片段,能够处理复杂的用例,如数据分析、数据可视化、文本处理、解决方程和优化问题。为了便于使用此功能,我们还增加了直接上传文档到代理的能力。
让我们详细看看这些新功能是如何工作的。
跨多次交互的内存保留
借助内存保留,你可以构建能够随着时间推移而学习和适应每位用户独特需求和偏好的代理。通过维护持久化内存,代理能够在用户离开时接着继续,提供更流畅的对话和工作流程,这对复杂的多步骤任务尤其有效。
想象一下,一个用户在预定航班。得益于内存保留功能,代理能够学习他们的旅行偏好,并利用这些知识来简化后续的预订请求,从而创造个性化和高效的体验。例如,它可以自动向用户推荐合适的座位或类似于他们之前选择的餐食。
利用内存保留提高上下文感知能力也简化了业务流程的自动化。例如,企业用来处理客户反馈的代理可以意识到与同一客户的先前和正在进行的交互,而无需处理自定义集成。
每个用户的对话历史和上下文会在唯一的内存标识符ID下安全存储,确保用户之间的完全分离。通过内存保留,构建能够提供无缝适配和个性化体验的代理更为简单,这些体验会随着时间的推移而不断改善。让我们看看如何在实践中使用这项功能。
在亚马逊 Bedrock 代理中使用内存保留
在 亚马逊 Bedrock 控制台 中,我选择导航窗格的 构建工具 部分中的 代理 并开始创建代理。
对于代理,我使用 agentbookflight 作为名称,描述如下:
帮助预定航班。
然后,在代理构建器中,我选择 Anthropic 的 Claude 3 Sonnet 模型,并输入这些指令:
要预定航班,您需要知道出发和到达机场及航班的起飞日期和时间。
在 附加设置 中,我启用 用户输入,允许代理提出澄清性问题以捕获必要输入。这将在请求预定航班时对缺少必要信息如出发地和目的地或航班的日期和时间时提供帮助。
在新的 内存 部分,我启用内存以在每次会话结束时生成和存储会话摘要,并将内存持续时间设置为默认的 30 天。
然后,我添加一个操作组以搜索和预定航班。我使用 searchandbookflights 作为名称,描述如下:
在指定日期之间搜索航班并预定特定航班。
接着,我选择定义操作组的功能细节,然后创建一个新的 Lambda 函数。这个 Lambda 函数将实现此操作组中所有功能的业务逻辑。
我向这个操作组添加两个函数:一个用于搜索航班,另一个用于预定航班。
第一个函数是 searchforflights,描述如下:
在给定日期之间搜索航班。
这个函数的所有参数都是必需的,并且类型为字符串。以下是参数的名称和描述:
参数名描述originairport出发地 IATA 机场代码destinationairport目的地 IATA 机场代码date航班的日期,格式为 YYYYMMDD第二个函数是 bookflight,描述如下:
在给定日期和时间之间预定两个目的地之间的航班。
同样,所有参数都是必需的,类型为字符串。以下是参数的名称和描述:
参数名描述originairport出发地 IATA 机场代码destinationairport目的地 IATA 机场代码date航班的日期,格式为 YYYYMMDDtime航班的时间,格式为 HHMM完成代理的创建后,我选择 创建。
要访问 Lambda 函数的源代码,我选择 searchandbookflights 操作组,然后点击 查看靠近 选择 Lambda 函数 设置。通常,我将使用这个 Lambda 函数与现有系统例如旅行预定平台进行集成。在这种情况下,我使用这段代码来模拟代理的预定平台。
pythonimport jsonimport randomfrom datetime import datetime time timedelta
def convertparamstodict(paramslist) paramsdict = {} for param in paramslist name = paramget(name) value = paramget(value) if name is not None paramsdict[name] = value return paramsdict
def generaterandomtimes(datestr numflights minhours maxhours) #基于输入日期设置种子 seed = int(datestr) randomseed(seed)
# 将最小小时和最大小时转换为分钟minminutes = minhours 60maxminutes = maxhours 60# 生成随机时间randomtimes = set()while len(randomtimes) lt numflights minutes = randomrandint(minminutes maxminutes) hours mins = divmod(minutes 60) timestr = f{hours02d}{mins02d} randomtimesadd(timestr)return sorted(randomtimes)def getflightsfordate(date) numflights = randomrandint(1 6) # 每天随机生成 1 到 6 个航班 minhours = 6 # 6am maxhours = 22 # 10pm flighttimes = generaterandomtimes(date numflights minhours maxhours) return flighttimes
def getdaysbetween(startdate enddate) # 将字符串日期转换为 datetime 对象 start = datetimestrptime(startdate Ymd) end = datetimestrptime(enddate Ymd)
# 计算日期之间的天数delta = end start# 生成开始和结束之间的所有日期列表包括datelist = [start timedelta(days=i) for i in range(deltadays 1)]# 将 datetime 对象转换回 YYYYMMDD 字符串格式return [datestrftime(Ymd) for date in datelist]
def lambdahandler(event context) print(event) agent = event[agent] actionGroup = event[actionGroup] function = event[function] param = convertparamstodict(eventget(parameters []))
if actionGroup == searchandbookflights if function == searchforflights flighttimes = getflightsfordate(param[date]) body = f在 {param[date]}YYYYMMDD,这些是从 {param[originairport]} 到 {param[destinationairport]} 的航班:n{jsondumps(flighttimes)} elif function == bookflight body = f航班从 {param[originairport]} 到 {param[destinationairport]},日期为 {param[date]}YYYYMMDD,时间为 {param[time]}HHMM已预定并确认。 elif function == getflightsindaterange days = getdaysbetween(param[startdate] param[enddate]) flights = {} for day in days flights[day] = getflightsfordate(day) body = f这是从 {param[originairport]} 到 {param[destinationairport]} 在 {param[startdate]}YYYYMMDD和 {param[enddate]}YYYYMMDD期间的所有航班时间HHMM以 JSON 格式呈现:n{jsondumps(flights)} else body = f未知的函数 {function},对应的操作组是 {actionGroup}。else body = f未知的操作组 {actionGroup}。# 格式化输出为代理所期望的格式responseBody = { TEXT { body body }}actionresponse = { actionGroup actionGroup function function functionResponse { responseBody responseBody }}functionresponse = {response actionresponse messageVersion event[messageVersion]}print(f响应 {functionresponse})return functionresponse我准备好在控制台上测试代理,并问这个问题:
从伦敦希思罗机场到罗马菲乌米奇诺机场在 2024 年 7 月 20 日有哪些航班?
代理回复我一个航班时间的列表。我选择 显示追踪 来获取关于代理处理我指令的更多信息。
在 追踪 选项卡中,我查看追踪步骤,以理解代理的调度使用的思维链。例如,我看到代理在调用 Lambda 函数之前,处理了机场名称到代码伦敦希思罗的 LHR,罗马菲乌米奇诺的 FCO的转换。
在新的 内存 选项卡中,我查看内存的内容。控制台使用了特定的测试内存 ID。在应用程序中,为了保持用户间内存的分离,我可以为每位用户使用不同的内存 ID。
我看到航班列表,并要求预定一班:
预定 602pm 的航班。
代理回复确认了预定。
几分钟后,在会话过期后,我在 内存 选项卡中看到了我的对话摘要。
我选择扫帚图标重新开始并问一个问题,这个问题本身并未提供完整的上下文给代理:
我航班当天还有哪些其他航班?
代理回忆起我在之前对话中预定的航班。为给出答案,代理要求我确认航班详情。请注意,Lambda 函数只是模拟,并未在任何数据库中存储预定信息。航班详情是从代理的内存中获取的。
我确认这些值后,得到当天同样出发地和目的地的其他航班列表。
为了更好地演示内存保留的好处,让我们使用 AWS SDK for Python (Boto3)。为此,我需要先创建一个代理别名和版本。由于在调用代理时需要代理 ID 和别名 ID,我将它们写下来。
在调用代理时,我添加新的 memoryId 选项来使用内存。通过包含此选项,我得到两个好处:
云梯npv加速器代理可以使用该 memoryId 所保留的内存来改善响应如果有的话。当前会话的对话摘要将被保留在该 memoryId 下,以便在另一次会话中使用。使用 AWS SDK,我还能获取特定 memoryId 的内存内容或删除内存内容。
pythonimport randomimport stringimport boto3import json
DEBUG = False # 启用调试以查看所有追踪步骤DATEFORMAT = Ymd HMS
AGENTID = URSVOGLFNXAGENTALIASID = JHLX9ERCMD
SESSIONIDLENGTH = 10SESSIONID = join( randomchoices(stringasciiuppercase stringdigits k=SESSIONIDLENGTH))
为每个用户提供唯一标识符
MEMORYID = danilop92f79781a3f341928de6890b67c63d8b bedrockagentruntime = boto3client(bedrockagentruntime regionname=useast1)
def invokeagent(prompt endsession=False) response = bedrockagentruntimeinvokeagent( agentId=AGENTID agentAliasId=AGENTALIASID sessionId=SESSIONID inputText=prompt memoryId=MEMORYID enableTrace=DEBUG endSession=endsession )
completion = for event in responseget(completion) if DEBUG print(event) if chunk in event chunk = event[chunk] completion = chunk[bytes]decode()return completion
def deletememory() try response = bedrockagentruntimedeleteagentmemory( agentId=AGENTID agentAliasId=AGENTALIASID memoryId=MEMORYID ) except Exception as e print(e) return None if DEBUG print(response)
def getmemory() response = bedrockagentruntimegetagentmemory( agentId=AGENTID agentAliasId=AGENTALIASID memoryId=MEMORYID memoryType=SESSIONSUMMARY ) memory = for content in response[memoryContents] if sessionSummary in content s = content[sessionSummary] memory = f会话 ID {s[sessionId]} 从 {s[sessionStartTime]strftime(DATEFORMAT)} 到 {s[sessionExpiryTime]strftime(DATEFORMAT)}n memory = s[summaryText] n if memory == memory = lt无内存gt return memory

def main() print(删除内存? (y/n)) if input() == y deletememory()
print(内存内容:)print(getmemory())prompt = input(gt )if len(prompt) gt 0 print(invokeagent(prompt endsession=False)) # 开始新会话 invokeagent(end endsession=True) # 结束会话
if name == main main()
我从我的笔记本电脑上运行 Python 脚本。我选择删除当前的内存尽管现在应该是空的,然后要求在特定日期预订一班早航班。
删除内存? (y/n)y内存内容:lt无内存gtgt 在 2024 年 7 月 20 日从 LHR 到 FCO 预定一个早上的航班。我已为您预定 2024 年 7 月 20 日从伦敦希思罗LHR往返罗马菲乌米奇诺FCO的早午航班,时间为 0644。
几分钟后我再次运行脚本。这个脚本每次运行时都会创建新的会话。在这种情况下,我并没有删除内存,看到与同