财务报告对于评估公司的健康状况至关重要。它们长达数百页,很难有效地提取具体的见解。分析师和投资者要花费数小时翻阅资产负债表、损益表和脚注,只为回答一些简单的问题,如:2024 年公司的收入是多少?随着 LLM 模型和矢量搜索技术的最新进展,我们可以使用 LlamaIndex 和相关框架自动进行财务报告分析。这篇博文将探讨我们如何使用 LlamaIndex、ChromaDB、Gemini2.0 和 Ollama 构建一个强大的财务 RAG 系统,精确地回答来自冗长报告的查询。
学习目标
- 了解高效分析对财务报告检索系统的需求。
- 了解如何使用 LlamaIndex 对财务报告进行预处理和矢量化。
- 探索 ChromaDB,为文档检索构建强大的向量数据库。
- 使用 Gemini 2.0 和 Llama 3.2 为金融数据分析实施查询引擎。
- 使用 LlamaIndex 探索高级查询路由技术,以增强洞察力。
为什么需要财务报告检索系统?
财务报告包含有关公司业绩的重要信息,包括收入、支出、负债和盈利能力。然而,这些报告篇幅巨大、冗长,而且充满专业术语,分析师、投资者和高管手动提取相关信息非常耗时。
财务报告检索系统可通过自然语言查询实现这一过程的自动化。用户可以简单地提出 “2023 年的收入是多少?”或 “总结一下 2023 年的流动性问题”等问题,而无需搜索 PDF 文件。系统会快速检索并总结相关部分,从而节省人工操作的时间。
项目实施
要实施项目,我们首先需要设置环境并安装所需的库:
步骤 1:设置环境
首先,我们将创建一个用于开发工作的 conda 环境。
$conda create --name finrag python=3.12 $conda activate finrag
步骤 2:安装必要的Python库
安装 libraires 是任何项目实施的关键步骤:
$pip install llama-index llama-index-vector-stores-chroma chromadb $pip install llama-index-llms-gemini llama-index-llms-ollama $pip install llama-index-embeddings-gemini llama-index-embeddings-ollama $pip install python-dotenv nest-asyncio pypdf
步骤 3:创建项目目录
现在创建一个项目目录,并创建一个名为 .env 的文件,在该文件中放入所有 API 密钥,以便安全管理 API 密钥。
# on .env file GOOGLE_API_KEY="<your-api-key>"
我们从 .env 文件加载环境变量,以安全地存储敏感的 API 密钥。这将确保我们的双子座应用程序接口(Gemini API)或谷歌应用程序接口(Google API)始终受到保护。
我们将使用 Jupyter Notebook 完成项目。创建一个 Jupyter Notebook 文件,然后开始逐步实施。
步骤 4:加载API密钥
现在,我们将加载下面的 API 密钥:
import os from dotenv import load_dotenv load_dotenv() GEMINI_API_KEY = os.getenv("GOOGLE_API_KEY") # Only to check .env is accessing properly or not. # print(f"GEMINI_API_KEY: {GEMINI_API_KEY}")
现在,我们的环境已经准备就绪,可以进入下一个最重要的阶段了。
使用Llamaindex处理文件
从 AnnualReports 网站收集赛车游戏公司的财务报告。
点击此处下载。
第一页看起来像
Source: Report
这些报告总共有 123 页,但我只需将报告中的财务报表提取出来,然后为我们的项目创建一个新的 PDF。
我是怎么做的呢?使用 PyPDF 库非常简单。
from pypdf import PdfReader from pypdf import PdfWriter reader = PdfReader("NASDAQ_MSGM_2023.pdf") writer = PdfWriter() # page 66 to 104 have financial statements. page_to_extract = range(66, 104) for page_num in page_to_extract: writer.add_page(reader.pages[page_num]) output_pdf = "Motorsport_Games_Financial_report.pdf" with open(output_pdf, "wb") as outfile: writer.write(output_pdf) print(f"New PDF created: {output_pdf}")
新报告文件只有 38 页,这有助于我们快速嵌入文件。
加载和分割财务报告
在项目数据目录中,放入新创建的 Motorsport_Games_Financial_report.pdf 文件,该文件将为项目编制索引。
财务报告通常为 PDF 格式,包含大量表格数据、脚注和法律声明。我们使用 LlamaIndex 的 SimpleDirectoryReader 来加载这些文件并将其转换为文档。
from llama_index.core import SimpleDirectoryReader documents = SimpleDirectoryReader("./data").load_data()
由于报告的篇幅非常大,无法作为单个文档进行处理,因此我们将其分割成较小的块或节点。每个小块对应一个页面或部分,有助于更有效地检索。
from copy import deepcopy from llama_index.core.schema import TextNode def get_page_nodes(docs, separator="\n---\n"): """Split each document into page node, by separator.""" nodes = [] for doc in docs: doc_chunks = doc.text.split(separator) for doc_chunk in doc_chunks: node = TextNode( text=doc_chunk, metadata=deepcopy(doc.metadata), ) nodes.append(node) return nodes
要了解文件提取过程,请参阅下图。
现在,我们的财务数据已经准备好进行矢量化和存储以备检索。
使用ChromaDB建立矢量数据库
我们将使用 ChromaDB 快速、准确地建立本地矢量数据库。我们的金融文本嵌入式表示法将存储到 ChromaDB 中。
我们将初始化矢量数据库,并使用 Ollama 配置 Nomic-embed-text 模型,以生成本地嵌入。
import chromadb from llama_index.llms.gemini import Gemini from llama_index.embeddings.ollama import OllamaEmbedding from llama_index.vector_stores.chroma import ChromaVectorStore from llama_index.core import Settings embed_model = OllamaEmbedding(model_name="nomic-embed-text") chroma_client = chromadb.PersistentClient(path="./chroma_db") chroma_collection = chroma_client.get_or_create_collection("financial_collection") vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
最后,我们使用 LLamaIndex 的 VectorStoreIndex 创建一个矢量索引。该索引将我们的矢量数据库与 LlamaIndex 的查询引擎连接起来。
from llama_index.core import VectorStoreIndex, StorageContext storage_context = StorageContext.from_defaults(vector_store=vector_store) vector_index = VectorStoreIndex.from_documents(documents=documents, storage_context=storage_context, embed_model=embed_model)
上述代码将使用 nomic-embed-text 从金融文本文件中创建矢量索引。这需要时间,具体取决于本地系统的规范。
索引创建完成后,您就可以在必要时重复使用嵌入的代码,而无需重新创建索引。
vector_index = VectorStoreIndex.from_vector_store( vector_store=vector_store, embed_model=embed_model )
这将允许你使用存储中的 chromadb 嵌入文件。
现在,我们的重载工作已经完成,是时候查询报告并放松一下了。
使用Gemini 2.0查询财务数据
一旦我们的财务数据建立了索引,我们就可以提出自然语言问题并得到准确的答案。我们将使用 Gemini-2.0 Flash 模型进行查询,该模型可与我们的矢量数据库交互,获取相关部分并生成有见地的回复。
设置Gemini 2.0
from llama_index.llms.gemini import Gemini llm = Gemini(api_key=GEMINI_API_KEY, model_name="models/gemini-2.0-flash")
使用带有矢量索引的Gemini 2.0启动查询引擎
query_engine = vector_index.as_query_engine(llm=llm, similarity_top_k=5)
示例查询和响应
下面是多个查询和不同的响应:
查询-1
response = query_engine.query("what is the revenue of on 2022 Year Ended December 31?") print(str(response))
响应
来自报告的相应图片:
查询-2
response = query_engine.query( "what is the Net Loss Attributable to Motossport Games Inc. on 2022 Year Ended December 31?" ) print(str(response))
响应
来自报告的相应图片:
查询-3
response = query_engine.query( "What are the Liquidity and Going concern for the Company on December 31, 2023" ) print(str(response))
响应
查询-4
response = query_engine.query( "Summarise the Principal versus agent considerations of the company?" ) print(str(response))
响应
来自报告的相应图片:
查询-5
response = query_engine.query( "Summarise the Net Loss Per Common Share of the company with financial data?" ) print(str(response))
响应
来自报告的相应图片:
查询-6
response = query_engine.query( "Summarise Property and equipment consist of the following balances as of December 31, 2023 and 2022 of the company with financial data?" ) print(str(response))
响应
来自报告的相应图片:
查询-7
response = query_engine.query( "Summarise The Intangible Assets on December 21, 2023 of the company with financial data?" ) print(str(response))
响应
查询-8
response = query_engine.query( "What are leases of the company with yearwise financial data?" ) print(str(response))
响应
来自报告的相应图片:
使用Llama 3.2进行本地查询
在本地利用 Llama 3.2 查询财务报告,而无需依赖基于云的模型。
设置Llama 3.2:1b
local_llm = Ollama(model="llama3.2:1b", request_timeout=1000.0) local_query_engine = vector_index.as_query_engine(llm=local_llm, similarity_top_k=3)
查询-9
response = local_query_engine.query( "Summary of chart of Accrued expenses and other liabilities using the financial data of the company" ) print(str(response))
响应
来自报告的相应图片:
使用LlamaIndex进行高级查询路由选择
有时,我们既需要详细的检索,也需要总结性的见解。我们可以通过结合矢量索引和摘要索引来实现这一点。
- 矢量索引用于精确的文档检索
- 摘要索引用于简明的财务摘要
我们已经建立了矢量索引,现在我们将创建一个摘要索引,使用分层方法来总结财务报表。
from llama_index.core import SummaryIndex summary_index = SummaryIndex(nodes=page_nodes)
然后集成 RouterQueryEngine,它可根据查询类型有条件地决定是从摘要索引还是矢量索引。
from llama_index.core.tools import QueryEngineTool from llama_index.core.query_engine.router_query_engine import RouterQueryEngine from llama_index.core.selectors import LLMSingleSelector
现在创建摘要查询引擎
summary_query_engine = summary_index.as_query_engine( llm=llm, response_mode="tree_summarize", use_async=True )
该摘要查询引擎将被集成到摘要工具中,而矢量查询引擎将被集成到矢量工具中。
# Creating summary tool summary_tool = QueryEngineTool.from_defaults( query_engine=summary_query_engine, description=( "Useful for summarization questions related to Motorsport Games Company." ), ) # Creating vector tool vector_tool = QueryEngineTool.from_defaults( query_engine=query_engine, description=( "Useful for retriving specific context from the Motorsport Games Company." ), )
这两种工具都已完成,现在我们通过路由器将这些工具连接起来,这样当查询通过路由器时,路由器就会通过分析用户查询来决定使用哪种工具。
# Router Query Engine adv_query_engine = RouterQueryEngine( llm=llm, selector=LLMSingleSelector.from_defaults(llm=llm), query_engine_tools=[summary_tool, vector_tool], verbose=True, )
我们的高级查询系统已全部安装完毕,现在可查询我们新推出的高级查询引擎。
查询-10
response = adv_query_engine.query( "Summarize the charts describing the revenure of the company." ) print(str(response))
响应
您可以看到,我们的智能路由器会决定使用摘要工具,因为用户在查询中要求摘要。
查询-11
response = adv_query_engine.query("What is the Total Assets of the company Yearwise?") print(str(response))
响应
在这里,路由器选择了矢量工具,因为用户询问的是具体信息,而不是摘要。
本文使用的所有代码都在这里。
小结
我们可以利用 LlamaIndex、ChromaDB 和高级 LLM 高效分析财务报告。该系统可实现自动财务洞察、实时查询和强大的汇总功能。这类系统使财务分析更方便、更高效,从而在投资、交易和经营过程中做出更好的决策。
- 由 LLM 驱动的文档检索系统可大幅减少分析复杂财务报告所花费的时间。
- 使用云和本地 LLM 的混合方法可确保系统设计的成本效益、隐私保护和灵活性。
- LlamaIndex 的模块化框架可以轻松实现财务报告整理工作流程的自动化。
- 这类系统可适用于法律文件、医疗报告和监管备案等不同领域,因此是一种通用的 RAG 解决方案。
常见问题
Q1. 系统如何处理不同的财务报告?
A. 系统设计用于处理任何结构化的财务文件,将其分解为文本块,嵌入并存储在 ChromaDB 中。新报告可以动态添加,无需重新建立索引。
Q2. 能否将其扩展到生成财务图表和可视化效果?
A. 可以,通过集成 Matplotlib、Pandas 和 Streamlit,您可以将收入增长、净亏损分析或资产分布等趋势可视化。
Q3. 查询路由系统如何提高准确性?
A. RouterQueryEngine 会自动检测查询是否需要汇总响应或特定的财务数据检索。这样可以减少不相关的输出,确保回复的准确性。
Q4. 该系统是否适用于实时财务分析?
A. 可以,但这取决于向量存储更新的频率。您可以使用 OpenAI 嵌入式应用程序接口(API)持续摄取管道,动态查询实时财务报告。
评论留言