Press "Enter" to skip to content

AI 遇见 PowerPoint

使用Streamlit、LangChain和Yahoo Finance自动化公司研究演示

本文最初发布于2023年8月2日的Streamlit博客。

Photo by Lukas Blazek on Unsplash

介绍

本文将介绍Instant Insight应用程序的后端工作原理,该应用程序是一个开源项目,在2023年5月的Snowflake Summit Streamlit Hackathon比赛中获得第三名。该Web应用程序旨在使用来自Yahoo Finance和ChatGPT的数据生成带有公司研究和价值主张幻灯片的PowerPoint演示文稿。

为了建立一个背景,让我们想象在一个B2B SaaS公司的销售部门工作,该公司有数百个潜在客户,并提供以下产品:会计和计划软件、CRM、聊天机器人和云数据存储。您的任务是进行潜在客户研究,包括财务和SWOT分析,探索竞争环境,制定价值主张,并与团队共享演示文稿。潜在客户的数据存储在Snowflake数据库中,该数据库供应您的CRM系统。您可以使用Instant Insight应用程序根据各种参数快速筛选潜在客户。然后,选择要包含在演示文稿中的潜在客户,并单击按钮生成演示文稿。一分钟内,包含所有研究的PowerPoint演示文稿将准备好下载。

该应用程序的高级架构如下所示:

Image by author

目录

  1. 将您的Streamlit应用程序连接到Snowflake
  2. 创建具有动态筛选器和交互式表格的用户界面
  3. 从Yahoo Finance获取公司数据
  4. 使用Plotly创建图表
  5. 使用Clearbit API获取公司标志
  6. 使用LangChain和GPT 3.5 LLM编写SWOT分析和价值主张
  7. 从GPT响应中提取结构化数据
  8. 使用python-pptx生成幻灯片

将您的Streamlit应用程序连接到Snowflake

首先,获取一些数据。为此,请使用Snowflake Connector:

import snowflake.connector# 从Streamlit secrets获取Snowflake凭据SNOWFLAKE_ACCOUNT = st.secrets["snowflake_credentials"]["SNOWFLAKE_ACCOUNT"]SNOWFLAKE_USER = st.secrets["snowflake_credentials"]["SNOWFLAKE_USER"]SNOWFLAKE_PASSWORD = st.secrets["snowflake_credentials"]["SNOWFLAKE_PASSWORD"]SNOWFLAKE_DATABASE = st.secrets["snowflake_credentials"]["SNOWFLAKE_DATABASE"]SNOWFLAKE_SCHEMA = st.secrets["snowflake_credentials"]["SNOWFLAKE_SCHEMA"]@st.cache_resourcedef get_database_session():    """返回一个数据库会话对象。"""    return snowflake.connector.connect(        account=SNOWFLAKE_ACCOUNT,        user=SNOWFLAKE_USER,        password=SNOWFLAKE_PASSWORD,        database=SNOWFLAKE_DATABASE,        schema=SNOWFLAKE_SCHEMA,    )@st.cache_datadef get_data():    """返回一个带有Snowflake数据的pandas DataFrame。"""    query = 'SELECT * FROM us_prospects;'    cur = conn.cursor()    cur.execute(query)    # 将结果作为pandas DataFrame获取    column_names = [col[0] for col in cur.description]    data = cur.fetchall()    df = pd.DataFrame(data, columns=column_names)    # 关闭与Snowflake的连接    cur.close()    conn.close()    return df# 从Snowflake获取数据conn = get_database_session()df = get_data(conn)

您的Snowflake帐户、用户名、密码、数据库名称和架构等敏感数据存储在secrets中,并通过调用st.secrets进行检索(在此处阅读更多信息)。

接下来,定义两个函数:

get_database_session()初始化连接对象get_data()执行SQL查询并返回一个pandas DataFrame使用一个简单的SELECT *查询从us_prospects表中检索所有数据。

创建一个带有动态筛选器和交互表格的用户界面

现在让我们使用一些Streamlit的魔法来开发一个应用的前端。创建一个包含四个动态多选筛选器的侧边栏面板,并添加一个复选框,让用户可以选择所有的值。

你的应用中的筛选器是按顺序工作的。用户需要逐个应用它们,从上到下。一旦应用了第一个筛选器,第二个筛选器就变得可用,并且只包含相关的标签。每次应用一个筛选器后,底层的DataFrame会被预先过滤,num_of_pros变量会更新以反映所选潜在客户的数量。

查看筛选器的实际效果:

作者图片

以下是创建前两个筛选器的代码:

# 创建侧边栏筛选器st.sidebar.write('**使用筛选器选择潜在客户** 👇')sector_checkbox = st.sidebar.checkbox('所有行业', help='选中此框以选择所有行业')unique_sector = sorted(df['SECTOR'].unique())# 如果选择了全部行业复选框,则选择所有行业if sector_checkbox:    selected_sector = st.sidebar.multiselect('选择行业', unique_sector, unique_sector)else:    selected_sector = st.sidebar.multiselect('选择行业', unique_sector)# 如果用户选择了行业,则允许选择全部行业复选框if len(selected_sector) > 0:    industry_checkbox = st.sidebar.checkbox('所有行业', help='选中此框以选择所有行业')    # 过滤数据    df = df[(df['SECTOR'].isin(selected_sector))]    # 显示选择的潜在客户数量    num_of_pros = str(df.shape[0])else:    industry_checkbox = st.sidebar.checkbox('所有行业', help='选中此框以选择所有行业',                                           disabled=True)    # 显示选择的潜在客户数量    num_of_pros = str(df.shape[0])# 如果选择了全部行业复选框,则选择所有行业unique_industry = sorted(df['INDUSTRY'].loc[df['SECTOR'].isin(selected_sector)].unique())if industry_checkbox:    selected_industry = st.sidebar.multiselect('选择行业', unique_industry, unique_industry)else:    selected_industry = st.sidebar.multiselect('选择行业', unique_industry)# 如果用户选择了行业,则允许选择全部状态复选框if len(selected_industry) > 0:    status_checkbox = st.sidebar.checkbox('所有潜在客户状态', help='选中此框以选择所有潜在客户状态')    # 过滤数据    df = df[(df['SECTOR'].isin(selected_sector)) & (df['INDUSTRY'].isin(selected_industry))]    # 显示选择的潜在客户数量    num_of_pros = str(df.shape[0])else:    status_checkbox = st.sidebar.checkbox('所有潜在客户状态', help='选中此框以选择所有潜在客户状态', disabled=True)

接下来,使用AgGrid创建一个交互式表格来显示数据,允许用户选择生成幻灯片的潜在客户(在这里阅读更多)。

在每一行的表格中放置一个复选框,允许用户只选择一行。此外,设置自定义的列宽和表格高度。

以下是创建此表格的代码:

from st_aggrid import AgGridfrom st_aggrid.grid_options_builder import GridOptionsBuilderfrom st_aggrid import GridUpdateMode, DataReturnModeimport pandas as pd# 创建AgGrid动态表格并设置配置gb = GridOptionsBuilder.from_dataframe(df)gb.configure_selection(selection_mode="single", use_checkbox=True)gb.configure_column(field='公司名称', width=270)gb.configure_column(field='行业', width=260)gb.configure_column(field='领域', width=350)gb.configure_column(field='潜在客户状态', width=270)gb.configure_column(field='产品', width=240)gridOptions = gb.build()response = AgGrid(    df,    gridOptions=gridOptions,    height=600,    update_mode=GridUpdateMode.SELECTION_CHANGED,    data_return_mode=DataReturnMode.FILTERED_AND_SORTED,    fit_columns_on_grid_load=False,    theme='alpine',    allow_unsafe_jscode=True)# 获取选择的行response_df = pd.DataFrame(response["selected_rows"])

从雅虎财经获取公司数据

假设用户已选择一家公司进行研究,您需要收集一些关于该公司的数据。您的主要数据源是雅虎财经,您将使用yahooquery库访问它——这是一个非官方雅虎财经API端点的Python接口。它允许用户通过雅虎财经前端检索几乎所有可见的数据。

这是使用雅虎财经数据的概述幻灯片:

Image by author

使用yahooquery的Ticker类获取所选公司的定量和定性数据。只需将公司的股票代码作为参数传递,调用所需的属性,并从返回的字典中检索数据。

以下是检索公司概述幻灯片数据的代码:

from yahooquery import Tickerselected_ticker = 'ABC'ticker = Ticker(selected_ticker)# 获取公司信息name = ticker.price[selected_ticker]['shortName']sector = ticker.summary_profile[selected_ticker]['sector']industry = ticker.summary_profile[selected_ticker]['industry']employees = ticker.summary_profile[selected_ticker]['fullTimeEmployees']country = ticker.summary_profile[selected_ticker]['country']city = ticker.summary_profile[selected_ticker]['city']website = ticker.summary_profile[selected_ticker]['website']summary = ticker.summary_profile[selected_ticker]['longBusinessSummary']

该应用程序利用雅虎财经数据创建图表,以展示随时间变化的财务表现。一个幻灯片显示基本财务指标,如股票价格、总债务、总收入和EBITDA随时间的变化。

稍后我们将讨论绘图。现在,让我们专注于从雅虎财经获取财务数据。get_stock()和get_financials()函数返回具有相关财务指标的数据帧。股票价格数据与其他财务指标存储在不同的位置,这就是为什么需要调用两个属性的原因:

ticker.history():检索给定符号的历史定价数据(在此处阅读文档)ticker.all_financial_data():检索所有财务数据,包括利润表、资产负债表、现金流量表和估值指标(在此处阅读文档)

以下是用于生成带有历史股票价格、收入、总债务和EBITDA的四个数据帧的代码:

from yahooquery import Tickerimport pandas as pddef get_stock(ticker, period, interval):    """从雅虎财经获取股票数据的函数。接受股票代码、时间段和间隔作为参数,并返回DataFrame"""    hist_df = ticker.history(period=period, interval=interval)    hist_df = hist_df.reset_index()    # 将列名转为大写    hist_df.columns = [x.capitalize() for x in hist_df.columns]    return hist_dfdef get_financials(df, col_name, metric_name):    """从DataFrame中获取财务指标的函数。接受DataFrame、列名和指标名作为参数,并返回DataFrame"""    metric = df.loc[:, ['asOfDate', col_name]]    metric_df = pd.DataFrame(metric).reset_index()    metric_df.columns = ['Symbol', 'Year', metric_name]    return metric_dfselected_ticker = 'ABC'ticker = Ticker(selected_ticker)# 获取所有财务数据fin_df = ticker.all_financial_data()# 为每个指标创建数据帧stock_df = get_stock(ticker=ticker, period='5y', interval='1mo')rev_df = get_financials(df=fin_df, col_name='TotalRevenue', metric_name='Total Revenue')debt_df = get_financials(df=fin_df, col_name='TotalDebt', metric_name='Total Debt')ebitda_df = get_financials(df=fin_df, col_name='NormalizedEBITDA', metric_name='EBITDA')

雅虎财经的数据还用于另一张幻灯片上的竞争对手分析,其中将一家公司的表现与其同行进行比较。为此,使用两个指标:总收入和销售、一般和行政费用(SG&A)占收入的百分比。它们在利润表中可用,因此使用ticker.income_statement()属性返回一个数据帧(在此处阅读更多信息)。

extract_comp_financials()函数从利润表数据帧中检索总收入和销售、一般和行政费用(SG&A),并仅保留2022年的数据。由于SG&A占收入的百分比指标不容易获得,可以通过将SG&A除以收入,然后乘以100来手动计算。

该函数对具有公司名称作为键和具有股票代码作为值的嵌套字典进行操作,然后将新值追加到现有字典中:

from yahooquery import Tickerimport pandas as pddef extract_comp_financials(tkr, comp_name, comp_dict):    """函数用于提取竞争对手的财务指标。接受一个股票代码、一个公司名称和一个嵌套字典作为参数,并将财务指标追加到字典中"""    ticker = Ticker(tkr)    income_df = ticker.income_statement(frequency='a', trailing=False)    subset = income_df.loc[:, ['asOfDate', 'TotalRevenue', 'SellingGeneralAndAdministration']].reset_index()    # 仅保留2022年的数据    subset = subset[subset['asOfDate'].dt.year == 2022].sort_values(by='asOfDate', ascending=False).head(1)    # 获取值    total_revenue = subset['TotalRevenue'].values[0]    sg_and_a = subset['SellingGeneralAndAdministration'].values[0]    # 计算销售、一般和行政费用占总收入的百分比    sg_and_a_pct = round(sg_and_a / total_revenue * 100, 2)    # 将值添加到字典中    comp_dict[comp_name]['总收入'] = total_revenue    comp_dict[comp_name]['销售、一般和行政费用占收入的百分比'] = sg_and_a_pct# 示例字典peers_dict_nested = {'公司1': {'股票代码': 'ABC'}, '公司2': {'股票代码': 'XYZ'}}# 提取每个竞争对手的财务数据for key, value in peers_dict_nested.items():    try:        extract_comp_financials(tkr=value['股票代码'], comp_name=key, dict=peers_dict_nested)    # 如果在雅虎财经中找不到股票代码,则从竞争对手字典中删除并继续    except:        del peers_dict_nested[key]        continue

运行上述代码后,得到一个类似以下结构的嵌套字典:

# 示例输出字典{'公司1': {'股票代码': 'ABC', '总收入': '1234', '销售、一般和行政费用占收入的百分比': '10'},  '公司2': {'股票代码': 'XYZ', '总收入': '5678', '销售、一般和行政费用占收入的百分比': '20'}}

接下来,将嵌套字典转换为DataFrame以将其传递给绘图函数:

# 创建一个包含竞争对手财务数据的数据框peers_df = pd.DataFrame.from_dict(peers_dict_nested, orient='index')peers_df = peers_df.reset_index().rename(columns={'index': '公司名称'})

生成的DataFrame应该类似于以下内容:

  公司名称 股票代码  总收入  销售、一般和行政费用占收入的百分比0    公司1    ABC           1234                 101    公司2    XYZ           5678                 20

使用Plotly创建图表

您已经筛选了财务数据,现在是时候绘制图表了!使用Plotly Express创建简单且具有视觉吸引力的图表(在此处阅读更多信息)。

在前一节中,您创建了一个DataFrame和一个用于公司名称的变量。在plot_graph()函数中使用这些作为参数,将数据框、x轴和y轴的列名以及图表标题传递给它:

import plotly.express as pxdef plot_graph(df, x, y, title, name):    """函数用于绘制折线图。接受DataFrame、x轴和y轴、标题和名称作为参数,并返回一个Plotly图表"""    fig = px.line(df, x=x, y=y, template='simple_white',                        title='<b>{} {}</b>'.format(name, title))    fig.update_traces(line_color='#A27D4F')    fig.update_layout(paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)')    return figstock_fig = plot_graph(df=stock_df, x='日期', y='开盘价', title='股价(美元)', name=name)rev_fig = plot_graph(df=rev_df, x='年份', y='总收入', title='总收入(美元)', name=name)debt_fig = plot_graph(df=debt_df, x='年份', y='总债务', title='总债务(美元)', name=name)ebitda_fig = plot_graph(df=ebitda_df, x='年份', y='EBITDA', title='EBITDA(美元)', name=name)

生成的图表应该类似于这样:

作者提供的图片

该应用程序还会生成一个包含给定公司竞争对手分析的幻灯片。要创建此幻灯片,请使用peers_plot()函数以及peers_df。此函数生成一个水平条形图,用于比较竞争对手的总收入和销售和管理费用占收入的百分比。

以下是代码:

import plotly.express as pximport pandas as pddef peers_plot(df, name, metric):    """使用对等体绘制条形图的函数。以DataFrame、名称、度量和股票代码作为参数,并返回一个Plotly图"""    # 删除缺少度量的行    df.dropna(subset=[metric], inplace=True)    df_sorted = df.sort_values(metric, ascending=False)    # 遍历标签并将颜色添加到颜色映射字典中,突出显示选择的公司    color_map = {}    for label in df_sorted['Company Name']:        if label == name:            color_map[label] = '#A27D4F'        else:            color_map[label] = '#D9D9D9'    fig = px.bar(df_sorted, y='Company Name', x=metric,                         template='simple_white', color='Company Name',                        color_discrete_map=color_map,                        orientation='h',                        title='<b>{} {} vs Peers FY22</b>'.format(name, metric))        fig.update_layout(paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', showlegend=False, yaxis_title='')    return fig# 绘制对等体图表rev_peers_fig = peers_plot(df=peers_df, name=name, metric='Total Revenue')sg_and_a_peers_fig = peers_plot(df=peers_df, name=name, metric='SG&A % Of Revenue')

使用自定义颜色可以使您的公司脱颖而出。

条形图应该类似于这样:

作者提供的图片

使用Clearbit API获取公司标志

如您在公司概述幻灯片上看到的那样,有一个研究公司的标志。Yahoo Finance上没有标志URL,所以请使用Clearbit。只需将公司的网站连接到“https://logo.clearbit.com/”,并使用几行代码:

from yahooquery import Tickerselected_ticker = 'ABC'ticker = Ticker(selected_ticker)# 获取所选公司的网站website = ticker.summary_profile[selected_ticker]['website']# 获取所选公司的标志URLlogo_url = '<https://logo.clearbit.com/>' + website

现在您有了标志URL,请检查它是否有效。如果有效,调整其大小并将其放置在幻灯片上。为此,请使用自定义函数resize_image(),该函数将标志图像放置在容器内并调整其大小,同时保持其纵横比。这样,即使初始大小有所不同,所有标志看起来都相同。

然后将图像对象保存在本地为“logo.png”,并从中检索以将其作为图片放置在幻灯片上。您可以以类似的方式将Plotly图表放置在幻灯片上。使用python-pptx库以编程方式操作PowerPoint幻灯片和形状(在此处阅读更多)。

以下是该过程:

作者提供的图片

以下代码使用了logo_url变量(在上一个代码片段中定义):

from PIL import Imageimport requestsfrom pptx import Presentationfrom pptx.util import Inchesimport osdef resize_image(url):    """函数用于保持纵横比调整标志大小。接受URL作为参数并返回图像对象"""    # 打开URL中的图像    image = Image.open(requests.get(url, stream=True).raw)    # 如果标志过高或过宽,则将背景容器扩大一倍    if image.height > 140:        container_width = 220 * 2        container_height = 140 * 2    elif image.width > 220:        container_width = 220 * 2        container_height = 140 * 2    else:        container_width = 220        container_height = 140    # 创建与原始图像具有相同纵横比的新图像    new_image = Image.new('RGBA', (container_width, container_height))    # 计算将图像粘贴到新图像上的位置,使其居中    x = (container_width - image.width) // 2    y = (container_height - image.height) // 2    # 将图像粘贴到新图像上    new_image.paste(image, (x, y))    return new_image# 创建演示文稿对象prs = Presentation('template.pptx')# 选择第二张幻灯片slide = prs.slides[1]# 检查标志URL是否返回代码200(有效链接)if requests.get(logo_url).status_code == 200:    # 创建标志图像对象    logo = resize_image(logo_url)    logo.save('logo.png')    logo_im = 'logo.png'    # 将标志添加到幻灯片slide.shapes.add_picture(logo_im, left=Inches(1.2), top=Inches(0.5), width=Inches(2))    os.remove('logo.png')

运行上面的代码应该在幻灯片上放置一个徽标:

作者图片

使用LangChain和GPT 3.5 LLM编写SWOT分析和价值主张。

是时候利用AI来进行公司研究了!

您将使用LangChain,这是一个流行的框架,旨在简化使用ChatOpenAI和Human/System Message LLMs创建应用程序的过程(在此处阅读更多信息)。

generate_gpt_response()函数接受两个参数:

  1. gpt_input,您将传递给模型的提示
  2. max_tokens,限制模型响应中的令牌数

您将在ChatOpenAI的参数中使用gpt-3.5-turbo-0613模型,并检索存储在Streamlit secrets中的OpenAI API密钥。您还将将温度设置为0以获得更确定性的响应(在此处阅读更多信息)。

为了提高GPT响应的质量,将以下文本传递给SystemMessage参数:“您是金融、市场和公司研究方面的专家,也具有销售B2B软件产品的卓越技能。”它将设置AI要遵循的目标(在此处阅读更多信息)。

from langchain.chat_models import ChatOpenAIfrom langchain.schema import HumanMessage, SystemMessagedef generate_gpt_response(gpt_input, max_tokens):    """从GPT-3生成响应的函数。接受输入和最大令牌数作为参数,并返回响应"""    # 创建OpenAI类的实例    chat = ChatOpenAI(openai_api_key=st.secrets["openai_credentials"]["API_KEY"], model='gpt-3.5-turbo-0613',                      temperature=0, max_tokens=max_tokens)    # 从模型生成响应    response = chat.predict_messages(        [SystemMessage(content='您是金融、市场和公司研究方面的专家。您还具备销售B2B软件产品的卓越技能。'),         HumanMessage(             content=gpt_input)])    return response.content.strip()

接下来,让我们为模型创建一个提示,并调用generate_gpt_response()函数。

使用以下代码提示模型为特定公司创建SWOT分析,并将结果作为Python字典返回:

input_swot = """使用{ticker}股票的{name}公司创建一个简要的SWOT分析?将输出作为一个具有以下键的Python字典返回:Strengths、Weaknesses、Opportunities、Threats和analysis作为值。不要返回其他任何东西。"""input_swot = input_swot.format(name='Company 1', ticker='ABC')# 从GPT-3返回响应gpt_swot = generate_gpt_response(input_swot, 1000)

生成的字典应该类似于:

{"Strengths": "文本", "Weaknesses": "文本", "Opportunities": "文本", "Threats": "文本"}

类似地,您可以提示GPT模型为特定公司的特定产品编写价值主张。该应用程序使用常见的价值主张框架,识别客户的痛点和收益,以及创造收益和缓解痛点:

input_vp = """"使用{industry}行业中运营的{ticker}股票的{name}公司的{product}创建一个简要的价值主张,使用Value Proposition Canvas框架。将输出作为一个具有以下键的Python字典返回:Pains、Gains、Gain Creators、Pain Relievers作为键,文本作为值。请具体和简洁。不要返回其他任何东西。"""input_vp = input_vp.format(product='会计软件', name='Company 1', ticker='ABC', industry='零售')# 从GPT-3返回响应gpt_value_prop = generate_gpt_response(input_vp, 1000)# 响应如下: # {"Pains": "文本", "Gains": "文本", "Gain Creators": "文本", "Pain Relievers": "文本"}

从GPT响应中提取结构化数据

在前一步中,您向GPT模型询问了一个Python字典的回复。但由于LLM有时会产生不合理的回复,返回的字符串可能并不总是只包含必要的字典。在这种情况下,您可能需要解析响应字符串以提取字典并将其转换为Python字典类型。

为了实现这一点,您需要两个标准库:re和ast。

dict_from_string()函数接收来自LLM的响应字符串,并以以下工作流返回一个字典:

作者提供的图片

以下是代码:

import reimport astdef dict_from_string(gpt_response):    """用于解析GPT响应并将其转换为字典的函数"""    # 查找以'{'开头,以'}'结尾的跨多行的子字符串    match = re.search(r'\\{.*?\\}', gpt_response, re.DOTALL)    dictionary = None    if match:        try:            # 尝试将子字符串转换为字典            dictionary = ast.literal_eval(match.group())        except (ValueError, SyntaxError):            # 不是字典            return None    return dictionaryswot_dict = dict_from_string(gpt_response=gpt_swot)vp_dict = dict_from_string(gpt_response=gpt_value_prop)

