DataWhale wow-agent task 2: llama-index搭建电商数据库查询&课本问答agent

创建demo agent

llama-index默认调用OpenAI大模型,调用其他模型需要继承CustomLLM类自定义一个类。教程中还实现了一个流式输出的功能,即各大ai聊天平台上的打字机效果。这节课构建的agent暂且不需要记忆对话历史。

llama-index库之于openai库的优势

方便连接外部数据

SQL数据库操作

  • 配置对话模型
  • 创建数据库对话引擎
    llama中的NLSQLTableQueryEngine本身就是一个特殊的查询agent,能够将自然语言转换为sql语句并返回结果。

代码见后文实例1。

文档RAG(检索增强生成)

  • 配置对话模型(llm)和嵌入模型(embedding)

    嵌入模型(embedding)将文本映射成高维向量,已经过训练,能反映词句间关系。这使得模型能匹配问题和文档中答案。
    根据wow-rag教程,有四种方法配置,但我只想直接调包…

base_url = "https://open.bigmodel.cn/api/paas/v4/"
chat_model = "glm-4-flash"
emb_model = "embedding-3"
from llama_index.llms.zhipuai import ZhipuAI
llm = ZhipuAI(
    api_key = api_key,
    model = chat_model,
)
from llama_index.embeddings.zhipuai import ZhipuAIEmbedding
embedding = ZhipuAIEmbedding(
    api_key = api_key,
    model = emb_model,
)
  • 读取文件
from llama_index.core import SimpleDirectoryReader,Document
documents = SimpleDirectoryReader(input_files=['./docs/macro.md']).load_data()

  • 构建节点
    让每个文本片段大小适合向量化处理
from llama_index.core.node_parser import SentenceSplitter
transformations = [SentenceSplitter(chunk_size = 512)]

from llama_index.core.ingestion.pipeline import run_transformations
nodes = run_transformations(documents, transformations=transformations)

  • 构建索引
    • FAISS是Facebook开发的高效向量检索库
    • IndexFlatL2使用L2距离(欧氏距离)进行向量相似度计算
    • len(emb)获取向量维度(这里是1024维)
    • 将所有文本节点转换为向量并存储
    • embed_model指定使用的嵌入模型(这里是智谱AI的embedding-3)
    • 创建可检索的向量索引
from llama_index.vector_stores.faiss import FaissVectorStore
import faiss
from llama_index.core import StorageContext, VectorStoreIndex

emb = embedding.get_text_embedding("Hi.")
vector_store = FaissVectorStore(faiss_index=faiss.IndexFlatL2(len(emb)))
storage_context = StorageContext.from_defaults(vector_store=vector_store)

index = VectorStoreIndex(
    nodes = nodes,
    storage_context=storage_context,
    embed_model = embedding,
)
  • 构建问答引擎
# 构建检索器
from llama_index.core.retrievers import VectorIndexRetriever
# 想要自定义参数,可以构造参数字典
kwargs = {'similarity_top_k': 5, 'index': index, 'dimensions': len(emb)} # 必要参数
retriever = VectorIndexRetriever(**kwargs)

# 构建合成器
from llama_index.core.response_synthesizers  import get_response_synthesizer
response_synthesizer = get_response_synthesizer(llm=llm, streaming=True)

# 构建问答引擎
from llama_index.core.query_engine import RetrieverQueryEngine
engine = RetrieverQueryEngine(
      retriever=retriever,
      response_synthesizer=response_synthesizer,
        )

RAG基本流程

回答生成阶段
查询阶段
索引构建阶段
构建Prompt
ResponseSynthesizer组合:
系统提示词+上下文+问题
生成回答
向量化
用户问题
向量相似度检索
VectorIndexRetriever
检索Top-K相似片段
将向量转回文本
作为上下文输入
文本分块
文档
向量化
存入向量数据库
使用FAISS
L2距离索引

加强对agent的人为控制——Function Tool

这个类使我们能自定义agent的一部分工作流程,而不只让它走大模型,加强了对它的控制。

增强模型推理能力——ReActAgent

ReActAgent 通过结合推理(Reasoning)和行动(Acting)来创建动态的 LLM Agent 的框架。

这两个工具经常一起使用。ReActAgent可以不用自定义函数,只使用内置工具创建;而Function tool的用途是给ReActAgent提供更多工具。

基本用法:自定义函数并写清楚注释,加入Function Tool对象,进一步构建ReActAgent。

from llama_index.core.agent import ReActAgent  
from llama_index.core.tools import FunctionTool  
# 创建工具函数
def f1(a: float, b: float) -> float:  
    """注释"""  
    return ...

f1_tool = FunctionTool.from_defaults(fn=f1)  

def f2(a: float, b: float) -> float:  
    """注释"""  
    return ...

f2_tool = FunctionTool.from_defaults(fn=f2)

query_tool = QueryEngineTool.from_defaults(
    query_engine,
    name="...",  # 这是工具的名字,用于 ReActAgent 识别
    description="..."  # 描述这个工具的功能
)
# 构建ReActAgent
agent = ReActAgent.from_tools([f1_tool, f2_tool, query_tool], verbose=True)  
# 通过agent给出指令
response = agent.chat("...")
print(response)

实例1:电商数据库查询agent

使用从kaggle上下载的一个电商数据库https://www.kaggle.com/datasets/terencicp/e-commerce-dataset-by-olist-as-an-sqlite-database/data
DataWhale wow-agent task 2: llama-index搭建电商数据库查询&课本问答agent_第1张图片

准备工作:

#本地文件
from ourllama import OurLLM
from config import api_key, base_url, chat_model
#sql包
import sqlite3
from sqlalchemy import create_engine, select,inspect,text  
#llama_index包
from llama_index.core import SQLDatabase,Settings  
from llama_index.core.query_engine import NLSQLTableQueryEngine
  
llm = OurLLM(
		api_key=api_key, 
		base_url=base_url, 
		model_name=chat_model,
		temperature=0)#生成回答随机性最低
		
Settings.llm = llm
engine = create_engine("sqlite:///olist.sqlite")  

sql_database = SQLDatabase(engine)  
query_engine = NLSQLTableQueryEngine(  
    sql_database=sql_database,   
    embed_model='local',
    #tables=all_tables这样输出会更离谱
    tables=['customers','orders','order_reviews','order_items','products'],
    llm=Settings.llm  
)

为使数据库管理agent回答稳定,其所使用的大模型中的temperature参数应该被设置得很低(默认参数是0.1)。
直接query:

while True:
    query = input("请输入查询语句:")
    if query.lower() == "exit":
        break
    print(f"问题:\n{query}")
    try:
        response = query_engine.query(query)
        print(f"回答:\n{response}")
    except Exception as e:
        print(f"查询出错:{e}")

存储要问的问题方便对照

queries = [
    "客户总数",
    "最受欢迎的产品",
    "订单最多的城市",
    "订单最多的前五个城市"
]

# 执行查询
for query in queries:
    print(f"\n问题:{query}")
    try:
        response = query_engine.query(query)
        print(f"回答:\n{response}")
    except Exception as e:
        print(f"查询出错:{e}")

某几次运行出的比较合理的结果:

问题:
客户总数
回答:
The total number of customers is 99,441.
问题:
最受欢迎的产品
回答:
The most popular product based on the number of reviews is the one with the product ID 'aca2eb7d00ea1a7b8ebd4e68314663af'. This product has a length of 44 characters for its name, a description length of 903 characters, 6 photos, and a total of 524 reviews.
问题:
订单最多的城市
回答:
订单最多的城市是圣保罗(Sao Paulo),该城市共有15540个订单。
问题:订单最多的前五个城市
回答:
订单数量最多的前五个城市分别是:圣保罗(15540单),里约热内卢(6882单),贝洛奥里藏特(2773单),巴西利亚(2131单),库里蒂巴(1521单)。

离谱的结果

问题:最受欢迎的产品
回答:
Response: The query has successfully identified the product that is the most popular based on the number of orders it has received. Unfortunately, I cannot execute the SQL query and retrieve the specific product details. However, upon executing the query against the database, the top result would reveal the product ID, the length of its name and description, the quantity of photos it has, and the total count of orders placed for that product. The product with the highest order count is the most popular product.

使用reactagent

query_tool = QueryEngineTool.from_defaults(
    query_engine,
    name="order_analysis",  
    description="分析订单、客户和产品数据的工具" 
)

agent = ReActAgent.from_tools([query_tool])

for query in queries:
    print(f"\n问题:{query}")
    try:
        response = agent.chat(query)
        print(f"回答:\n{response}")
    except Exception as e:
        print(f"查询出错:{e}")
问题:客户总数
回答:
客户总数是 96,096。

问题:最受欢迎的产品
查询出错:Reached max iterations.

问题:订单最多的城市
回答:
订单最多的城市是圣保罗,该城市共有15,540个订单。

问题:订单最多的前五个城市
查询出错:Reached max iterations.

经常查询出错:Reached max iterations.
总之运行效果很差劲。
经过大佬提醒,去查了下包里的代码。我们分析可能的原因是模型上下文太长会导致输出不稳定。最简单的的解决方法是直接换模型(目前为止一直用的智谱)。

实例2:课本问答agent

拿手头的pdf体验一下RAG流程,比如一本宏观经济学。

llamaparse解析pdf

英文课本拖到官网https://cloud.llamaindex.ai/project/2c823e55-fd21-476b-9d21-b946d3f4c4f9/parse上可以直接转为txt或md

随便问一个课后题

question = '''Describe the possible effects of falling prices on
equilibrium income.'''
response = engine.query(question)
for text in response.response_gen:
    print(text,end='')

输出

Falling prices can have varied effects on equilibrium income, depending on the specific economic circumstances and theoretical models at play. In the context of the aggregate demand (AD) framework, a fall in the price level can lead to an increase in real money balances. This, in turn, can shift the LM curve to the right, which typically raises the equilibrium level of income.

However, there are several factors and theories to consider:

1. **Debt-Deflation Theory**: As mentioned, unexpected deflation can increase the real value of debt, leading to a redistribution of wealth from debtors to creditors. This may lead to reduced spending by debtors and thus a contractionary effect on income.

2. **IS-LM Model**: In this model, an unexpected fall in the price level would shift the LM curve to the right, which can raise the equilibrium income. But, as discussed, this assumes that other factors like debt levels and the responsiveness of consumers and investors to changes in interest rates

使用ReActAgent

from llama_index.core.tools import QueryEngineTool
from llama_index.core.tools import ToolMetadata
query_engine_tools = [
    QueryEngineTool(
        query_engine=engine,
        metadata=ToolMetadata(
            name="RAG工具",
            description=(
                "用于在原文中检索相关信息"
            ),
        ),
    ),
]
from llama_index.core.agent import ReActAgent
agent = ReActAgent.from_tools(query_engine_tools, llm=llm, verbose=True)
response = agent.chat(question)

输出

Falling prices can have various effects on equilibrium income. Firstly, they can increase real money balances, making consumers feel wealthier and potentially leading to increased spending and higher income. However, falling prices can also harm debtors, increasing the real value of their debts and reducing spending, which might lower national income. Additionally, changes in monetary policy, such as changes in the money supply, can also influence interest rates and consequently affect spending and income. The overall effect on equilibrium income depends on the interplay of these factors and the specific economic context.

ReActAgent的回答似乎更加有条理。

搜索引擎

使用博查API但似乎要花钱

import requests
# 需要先把BOCHA_API_KEY填写到.env文件中去。
BOCHA_API_KEY = os.getenv('BOCHA_API_KEY')

# 定义Bocha Web Search工具
def bocha_web_search_tool(query: str, count: int = 8) -> str:
    """
    使用Bocha Web Search API进行联网搜索,返回搜索结果的字符串。
    
    参数:
    - query: 搜索关键词
    - count: 返回的搜索结果数量

    返回:
    - 搜索结果的字符串形式
    """
    url = 'https://api.bochaai.com/v1/web-search'
    headers = {
        'Authorization': f'Bearer {BOCHA_API_KEY}',  # 请替换为你的API密钥
        'Content-Type': 'application/json'
    }
    data = {
        "query": query,
        "freshness": "noLimit", # 搜索的时间范围,例如 "oneDay", "oneWeek", "oneMonth", "oneYear", "noLimit"
        "summary": True, # 是否返回长文本摘要总结
        "count": count
    }

    response = requests.post(url, headers=headers, json=data)

    if response.status_code == 200:
        # 返回给大模型的格式化的搜索结果文本
        # 可以自己对博查的搜索结果进行自定义处理
        return str(response.json())
    else:
        raise Exception(f"API请求失败,状态码: {response.status_code}, 错误信息: {response.text}")

search_tool = FunctionTool.from_defaults(fn=bocha_web_search_tool)
from llama_index.core.agent import ReActAgent
agent = ReActAgent.from_tools([search_tool], llm=llm, verbose=True)

多agent工作流

openai库,llama-index都可以实现多agent工作流。
对于电商数据库查询agent效果不好的问题,或许可以结合第一次打卡笔记中使用的结构化prompt,定义多个分工明确的agent……?(继续画饼)

你可能感兴趣的:(数据库,llama)