成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

基于Agent的金融問答系統:代碼重構 原創

發布于 2024-11-25 10:28
瀏覽
0收藏

前言

在上一章??【項目實戰】基于Agent的金融問答系統:前后端流程打通??,我們已經完成了金融問答系統的前后端搭建,形成了可用的Demo。本章,我們將介紹代碼重構的過程,并介紹一些優化點。

代碼重構簡介

在開啟本章介紹之前,請允許我花點時間啰嗦兩句,聊一聊代碼重構的哪些事兒。

在過去經歷的項目中,代碼重構很少被人重視。看著像??一樣的代碼(抱歉爆粗口,我所經歷的一些項目包括我自己曾經寫的代碼,回看確實像??一樣),它們并沒有被好好清理,然后我們在??上面不斷加需求,導致需求迭代越來越難,Bug越來越多...

這種事情現在每天還在不斷地發生著,所以我決定有必要聊一聊代碼重構。

什么是代碼重構

代碼重構是指對現有代碼進行修改,以改善其結構、可讀性和可維護性,而不改變其外部行為。重構的主要目的是提高代碼質量,使其更易于理解和擴展。

代碼重構的目的

? 提高可讀性:使代碼更易于理解,便于團隊成員快速上手。

? 增強可維護性:降低后續修改和擴展的難度,減少潛在的錯誤。

? 優化性能:在不改變功能的情況下,提升代碼的執行效率。

? 消除重復代碼:通過抽象和重用,減少冗余,提高代碼的整潔性。

代碼重構的重要性

據統計,不好的代碼會占用更多開發的時間。

基于Agent的金融問答系統:代碼重構-AI.x社區

代碼重構的難點

通過代碼重構提升代碼質量既然如此重要,那么為什么很少有項目開展呢?

究其原因,可能有三點:

? 第一種:沒有精力重構。開發工程師經常性被老板或者產品牽著鼻子走,完成一個需求接著一個新的需求,所以很少開展重構工作。這種情況在技術性為導向的項目還好,在以產品或市場為導向的項目中,尤其嚴重。

? 第二種:沒有重構的思維。很多的開發工程師沒有重構的思維甚至想法,他們以完成需求交付為目的,需求交付了也就代表他的工作結束了。

我曾經與谷歌回來的一位朋友有次交流,我們探討的內容是:為什么國內的研發人員代碼質量意識薄弱?他說其中一個很重要的原因是:硅谷的很多從業者,是因為熱愛,熱愛編程、熱愛技術,所以視自己寫的代碼為一件藝術品,力求精益求精;而國內有很多從業者,是因為生存,是因為做開發給錢多,是一份養家糊口的一份工作而已,因為缺少熱愛,所以交差了事即可。對此,我深以為然。 ? 第三種:沒有重構的方法論。雖然我們很像做重構,但是重構工作就像修復一輛越開越慢的車子,如果沒有科學的方法,有可能出現拆了重裝之后,反而多了幾個螺絲的問題,這會讓老板更加恐怖。

本章,我將試圖以這個金融問答系統為例,簡單介紹一些代碼重構的原則、方法。

代碼重構的過程

1、搭建測試框架以及用例集

在開展代碼重構前,我們要搭建好一個便于回歸測試的測試框架,通過邊重構邊回歸的方式,可以快速定位問題所在,以此降低問題排查的成本。

我們在app目錄下,已經創建了一個test_framework.py中,繼續補充測試用例集,例如:

基于Agent的金融問答系統:代碼重構-AI.x社區

在大廠中,回歸測試一般會使用單元測試框架(如pytest)來進行執行,由于本例中我們的方法較為簡單,所以就沒有使用pytest。

2、消滅代碼中的壞味道

2.1、統一管理配置相關內容

在之前實現的RAG管理模塊中,有很多的配置是硬編碼寫在代碼初始化中的,例如:

# 原始的rag.py
class RagManager:
    def __init__(self,
                 chroma_server_type="http",
                 host="localhost", port=8000,
                 persist_path="chroma_db",
                 llm=None, embed=None):
        self.llm = llm
        self.embed = embed

        chrom_db = ChromaDB(chroma_server_type=chroma_server_type,
                            host=host, port=port,
                            persist_path=persist_path,
                            embed=embed)
        self.store = chrom_db.get_store()

我們可以將所有的配置相關抽取到一個settings.py中,然后在使用的代碼中通過引用settings.py來進行配置。

# setttings.py

"""
Chroma向量數據庫使用時的相關的配置
"""
# 默認的ChromaDB的服務器類別
CHROMA_SERVER_TYPE ="http"
# 默認本地數據庫的持久化目錄
CHROMA_PERSIST_DB_PATH ="chroma_db"

CHROMA_HOST = os.getenv("CHROMA_HOST","localhost")
CHROMA_PORT =int(os.getenv("CHROMA_PORT",8000))
CHROMA_COLLECTION_NAME ="langchain"

說明:

  • 為了有別于變量的命名,對于配置我們使用大寫的變量名,例如:CHROMA_HOST、CHROMA_PORT等。

# 重構的rag.py
import settings


classRagManager:
def__init__(self,
                 vector_db_class=ChromaDB,  # 默認使用 ChromaDB
                 db_config=None,  # 數據庫配置參數
                 llm=None, embed=None,
                 retriever_cls=SimpleRetrieverWrapper, **retriever_kwargs):
        self.llm = llm
        self.embed = embed
        logger.info(f'初始化llm大模型:{self.llm}')
        logger.info(f'初始化embed模型:{self.embed}')

# 如果沒有提供 db_config,使用默認配置
if db_config isNone:
            db_config ={
"chroma_server_type": settings.CHROMA_SERVER_TYPE,
"host": settings.CHROMA_HOST,
"port": settings.CHROMA_PORT,
"persist_path": settings.CHROMA_PERSIST_DB_PATH,
"collection_name": settings.CHROMA_COLLECTION_NAME
}
            logger.info(f'初始化向量數據庫配置:{db_config}')

# 創建向量數據庫實例
        self.vector_db = vector_db_class(**db_config, embed=self.embed)
        self.store = self.vector_db.get_store()

說明:

? 上述代碼中通過import settings,在使用配置時通過settings.CHROMA_SERVER_TYPE、settings.CHROMA_HOST等來引用。

2.2、處理參數過長的問題

在原始代碼中,隨著我們的需求迭代,在創建RAG時需要傳入多個的參數,例如:

? chroma_server_type

? host

? port

? persist_path

? collection_name

如果按照原來的方法寫函數,那么函數的參數列表就會非常長,如下:


RagManager(chroma_server_type="http", host="localhost", port=8000, persist_path="chroma_db", collection_name="langchain",llm , embed)

對于這種參數的問題,我們可以通過使用字典來處理,如下:


db_config = {
"chroma_server_type": settings.CHROMA_SERVER_TYPE,
"host": settings.CHROMA_HOST,
"port": settings.CHROMA_PORT,
"persist_path": settings.CHROMA_PERSIST_DB_PATH,
"collection_name": settings.CHROMA_COLLECTION_NAME,
}
RagManager(vector_db_class=ChromaDB, db_config=db_config, llm=self.llm, embed=self.embed)

說明:

? db_config是一個字典,可以包含多個配置參數,例如:chroma_server_type、host、port、persist_path、collection_name等。

? db_config中的參數可以通過**關鍵字來解包,從而傳入到函數中。

? RagManager 的初始化函數中,通過**關鍵字來解包db_config,從而傳入到ChromaDB的初始化函數中。

2.3、減少重復代碼

在【項目實戰】基于Agent的金融問答系統:RAG檢索模塊初建成中,我們曾實現了一個pdf_processor.py, 該函數主要的工作是:


def process_pdfs(self)# 處理pdf文件
defprocess_pdfs_group(self, pdf_files_group)# 分組處理pdf文件
defload_pdf_files(self)# 加載pdf文件
defload_pdf_content(self, pdf_path)# 讀取pdf文件內容
defsplit_text(self, documents)# 分割讀取到的文本
definsert_docs_chromadb(self, docs, batch_size)  # 向向量數據庫中插入數據

如果我們要將PDF文件給ElasticSearch服務里,那么這個過程大部分實現邏輯都是一樣的,只是插入的對象不同,一個是向向量數據庫中插入,一個是向elasticsearch中插入。

這種情況下,

? 不好的做法:復制上述代碼到一個新的函數中,然后將最后一步insert_docs_chromadb()改為insert_docs_elasticsearch(),這樣會導致代碼重復。

? 較好的做法:對上述的插入過程進行重構,將插入函數通過函數類來調用,通過一個參數vector_db_class來決定插入向量數據庫還是ElasticSearch。

重構后的pdf_processor.py
import os
import logging
import time
from tqdm import tqdm
from langchain_community.document_loaders importPyMuPDFLoader
from langchain_text_splitters importRecursiveCharacterTextSplitter
from rag.vector_db importVectorDB
from rag.elasticsearch_db importTraditionDB
from utils.logger_config importLoggerManager
logger =LoggerManager().logger
classPDFProcessor:
def__init__(self, directory, db_type='vector', **kwargs):
"""
初始化 PDF 處理器
:param directory: PDF 文件所在目錄
:param db_type: 數據庫類型 ('vector' 或 'es')
:param kwargs: 其他參數
"""
self.directory = directory  # PDF 文件所在目錄
self.db_type = db_type  # 數據庫類型
self.file_group_num = kwargs.get('file_group_num',20)# 每組處理的文件數
self.batch_num = kwargs.get('batch_num',6)# 每次插入的批次數量
self.chunksize = kwargs.get('chunksize',500)# 切分文本的大小
self.overlap = kwargs.get('overlap',100)# 切分文本的重疊大小
logger.info(f"""
初始化PDF文件導入器:
配置參數:
- 導入的文件路徑:{self.directory}
- 每次處理文件數:{self.file_group_num}
- 每批次處理樣本數:{self.batch_num}
- 切分文本的大小:{self.chunksize}
- 切分文本重疊大小:{self.overlap}
""")
根據數據庫類型初始化相應的客戶端
if db_type =='vector':
self.vector_db = kwargs.get('vector_db')# 向量數據庫實例
self.es_client =None
logger.info(f'導入的目標數據庫為:向量數據庫')elif db_type =='es':
self.vector_db =None
self.es_client = kwargs.get('es_client')# Elasticsearch 客戶端
logger.info(f'導入的目標數據庫為:ES數據庫')else:
raiseValueError("db_type must be either 'vector' or 'es'.")
defload_pdf_files(self):
這部分代碼未做修改,具體內容省略
defload_pdf_content(self, pdf_path):
這部分代碼未做修改,具體內容省略
defsplit_text(self, documents):
這部分代碼未做修改,具體內容省略
defprocess_pdfs(self):
這部分代碼未做修改,具體內容省略
definsert_docs(self, docs, insert_function, batch_size=None):
"""
將文檔插入到指定的數據庫,并顯示進度
:param docs: 要插入的文檔列表
:param insert_function: 插入函數
:param batch_size: 批次大小
"""
if batch_size isNone:
batch_size = self.batch_num
logging.info(f"Inserting {len(docs)} documents.")
    start_time = time.time()
    total_docs_inserted =0

    total_batches =(len(docs)+ batch_size -1)// batch_sizewith tqdm(total=total_batches, desc="Inserting batches", unit="batch")as pbar:
for i inrange(0,len(docs), batch_size):
batch = docs[i:i + batch_size]
insert_function(batch)# 調用傳入的插入函數
total_docs_inserted +=len(batch)計算并顯示當前的TPM
elapsed_time = time.time()- start_timeif elapsed_time >0:
tpm =(total_docs_inserted / elapsed_time)*60
pbar.set_postfix({"TPM":f"{tpm:.2f}"})
pbar.update(1)definsert_to_vector_db(self, docs):
"""
將文檔插入到 VectorDB
"""
self.vector_db.add_with_langchain(docs)
definsert_to_elasticsearch(self, docs):
"""
將文檔插入到 Elasticsearch
"""
self.es_client.add_documents(docs)
defprocess_pdfs_group(self, pdf_files_group):
讀取PDF文件內容
pdf_contents =[]for pdf_path in pdf_files_group:
讀取PDF文件內容
documents = self.load_pdf_content(pdf_path)將documents 逐一添加到pdf_contents
pdf_contents.extend(documents)將文本切分成小段
docs = self.split_text(pdf_contents)if self.db_type =='vector':
將文檔插入到 VectorDB
self.insert_docs(docs, self.insert_to_vector_db)elif self.db_type =='es':
將文檔插入到 Elasticsearch
self.insert_docs(docs, self.insert_to_elasticsearch)else:
raiseValueError("db_type must be either 'vector' or 'es'.")

說明:

? 在類的初始化函數中,我們通過一個參數vector_db來連接對應的數據庫實例,同時傳入db_type告知PDF處理器需要操作的數據庫類型。

? 在處理PDF文件時,我們通過參數db_type來決定插入向量數據庫還是ElasticSearch。

? 在插入文檔 insert_docs 中,根據上一步驟傳入的 insert_function 來調用具體的插入函數:如果是插入向量數據庫,則傳入的函數為self.insert_to_vector_db,那么調用時也會調用 insert_to_vector_db ;如果是插入ElasticSearch,則傳入的函數為self.insert_to_elasticsearch,那么調用時會調用 insert_to_elasticsearch 。

2.4、使用靜態掃描工具優化代碼風格

我們可以使用靜態掃描工具對代碼進行風格優化,如Pylint、Flake8等,一般情況下PyCharm中會自帶這些工具。

具體方法:

  1. 啟動PyCharm
  2. 打開工程時,選擇app目錄
  3. 打開任意.py文件后,右上角會有靜態掃描問題提示(如下圖)
  4. 根據靜態掃描的問題,進行代碼風格修正(常見代碼風格問題請見附錄部分)


基于Agent的金融問答系統:代碼重構-AI.x社區

3、回歸測試

在進行上面每一步重構時,都需要使用test_framework.py進行回歸測試,確保重構后的代碼沒有引入新的錯誤。

由于本項目重構細節的內容非常多,不能一一列舉,重構后的內容請查看Gitee或者Github倉庫的代碼。

?

本文轉載自公眾號一起AI技術 作者:Dongming

原文鏈接:??https://mp.weixin.qq.com/s/L3oBKllE7SiqdzBAwTXEiQ??

?著作權歸作者所有,如需轉載,請注明出處,否則將追究法律責任
收藏
回復
舉報
回復
相關推薦
主站蜘蛛池模板: caoporon| 99精品欧美一区二区三区综合在线 | 欧美精品一区二区三区蜜桃视频 | 亚洲三级av | 97伦理最新伦理 | 一级电影免费看 | 国产7777| 亚卅毛片 | 色婷婷国产精品 | 中文成人无字幕乱码精品 | 天天天天天操 | 视频在线h| 午夜国产一级片 | 午夜av成人 | 91精品国产91久久综合桃花 | 久久日韩精品 | 国产在线观看 | 亚洲成人精品免费 | 亚洲综合一区二区三区 | 免费的av网站 | 在线免费观看黄网 | 黄色亚洲网站 | 亚洲第一av | 亚洲成人午夜电影 | 亚洲欧美日韩高清 | 成人久久久 | 福利视频一二区 | 黄色免费网 | 亚洲欧美日韩系列 | 国产精品久久久久一区二区三区 | 国产精品免费大片 | 国产一级片免费视频 | 黄色骚片| 欧美激情视频一区二区三区免费 | 国产区视频在线观看 | 亚洲国产日韩一区 | 噜久寡妇噜噜久久寡妇 | 亚洲第一色站 | 久久久久综合 | 成人黄色网址大全 | 日韩伦理一区二区 |