使用python-pptx生成幻灯片

现在您已经有了数据,是时候填充幻灯片了。使用PowerPoint模板,并使用python-pptx库将占位符替换为实际值。

以下是SWOT幻灯片模板的样子:

作者提供的图片

要使用数据填充幻灯片,使用replace_text()函数,该函数接受两个参数:

  1. 一个包含占位符作为键和替换文本作为值的字典
  2. 一个PowerPoint幻灯片对象

使用之前步骤中定义的swot_dict变量:

from pptx import Presentationdef replace_text(replacements, slide):    """用于替换PowerPoint幻灯片上的文本的函数。接受{匹配项:替换项,...}的字典,并替换所有匹配项"""    # 遍历幻灯片上的所有形状    for shape in slide.shapes:        for match, replacement in replacements.items():            if shape.has_text_frame:                if (shape.text.find(match)) != -1:                    text_frame = shape.text_frame                    for paragraph in text_frame.paragraphs:                        whole_text = "".join(run.text for run in paragraph.runs)                        whole_text = whole_text.replace(str(match), str(replacement))                        for idx, run in enumerate(paragraph.runs):                            if idx != 0:                                p = paragraph._p                                p.remove(run._r)                        if bool(paragraph.runs):                            paragraph.runs[0].text = whole_textprs = Presentation("template.pptx")swot_slide = prs.slides[2]# 为幻灯片创建标题swot_title = 'Company 1的SWOT分析'# 初始化一个包含占位符和替换值的字典replaces_dict = {    '{s}': swot_dict['Strengths'],    '{w}': swot_dict['Weaknesses'],    '{o}': swot_dict['Opportunities'],    '{t}': swot_dict['Threats'],    '{swot_title}': swot_title}# 运行函数以用值替换占位符replace_text(replacements=replaces_dict, slide=swot_slide)

简而言之,replace_text()函数遍历幻灯片上的所有形状,查找占位符值,并在找到时将其替换为字典中的值。

填充了所有幻灯片的数据或图像后,将演示对象保存为二进制输出,并传递给st.download_button(),以便用户可以下载PowerPoint文件(在此处阅读更多信息)。

以下是前端的下载按钮的样子:

作者提供的图片

以下是代码:

from pptx import Presentation
from io import BytesIO
from datetime import date
import streamlit as st

# 创建文件名
filename = '{name} {date}.pptx'.format(name='Company 1', date=date.today())

# 将演示文稿保存为二进制输出
binary_output = BytesIO()
prs.save(binary_output)

# 显示成功消息和下载按钮
st.success('幻灯片已生成!:tada:')
st.download_button(label='点击下载 PowerPoint',                   data=binary_output.getvalue(),                   file_name=filename)

在此处查看示例演示文稿。

总结

感谢您阅读到最后!现在您可以使用 Streamlit、Snowflake、YahooFinance 和 LangChain 开发一款公司研究的幻灯片自动生成应用程序。希望本文能给您带来新的有用信息。

正如您所见,该应用程序存在一些限制。首先,它只能生成关于公共公司的研究报告。其次,GPT 模型使用有关产品的一般知识(例如 ChatBot 或会计软件)来编写价值主张。在更高级的应用程序中,可以通过使用产品数据对模型进行微调来解决第二个限制。可以通过将产品详细信息作为提示传递或将这些数据存储为向量数据库中的嵌入来实现此目的(在此处阅读更多信息)。

如果您有任何问题或反馈,请在下方评论区留言或通过 GitHub、LinkedIn 或 Twitter 联系我。

  1. 一个以占位符为键,替换文本为值的字典
  2. 一个 PowerPoint 幻灯片对象

使用之前步骤中定义的 swot_dict 变量:

Leave a Reply

Your email address will not be published. Required fields are marked *