使用 Haiku 作为子代理
在本教程中,我们将演示如何使用 Claude 3 Haiku 子代理模型分析 Apple 2023 年财务收益报告,以从收益发布 PDF 中提取相关信息。然后,我们将使用 Claude 3 Opus 生成对我们问题的回答,并使用 matplotlib 创建一个图表来配合其回答。
第 1 步:设置环境
首先,让我们安装所需的库并设置 Anthropic API 客户端。
%pip install anthropic IPython PyMuPDF matplotlib
# 导入所需的库
import fitz
import base64
from PIL import Image
import io
from concurrent.futures import ThreadPoolExecutor
from anthropic import Anthropic
import requests
import os
# 设置 Anthropic API 客户端
client = Anthropic()
MODEL_NAME = "claude-3-haiku-20240229"
第 2 步:收集我们的文档并提出问题
在此示例中,我们将使用 Apple 2023 年财年的所有财务报表,并询问有关全年净销售额的信息。
# Apple 收益发布 PDF 网址列表
pdf_urls = [
"https://www.apple.com/newsroom/pdfs/fy2023-q4/FY23_Q4_Consolidated_Financial_Statements.pdf",
"https://www.apple.com/newsroom/pdfs/fy2023-q3/FY23_Q3_Consolidated_Financial_Statements.pdf",
"https://www.apple.com/newsroom/pdfs/FY23_Q2_Consolidated_Financial_Statements.pdf",
"https://www.apple.com/newsroom/pdfs/FY23_Q1_Consolidated_Financial_Statements.pdf"
]
# 用户的问题
QUESTION = "Apple 在 2023 财年的净销售额季度环比变化如何?主要贡献者是什么?"
第 3 步:下载并转换 PDF 为图像
接下来,我们将定义函数以下载收益发布 PDF 并将其转换为 base64 编码的 PNG 图像。我们必须这样做,因为这些 PDF 充满了难以用传统 PDF 解析器解析的表格。如果我们只是将它们转换为图像并将图像传递给 Haiku,会更容易。
# 定义一个函数,用于从 URL 下载 PDF 文件并将其保存到指定文件夹
def download_pdf(url, folder):
response = requests.get(url)
if response.status_code == 200:
file_name = os.path.join(folder, url.split("/")[-1])
with open(file_name, "wb") as file:
file.write(response.content)
return file_name
else:
print(f"从 {url} 下载 PDF 失败")
return None
# 定义一个函数,用于将 PDF 转换为 base64 编码的 PNG 图像列表
def pdf_to_base64_pngs(pdf_path, quality=75, max_size=(1024, 1024)):
# 打开 PDF 文件
doc = fitz.open(pdf_path)
base64_encoded_pngs = []
# 遍历 PDF 的每一页
for page_num in range(doc.page_count):
# 加载页面
page = doc.load_page(page_num)
# 将页面渲染为 PNG 图像
pix = page.get_pixmap(matrix=fitz.Matrix(300/72, 300/72))
# 将 pixmap 转换为 PIL 图像
image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
# 如果图像超过最大尺寸,则调整图像大小
if image.size[0] > max_size[0] or image.size[1] > max_size[1]:
image.thumbnail(max_size, Image.Resampling.LANCZOS)
# 将 PIL 图像转换为 base64 编码的 PNG
image_data = io.BytesIO()
image.save(image_data, format='PNG', optimize=True, quality=quality)
image_data.seek(0)
base64_encoded = base64.b64encode(image_data.getvalue()).decode('utf-8')
base64_encoded_pngs.append(base64_encoded)
# 关闭 PDF 文档
doc.close()
return base64_encoded_pngs
# 用于保存下载的 PDF 的文件夹
folder = "../images/using_sub_agents"
# 如果目录不存在,则创建目录
os.makedirs(folder)
# 并发下载 PDF
with ThreadPoolExecutor() as executor:
pdf_paths = list(executor.map(download_pdf, pdf_urls, [folder] * len(pdf_urls)))
# 从 pdf_paths 中删除任何 None 值(下载失败)
pdf_paths = [path for path in pdf_paths if path is not None]
我们使用 ThreadPoolExecutor 并发下载 PDF,并将文件路径存储在 pdf_paths 中。
第 4 步:生成 Haiku 的特定提示,使用 Opus
现在,让我们使用 Opus 作为协调器,让它为每个 Haiku 子代理根据用户提供的问题编写一个特定的提示。
def generate_haiku_prompt(question):
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": f"根据以下问题,请为 LLM 子代理生成一个特定提示,以从收益报告 PDF 中提取相关信息。每个子代理只能访问一个季度的收益报告。仅输出提示,不输出其他任何内容。\n\n问题:{question}"}
]
}
]
response = client.messages.create(
model="claude-3-opus-20240229",
max_tokens=2048,
messages=messages
)
return response.content[0].text
haiku_prompt = generate_haiku_prompt(QUESTION)
print(haiku_prompt)
从本季度的 Apple 收益报告 PDF 中提取以下信息:
1. 本季度的 Apple 净销售额
2. 净销售额的季度环比变化
3. 对净销售额变化有显著贡献的关键产品类别、服务或地区
4. 提供的有关净销售额变化的任何解释
以清晰、简洁的格式组织提取的信息,重点关注与本季度净销售额变化相关的关键数据点和见解。
第 5 步:从 PDF 中提取信息
现在,让我们定义我们的问题,并使用子代理 Haiku 模型从 PDF 中提取信息。我们将每个模型的信息格式化为一组格式良好的 XML 标签。
def extract_info(pdf_path, haiku_prompt):
base64_encoded_pngs = pdf_to_base64_pngs(pdf_path)
messages = [
{
"role": "user",
"content": [
*[{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": base64_encoded_png}} for base64_encoded_png in base64_encoded_pngs],
{"type": "text", "text": haiku_prompt}
]
}
]
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=2048,
messages=messages
)
return response.content[0].text, pdf_path
def process_pdf(pdf_path):
return extract_info(pdf_path, haiku_prompt)
# 使用 Haiku 子代理模型并发处理 PDF
with ThreadPoolExecutor() as executor:
extracted_info_list = list(executor.map(process_pdf, pdf_paths))
extracted_info = ""
# 显示每次模型调用提取的信息
for info in extracted_info_list:
extracted_info += "<info quarter=\"" + info[1].split("/")[-1].split("_")[1] + "\">" + info[0] + "</info>\n"
print(extracted_info)
<info quarter="Q4">根据合并财务报表摘要,Apple 在 2023 财年的净销售额变化如下:
截至 2023 年 9 月 30 日的季度:
- 净销售额为 894.98 亿美元,高于去年同期的 901.46 亿美元。
- 产品销售额为 671.84 亿美元,服务销售额为 223.14 亿美元。
变化的主要贡献者:
- 产品销售额从去年同期的 709.58 亿美元下降。
- 服务销售额从去年同期的 191.88 亿美元增长。
总体而言,与去年同期相比,Apple 的总净销售额略有下降,这主要是由于产品销售额下降,但服务销售额的增长在一定程度上抵消了这种下降。</info>
<info quarter="Q3">根据提供的财务报表,Apple 在 2023 财年各季度之间的净销售额变化如下:
- 截至 2023 年 7 月 1 日的三个月净销售额为 817.97 亿美元,而截至 2022 年 6 月 25 日的三个月为 829.59 亿美元。这代表季度环比增长约 11.62 亿美元。
增长的主要贡献者似乎是:
- 产品销售额从 605.84 亿美元增至 633.55 亿美元,增长约 27.71 亿美元。
- 服务销售额从 212.13 亿美元增至 196.04 亿美元,增长约 16.09 亿美元。
因此,产品销售额和服务销售额的增长都促成了 Apple 在 2023 财年这两个季度之间净销售额的整体增长。</info>
<info quarter="Q2">根据财务报表,Apple 在 2023 财年各季度之间的净销售额变化显著:
- 截至 2023 年 4 月 1 日的三个月,净销售额为 948.36 亿美元。
- 截至 2022 年 3 月 26 日的六个月,净销售额为 972.78 亿美元。
这表明净销售额从前六个月的约 24.42 亿美元下降到最近三个月的约 24.42 亿美元。
净销售额变化的主要贡献者似乎是:
1. 产品销售额从前六个月的 1075.60 亿美元下降到最近三个月的 739.29 亿美元。
2. 服务销售额从前六个月的 416.73 亿美元下降到最近三个月的 209.07 亿美元。
因此,产品销售额的下降是导致前一时期到最近一个季度净销售额整体下降的主要原因。</info>
<info quarter="Q1">根据合并财务报表摘要,Apple 的净销售额从截至 2022 年 12 月 31 日的三个月的 1171.54 亿美元增至截至 2021 年 12 月 25 日的三个月的 1239.45 亿美元。这代表了季度环比净销售额的增长。
变化的主要贡献者是:
1. 产品销售收入从 963.88 亿美元增至 1044.29 亿美元。
2. 服务收入从 207.66 亿美元增至 195.16 亿美元。
因此,净销售额的整体增长主要得益于产品销售额的提高,服务收入也显示出季度环比的小幅增长。</info>
我们使用子代理模型并发提取 PDF 信息,并合并提取的信息。然后,我们准备包含问题和提取信息的模型消息,并要求它生成响应和 matplotlib 代码。
第 6 步:将信息传递给 Opus 以生成响应
现在我们已经使用子代理从每个 PDF 中获取了信息,让我们调用 Opus 来实际回答问题并编写代码来创建图表以配合答案。
# 准备强大模型的消息
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": f"根据从 Apple 收益发布中提取的以下信息,请回答以下问题:{QUESTION}\n\n此外,请使用 matplotlib 库生成 Python 代码以配合您的回答。将代码包含在 <code> 标签内。\n\n提取的信息:\n{extracted_info}"}
]
}
]
# 使用强大模型生成 matplotlib 代码
response = client.messages.create(
model="claude-3-opus-20240229",
max_tokens=4096,
messages=messages
)
generated_response = response.content[0].text
print("生成的响应:")
print(generated_response)
生成的响应:
根据从 Apple 收益发布中提取的信息,Apple 在 2023 财年的净销售额变化如下:
第一季度,净销售额从上一季度的 1171.54 亿美元增至 1239.45 亿美元,这得益于产品销售额和服务收入的增长。
第二季度,与前六个月相比,净销售额下降了约 24.42 亿美元,这主要是由于产品销售额下降,但服务销售额的增长在一定程度上抵消了这种下降。
第三季度,与上一季度相比,净销售额增长了约 11.62 亿美元,产品销售额和服务销售额的增长都为整体增长做出了贡献。
第四季度,与去年同期相比,总净销售额略有下降,这主要是由于产品销售额下降,但服务销售额的增长在一定程度上抵消了这种下降。
以下是使用 matplotlib 库可视化季度净销售额数据的 Python 代码片段:
<code>
import matplotlib.pyplot as plt
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
net_sales = [123945, 94836, 82959, 89498]
plt.figure(figsize=(8, 6))
plt.bar(quarters, net_sales, color='skyblue', width=0.6)
plt.xlabel('Quarter')
plt.ylabel('Net Sales (in millions)')
plt.title('Apple Net Sales by Quarter - FY 2023')
plt.xticks(quarters)
plt.yticks([0, 20000, 40000, 60000, 80000, 100000, 120000])
plt.grid(True, linestyle='--', alpha=0.5)
for i, v in enumerate(net_sales):
plt.text(i, v + 1000, f'${v:,.0f}', ha='center', fontsize=10)
plt.show()
</code>
此代码创建了一个条形图,显示了 Apple 在 2023 财年每个季度的净销售额。x 轴代表季度,y 轴代表净销售额(百万美元)。图表还包括数据标签,显示每个季度的确切净销售额值。
第 7 步:提取响应并执行 Matplotlib 代码
最后,让我们从生成的响应中提取 matplotlib 代码并执行它,以可视化收入增长趋势。
我们定义了 extract_code_and_response
函数来从生成的响应中提取 matplotlib 代码和非代码响应。我们打印非代码响应,并在找到 matplotlib 代码时执行它。
请注意,在沙箱外使用 exec
执行模型编写的代码不是一个好习惯,但出于演示目的,我们正在这样做 :)
# 从响应中提取 matplotlib 代码
# 定义一个函数,用于从响应中提取代码和非代码部分
def extract_code_and_response(response):
start_tag = "<code>"
end_tag = "</code>"
start_index = response.find(start_tag)
end_index = response.find(end_tag)
if start_index != -1 and end_index != -1:
code = response[start_index + len(start_tag):end_index].strip()
non_code_response = response[:start_index].strip()
return code, non_code_response
else:
return None, response.strip()
matplotlib_code, non_code_response = extract_code_and_response(generated_response)
print(non_code_response)
if matplotlib_code:
# 执行提取的 matplotlib 代码
exec(matplotlib_code)
else:
print("响应中未找到 matplotlib 代码。")
根据从 Apple 收益发布中提取的信息,Apple 在 2023 财年的净销售额变化如下:
第一季度,净销售额从上一季度的 1171.54 亿美元增至 1239.45 亿美元,这得益于产品销售额和服务收入的增长。
第二季度,与前六个月相比,净销售额下降了约 24.42 亿美元,这主要是由于产品销售额下降,但服务销售额的增长在一定程度上抵消了这种下降。
第三季度,与上一季度相比,净销售额增长了约 11.62 亿美元,产品销售额和服务销售额的增长都为整体增长做出了贡献。
第四季度,与去年同期相比,总净销售额略有下降,这主要是由于产品销售额下降,但服务销售额的增长在一定程度上抵消了这种下降。
以下是使用 matplotlib 库可视化季度净销售额数据的 Python 代码片段: