手记

构建一个获奖的基于检索增强生成的旅行行程应用

使用增强检索生成 (RAG) 和向量搜索打造个性化的旅行行程

Generated Image by Microsoft Bing

在这篇文章里,我会分享我们如何构建Hangout AI,一个在TiDB Future App Hackathon 2024中获胜的个性化旅行行程生成器。我们将探讨RAG在项目中的关键作用、架构及其未来发展。无论你对RAG还是黑客马拉松感兴趣,这里都有你可能会用到的信息。

TiDB 未来应用黑客松挑战赛 🏆

2024 TiDB Future App Hackathon 为开发者提供了一个激动人心的平台,他们可以使用具有向量搜索功能的 TiDB Serverless 构建创新性的人工智能应用程序,展示他们的创意。

比赛概况

此次竞赛挑战开发者使用 TiDB Serverless 和向量搜索来构建各种 AI 应用程序。参赛者可以针对各种 AI 应用场景,包括图像处理、自然语言处理(NLP)和检索增强生成(RAG),有机会赢取超过 30,000 美元的奖金。竞赛得到了热烈响应,来自印度尼西亚的两个团队分别获得了第一名和第五名的成绩,而我的项目 Hangout AI 也获得了第五名。

我们为什么决定专注于定制化的旅行安排

Hangout AI(由我和Ayu Sudi Dwijayanti共同创建)受到我们在雅加达东区作为咖啡店工作爱好者探索咖啡馆的习惯启发。我们经常讨论新的聚会地点,但很少真正去实施。随着时间的推移,我们意识到共同的兴趣是探索雅加达、新加坡和吉隆坡的活动,利用AI来创建个性化的行程感觉是完美的解决方案。

Hangout AI 旅行助手根据用户的输入、位置、日期和天气生成个性化的旅行计划,使用 大规模语言模型 (LLM) 和 检索增强生成 (RAG) 技术提供相关推荐。利用 TiDB 从向量数据库中提取数据,它提供定制化的建议和视觉预览,让旅行规划变得轻松。我们的目标受众包括旅行者和寻求个性化行程的任何人,该应用适用于 TiDB 未来应用黑客松 2024 的推荐系统和 RAG 类别比赛。

🔍了解RAG(检索增强生成)

Basic RAG Flow

检索增强生成技术(RAG)是一种先进的AI技术,它结合了传统的大型语言模型(LLMs)和外部数据检索系统,以提高生成输出的质量和相关性。

RAG是怎么运作的?

RAG 通过首先从外部数据源(如向量数据库或知识库)检索相关信息来操作,使用诸如相似度搜索或关键词匹配等技术手段。一旦找到了相关的信息,这些信息就被输入到大语言模型中,生成基于检索数据的响应或输出。这种两步过程——检索然后生成——有助于模型生成更准确且内容丰富的响应。比如,在像 Hangout AI 这样的旅行行程应用中,RAG 让模型能够提取特定位置的数据(比如天气、用户评论和其他实时信息),来生成个性化的旅行建议,确保推荐符合用户的偏好和当前环境。

我们为什么选择RAG而不是传统的LLM管道:

在我们的项目中,我们选择了RAG而不是传统的LLM流程,因为RAG可以将外部数据源整合到生成过程中,从而产生更准确、相关和个性化的结果。传统的LLM流程仅依赖于模型的预训练知识,这可能会受到限制并且相对固定。

借助RAG(检索与生成),我们可以从数据库中实时获取信息,确保回答或回应的内容能反映最新的情况,比如天气、可用或新景点。这使得系统更加动态和灵活,提供更好的用户体验。此外,利用向量数据库可以加快和提高数据检索的效率,从而提高系统性能和准确性。

❓ 我们为什么需要RAG呢?

RAG 通过结合数据检索和文本生成来改进 AI 模型,使其更加准确和有效。虽然独立的大型语言模型非常强大,但它们面临着生成错误信息、提供过时响应以及难以扩展的问题。RAG 通过让 AI 从外部来源获取相关和实时的数据来解决这些问题,使它们更加智能、可靠和高效,适用于个性化推荐、实时信息检索等任务。

大语言模型独立运行时遇到的问题

独立的大规模语言模型虽然很强大,但经常遇到一些难题:

  • 准确性:尽管大语言模型是基于大规模数据集训练的,但它们无法访问实时或最新信息,这可能导致不准确或过时的回复。
  • 幻觉:大语言模型有时会产生看似正确但实际上编造的信息,因为它们缺乏与现实世界事实的关联。
  • 可扩展性:当任务变得更加复杂或需要处理大量数据时,大语言模型可能会难以应对这种复杂性和大数据量,因为它们只能处理已学过的知识,而没有动态数据的支持。
RAG 如何通过获取外部数据来解决这些问题

RAG通过利用外部数据来改进大型语言模型(LLMs)。

  • 准确性:RAG 实时地从数据库或其他来源获取相关数据,确保生成的信息准确。例如,它可以使用当前天气或营业时间来生成更准确的建议。
  • 减少虚构内容:通过使用真实数据,RAG 确保 AI 生成可靠的回答,减少编造信息的风险性。例如,在旅行应用中,AI 可以根据实际用户评价和实时可用性来推荐地点。
  • 可扩展性:RAG 可以通过从外部来源检索相关信息来处理大量数据,使 AI 能够生成更复杂的响应而不会变慢。这有助于系统在扩展和适应不断变化的信息时保持效率。
🏗️ 聊聊Hangout AI的架构设计

Hangout AI RAG Architecture

Hangout AI 的架构经过精心设计,通过结合尖端技术以及组件间无缝的数据交换,提供高效且个性化的旅行计划。

系统概述介绍:组件和模块:数据流

Hangout AI 基于三个核心元素:数据抓取、向量搜索和大模型生成。

  1. 数据收集:一个 Google 地图抓取工具获取相关地点的详情,如评分、描述和坐标。使用 Pandas 清洗抓取的数据,确保一致性和可用性。
  2. 数据嵌入:Gemini 嵌入模型将清洗后的数据转换为高维向量表示,以优化相似性搜索。
  3. 存储和检索:这些向量存储在 TiDB 中,这是一个具有向量搜索功能的分布式数据库,允许快速且可扩展的相关数据检索。
  4. LLM 生成:Groq 的 llama3-70b-8192 LLM 结合用户的输入参数(如地点、日期和天气)与检索到的数据,生成详细且个性化的行程计划。
利用向量数据库来实现快速检索

TiDB 作为 Hangout AI 的检索系统的基础,可以实现以下功能:

  • 快速搜索:凭借其向量功能,TiDB 可以在毫秒内匹配用户偏好与预嵌入的数据。
  • 可扩展性:随着数据库随着新地点和行程的增加而扩展,其分布式特性确保了性能的一致性。
  • 灵活性:TiDB 能够无缝处理将结构化数据(如日期)与非结构化数据(如评论)混合在一起的复杂查询。
LLM 和检索组件是如何进行互动的
  1. 用户查询输入:用户提供参数,如位置、喜欢的活动类型和旅行日期。
  2. 数据查找:查询触发 TiDB 中的相似性搜索,找到与用户喜好最匹配的位置信息。
  3. 上下文构建:检索的数据被整理成结构化的提示,并加入天气和其它相关信息。
  4. LLM 处理:提示被发送到 llama3–70b-8192 模型,该模型利用其语言生成能力来生成一个个性化的行程安排。
  5. 结果交付:最终行程通过 FastAPI 端点返回给用户,确保体验既流畅又互动。
🛠️ 搭建RAG系统

Hangout AI 的开发利用了诸如 LlamaIndex 这样的前沿工具,以高效地集成大型语言模型 (LLMs),使用 TiDB 管理向量化的位置数据,并通过外部 API 获取天气和位置信息。数据通过 Gemini 嵌入模型进行收集和索引,并存储在 TiDB 中,从而实现快速的相似度搜索。该应用动态地将这些数据与天气信息和用户输入结合起来,借助 Groq 的 llama3–70b-8192 模型生成个性化的旅行行程,确保无缝且智能的使用体验。

1. 向量库(一种存储向量数据的数据库)设置

在这个项目中,我们使用TiDB作为向量存储数据库。这段代码初始化了TiDB向量存储,将其连接到一个TiDB实例上,并配置以高效检索为目的存储向量。它使用余弦距离进行计算并指定用于嵌入的向量维度。

    from sqlalchemy import URL, create_engine  
    from llama_index.vector_stores.tidbvector import TiDBVectorStore  
    import os  

    # 使用环境变量构建 TiDB 连接 URL 以确保安全  
    tidb_connection_url = URL(  
        "mysql+pymysql",  
        username=os.environ['TIDB_USERNAME'],  # 从环境变量获取 TiDB 用户名  
        password=os.environ['TIDB_PASSWORD'],  # 从环境变量获取 TiDB 密码  
        host=os.environ['TIDB_HOST'],          # 从环境变量获取 TiDB 主机  
        port=4000,                             # TiDB 的端口号  
        database="test",                       # 数据库名称  
        query={"ssl_verify_cert": False, "ssl_verify_identity": True},  # SSL 设置(验证 SSL 证书身份)  
    )  

    # 创建一个 SQLAlchemy 引擎  
    create_engine(tidb_connection_url, pool_recycle=3600)  

    # 初始化 TiDBVectorStore 对象  
    tidbvec = TiDBVectorStore(  
        connection_string=tidb_connection_url,  # TiDB 连接字符串  
        table_name=os.getenv("VECTOR_TABLE_NAME"),  # 表名从环境变量获取  
        distance_strategy="cosine",  # 向量相似度搜索的距离策略(如余弦相似度)  
        vector_dimension=768,        # 向量维度  
        drop_existing_table=False,   # 是否删除现有表  
    )
2. 处理目标数据的预处理

此功能预先处理原始目的地数据,通过将数据结构化为Document对象并嵌入相关元数据,如位置、标题和类别,确保数据已准备好进行向量化和查询。

from llama_index.core import Document  
import json  

# 函数用于预处理来自 JSON 文件的数据  
def preprocess_data(path):  
    documents = []  
    # 从指定的 JSON 文件中加载数据  
    data = json.load(open(path))  
    for item in data:  
        # 构造每个文档的文本内容  
        text = f"""  
        地址: {item['address']}  
        标题: {item['title']}  
        国家: {item['complete_address']['country']}  
        分类: {', '.join(item['categories'] if item['categories'] else [])}  
        描述: {item.get('description', '无说明')}  
        评论数: {item['review_count']}  
        评分: {item['review_rating']}  
        营业时间: {item['open_hours']}  
        纬度: {item['latitude']}  
        经度: {item['longitude']}  
        """  
        # 为每个文档创建元数据  
        metadata = {  
            "id": item["cid"],  
            "标题": item["title"],  
            "描述": item["description"],  
            "地址": item["address"],  
            "详细地址": item["complete_address"],  
        }  
        # 使用文本和元数据创建一个 Document 对象  
        document = Document(text=text, metadata=metadata)  
        # 将文档添加到文档列表中  
        documents.append(document)  
    return documents
3. 数据导入

此功能负责将目标数据摄入到 TiDB 向量数据库中。它对数据进行预处理,将其转换为向量文档,并对其进行索引,以便高效检索。

    def 初始化向量存储():
        """
        将数据摄入到 TiDB 向量存储中,用于规划行程。

1. 从 JSON 文件中预处理数据。

2. 将数据转换成带有元数据的 Document 对象。

3. 在 TiDB 向量存储中建立向量索引。

        返回:
            VectorStoreIndex: 初始化的向量存储索引对象。
        """
        # 从 JSON 文件加载并预处理数据
        documents = 预处理数据("./data/destinations.json")

        # 从预处理的文档创建向量索引
        index = VectorStoreIndex.from_documents(
            documents,
            storage_context=storage_context,
            insert_batch_size=1000,
            show_progress=True
        )
        return index
  • 向量索引的创建VectorStoreIndex.from_documents() 方法从文档构建索引。此索引支持在 TiDB 向量存储中的快速相似性搜索。
  • 批量插入:数据以每批 1,000 条记录的形式分批加载,以优化性能并确保大规模数据集的顺畅处理。
4. 大型语言模型与天气和位置过滤器的结合.

此功能结合了基于位置的筛选和天气数据,与大型语言模型的查询引擎整合在一起。它在考虑用户偏好的情况下,检索符合用户偏好的相关目的地。

    从 llama_index.core.vector_stores 导入 MetadataFilters, MetadataFilter  

    # 用于使用特定过滤器和参数查询向量存储的函数  
    def query(date, country, startTime, endTime, address, lat, lng):  
        # 根据国家创建元数据过滤器  
        filters = MetadataFilters(  
            filters=[MetadataFilter(key="complete_address.country", value=[country])]  
        )  

        # 使用指定过滤器和top-k相似度创建查询引擎  
        query_engine = index.as_query_engine(filters=filters, similarity_top_k=2)  

        # 使用生成的提示查询向量存储  
        response = query_engine.query(create_prompt(date, startTime, endTime, address))  

        # 返回响应、元数据和天气数据  
        return {  
            "response": response.response,  # 查询引擎的响应  
            "metadata": get_data_from_cids([node.node.metadata["id"] for node in response.source_nodes]),  # 来源节点的元数据  
            "weathers": weather_data,  # 天气数据(假设已在其他地方定义)  
        }
  • 位置过滤器:该应用程序利用例如国家或地点的元数据过滤器来精炼搜索结果,确保推荐符合用户的偏好区域。
  • 基于LLM的行程生成:LLM利用天气情况和位置过滤器生成既符合用户的偏好又能适应实时条件的定制化且实用的旅行行程。
5. 基于聊天的带有历史记录的行程计划

此功能通过保持聊天历史中的上下文,实现通过聊天生成行程。这样一来,它优化了与LLM的互动,根据用户的输入提供动态和对话式的响应,以满足用户的输入需求。

    从 llama_cloud 导入 ChatMessage 类  
    从 llama_index.core.chat_engine.types 导入 ChatMode  
    从 llama_index.core.vector_stores 导入 MetadataFilters 类及其内部的 MetadataFilter 类  

    # 处理旅行行程规划聊天查询的函数  
    def chat_query(messages, query, day, country, startTime, endTime, address):  
        # 根据国家创建元数据过滤器  
        filters = MetadataFilters(filters=[MetadataFilter(key="complete_address.country", value=[country])])  

        # 准备聊天历史消息  
        histories = [  
            ChatMessage(content=msg.content, role=msg.role, additional_kwargs={}) for msg in messages.histories  
        ]  
        # 在聊天历史的开头插入系统消息  
        histories.insert(0, ChatMessage(  
            content=f"你是一名旅行行程规划师。规划在 {day} 从 {startTime} 到 {endTime} 访问 {address} 的行程安排。",  
            role="system"  
        ))  
        # 将用户的查询追加到聊天历史中  
        histories.append(ChatMessage(content=query, role="user"))  

        # 使用指定的过滤器和上下文模式创建聊天引擎实例  
        query_engine = index.as_chat_engine(filters=filters, chat_mode=ChatMode.CONTEXT)  

        # 使用准备好的聊天历史执行聊天查询  
        response = query_engine.chat(query, chat_history=histories)  

        # 返回聊天引擎的回复和来源节点的元数据  
        return {  
            "response": response.response,  # 聊天引擎的回复  
            "metadata": get_data_from_cids([node.node.metadata["id"] for node in response.source_nodes]),  # 来源节点的元数据  
        }
  • 聊天界面:用户通过一个对话界面与应用程序互动,在这里他们可以请求个性化的旅行计划。
  • 消息历史:应用程序会记录用户的聊天历史,以提供上下文,随着时间推移,提高服务的响应质量。
  • 系统消息:应用程序使用预定义的系统消息来指导LLM根据用户的偏好及之前的对话内容生成相关行程。
  • 动态行程更新:随着对话的进展,行程根据新的输入(包括偏好、日期或天气变化)进行调整,确保行程保持最新。
嘿,留意一下💡

更详细的代码可以在这里查看:这里

对于代码的杂乱表示歉意——这是我第一次做LLM的项目,所以代码有些粗糙。还有很多其他更干净的LLM项目,我们将在以后的文章中讨论这些。谢谢您的理解!

⛓️ 不用框架构建RAG

完全可以在不依赖像LlamaIndex或LangChain这样的框架的情况下实现检索增强生成(RAG)。自建的RAG管道可以提供更多对每个组件的控制,比如向量嵌入、数据库查询和提示工程。下面是如何工作的:

  • 数据摄入:收集和预处理数据,然后使用像OpenAI或Hugging Face transformers这样的嵌入模型将其转换为嵌入表达。
  • 向量存储:使用像TiDB、Pinecone、Milvus或自托管解决方案如FAISS这样的向量数据库来存储和检索这些嵌入表达。
  • 检索:在数据库中执行相似性搜索以检索与用户查询相关的文档。
  • 生成:将检索到的文档与用户输入结合,生成提示并发送给大型语言模型以生成响应。

这种方法灵活,但需要大量的开发工作和专业知识来处理向量数据库,管理流程,并确保可扩展性。

我为什么选择了LlamaIndex

在这个项目里,我选择了使用LlamaIndex,因为它是我们比赛的官方赞助商,并提供了生成-检索-生成流程的简化工具。LlamaIndex简化了数据摄入、查询编排以及与TiDB的集成等任务,让我可以专注于实现关键功能。

⚠️ 注意:当前版本尚未准备好用于生产环境

虽然Hangout AI是一个有前景的原型,展示了利用大语言模型为个性化旅行行程提供支持的潜力,但是值得注意的是,它目前还不适合投入生产。该应用程序仍在开发中,为了确保其稳定性和可扩展性,使其能在实际高需求环境中使用,还有许多需要改进的地方。

为什么我们需要改进措施和保护措施
  • 准确性:应用程序可能会因为现有数据的局限或处理误差,偶尔提供不准确或不完整的行程建议。
  • 错误处理:尽管我们已经实现了失败查询的重试功能,但仍需要更高级的错误处理机制来确保顺畅的用户体验,特别是在处理天气和位置等外部数据时。
  • 安全性:需要采取适当的保护措施,例如设置速率限制功能和安全的 API 密钥管理,以保护用户的敏感信息并防止滥用应用程序。
  • 可扩展性:当前的系统架构可能无法在没有进行扩展、负载均衡和数据库管理优化的情况下处理大量同时在线的用户。
让应用更可扩展和可靠的下一步举措
  • 优化数据库性能:通过更好的索引策略和查询优化来提高 TiDB 的效率,以处理大规模数据集。
  • 改进错误管理:实现更强大的日志记录和监控系统,以便在问题影响到用户之前主动识别并解决这些问题。
  • 增强安全功能:实施更严格的安全部协议,例如使用 OAuth 进行用户认证,同时加密敏感数据。

了解更多关于使用LLM构建的应用程序安全性的更多信息,您可以查阅LlamaIndex的RAG安全指南,了解LlamaIndex的RAG安全功能和Protect AI的LLM Guard。

🎉 最后,大结局来了

Hangout AI 机器人,专门为 TiDB 未来应用黑客松 2024 设计,展示了 Retrieval-Augmented Generation (RAG) 结合向量搜索来创建个性化旅行行程的功能。这种创新方法利用实时数据检索和大型语言模型提供准确且相关的旅行建议。

尽管Hangout AI很有潜力,但它仍然是一个原型,还未准备好投入生产。需要改进的关键领域包括提高准确性、错误管理、安全性和可扩展性。未来的工作将集中在优化数据库性能、实施稳健的错误管理以及执行更严格的安全部措。

我们期待将Hangout AI打造成一个更可靠、更可扩展的个性化旅行规划工具。

恭喜啦!

你读完了这篇文章。希望你喜欢这篇文章的内容。如果有任何想讨论的话题或问题,可以在下面留言或通过我的GitHubLinkedIn联系我。欢迎通过我的GitHubLinkedIn联系我。

谢谢,别忘了给这篇文章点个赞哦 👏。

🌐 探索代码世界

如果你想了解更多关于这个应用案例,并更深入地了解该项目,你可以在该完整仓库中找到。

GitHub - luthfiarifin/hangout-llm在GitHub上创建账户以贡献于luthfiarifin/hangout-llm项目的开发
0人推荐
随时随地看视频
慕课网APP