Google 再次通过为 Vertex AI 和 Google AI Studio 用户推出 Gemini 上下文缓存功能,推动了人工智能创新的边界。这一开创性功能充分发挥了 Gemini 巨大上下文窗口的全部潜力,允许用户轻松缓存庞大的复杂数据,包括法律文件、医疗记录、长视频、图像和音频文件等。通过消除每次提问时重复发送这些内容的必要,上下文缓存显著降低了成本,提升了用户体验,为高效和强大的人工智能应用铺平了道路。
我们不妨花点时间再看看那些重要的突破,正是这些突破让 Gemini 成为人工智能创新领域的领头羊。
- 2017年6月:Transformer
- ……….
- 2023年12月:多模态Gemini
- 2024年2月:1百万上下文(现为2百万)和1千万实验室版本——无限制,正如研究论文中所述……换句话说:谷歌解决了这一难题,而许多人认为这是无法解决的Transformer架构限制
- 2024年5月:缓存上下文
想象一下,上下文缓存就像给 Gemini 背的一个背包。之前,当你在视频、代码仓库、大量文档等地方提问复杂问题时,你不得不背着一个装满这些信息的大包。这既繁琐又费时,就像每次想要查找一个事实时,都得拖着一本巨大的百科全书那样繁琐。
不过现在,有了上下文缓存功能,Gemini 只需要你把那个包放在一个方便的地方,这样就好。
跟着我看看实际操作。我将使用Gemini的上下文缓存特性来询问来自整个代码库的问题。通过提供整个Git仓库的访问给Gemini,我们为Gemini提供了您项目代码的完整上下文。这种深入的上下文理解使Gemini能够分析代码并在多个文件之间连接依赖关系。
我们将在Vertex AI Colab上进行编码,要跟着做,请打开一个新的空笔记本。
首先我们需要的是一个库来帮助我们用Python克隆代码库。我将使用这个GitPython:
安装GitPython包
python
!pip install GitPython
这里有一个函数可以克隆一个 Git 仓库,并创建一个文件路径及其内容的字典。这种方法对于较小的仓库来说效果不错,但如果面对的是一个庞大的代码库,也不必担心!Gemini 可以帮助你重新设计方案以提高内存效率——只需提出需求即可!
import git
import os
def list_and_read_repo_files(repo_url, branch="main"):
"""
克隆一个Git仓库,列出所有文件(不包括.git文件夹及其内容),并读取其内容。
返回一个字典,键为文件路径,值为文件内容。
参数:
repo_url (str): Git仓库的URL。
branch (str, optional): 要切换的分支。默认为"main"。
返回:
dict: 键为文件路径,值为文件内容的字典。
"""
try:
# 用于克隆的临时目录
repo_dir = "temp_repo"
# 克隆仓库
print(f"从{repo_url}克隆仓库...")
git.Repo.clone_from(repo_url, repo_dir, branch=branch)
print("克隆完成!")
file_contents = {}
for root, _, files in os.walk(repo_dir):
for file in files:
# 排除包含.git的文件夹及其内容
if ".git" not in root:
file_path = os.path.join(root, file)
try:
with open(file_path, "r", encoding="utf-8") as f:
file_contents[file_path] = f.read()
except Exception as e: # 捕获任何未预料的错误
print(f"出现意外错误: {e}")
return file_contents
except git.exc.GitCommandError as e:
print(f"克隆仓库错误: {e}")
except UnicodeDecodeError as e:
print(f"字符解码错误: {e}")
except Exception as e: # 捕获任何未预料的错误
print(f"出现意外错误: {e}")
finally:
# 清理临时仓库目录
if os.path.exists(repo_dir):
git.rmtree(repo_dir)
那我们就来运行一下吧。我会下载最近谷歌DeepMind开源的一个项目的代码仓库。这个项目叫做OneTwo。
repo_url = "https://github.com/google-deepmind/onetwo.git" # 请根据实际情况替换此URL
branch = "main" # 如果分支名不同,请更改
file_data = list_and_read_repo_files(repo_url, branch)
接下来要讲的是,我们将深入克隆的OneTwo仓库,并创建一个包含所有文件内容的单一文本文件。为了方便Gemini处理这些文件,每个文件的内容都将被包装在一个XML“信封”中。这将给我们提供一个结构化的纯文本文件,包含了许多XML条目,准备好供Gemini分析。
<文件>
...内容...
</file>
如果 file_data 存在:
output_file = 'fullcode.text'
with open(output_file, "w", encoding="utf-8") as outfile:
for file_path, content in file_data.items():
outfile.write(f"<路径:{file_path}>")
outfile.write(f"{content}")
outfile.write(
如果一切顺利,你应该在当前工作目录中找到一个名为 fullcode.text
的文件。此文件包含来自 OneTwo 仓库中的所有代码,并且这些代码已经用 XML 标签整齐地组织好了。如果在过程中遇到任何问题,请不要犹豫向 Gemini 寻求帮助进行故障排查,我们可以解决所有问题后继续前进。
让我们看一下我们刚创建的 fullcode.text
文件。
文件的字节大小 = os.path.getsize(output_file)
print(f"文件大小:{文件的字节大小/1024/1024} MB")
对于上下文缓存来说,关键是看它是否超过10MB。如果超过10MB,我们就需要从Google云存储加载到缓存中。
现在我们已经准备好直奔主题去了:缓存上下文。
这里是一些关于上下文缓存的常见问题及其答案,这些问题你可能经常会遇到。
- 你必须先创建上下文缓存,才能使用它。
缓存存储于创建缓存的同一区域,你创建的上下文缓存数据包含大量数据,这些数据可以在向 Gemini 模型发出的多个请求中重复使用。
- 缓存的内容可以是Gemini支持的任何MIME类型的多模态内容。
- 您可以通过指定一个blob、文本或存储在Cloud Storage存储桶中的文件来指定要缓存的内容。
- 缓存的内容具有有限的生命周期。默认情况下,缓存会在创建后60分钟后过期。
- 您可以在创建缓存的上下文时使用ttl或expire_time属性来指定不同的过期时间。您也可以更新过期时间。
- 缓存的上下文过期后将不可用,过期后需要重新创建。
- 最小缓存大小:32K令牌
- 缓存创建后最小过期时间是1分钟。
- 没有最大过期时间限制
- 它支持流式和非流式响应。
现在是确保你使用最新版本的Vertex AI SDK的最佳时机。
安装 Python 包命令
!pip install --upgrade google-cloud-aiplatform
接下来,我们来导入一些必需的库。
import vertexai
from vertexai.preview import caching
引入vertexai库以及其中的缓存功能预览模块
现在,我们将制定系统指令来引导 Gemini 进入 OneTwo 框架。把 Gemini 当作您的主要软件工程师,准备分析代码库并提供专业意见:
project_id = "<在这里填入您的项目ID>""."
vertexai.init(project=project_id, location="us-central1")
system_instruction = """
你是一位首席软件工程师。你总是根据提供的资料中的事实行事,从不编造任何新的事实。
现在查看这个项目的代码库,并回答这些问题。
"""
现在让我们将代码库的内容加载到内容缓存中。为了提高效率,我们将根据内容类型采取不同的处理方式。在处理音频、视频和图片等大文件时,通常最好先将它们上传到谷歌云存储,然后使用如下类似的命令:
contents = [
Part.from_uri(
"gs://cloud-samples-data/generative-ai/pdf/2312.11805v3.pdf",
mime_type="application/pdf",
), # 下面的代码将PDF文件的内容加载到contents列表中。
]
在我们的情况下,我们处理的是纯文本内容,并且我们的fullcode.txt
文件远低于10MB的限制值。因此,我们将采取直接的方法来创建一个包含整个文件内容的Python列表,该列表中只有一个长字符串。
with open("fullcode.text", "r", encoding="utf-8") as f: # 打开文件并以UTF-8编码读取
fullcode_as_string = f.read() # 读取文件内容并存储为字符串
contents = [ # 将读取的内容放入列表中
fullcode_as_string
]
当我们的内容数组准备好了之后,现在是时候释放上下文缓存了!我已经配置它保留一小时的信息,并将其与我们选择的模型 gemini-1.5-pro-001
关联。让我们看看这会如何让我们的OneTwo代码库互动更加流畅吧!
导入 datetime 模块
cached_content = 缓存内容的创建(
模型名称="gemini-1.5-pro-001",
系统指令=system_instruction,
内容=contents,
有效期=datetime.timedelta(minutes=60), # 有效期为60分钟
)
当你运行代码时,你应该看到类似上面的输出。关键的是缓存名称。有了这个信息,我们只需一步就能直接访问模型,直接从缓存中创建它的实例。
当你运行代码时,你应该看到类似于上面显示的输出。最关键的信息是缓存名称。有了这个,我们只需一步就能直接使用模型:直接从缓存中创建模型实例。
从 vertexai.preview.generative_models 导入 GenerativeModel
cache_id = cached_content.name # 缓存ID为cached_content的name属性
cached_content = caching.CachedContent(cached_content_name=cache_id)
model = GenerativeModel.from_cached_content(cached_content=cached_content)
好的,让我们开始,问第一个问题:既然我们已经加载了OneTwo代码库,简单地问一句,这段代码实际在做什么?
resp = model.generate_content("这段代码在干嘛?")
resp
哇,这真是好多要消化的信息啊!我们先别被细节困住。先解决眼前的问题,我们来看看这个查询用了多少个token。
resp.使用元数据
好的,完整的OneTwo代码库占据了令人惊讶的449,556个token,但多亏了缓存,我们所有的内容都触手可及,轻而易举!我们的第一个查询及其响应仅仅使用了1,114个token——这就是上下文缓存的作用!
准备好迎接下一个问题了吗?开炮吧!
resp = model.generate_content("解释这个框架,就像我五岁一样简单。")
再来看看 token 数量
太酷了!仅用217个字符就能完成那个对话——上下文缓存真是个革命性的特性!
太棒了!现在我们知道了关于“one two”的事,让我们用一个真实的任务来测试一下“one two”。
resp = model.generate_content("生成一段执行两个任务的Python代码段")
回复:
太好了!听起来我们可以先试试这段生成的代码。由于OneTwo不是Colab的标准库,我们首先需要安装它,这样我们才能运行代码。这应该是个快速的步骤。
!pip install git+https://github.com/google-deepmind/onetwo
嗯……看来我们有点问题:
错误信息是:‘ValueError: 尝试调用一个尚未配置的内置函数 (onetwo.builtins.llm.generate_text)。’
好的,让我们询问热缓存版本的 Gemini 这个错误可能意味着什么,假设它会重新生成代码以便我们运行并了解……为了修复这个错误,我需要注册一个后端。
到目前为止,我们在这种简单的问答模式中的问答并没有被保存到上下文缓存中。这是这种模式下的预期行为。因此,Gemini在回答我最新问题时没有参考之前的交流,而是更专注于提供关于代码的澄清和解释,而不是直接修复代码的问题。
让我们切换到聊天模式,看看上下文缓存是否按预期工作。在这种模式下,我们所有的对话会自动添加到新查询中,但不会保存到上下文缓存里。这意味着我可以引用之前的问答,比如请求Gemini修复上一步生成的代码。
chat = 模型开始对话() # 开始一个新的对话
使用辅助函数来简化聊天响应的打印是一个明智之举,这将节省我们的时间,并让我们更专注于Gemini提供的信息。
从vertexai.generative_models导入ChatSession # (导入ChatSession模块,用于聊天会话)
def get_chat_response(chat: ChatSession, prompt: str) -> str:
text_response = []
responses = chat.send_message(prompt, stream=True) # (将prompt发送出去,并设置stream=True以接收分块回复)
for chunk in responses:
text_response.append(chunk.text)
# (将所有分块回复合并成一个字符串并返回)
return "".join(text_response)
我们准备再做一次这个测试。
prompt = "生成用于执行两个任务的Python代码" # 获取聊天响应的函数
print(get_chat_response(chat, prompt))
当我把这个东西粘贴到新单元格时:
嗯,有一点错误
那么让我们在这个聊天模式里问一下Gemini怎么修好它。
prompt = "我在运行你的代码时遇到了一个错误:嗯,是这样的错误:ValueError: 好像尝试调用未配置的内置函数 (onetwo.builtins.llm.generate_text 这个函数)"
print(get_chat_response(chat, prompt))
双子座保证:这段代码现在应该能正常运行,不会出现错误。请注意,它使用了一个总是返回“Test Reply”的测试后端。要使用真实的后端,您需要使用相应的API密钥和模型名称进行配置,如在OneTwo Colab中的说明所示。
那也行,但让我来确认一下这个说法;)
哇塞!Gemini果然说到做到!
本文作者是Lukasz Olejniczak — Google Cloud 的客户工程师。文中观点仅代表作者个人意见,不一定反映 Google 的立场。
如果你喜欢这篇文章,请为它鼓掌。如果你想了解更多关于Google云、数据科学、数据工程和AI/ML的信息,请关注我LinkedIn上的内容。
共同学习,写下你的评论
评论加载中...
作者其他优质文章