微信扫码
与创始人交个朋友
我要投稿
LLM 在金融领域的一个引人入胜的用例是在公司年度报告(10-K表格)中使用它们。这些报告是公开可用的信息,几乎每个投资组合经理、财务分析师和股东都会定期使用这些信息来做出明智的决策。
然而,阅读、理解和评估这些报告,尤其是对于多家公司来说,可能是乏味和耗时的。因此,在年度报告中使用 LLM 来提取见解和总结将解决很多问题并节省宝贵的时间。
当 Streamlit LLM Hackathon 宣布时,我认为这是探索这个想法的最佳时机。这就是 FinSight 的诞生方式。
FinSight 有两个主要功能,分别称为年度报告分析器和财务指标审查,但在这篇博文中,我们将专注于前者。
年度报告分析器是基于 RAG(Retrieval Augmented Generation)的功能,这意味着 LLM 将根据知识库(在本例中为公司的年度报告)中的信息生成见解。以下是系统设计工作原理:
虽然这是架构的基本表示形式,但我们将深入探讨每个组件的重要性以及它们的工作原理。
我们将使用 LlamaIndex 来构建知识库,并使用 LLM 对其进行查询(gpt-4 最适合)。LlamaIndex 是一个简单、灵活的数据框架,用于将自定义数据源连接到大型语言模型。
对于前端,Streamlit 是构建和共享 Web 应用程序的最方便的工具。
克隆存储库
git clone <https://github.com/vishwasg217/finsight.git>cd finsight
设置虚拟环境
# For macOS and Linux:
python3 -m venv venv
# For Windows:
python -m venv venv
激活虚拟环境
# For macOS and Linux:source venv/bin/activate# For Windows:.\\venv\\Scripts\\activate
安装所需的依赖项:
pip install -r requirements.txt
设置环境变量:
# create directorymkdir .streamlit# create toml filetouch .streamlit/secrets.toml
您可以在此处获取 API 密钥:AlphaVantage、OpenAI、
# Add the following API keysav_api_key = "ALPHA_VANTAGE API KEY"openai_api_key = "OPEN AI API KEY"
尽管 LlamaIndex 有自己的一组数据连接器来读取 PDF,但我们仍然需要编写一个小函数来加载 PDF,因为我们是通过 Streamlit 完成的。process_pdf()
from pypdf import PdfReader
from llama_index.schema import Document
def process_pdf(pdf):
file = PdfReader(pdf)
text = ""
for page in file.pages:
text += str(page.extract_text())
doc = Document(text=text)
return [doc]
下一步是引入此文档,将其索引并存储在向量数据库中。在这种情况下,我们将使用 FAISS DB,正如我们在内存向量数据库中所要求的那样。FAISS使用起来也非常方便。因此,我们编写了一个函数来做到这一点。get_vector_index()
如果您有兴趣查看其他矢量数据库选项,请阅读此内容。
from llama_index.llms import OpenAI
from llama_index import VectorStoreIndex, ServiceContext, StorageContext
from llama_index.vector_stores import FaissVectorStore
def get_vector_index(documents):
llm = OpenAI(OPENAI_API_KEY)
faiss_index = faiss.IndexFlatL2(d)
vector_store = FaissVectorStore(faiss_index=faiss_index)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
service_context = ServiceContext.from_defaults(llm=llm)
index = VectorStoreIndex.from_documents(documents,
service_context=service_context,
storage_context=storage_context
)
return index
ServiceContext()并用于设置向量存储的配置。使用,我们将文档摄取、索引并存储为 FAISS 数据库中的向量嵌入。StorageContext()VectorStoreIndex.from_documents()
# Calling the functions through streamlit frontend
import streamlit as st
if "index" not in st.session_state:
st.session_state.index = None
if "process_doc" not in st.session_state:
st.session_state.process_doc = False
if st.sidebar.button("Process Document"):
with st.spinner("Processing Document..."):
documents = process_pdf(pdfs)
st.session_state.index = get_vector_index(documents)
st.session_state.process_doc = True
st.toast("Document Processsed!")
现在我们已经准备好了知识库,是时候构建一个机制来查询它了。
index = get_vector_index(documents)engine = index.as_query_engine()query = "How has Microsoft performed in this fiscal year?"response = engine(query)
理想情况下,上述代码应该足以从向量数据库中的信息查询和合成响应。但是,回答不够全面和详细,尤其是对于此类开放式问题。我们需要开发一种更好的机制,使我们能够将查询分解为更详细的问题,并从向量数据库的多个部分检索上下文。
def get_query_engine(engine):
query_engine_tools = [
QueryEngineTool(
query_engine=engine,
metadata=ToolMetadata(
name="Annual Report",
description=f"Provides information about the company from its annual report.",
),
),
]
s_engine = SubQuestionQueryEngine.from_defaults(query_engine_tools=query_engine_tools)
return s_engine
index = get_vector_index(documents)
engine = index.as_query_engine()
s_engine = get_query_engine(engine)
让我们分解一下上面的功能。该模块环绕 并帮助向引擎提供上下文和元数据。当您有多个引擎并且希望向 LLM 提供上下文以了解将哪个引擎用于给定查询时,这尤其有用。QueryEngineToolengine
下面是它的样子:
# example for multiple query engine toolsquery_engine_tools = [QueryEngineTool(query_engine=sept_engine,metadata=ToolMetadata(name="sept_22",description="Provides information about Uber quarterly financials ending September 2022",),),QueryEngineTool(query_engine=june_engine,metadata=ToolMetadata(name="june_22",description="Provides information about Uber quarterly financials ending June 2022",),)]
但是,我们目前只坚持使用一个 QueryEnginerTool。
该模块将复杂的查询分解为许多子问题及其目标查询引擎以供执行。在执行完所有子问题后,所有响应都会被收集并发送到响应合成器以生成最终响应。使用此模块至关重要,因为从年度报告生成见解需要复杂的查询,这些查询需要从向量数据库中的多个节点检索信息。SubQuestionQueryEngine
快速工程对整个过程至关重要,主要有两个原因:
通过编写精确且相关的查询,使代理清楚地了解它需要从向量数据库中检索的内容
然后,通过为要生成的输出提供结构和描述来控制从检索到的上下文生成的输出的质量。
这两个点都是通过使用 和 模块来处理的。PromptTemplatePydanticOutputParserlangchain
使用 我们为要生成的见解的不同部分编写描述。在与财务专家进行了几次对话后,我总结了对这 4 个部分的见解:不同的部分:财政年度亮点、战略展望和未来方向、风险管理、创新和研发。现在,让我们为这些部分编写类:PydanticOutputParserpydantic
from pydantic import BaseModel, Field
class FiscalYearHighlights(BaseModel):
performance_highlights: str = Field(..., description="Key performance metrics and financial stats over the fiscal year.")
major_events: str = Field(..., description="Highlight of significant events, acquisitions, or strategic shifts that occurred during the year.")
challenges_encountered: str = Field(..., description="Challenges the company faced during the year and, if and how they managed or overcame them.")
class StrategyOutlookFutureDirection(BaseModel):
strategic_initiatives: str = Field(..., description="The company's primary objectives and growth strategies for the upcoming years.")
market_outlook: str = Field(..., description="Insights into the broader market, competitive landscape, and industry trends the company anticipates.")
class RiskManagement(BaseModel):
risk_factors: str = Field(..., description="Primary risks the company acknowledges.")
risk_mitigation: str = Field(..., description="Strategies for managing these risks.")
class InnovationRnD(BaseModel):
r_and_d_activities: str = Field(..., description="Overview of the company's focus on research and development, major achievements, or breakthroughs.")
innovation_focus: str = Field(..., description="Mention of new technologies, patents, or areas of research the company is diving into.")
注意:这些部分及其描述适用于通用用例。它们可以根据您的特定需求进行更改。
这些 pydantic 类将向提示提供每个部分的格式和描述。因此,让我们编写一个函数,允许我们将任何pydantic类插入到提示符中:
from langchain.prompts import PromptTemplate
from langchain.output_parsers import PydanticOutputParser
prompt_template = """
You are given the task of generating insights for {section} from the annual report of the company.
Given below is the output format, which has the subsections.
Must use bullet points.
Always use $ symbol for money values, and round it off to millions or billions accordingly
Incase you don't have enough info you can just write: No information available
---
{output_format}
---
"""
def report_insights(engine, section, pydantic_model):
parser = PydanticOutputParser(pydantic_object=pydantic_model)
prompt_template = PromptTemplate(
template=prompt_template,
input_variables=["section"],
partial_variables={"output_format": parser.get_format_instructions()}
)
formatted_input = prompt_template.format(section=section)
response = engine.query(formatted_input)
parsed_response = parser.parse(response.response)
return parsed_response
PromptTemplate将所有值(如 和 )插入到提示模板中。将 pydantic 类转换为 LLM 可读的格式。生成的响应将采用字符串格式,因此我们使用该函数来解析响应并获得结构化输出。sectionoutput_formatPydanticOutputParserparser.parse()
# calling the function in streamlit frontend
if st.session_state.process_doc:
if st.button("Analyze Report"):
engine = get_query_engine(st.session_state.index.as_query_engine(similarity_top_k=3))
with st.status("**Analyzing Report...**"):
st.write("Fiscal Year Highlights...")
st.session_state.fiscal_year_highlights = report_insights(engine, "Fiscal Year Highlights", FiscalYearHighlights)
st.write("Strategy Outlook and Future Direction...")
st.session_state.strategy_outlook_future_direction = report_insights(engine, "Strategy Outlook and Future Direction", StrategyOutlookFutureDirection)
st.write("Risk Management...")
st.session_state.risk_management = report_insights(engine, "Risk Management", RiskManagement)
st.write("Innovation and R&D...")
st.session_state.innovation_and_rd = report_insights(engine, "Innovation and R&D", InnovationRnD)
# displaying the generated insights
if st.session_state.fiscal_year_highlights:
with tab1:
st.write("## Fiscal Year Highlights")
st.write("### Performance Highlights")
st.write(st.session_state.fiscal_year_highlights.performance_highlights)
st.write("### Major Events")
st.write(st.session_state.fiscal_year_highlights.major_events)
st.write("### Challenges Encountered")
st.write(st.session_state.fiscal_year_highlights.challenges_encountered)
st.write("### Milestone Achievements")
st.write(str(st.session_state.fiscal_year_highlights.milestone_achievements))
if st.session_state.strategy_outlook_future_direction:
with tab2:
st.write("## Strategy Outlook and Future Direction")
st.write("### Strategic Initiatives")
st.write(st.session_state.strategy_outlook_future_direction.strategic_initiatives)
st.write("### Market Outlook")
st.write(st.session_state.strategy_outlook_future_direction.market_outlook)
st.write("### Product Roadmap")
st.write(st.session_state.strategy_outlook_future_direction.product_roadmap)
if st.session_state.risk_management:
with tab3:
st.write("## Risk Management")
st.write("### Risk Factors")
st.write(st.session_state.risk_management.risk_factors)
st.write("### Risk Mitigation")
st.write(st.session_state.risk_management.risk_mitigation)
if st.session_state.innovation_and_rd:
with tab4:
st.write("## Innovation and R&D")
st.write("### R&D Activities")
st.write(st.session_state.innovation_and_rd.r_and_d_activities)
st.write("### Innovation Focus")
st.write(st.session_state.innovation_and_rd.innovation_focus)
选择和存储见解:我一直在开发一项功能,允许用户选择所需的任何见解并将其保存到用户的帐户中
添加更多特定于职业的见解:目前,该见解适用于通用目的。然而,不同的职业对年度报告的使用方式不同,因此我自然需要根据用户的用例创建一组不同的见解。
PandasQueryEngine用于查询财务报表的模块:使用此模块,LLM将能够从通常采用结构化格式的财务报表中提取更好的见解。
总之,FinSight 的年度报告分析器通过利用 LLM 的强大功能使财务分析更容易、更有洞察力。对于投资组合经理、财务分析师和股东来说,这是一个有价值的工具,可以节省时间并改善决策。虽然核心管道保持一致,但请注意,我们部署的应用代码可能会不断发展,以包含升级和增强功能,从而确保持续改进。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-04-30
2024-07-18
2024-07-04
2024-07-10
2024-06-11
2024-06-20
2024-03-29
2024-07-04
2024-06-29
2024-07-10