本教程带你快速搭建一个强大的AI问答系统,它能实时搜索网络、智能缓存文档、保持对话记忆,让你的AI助手更智能、更精准。我们将使用业界顶尖的四大工具,零门槛实现专业级AI应用。
技术栈概览:四大神器强强联合
1. Tavily Search API:专为AI设计的搜索引擎,提供实时高质量的网络搜索结果
- Tavily是"专为AI代理(LLMs)构建的搜索引擎,以极快的速度提供实时、准确、事实性的结果"[1]
- 它能有效减少AI幻觉,专为RAG(检索增强生成)系统优化[2]
2. Chroma向量数据库:实现语义文档缓存
- 开源向量存储解决方案,支持高效的相似性搜索
- 通过语义缓存减少重复API调用,大幅降低延迟和成本
3. Google Gemini LLM:强大的语言模型
- Google最先进的语言模型之一,支持丰富的上下文理解
- 提供高质量的回答生成和文本摘要能力
4. LangChain框架:无缝整合以上组件
- 提供模块化的接口连接各种AI工具
- 易于扩展,支持复杂工作流管理
核心代码实现
首先安装必要的库:
# 安装所有必要的库
!pip install -qU langchain-community tavily-python langchain-google-genai streamlit matplotlib pandas tiktoken chromadb langchain_core pydantic langchain
# 导入基础库和设置API密钥
import os
import getpass
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import json
import time
from typing import List, Dict, Any, Optional
from datetime import datetime
# 安全地设置API密钥
if "TAVILY_API_KEY" not in os.environ:
os.environ["TAVILY_API_KEY"] = getpass.getpass("Enter Tavily API key: ")
if "GOOGLE_API_KEY" not in os.environ:
os.environ["GOOGLE_API_KEY"] = getpass.getpass("Enter Google API key: ")
# 导入LangChain组件
from langchain_community.retrievers import TavilySearchAPIRetriever
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains.summarize import load_summarize_chain
from langchain.memory import ConversationBufferMemory
接下来,我们构建系统的核心组件:
# 自定义异常和文档格式化
class SearchQueryError(Exception):
"""查询错误的自定义异常类"""
pass
def format_docs(docs):
"""将检索到的文档格式化为易读的字符串"""
formatted_content = []
for i, doc in enumerate(docs):
metadata = doc.metadata
source = metadata.get('source', 'Unknown source')
title = metadata.get('title', 'Untitled')
score = metadata.get('score', 0)
formatted_content.append(
f"Document {i+1} [Score: {score:.2f}]:\n"
f"Title: {title}\n"
f"Source: {source}\n"
f"Content: {doc.page_content}\n"
)
return "\n\n".join(formatted_content)
# 搜索结果解析器
class SearchResultsParser:
def parse(self, text):
"""解析LLM输出,提取结构化信息"""
try:
if isinstance(text, str):
import re
import json
json_match = re.search(r'{.*}', text, re.DOTALL)
if json_match:
json_str = json_match.group(0)
return json.loads(json_str)
return {"answer": text, "sources": [], "confidence": 0.5}
elif hasattr(text, 'content'):
return {"answer": text.content, "sources": [], "confidence": 0.5}
else:
return {"answer": str(text), "sources": [], "confidence": 0.5}
except Exception as e:
logger.warning(f"JSON解析失败: {e}")
return {"answer": str(text), "sources": [], "confidence": 0.5}
现在实现增强型搜索和语义缓存功能:
# 增强型Tavily搜索检索器
class EnhancedTavilyRetriever:
def __init__(self, api_key=None, max_results=5, search_depth="advanced", include_domains=None, exclude_domains=None):
self.api_key = api_key
self.max_results = max_results
self.search_depth = search_depth
self.include_domains = include_domains or []
self.exclude_domains = exclude_domains or []
self.retriever = self._create_retriever()
self.previous_searches = []
def _create_retriever(self):
try:
return TavilySearchAPIRetriever(
api_key=self.api_key,
k=self.max_results,
search_depth=self.search_depth,
include_domains=self.include_domains,
exclude_domains=self.exclude_domains
)
except Exception as e:
logger.error(f"创建Tavily检索器失败: {e}")
raise
def invoke(self, query, **kwargs):
if not query or not query.strip():
raise SearchQueryError("空查询")
try:
start_time = time.time()
results = self.retriever.invoke(query, **kwargs)
end_time = time.time()
# 记录搜索性能数据
search_record = {
"timestamp": datetime.now().isoformat(),
"query": query,
"num_results": len(results),
"response_time": end_time - start_time
}
self.previous_searches.append(search_record)
return results
except Exception as e:
logger.error(f"搜索失败: {e}")
raise SearchQueryError(f"执行搜索失败: {str(e)}")
def get_search_history(self):
return self.previous_searches
# 搜索语义缓存
class SearchCache:
def __init__(self):
self.embedding_function = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
self.vector_store = None
self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
def add_documents(self, documents):
if not documents:
return
try:
if self.vector_store is None:
self.vector_store = Chroma.from_documents(
documents=documents,
embedding=self.embedding_function
)
else:
self.vector_store.add_documents(documents)
except Exception as e:
logger.error(f"向缓存添加文档失败: {e}")
def search(self, query, k=3):
if self.vector_store is None:
return []
try:
return self.vector_store.similarity_search(query, k=k)
except Exception as e:
logger.error(f"向量搜索失败: {e}")
return []
接下来设置系统提示和主要功能:
# 初始化核心组件和提示模板
search_cache = SearchCache()
enhanced_retriever = EnhancedTavilyRetriever(max_results=5)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 定义系统提示
system_template = """你是一个研究助手,基于提供的搜索结果提供准确答案。
遵循以下准则:
1. 只使用提供的上下文回答问题
2. 如果上下文不含答案,说"我没有足够信息回答这个问题"
3. 引用你的来源,参考文档编号
4. 不要编造信息
5. 保持答案简洁但完整
上下文: {context}
聊天历史: {chat_history}
"""
system_message = SystemMessagePromptTemplate.from_template(system_template)
human_template = "问题: {question}"
human_message = HumanMessagePromptTemplate.from_template(human_template)
prompt = ChatPromptTemplate.from_messages([system_message, human_message])
# 初始化LLM
def get_llm(model_name="gemini-2.0-flash-lite", temperature=0.2):
try:
return ChatGoogleGenerativeAI(
model=model_name,
temperature=temperature,
convert_system_message_to_human=True,
top_p=0.95,
top_k=40,
max_output_tokens=2048
)
except Exception as e:
logger.error(f"初始化LLM失败: {e}")
raise
output_parser = SearchResultsParser()
实现智能检索和问答链:
# 混合检索和文档摘要功能
def retrieve_with_fallback(query):
"""先检查缓存,没有则进行网络搜索"""
cached_results = search_cache.search(query)
if cached_results:
logger.info(f"从缓存中检索到{len(cached_results)}个文档")
return cached_results
logger.info("缓存未命中,执行网络搜索")
search_results = enhanced_retriever.invoke(query)
# 将新结果添加到缓存
search_cache.add_documents(search_results)
return search_results
def summarize_documents(documents, query):
"""使用LLM总结文档"""
llm = get_llm(temperature=0)
summarize_prompt = ChatPromptTemplate.from_template(
"""创建以下与查询相关文档的简明摘要: {query}
{documents}
提供一个与查询相关的关键点的全面摘要。
"""
)
chain = (
{"documents": lambda docs: format_docs(docs), "query": lambda _: query}
| summarize_prompt
| llm
| StrOutputParser()
)
return chain.invoke(documents)
# 高级问答链
def advanced_chain(query_engine="enhanced", model="gemini-1.5-pro", include_history=True):
llm = get_llm(model_name=model)
if query_engine == "enhanced":
retriever = lambda query: retrieve_with_fallback(query)
else:
retriever = enhanced_retriever.invoke
def chain_with_history(input_dict):
query = input_dict["question"]
chat_history = memory.load_memory_variables({})["chat_history"] if include_history else []
# 检索相关文档
docs = retriever(query)
# 格式化文档内容
context = format_docs(docs)
# 使用提示模板生成响应
result = prompt.invoke({
"context": context,
"question": query,
"chat_history": chat_history
})
# 保存到对话历史
memory.save_context({"input": query}, {"output": result.content})
return llm.invoke(result)
return RunnableLambda(chain_with_history) | StrOutputParser()
# 初始化问答链
qa_chain = advanced_chain()
# 查询分析功能
def analyze_query(query):
llm = get_llm(temperature=0)
analysis_prompt = ChatPromptTemplate.from_template(
"""分析以下查询并提供:
1. 主要主题
2. 情感(积极、消极、中性)
3. 提到的关键实体
4. 查询类型(事实性、观点、指导等)
查询: {query}
以JSON格式返回分析,结构如下:
{{
"topic": "主要主题",
"sentiment": "情感",
"entities": ["实体1", "实体2"],
"type": "查询类型"
}}
"""
)
chain = analysis_prompt | llm | output_parser
return chain.invoke({"query": query})
实际应用示例
下面是如何使用这个系统回答问题的示例:
# 测试系统
print("高级Tavily-Gemini实现")
print("="*50)
# 示例查询
query = "塞尔达传说:旷野之息什么时候发布的,评价如何?"
print(f"查询: {query}")
try:
print("\n搜索答案中...")
answer = qa_chain.invoke({"question": query})
print("\n回答:")
print(answer)
print("\n分析查询...")
query_analysis = analyze_query(query)
print("\n查询分析:")
print(json.dumps(query_analysis, indent=2))
except Exception as e:
print(f"处理错误: {e}")
# 带域名过滤的专业搜索
print("\n带域名过滤的专业搜索:")
specialized_retriever = EnhancedTavilyRetriever(
max_results=3,
search_depth="advanced",
include_domains=["nintendo.com", "zelda.com"],
exclude_domains=["reddit.com", "twitter.com"]
)
specialized_results = specialized_retriever.invoke("塞尔达旷野之息销量")
print(f"找到{len(specialized_results)}个专业化结果")
summary = summarize_documents(specialized_results, "塞尔达旷野之息销量")
print("\n专业化结果摘要:")
print(summary)
# 显示搜索历史和指标
history = enhanced_retriever.get_search_history()
print("\n搜索历史:")
for i, h in enumerate(history):
print(f"{i+1}. 查询: {h['query']} - 结果: {h['num_results']} - 时间: {h['response_time']:.2f}秒")
这段代码演示了系统如何:
- 回答关于《塞尔达传说:旷野之息》的问题
- 分析查询的主题、情感和实体
- 使用域名过滤器执行专业化搜索
- 跟踪和显示搜索性能指标
总结
通过结合Tavily Search API、Chroma向量数据库、Google Gemini和LangChain框架,我们构建了一个功能强大的AI问答系统。它能实时访问网络信息,通过语义缓存提高响应速度,并保持上下文连贯性。
系统设计注重:
- 模块化 - 各组件可以单独升级或替换
- 可扩展性 - 轻松添加新功能或集成更多数据源
- 性能与准确性平衡 - 优化用户体验的同时保持高质量回答
参考资料: