基于LLM与向量数据库的智能电影推荐系统架构与实践
1. 项目概述当大语言模型成为你的私人电影管家最近在折腾一个挺有意思的开源项目叫tomasonjo/llm-movieagent。光看名字你大概能猜到它和电影、智能体Agent以及大语言模型LLM有关。简单来说这是一个利用大语言模型来构建一个“电影智能体”的项目。它不是一个简单的电影推荐系统而是一个能理解你的复杂、模糊甚至自相矛盾的观影需求并像一位资深影迷朋友一样与你进行多轮对话最终帮你找到那部“对”的电影的智能助手。想象一下这个场景你下班回家身心俱疲只想看一部电影放松。但你脑子里蹦出的念头可能是“我想看一部能让我彻底放松但又不想太无脑的喜剧最好是90年代的带点怀旧感主角最好是个普通人逆袭的故事。” 面对如此具体又充满主观感受的需求传统的基于标签喜剧、90年代或协同过滤喜欢A的人也喜欢B的推荐系统基本就“死机”了。它们无法理解“彻底放松但不想太无脑”这种微妙的情绪平衡也无法将“普通人逆袭”这个叙事弧线与电影内容关联起来。这正是llm-movieagent要解决的问题。它背后的核心思路是将大语言模型强大的自然语言理解和推理能力与结构化的电影知识库元数据结合起来。LLM负责充当“大脑”理解你的意图、拆解你的需求、进行逻辑推理而电影知识库则充当“记忆库”提供准确、丰富的电影信息供其检索和筛选。这个项目本质上是在探索如何将LLM的“认知智能”与垂直领域的“领域知识”进行有效融合从而创造出更智能、更人性化的交互体验。对于开发者、AI爱好者以及对推荐系统前沿感兴趣的朋友来说这是一个非常值得深入研究的案例它展示了Agent架构在解决复杂信息检索问题上的巨大潜力。2. 核心架构与设计思路拆解要理解llm-movieagent是如何工作的我们不能只看表面功能必须深入其架构设计。这个项目的巧妙之处在于它没有试图让LLM“记住”所有电影信息这既不现实成本也极高而是采用了一种更优雅的“大脑”“工具”的智能体Agent范式。2.1 大脑大语言模型的角色与能力边界项目中的大语言模型例如通过OpenAI API调用的GPT-4或GPT-3.5扮演着“智能中枢”或“推理引擎”的角色。它的核心能力包括意图理解与需求拆解将用户自然语言描述的需求解析成结构化的查询条件。例如将“我想看一部让人热血沸腾的体育电影”解析为{ genre: “体育”, mood: “励志、热血”, era: “不限” }。更重要的是它能处理模糊和复合条件。对话状态管理在整个多轮对话中记住用户之前说过的话理解当前问题是对之前需求的补充、修改还是全新的开始。这是实现自然对话的关键。工具调用决策决定在对话的哪个环节需要调用哪些“工具”如下文的知识库检索工具来获取外部信息。例如当用户说“推荐几部类似的”时LLM需要知道此时应该调用“根据某部电影找相似影片”的工具。信息综合与自然语言生成将从工具获取到的结构化电影信息如片名、导演、简介、评分重新组织成一段流畅、友好、个性化的推荐语回复给用户。这里有一个重要的设计考量LLM本身不存储电影知识。它的训练数据中虽然包含大量电影信息但这些信息可能过时、不准确或不完整。让LLM直接“回忆”电影细节会引发“幻觉”胡编乱造问题。因此项目的设计严格限制了LLM的“知识输出”必须基于从可靠工具中获取的信息。2.2 工具向量数据库与精准检索既然LLM不存储知识那么知识从哪里来答案就是向量数据库。这是本项目乃至当前AI应用架构中的一个核心组件。第一步知识嵌入Embedding项目需要预先准备一个电影数据集包含每部电影的详细信息标题、导演、演员、剧情简介、类型、标签、评分等。然后使用一个嵌入模型Embedding Model将每部电影的文本信息尤其是剧情简介转换成一个高维度的向量一组数字。这个向量可以理解为这部电影在“语义空间”中的坐标。语义相近的电影它们的向量在空间中的距离也会很近。例如《肖申克的救赎》和《绿里奇迹》的向量距离会比《肖申克的救赎》和《速度与激情》的近得多。第二步需求向量化与检索当LLM将用户需求解析成一段描述文本如“寻找关于人工智能伦理的科幻片”后同样用相同的嵌入模型将这段描述文本转换成向量。随后系统在向量数据库中进行“相似度搜索”寻找与这个“需求向量”最接近的“电影向量”。这就是语义检索它超越了关键词匹配能理解“人工智能伦理”和“AI”、“机器人”、“技术反思”之间的深层关联。第三步上下文增强仅仅返回一部电影的向量还不够。系统会将被检索到的电影的相关元数据标题、导演、简介等作为“上下文”Context连同用户的原始问题和对话历史一并提交给LLM。LLM的职责是“阅读理解”这段上下文并生成最终的回答。这就确保了回答的内容有据可依大大减少了幻觉。2.3 智能体工作流从对话到推荐的完整链条将大脑和工具组合起来就形成了智能体的工作流通常遵循“规划-执行-反思”的循环用户输入用户提出需求“今晚想看一部温暖的、关于家庭的动画电影。”规划LLM分析输入判断需要调用“电影检索工具”。同时它可能会将用户需求重写或扩展为更利于检索的查询语句例如“温暖感人核心主题是家庭亲情动画形式适合合家欢观看。”执行系统将重写后的查询转换为向量在向量数据库中执行相似度搜索返回Top K例如前5部最相关的电影及其元数据。反思与生成LLM接收到检索结果上下文审视这些电影是否真的符合“温暖”、“家庭”的定义。它可能会发现某部电影虽然标签是“家庭”但剧情实则有黑暗元素。于是LLM会综合所有信息筛选并组织语言生成回复“根据您的需求我找到了以下几部影片《寻梦环游记》——以家庭和记忆为核心音乐动人《克劳斯圣诞节的秘密》——讲述友谊如何构建起一个社区的家庭纽带画风独特且非常暖心…”这个循环可能会在单轮对话中完成也可能在多轮中迭代。例如用户看了推荐后说“但我不太喜欢音乐题材”那么工作流会重新开始LLM会在新的规划阶段将“排除音乐元素”作为新的约束条件加入。注意在实际架构中工具可能不止一个。除了基于简介的语义检索工具可能还有基于导演、演员的精确过滤工具或者基于评分、年份的排序工具。LLM需要学会在正确的时间调用正确的工具组合这通常通过“函数调用”Function Calling或“智能体框架”如LangChain, LlamaIndex来实现。llm-movieagent项目很可能就是基于此类框架构建的。3. 关键技术细节与实操要点解析理解了宏观架构我们深入到代码和实现层面看看有哪些技术细节决定了这个智能体的“智商”和“情商”。这里我会结合常见实践补充llm-movieagent项目可能需要处理的关键环节。3.1 电影数据源的准备与处理任何推荐系统的基石都是数据。对于电影智能体你需要一个丰富、干净、结构化的电影数据集。常见数据源TMDB API最主流的选择。提供了极其丰富的电影元数据包括简介、演职员表、类型、关键词、评分、海报等。免费层有调用限制但对于个人项目足够。Kaggle数据集例如“The Movies Dataset”它包含了TMDB的数据快照适合离线实验避免频繁API调用。OMDb API另一个流行的API数据相对简洁包含IMDb评分但有每日调用次数限制。数据处理流水线数据获取与清洗通过API或下载数据集获取原始JSON或CSV数据。清洗工作包括处理缺失值如缺失简介的电影可能需要剔除或填充、统一格式将电影类型从列表转换为字符串、去除重复项。文本字段拼接为了生成高质量的向量表示我们需要为每部电影构造一个富含信息的文本描述。通常不是只用简介而是将多个字段拼接起来。例如# 一个构造电影文本的示例 def create_movie_text(row): text_parts [] text_parts.append(f标题{row[title]}) text_parts.append(f概述{row[overview]}) text_parts.append(f类型{, .join(row[genres])}) text_parts.append(f导演{row[director]}) text_parts.append(f主演{, .join(row[main_cast][:3])}) # 取前三主演 text_parts.append(f标签{, .join(row[keywords][:5])}) # 取前五个关键词 return 。.join(text_parts)这样构造的文本比单纯的简介包含了更全面的语义信息能提高检索的准确性。比如用户搜索“诺兰的科幻片”即使简介里没提导演拼接的文本里包含“导演克里斯托弗·诺兰”也能被有效检索到。分块与向量化对于超长简介的电影可能需要将文本分块Chunking分别生成向量。但电影文本一般不会太长通常整部电影作为一个文档处理即可。使用如text-embedding-ada-002(OpenAI) 或开源的BGE-M3、Snowflake Arctic Embed等模型进行向量化。3.2 嵌入模型的选择与优化嵌入模型的质量直接决定了语义检索的精度。选择时需要考虑维度通常维度越高表征能力越强但存储和计算成本也越高。text-embedding-3-large有3072维而text-embedding-3-small只有1536维后者速度更快成本更低对于电影检索这种任务小模型通常已足够。上下文长度模型能处理的最大文本长度。确保它能处理你拼接后的电影文本。微调对于极致追求可以使用电影相关的语料如影评、专业电影分析对开源嵌入模型进行微调使其在电影领域的语义空间更准确。但这属于进阶操作初期不必考虑。实操心得在本地部署场景下BGE-M3或Snowflake Arctic Embed是非常优秀的开源选择它们支持多语言且在MTEB等基准测试上表现优异可以避免对商用API的依赖和产生费用。3.3 向量数据库的选型与部署向量数据库负责高效存储和检索数百万个高维向量。常见选择有Pinecone全托管服务上手简单性能强劲但长期使用有成本。Weaviate开源可以自托管也提供云服务。功能丰富支持混合搜索向量关键词。Qdrant开源Rust编写性能出色API友好是目前非常热门的选择。Chroma轻量级易于集成特别适合原型开发和中小规模项目。PGVectorPostgreSQL的扩展如果你的应用本身就用PostgreSQL这是一个非常自然的选择能简化技术栈。对于llm-movieagent这类项目如果数据量在几万到几十万部电影Qdrant或Weaviate的自托管版本是平衡功能、性能和复杂度的好选择。如果追求极简和快速验证想法Chroma是完美的起点。部署与索引技巧创建集合在数据库中创建一个集合Collection并定义向量的维度与你使用的嵌入模型维度一致。上传数据将每部电影的向量及其对应的元数据id, title, overview等作为有效载荷Payload上传。索引算法大多数向量数据库默认使用HNSWHierarchical Navigable Small World算法它在精度和速度之间取得了很好的平衡。通常使用默认参数即可。过滤搜索这是关键功能。除了语义相似度你经常需要结合过滤条件如“年份在2000年以后”、“评分高于8.0”。向量数据库如Qdrant、Weaviate都支持在检索时添加过滤器先过滤再按向量距离排序或按向量距离排序后再过滤。3.4 提示工程与对话逻辑设计这是塑造智能体“性格”和“能力”的关键。你需要为LLM设计系统提示词System Prompt和对话流程。系统提示词示例你是一个专业的电影推荐助手知识渊博且风趣幽默。你的核心任务是帮助用户找到他们想看的电影。 你拥有一个电影知识库你必须基于知识库返回的信息进行推荐严禁编造不存在的电影信息。 你的回复应遵循以下步骤 1. 理解用户的需求包括明确的类型、演员、导演要求以及模糊的情绪、氛围描述。 2. 如果需要从知识库查询你会生成一个简洁的搜索查询语句。 3. 收到知识库返回的电影列表后仔细分析每部电影是否真正符合用户需求。 4. 组织你的回复首先用一两句话总结推荐理由然后以清晰的项目符号列出推荐的电影每部电影包含片名、上映年份、简要契合点。最后可以询问用户是否想了解更详细的信息或进行调整。 请保持对话自然、热情。多轮对话管理状态保持需要维护一个对话历史列表将之前的用户消息和助手消息都包含在每次请求的上下文窗口中。这能让LLM记住之前的对话。需求澄清当用户需求过于模糊如“推荐个好电影”时LLM应该主动提问引导用户提供更多信息例如“当然不过好电影的范围太广了。能告诉我你最近看过哪部喜欢的电影吗或者你现在想看什么类型的轻松的还是烧脑的”上下文切换与遗忘LLM的上下文长度有限。对于非常长的对话需要考虑摘要历史对话或主动遗忘早期不相关的部分以节省Token并保持焦点。4. 核心功能实现与代码实践现在让我们勾勒一个简化的、可运行的llm-movieagent核心流程。这里以 Python 语言结合 LangChain 框架这是一个流行的LLM应用开发框架为例进行说明。请注意以下代码是概念性示例tomasonjo/llm-movieagent的实际实现可能有所不同。4.1 环境搭建与依赖安装首先你需要准备Python环境建议3.9以上并安装必要的包。# 创建虚拟环境可选但推荐 python -m venv movieagent_env source movieagent_env/bin/activate # Linux/Mac # movieagent_env\Scripts\activate # Windows # 安装核心依赖 pip install langchain langchain-openai langchain-community # LangChain核心及OpenAI集成 pip install qdrant-client # 向量数据库客户端 pip install tmdbsimple # TMDB API客户端用于获取数据 pip install openai # OpenAI SDK你需要准备以下API密钥OpenAI API Key用于调用GPT模型和Embedding模型。TMDB API Key用于获取电影数据如果你选择动态获取。Qdrant Cloud Key或本地Qdrant地址用于连接向量数据库。4.2 数据获取、处理与向量化入库假设我们使用TMDB API获取数据并存入本地运行的Qdrant。import os from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Qdrant from langchain.docstore.document import Document import tmdbsimple as tmdb # 设置API密钥 os.environ[OPENAI_API_KEY] your-openai-api-key tmdb.API_KEY your-tmdb-api-key # 1. 获取电影数据示例获取流行电影 def fetch_popular_movies(num_pages5): movies [] for page in range(1, num_pages 1): response tmdb.Movies().popular(pagepage) for movie in response[results]: # 获取电影详情以拿到完整简介 details tmdb.Movies(movie[id]).info() # 获取导演信息 credits tmdb.Movies(movie[id]).credits() director next((crew[name] for crew in credits.get(crew, []) if crew[job] Director), N/A) # 构造电影文本 text f标题{details.get(title)}。概述{details.get(overview, 暂无概述)}。导演{director}。类型{, .join([g[name] for g in details.get(genres, [])])}。 # 创建LangChain Document对象 metadata { id: details[id], title: details[title], year: details.get(release_date, )[:4], director: director, genres: , .join([g[name] for g in details.get(genres, [])]), overview: details.get(overview, ), popularity: details.get(popularity), vote_average: details.get(vote_average) } movies.append(Document(page_contenttext, metadatametadata)) return movies # 2. 初始化嵌入模型 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) # 3. 获取电影数据并存入Qdrant documents fetch_popular_movies(num_pages2) # 先获取2页数据做测试 # 连接本地Qdrant需先通过docker运行Qdrant qdrant Qdrant.from_documents( documentsdocuments, embeddingembeddings, urlhttp://localhost:6333, # Qdrant默认端口 collection_namemovies_collection, force_recreateTrue # 如果集合存在则重新创建 ) print(电影数据已成功向量化并存入Qdrant。)4.3 构建检索链与智能体逻辑接下来我们构建一个结合检索和LLM的链。from langchain.chains import RetrievalQA from langchain.chat_models import ChatOpenAI from langchain.prompts import PromptTemplate # 1. 初始化LLM llm ChatOpenAI(modelgpt-3.5-turbo, temperature0.7) # temperature控制创造性 # 2. 从已存在的集合创建检索器 qdrant_retriever qdrant.as_retriever( search_typesimilarity, # 相似度搜索 search_kwargs{k: 4} # 每次检索返回4部电影 ) # 3. 自定义提示模板让LLM更好地利用检索到的上下文 prompt_template 你是一个专业的电影推荐助手。请根据以下检索到的电影信息回答用户的问题。 如果你不知道答案就诚实地说不知道不要编造信息。 检索到的电影信息 {context} 用户问题{question} 请以友好、热情的语气基于以上信息进行回答。如果信息中有多部相关电影请简要介绍并比较它们。如果信息不足可以询问用户更具体的偏好。 回答 PROMPT PromptTemplate( templateprompt_template, input_variables[context, question] ) # 4. 创建检索问答链 movie_agent_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 将检索到的所有文档“塞”进上下文 retrieverqdrant_retriever, chain_type_kwargs{prompt: PROMPT}, return_source_documentsTrue # 返回源文档便于调试 ) # 5. 测试智能体 def chat_with_agent(query): result movie_agent_chain.invoke({query: query}) print(f用户: {query}) print(f助手: {result[result]}) print(\n--- 本次检索到的源电影 ---) for i, doc in enumerate(result[source_documents]): print(f{i1}. {doc.metadata[title]} ({doc.metadata.get(year, N/A)}) - {doc.metadata[overview][:100]}...) print(- * 50) # 进行测试对话 if __name__ __main__: chat_with_agent(推荐一部克里斯托弗·诺兰导演的科幻电影。) chat_with_agent(有没有类似《肖申克的救赎》这种讲述希望与救赎的电影)4.4 实现多轮对话与状态管理上面的例子是单次问答。要实现多轮对话需要维护一个对话历史。我们可以使用ConversationBufferMemory。from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationalRetrievalChain # 创建记忆体 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue, output_keyanswer) # 创建带记忆的对话检索链 conversational_agent ConversationalRetrievalChain.from_llm( llmllm, retrieverqdrant_retriever, memorymemory, combine_docs_chain_kwargs{prompt: PROMPT}, # 使用之前的自定义提示 verboseTrue # 打印详细日志便于理解链的运作 ) # 模拟多轮对话 def multi_turn_chat(): queries [ 我喜欢看紧张刺激的悬疑片。, 最好是欧洲导演拍的。, 不要有太暴力的镜头。 ] for query in queries: print(f\n[用户]: {query}) result conversational_agent.invoke({question: query}) print(f[助手]: {result[answer]}) # 记忆体会自动更新 # 运行多轮对话 multi_turn_chat()在这个多轮对话中助手会记住之前的上下文。当你说“最好是欧洲导演拍的”时它会在“紧张刺激的悬疑片”基础上叠加“欧洲导演”这个过滤器进行检索。第三轮“不要有太暴力的镜头”则进一步增加了限制。LLM会综合所有历史信息来理解当前查询的完整意图。5. 性能优化、问题排查与扩展方向一个基础的原型跑起来后接下来就要面对真实场景中的挑战速度、准确性、成本以及更复杂的需求。5.1 常见问题与排查技巧问题1检索结果不相关或精度低。可能原因电影文本构造不佳拼接的文本信息量不足或噪声太大。嵌入模型不匹配使用的嵌入模型对电影领域文本的语义捕捉能力弱。检索参数不当k值返回数量太小或太大或相似度算法不合适。排查与解决检查源数据打印出几部电影的拼接文本看是否包含了关键信息导演、主演、类型、标签。优化文本构造尝试不同的字段组合和拼接格式。有时为不同字段添加前缀如“导演”、“关键词”能帮助模型理解。尝试不同嵌入模型在开源模型中BGE-M3和Snowflake Arctic Embed通常比text-embedding-ada-002在某些任务上表现更好且免费。调整检索策略尝试search_typemmr(最大边际相关性)它能在相关性和多样性之间取得平衡。或者使用混合搜索结合向量相似度和关键词匹配BM25的分数。使用重排序器先检索出较多的候选如k20然后用一个更小、更精准的模型如BGE-reranker对结果进行重排序返回Top N。这能显著提升最终结果的精度。问题2LLM的回答出现“幻觉”推荐了不存在的电影或错误信息。可能原因系统提示词约束力不够或LLM过于“自信”地补全了知识库中没有的信息。排查与解决强化系统提示在提示词中明确且严厉地强调“必须且只能基于提供的上下文信息回答”。可以多次重复此要求。启用引用功能让LLM在回答中引用来源电影的标题例如“根据知识库中关于《盗梦空间》的信息...”。这不仅能减少幻觉还能增加可信度。后处理检查在LLM生成回答后添加一个简单的后处理步骤检查回答中提到的电影片名是否在本次检索返回的源文档列表中。如果不在可以触发一个安全回复如“我暂时没有找到完全符合您描述的电影也许是知识库的限制您可以尝试换一种描述方式吗”问题3响应速度慢。可能原因嵌入模型调用慢如果每次用户查询都实时调用OpenAI的Embedding API网络延迟是主要瓶颈。LLM生成慢使用了较大的模型如GPT-4且生成了过长的回复。向量检索慢数据量过大或索引未优化。排查与解决缓存嵌入向量对于常见的、通用的用户查询如“推荐喜剧”可以将其向量结果缓存起来下次相同或相似查询直接使用缓存。使用更快的LLM在大多数对话场景下gpt-3.5-turbo的响应速度和成本都优于gpt-4且效果足够好。优化向量数据库确保使用了正确的索引如HNSW并根据数据量调整ef_construction和m等HNSW参数。对于海量数据千万级可以考虑分区索引。问题4无法处理复杂的多条件过滤。可能原因简单的语义检索可能无法完美结合“向量相似度”和“结构化过滤”如年份2020且评分8.5。排查与解决利用向量数据库的过滤功能像Qdrant、Weaviate都支持在检索时添加过滤条件。你需要将用户查询中的结构化部分年份、评分、导演名解析出来作为过滤参数传递给检索器。使用LangChain的Self-Query Retriever这是一个高级检索器它利用LLM自动将用户自然语言查询中的“元数据过滤”部分解析出来。例如用户说“找几部2010年之后的高分科幻片”LLM会解析出query: “科幻片”filter: year 2010 AND vote_average 7.5。这大大简化了复杂查询的处理。5.2 扩展功能与进阶方向一个基础的电影推荐智能体已经很有用但我们可以让它变得更强大个性化推荐引入用户历史交互数据点击、评分、收藏。在检索时不仅考虑当前查询的向量还将用户的历史偏好向量一个代表用户平均喜好的向量融合进去实现“千人千面”。混合检索策略结合多种检索方式语义检索基于电影简介和用户描述的向量相似度。协同过滤基于“喜欢这部电影的人也喜欢...”的逻辑需要用户-物品交互矩阵。知识图谱检索如果构建了电影知识图谱电影-导演-演员-类型的关系网可以基于图谱路径进行推荐例如“推荐由这位演员主演、且与该导演合作过的其他演员演过的电影”。工具增强为智能体集成更多外部工具实时信息查询连接IMDb或豆瓣API获取最新的评分、评论、上映信息。流媒体可用性检查接入JustWatch等服务的API告诉用户这部电影在哪个平台Netflix, Disney等可以观看。预告片或海报获取返回电影海报或预告片链接丰富回复内容。评估与迭代建立评估体系。可以设计一批测试用例标准问题定期运行检查检索准确率、回答相关性、用户满意度等指标持续优化提示词、数据质量和检索策略。5.3 成本控制与部署考量对于个人项目或小规模应用成本是需要认真考虑的。LLM API成本这是主要成本。策略包括使用更便宜的模型对话用gpt-3.5-turbo嵌入用text-embedding-3-small。缓存缓存频繁出现的查询及其LLM回复。设置Token上限限制LLM生成回复的最大长度。考虑开源模型在本地部署像Llama 3、Qwen这样的开源大模型虽然需要GPU资源但可以彻底消除API调用费用。可以使用Ollama或vLLM等工具进行本地部署和推理。向量数据库成本如果使用云服务如Pinecone需关注存储量和查询次数。自托管Qdrant, Weaviate可以避免此项但需要运维成本。部署架构对于Web应用典型的架构是前端如Streamlit, Gradio - 后端APIFastAPI, Flask - 智能体核心LangChain链 - 向量数据库/LLM API。容器化Docker部署可以简化环境管理。tomasonjo/llm-movieagent项目为我们提供了一个将前沿AI技术应用于经典问题电影推荐的绝佳范本。它不仅仅是技术的堆砌更体现了一种设计思想让LLM专注于它擅长的语言理解和推理让专业的外部系统向量数据库负责海量知识的存储和检索两者通过清晰的接口协同工作。构建这样一个系统的过程会让你对智能体架构、检索增强生成、提示工程以及整个LLM应用开发生态有深刻的理解。从获取数据、处理文本、选择嵌入模型、部署向量数据库到设计提示词、实现多轮对话、优化性能每一步都充满了实践性的挑战和乐趣。你可以从这个项目出发将其改造成书籍推荐、音乐推荐、餐厅推荐甚至是任何需要复杂对话式检索的场景其核心架构是相通的。