Speckly:基于Speckle文档的RAG智能问答机器人
前言
Speckly 是一个基于 检索增强生成 (RAG) 技术的智能问答机器人,它能像一位经验丰富的工程师,理解你的问题,并从 Speckle 文档中精准地找到答案。更厉害的是,它甚至可以帮你生成代码片段!🚀
本文将详细介绍 Speckly 的完整开发流程,涵盖从创建图管道到搭建服务器,再到设计用户界面的所有环节,最终实现一个可交互的智能问答系统。
您将学习如何:
- 构建处理用户提问和文档信息的核心逻辑(图管道)。
- 搭建本地服务器,模拟 Speckly 的线上运行环境。
- 使用 Streamlit 和 Gradio 设计用户友好的交互界面。
通过学习本项目,您将掌握在部署模型到生产环境之前进行本地测试的方法,并了解如何构建简洁易用的用户界面。
步骤 1:导入 API 密钥
首先,我们从 .env 文件中导入 API 密钥。另外,我们还可以使用 LangSmith 设置跟踪。
import osfrom dotenv import load_dotenv, find_dotenvload_dotenv(find_dotenv()) 重要提示:如果无法加载 API 密钥,请检查此行Getting the api keys from the .env fileos.environ['OPENAI_API_KEY'] = os.getenv('OPENAI_API_KEY')os.environ['LANGCHAIN_API_KEY'] = os.getenv('LANGCHAIN_API_KEY')Langsmith Tracingos.environ['LANGCHAIN_TRACING_V2'] = os.getenv('LANGCHAIN_TRACING_V2')os.environ['LANGCHAIN_ENDPOINT'] = os.getenv('LANGCHAIN_ENDPOINT')os.environ['LANGCHAIN_PROJECT'] = os.getenv('LANGCHAIN_PROJECT')Fire Crawl APIos.environ['FIRE_API_KEY']=os.getenv('FIRE_API_KEY')
以下是一个示例 .env 文件。请获取您的 API 密钥(如果您没有),并将它们粘贴在字符串之间。我在第一篇博文中详细描述了这一点。
OPENAI_API_KEY=''
LANGCHAIN_API_KEY=''
LANGCHAIN_TRACING_V2='true'
LANGCHAIN_ENDPOINT='https://api.smith.langchain.com'
LANGCHAIN_PROJECT=''
步骤 2:加载文档
我们将使用 Mendable.ai 创建的名为 FireCrawl 的产品,它可以将网站转换为对大语言模型友好的文档。这正是我们想要的。我们将抓取 Speckle 的开发者文档,并将所有页面和子页面转换为文档列表。您需要一个 API 密钥才能在加载器函数中使用。
我创建了 DocumentLoader 类,它将 API 密钥作为字符串输入,并使用 get_docs 函数,该函数将 URL 作为输入,并提供一个文档列表(包括元数据)作为输出。
from typing import List
from langchain_community.document_loaders import FireCrawlLoader
from document import Documentclass DocumentLoader:def __init__(self, api_key: str):self.api_key = api_keydef get_docs(self, url: str) -> List[Document]:"""使用 FireCrawlLoader 从指定的 URL 检索文档。Args:url (str): 要抓取文档的 URL。Returns:List[Document]: 包含检索到的内容的 Document 对象列表。"""loader = FireCrawlLoader(api_key=self.api_key, url=url, mode="crawl")raw_docs = loader.load()docs = [Document(page_content=doc.page_content, metadata=doc.metadata) for doc in raw_docs]return docs
就我而言,我已经抓取了文档,并将文档保存在本地,这样我就不会重复这个过程并浪费我的积分了。第一次使用时,您可以使用 get_docs 函数;之后,您可以直接加载文档。
import pickle# 从本地文件加载已抓取并保存的文档
with open("crawled_docs/saved_docs.pkl", "rb") as f:saved_docs = pickle.load(f)
步骤 3:创建向量存储和检索器
现在我们有了文档,我们希望将它们分成更小的部分,并将嵌入存储在一个开源向量存储中以供检索。我们将依赖 OpenAI 嵌入模型和 FAISS 向量存储。您还可以选择提供一个路径,以便将向量存储保存在本地。
from typing import List, Optional
from langchain_openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitterdef create_vector_store(docs, store_path: Optional[str] = None) -> FAISS:"""从文档列表创建 FAISS 向量存储。Args:docs (List[Document]): 包含要存储的内容的 Document 对象列表。store_path (Optional[str]): 用于在本地存储向量存储的路径。如果为 None,则不会存储向量存储。Returns:FAISS: 包含文档的 FAISS 向量存储。"""# 创建文本拆分器text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000,chunk_overlap=200,)texts = text_splitter.split_documents(docs)# 嵌入对象embedding_model = OpenAIEmbeddings()# 创建 FAISS 向量存储store = FAISS.from_documents(texts, embedding_model)# 如果提供了路径,则将向量存储保存在本地if store_path:store.save_local(store_path)return store# 创建向量存储
store = create_vector_store(saved_docs)# 创建检索器
retriever = store.as_retriever()
步骤 4:创建用于响应生成的检索链
现在,我们将创建 create_generate_chain
函数来创建一个响应生成链。该函数首先使用一个 generate_template
来提供有关该过程的详细说明。这个模板有两个占位符:{context}
用于存储相关信息,{input}
用于存储问题。然后,使用 LangChain 中的 PromptTemplate
模块,它接受两个变量:template = generate_template
和 input_variables = ["context", "input"]
。最后一步是使用 generate_prompt
、大语言模型和 StrOutputParser()
创建 generate_chain
。
# generate_chain.py
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParserdef create_generate_chain(llm):"""创建一个用于回答代码相关问题的生成链。Args:llm (LLM): 用于生成响应的语言模型。Returns:一个可调用函数,它接受上下文和问题作为输入,并返回字符串响应。"""generate_template = """你是一个名为 Speckly 的乐于助人的代码助手。用户向你提供了一个与代码相关的问题,其内容由以下上下文部分表示(由 <context></context> 分隔)。使用这些来回答最后的问题。这些文件涉及 Speckle 开发者文档。你可以假设用户是土木工程师、建筑师或软件开发人员。如果你不知道答案,就说你不知道。不要试图编造答案。如果问题与上下文无关,请礼貌地回复说你只回答与上下文相关的问题。提供尽可能详细的答案,并使用 Python(默认)生成代码,除非用户在问题中特别说明。<context>{context}</context><question>{input}</question>"""generate_prompt = PromptTemplate(template=generate_template, input_variables=["context", "input"])# 创建生成链generate_chain = generate_prompt | llm | StrOutputParser()return generate_chain# 创建生成链
generate_chain = create_generate_chain(llm)
这里需要注意的是,StrOutputParser()
用于从大语言模型获取字符串输出。否则,输出可能很复杂,例如 JSON 或结构化消息对象,这些对象无法直接用于进一步处理或向用户显示。没有 StrOutputParser()
的输出可能如下所示:
{"content": "This is the response from the LLM.","metadata": {"confidence": 0.8,"response_time": 0.5}
}
而使用 StrOutputParser()
,输出如下所示:
This is the response from the LLM.
步骤 5:创建评分器
在这一步中,我们将创建不同的评分器,用于评估检索到的文档与用户问题的相关性、评估生成的答案、检查答案是否合理,以及在没有获得相关文档时重新编写查询。
检索评分器
首先,我们创建一个检索评分器,以评估检索到的文档与用户问题的相关性。为此,我们定义一个 create_retrieval_grader
函数,该函数接受一个带有新指令的提示模板 grade_prompt
。该函数指示评分器在文档中查找与用户问题相关的关键字。如果存在此类关键字,则该文档被视为相关。评分器应该提供一个二进制分数,“yes” 或 “no”,表示该文档是否与问题相关,并以 JSON 格式提供结果,其中包含一个键“score”。
def create_retrieval_grader(model):"""创建一个检索评分器,用于评估检索到的文档与用户问题的相关性。Returns:一个可调用函数,它接受文档和问题作为输入,并返回一个 JSON 对象,其中包含一个二进制分数,表示该文档是否与问题相关。"""grade_prompt = PromptTemplate(template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>你是一个评分器,负责评估检索到的文档与用户问题的相关性。如果文档包含与用户问题相关的关键字,则将其评级为相关。它不需要是一个严格的测试。目标是过滤掉错误的检索结果。给出一个二进制分数“yes”或“no”,表示该文档是否与问题相关。以 JSON 格式提供二进制分数,其中包含一个键“score”,并且没有前言或解释。<|eot_id|><|start_header_id|>user<|end_header_id|>以下是检索到的文档: \n\n {document} \n\n以下是用户问题: {input} \n<|eot_id|><|start_header_id|>assistant<|end_header_id|>""",input_variables=["document", "input"],)# 创建检索器链retriever_grader = grade_prompt | model | JsonOutputParser()return retriever_grader
以下是一个示例:
model = ... 在此处提供您的 llm
grader = create_retrieval_grader(model)
document = "France is a country in Europe. Paris is the capital of France."
question = "What is the capital of France?"
score = grader(document, question)
print(score) 输出: {"score": "yes"}
幻觉评分器
接下来,我们定义一个幻觉评分器,用于评估从大语言模型获得的答案是否基于一组事实。该评分器提供一个二进制分数(“yes”或“no”),表示答案是否合理。提示模板将包括事实 ({documents}) 和答案 ({generation}) 的占位符,这些占位符将在使用提示时填充。
def create_hallucination_grader(self):"""创建一个幻觉评分器,用于评估答案是否基于/得到一组事实的支持。Returns:一个可调用函数,它接受生成(答案)和文档列表(事实)作为输入,并返回一个 JSON 对象,其中包含一个二进制分数,表示答案是否基于/得到事实的支持。"""hallucination_prompt = PromptTemplate(template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>你是一个评分器,负责评估答案是否基于/得到一组事实的支持。给出一个二进制分数“yes”或“no”,表示答案是否基于/得到一组事实的支持。以 JSON 格式提供二进制分数,其中包含一个键“score”,并且没有前言或解释。<|eot_id|><|start_header_id|>user<|end_header_id|>以下是事实:\n ------- \n{documents}\n ------- \n以下是答案: {generation}<|eot_id|><|start_header_id|>assistant<|end_header_id|>""",input_variables=["generation", "documents"],)hallucination_grader = hallucination_prompt | self.model | JsonOutputParser()return hallucination_grader
以下是一个示例:
from langchain_openai import ChatOpenAI## LLM model
model = ChatOpenAI(model="gpt-4o", temperature=0)
## Grader
grader = create_hallucination_grader(model)
answer = "The capital of France is Paris."
facts = ["France is a country in Europe.", "Paris is the capital of France."]
score = grader(answer, facts)
print(score) # 输出: {"score": "yes"}
代码评估器
接下来,我们定义一个 create_code_evaluator
函数,该函数创建一个代码评估器,以评估生成的代码是否正确以及是否与给定问题相关。它使用 PromptTemplate
指示评估器提供一个带有二进制分数和反馈的 JSON 响应。评估器接受生成(代码)、问题和文档列表作为输入,并返回一个 JSON 对象,其中包含一个分数(表示代码是否正确和相关)以及对评估的简要说明。
def create_code_evaluator(self):"""创建一个代码评估器,用于评估生成的代码是否正确以及是否与给定问题相关。Returns:一个可调用函数,它接受生成(代码)、问题和文档列表作为输入,并返回一个带有二进制分数和反馈的 JSON 对象。"""eval_template = PromptTemplate(template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|> 你是一个代码评估器,负责评估生成的代码是否正确以及是否与给定问题相关。提供一个带有以下键的 JSON 响应:“score”:一个二进制分数“yes”或“no”,表示代码是否正确和相关。“feedback”:对你的评估的简要说明,包括任何问题或需要改进的地方。<|eot_id|><|start_header_id|>user<|end_header_id|>以下是生成的代码:\n ------- \n{generation}\n ------- \n以下是问题: {input}\n ------- \n以下是相关文档: {documents}<|eot_id|><|start_header_id|>assistant<|end_header_id|>""",input_variables=["generation", "input", "documents"],)code_evaluator = eval_template | self.model | JsonOutputParser()return code_evaluator
以下是一个使用示例:
model = ... # 初始化一个语言模型code_evaluator = create_code_evaluator(model)code = "def greet(name): return f'Hello, {name}!'"
question = "Write a function to greet someone by name."
documents = ["A function should take a name as input and return a greeting message."]result = code_evaluator(code, question, documents)
print(result) # 输出: {"score": "yes", "feedback": "The code is correct and relevant to the question."}
最后,我们创建 create_question_rewriter
函数,该函数构建一个重写器链,用于优化给定问题,以增强其清晰度和相关性。此函数返回一个可调用函数,该函数接受一个问题作为输入,并输出重写后的问题作为字符串。
def create_question_rewriter(model):"""创建一个问题重写器链,用于重写给定问题以提高其清晰度和相关性。Returns:一个可调用函数,它接受一个问题作为输入,并返回重写后的问题作为字符串。"""re_write_prompt = hub.pull("efriis/self-rag-question-rewriter")question_rewriter = re_write_prompt | self.model | StrOutputParser()return question_rewriter
以下是一个使用示例:
rewriter = create_question_rewriter()
original_question = "how to use speckle's python sdk?"
rewritten_question = rewriter(original_question)
print(rewritten_question) # 输出: "How to install speckle's python sdk?"
现在我们已经定义了所有组件,我们可以创建一个名为 GraderUtils
的类来包含所有这些函数。然后,我们可以使用我们的 LLM 模型作为唯一必要的输入来初始化这个类的一个实例。
from langchain_openai import ChatOpenAIclass GraderUtils:def __init__(self, model):self.model = modeldef create_retrieval_grader(self):...def create_hallucination_grader(self):...def create_code_evaluator(self):... def create_question_rewriter(self):...## LLM model
llm = ChatOpenAI(model="gpt-4o", temperature=0)# 创建 GraderUtils 类的一个实例
grader = GraderUtils(llm)# 获取检索评分器
retrieval_grader = grader.create_retrieval_grader()# 获取幻觉评分器
hallucination_grader = grader.create_hallucination_grader()# 获取代码评估器
code_evaluator = grader.create_code_evaluator()# 获取问题重写器
question_rewriter = grader.create_question_rewriter()
想要了解更多信息,您可以参考 langchain-ai 仓库中的 RAG 笔记本。
步骤 6:创建图
现在我们已经拥有了所有组件,接下来我们将使用 LangGraph 创建图。
定义图的状态
首先,我们定义一个 GraphState
类来表示图的状态,该状态包含三个关键属性:input
、generation
和 documents
。其中,input
属性存储用户输入的问题,generation
属性存储大语言模型根据输入生成的答案,documents
属性存储相关文档列表。
from typing_extensions import TypedDict
from typing import Listclass GraphState(TypedDict):"""表示图的状态。Attributes:question: 问题generation: LLM 生成documents: 文档列表"""input: strgeneration: strdocuments: str #List[str]
GraphState
中定义的状态在整个图中全局可访问,并且这些属性是节点函数可以修改的唯一变量。
节点
接下来,我们定义节点。节点是 Python 函数,它们接收图的状态,执行一些操作,并修改状态变量。我们定义一个名为 GraphNodes
的类来包含所有节点函数。
from document import Document
from utils.generate_chain import create_generate_chainclass GraphNodes:def __init__(self, llm, retriever, retrieval_grader, hallucination_grader, code_evaluator, question_rewriter):self.llm = llmself.retriever = retrieverself.retrieval_grader = retrieval_graderself.hallucination_grader = hallucination_graderself.code_evaluator = code_evaluatorself.question_rewriter = question_rewriterself.generate_chain = create_generate_chain(llm)def retrieve(self, state):"""检索文档Args:state (dict): 当前图状态Returns:state (dict): 添加到状态的新键,文档,其中包含检索到的文档"""print("---RETRIEVE---")question = state["input"]# 检索documents = self.retriever.invoke(question)return {"documents": documents, "input": question}def generate(self, state):"""生成答案Args:state (dict): 当前图状态Returns:state (dict): 添加到状态的新键,生成,其中包含 LLM 生成"""print("---GENERATE---")question = state["input"]documents = state["documents"]# RAG 生成generation = self.generate_chain.invoke({"context": documents, "input": question})return {"documents": documents, "input": question, "generation": generation}def grade_documents(self, state):"""确定检索到的文档是否与问题相关。Args:state (dict): 当前图状态Returns:state (dict): 使用仅过滤后的相关文档更新文档键"""print("---CHECK DOCUMENT RELEVANCE TO QUESTION---")question = state["input"]documents = state["documents"]# 对每个文档进行评分filtered_docs = []for d in documents:score = self.retrieval_grader.invoke({"input": question, "document": d.page_content})grade = score["score"]if grade == "yes":print("---GRADE: DOCUMENT RELEVANT---")filtered_docs.append(d)else:print("---GRADE: DOCUMENT IR-RELEVANT---")continuereturn {"documents": filtered_docs, "input": question}def transform_query(self, state):"""转换查询以生成更好的问题。Args:state (dict): 当前图状态Returns:state (dict): 使用重新表述的问题更新问题键"""print("---TRANSFORM QUERY---")question = state["input"]documents = state["documents"]# 重新编写问题better_question = self.question_rewriter.invoke({"input": question})return {"documents": documents, "input": better_question}
retrieve
:根据输入问题检索文档,并将它们添加到图状态中。generate
:使用输入问题和检索到的文档生成答案,并将生成添加到图状态中。grade_documents
:根据检索到的文档与输入问题的相关性对其进行过滤,仅使用相关文档更新图状态。transform_query
:重新表述输入问题以提高其清晰度和相关性,使用转换后的问题更新图状态。
边
边函数引导图处理流程,根据当前状态和各种节点函数的结果做出决策。
class EdgeGraph:def __init__(self, hallucination_grader, code_evaluator):self.hallucination_grader = hallucination_graderself.code_evaluator = code_evaluatordef decide_to_generate(self, state):"""确定是生成答案还是重新生成问题。Args:state (dict): 当前图状态Returns:str: 对要调用的下一个节点的二进制决策"""print("---ASSESS GRADED DOCUMENTS---")question = state["input"]filtered_documents = state["documents"]if not filtered_documents:# 所有文档都已过滤 check_relevance# 我们将重新生成一个新查询print("---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---")return "transform_query" # "retrieve_from_community_page", "transform_query"else:# 我们有相关文档,因此生成答案print("---DECISION: GENERATE---")return "generate"def grade_generation_v_documents_and_question(self, state):"""确定生成是否基于文档并回答问题。Args:state (dict): 当前图状态Returns:str: 对要调用的下一个节点的决策"""print("---CHECK HALLUCINATIONS---")question = state["input"]documents = state["documents"]generation = state["generation"]score = self.hallucination_grader.invoke({"documents": documents, "generation": generation})grade = score["score"]# 检查幻觉if grade == "yes":print("---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---")# 检查问答print("---GRADE GENERATION vs QUESTION---")score = self.code_evaluator.invoke({"input": question, "generation": generation, "documents": documents})grade = score["score"]if grade == "yes":print("---DECISION: GENERATION ADDRESSES QUESTION---")return "useful"else:print("---DECISION: GENERATION DOES NOT ADDRESS QUESTION---")return "not useful"else:print("---DECISION: GENERATIONS ARE HALLUCINATED, RE-TRY---")return "not supported"
EdgeGraph
类定义了以下边函数:
decide_to_generate
:根据过滤后的文档与输入问题的相关性,决定是生成答案还是重新生成问题。grade_generation_v_documents_and_question
:根据生成的答案是否基于文档及其回答问题的能力来评估生成的答案。
构建图
现在我们已经定义了图状态、节点和边函数,我们可以开始构建图了。
# 初始化图
workflow = StateGraph(GraphState)
# 创建 GraphNodes 类的一个实例
graph_nodes = GraphNodes(llm, retriever, retrieval_grader, hallucination_grader, code_evaluator, question_rewriter)
# 创建 EdgeGraph 类的一个实例
edge_graph = EdgeGraph(hallucination_grader, code_evaluator)
# 定义节点
workflow.add_node("retrieve", graph_nodes.retrieve) # 检索文档
workflow.add_node("grade_documents", graph_nodes.grade_documents) # 对文档进行评分
workflow.add_node("generate", graph_nodes.generate) # 生成答案
workflow.add_node("transform_query", graph_nodes.transform_query) # 转换查询
# 构建图
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges("grade_documents",edge_graph.decide_to_generate,{"transform_query": "transform_query", # "transform_query": "transform_query","generate": "generate",},
)
workflow.add_edge("transform_query", "retrieve")
workflow.add_conditional_edges("generate",edge_graph.grade_generation_v_documents_and_question,{"not supported": "generate","useful": END,"not useful": "transform_query", # "transform_query"},
)
# 编译
chain = workflow.compile()
首先,我们使用 StateGraph
类初始化图。然后,我们创建 GraphNodes
和 EdgeGraph
类的实例。接下来,我们添加已经定义函数的节点:
retrieve
:根据输入问题检索相关文档。grade_documents
:根据检索到的文档与问题的相关性对其进行过滤。generate
:根据过滤后的文档生成答案。transform_query
:转换输入问题以提高其清晰度和相关性。
图的起始节点是 retrieve
节点。retrieve
节点和 grade_documents
节点之间有一条普通边连接。在 grade_documents
节点之后,工作流到达一个条件边。此时,会调用 edge_graph.decide_to_generate
函数来决定工作流的下一步。该函数评估已评分的文档,并决定是转换查询还是生成答案。如果函数返回 transform_query
,则工作流将移动到 transform_query
节点,该节点转换输入问题以提高其清晰度和相关性。如果函数返回 generate
,则工作流将移动到 generate
节点,该节点根据过滤后的文档生成答案。
transform_query
和 retrieve
之间也有一条普通边连接。这是因为在转换查询之后,工作流会移回 retrieve
节点,以根据转换后的查询检索新文档。
生成答案后,工作流到达一个条件边。此时,会调用 edge_graph.grade_generation_v_documents_and_question
函数,根据生成的答案是否基于文档及其回答问题的能力来评估生成的答案。如果函数返回 not supported
,则工作流将移回 generate
节点以重新生成答案。此步骤对于确保工作流生成受文档支持的答案是必需的。如果函数返回 useful
,则工作流将结束,表示已生成有用的答案。如果函数返回 not useful
,则工作流将移动到 transform_query
节点以再次转换查询。
最后,我们将编译图以将其转换为可执行链。
步骤 7:使用 FastAPI 启动服务器
现在,我们将使用 FastAPI 启动服务器。
首先,我们创建一个 FastAPI 应用程序。
app = FastAPI(title="Speckle服务器",version="1.0",description="用于回答有关Speckle Developer Docs的问题的API服务器"
)
接下来,我们为根 URL (/) 定义一个路由,该路由重定向到文档 URL (/docs)。
@app.get("/")
async def redirect_root_to_docs():return RedirectResponse("/docs")
然后,我们使用 Pydantic 的 BaseModel
定义两个模型:Input
和 Ou``tput
。这些模型将用于定义 API 的输入和输出数据的结构。
class Input(BaseModel):input: strclass Output(BaseModel):output: dict
接下来,我们使用 add_routes
函数向应用程序添加路由。
add_routes(app,chain.with_types(input_type=Input, output_type=Output),path="/speckle_chat",
)
最后,我们使用 Uvicorn 运行服务器。
if __name__ == "__main__":import uvicornuvicorn.run(app, host="localhost", port=8000)
现在,我们已经创建了一个 FastAPI 应用程序,并启动了一个可以从 http://localhost:8000
访问的服务器。
步骤 8:使用 Streamlit/Gradio 创建客户端
现在,我们将创建客户端来与服务器进行交互。
使用 Streamlit 创建 UI
首先,我们使用 Python 中的 Streamlit 库创建一个客户端。
import streamlit as st
from langserve import RemoteRunnable
from pprint import pprintst.title('Welcome to Speckle Server')
input_text = st.text_input('ask speckle related question here')if input_text:with st.spinner("Processing..."):try:app = RemoteRunnable("http://localhost:8000/speckle_chat/")for output in app.stream({"input": input_text}):for key, value in output.items():# 节点pprint(f"Node '{key}':")# 可选:在每个节点打印完整状态# pprint.pprint(value["keys"], indent=2, width=80, depth=None)pprint("\n---\n")output = value['generation'] st.write(output)except Exception as e:st.error(f"Error: {e}")
我们首先设置 Streamlit 应用程序,包括一个标题和一个文本输入字段,供用户输入问题。当用户输入任何文本时,应用程序会显示一个微调器,表示正在处理输入。然后,应用程序使用 langserve
中的 RemoteRunnable
模块和服务器 URL 连接到服务器。它使用 stream
命令流式传输来自大语言模型的响应,同时打印图工作流中触发的节点。最后,我们从存储在值字典中的 generation
键中检索最终输出。如果在处理过程中出现错误,它将显示错误消息。
使用 Gradio 创建 UI
我们还可以使用 Gradio 来创建客户端 UI。Gradio 是一个开源 Python 库,用于为机器学习模型、API 和任意 Python 函数创建交互式基于 Web 的用户界面。
首先,我们创建一个函数,该函数将允许从大语言模型中获取最终响应。
def get_response(input_text):app = RemoteRunnable("http://localhost:8000/speckle_chat/")for output in app.stream({"input": input_text}):for key, value in output.items():# 节点pprint(f"Node '{key}':")# 可选:在每个节点打印完整状态# pprint.pprint(value["keys"], indent=2, width=80, depth=None)pprint("\n---\n")output = value['generation']return output
然后,我们创建一个简单的 Gradio UI。
import gradio as gr
from langserve import RemoteRunnable
from pprint import pprint# 在 Gradio 中创建 UI
iface = gr.Interface(fn=get_response, inputs=gr.Textbox(value="Enter your question"), outputs="textbox", title="Q&A over Speckle's developer docs",description="Ask a question about Speckle's developer docs and get an answer from the code assistant. This assistant looks up relevant documents and answers your code-related question.",examples=[["How do I install Speckle's python sdk?"], ["How to commit and retrieve an object from Speckle?"],],theme=gr.themes.Soft(),allow_flagging="never",)iface.launch(share=True) # 将 share 设置为 True 以获取公共 URL
在 launch
函数中设置 share=True
可以获取公共 URL。
总结
今天,我们探讨了如何为包含高级 RAG(检索增强生成)概念的图工作流开发服务器-客户端架构。我们创建了一个服务器组件,该组件涵盖了一个全面的管道,包括对检索到的文档进行评分、对响应进行评分、检查幻觉和查询重写。为了与此本地服务器交互,我们创建了两个客户端应用程序,一个使用 Streamlit,另一个使用 Gradio。这两个 UI 都为用户提供了一个友好的界面,让他们可以输入查询并实时接收服务器的响应。这是一个端到端的项目,允许开发人员在将应用程序部署到生产环境之前构建应用程序并在本地对其进行测试。
如何学习AI大模型?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】

一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。
四、AI大模型商业化落地方案
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。
相关文章:
Speckly:基于Speckle文档的RAG智能问答机器人
前言 Speckly 是一个基于 检索增强生成 (RAG) 技术的智能问答机器人,它能像一位经验丰富的工程师,理解你的问题,并从 Speckle 文档中精准地找到答案。更厉害的是,它甚至可以帮你生成代码片段!🚀 本文将详…...
NodeRed使用心得,实现增删改查等
使用场景介绍 在VUE中使用nodeRed实现对节点的 增删改查等功能,且储存成功之后下点击时启动对应流程 安装与配置 1.安装NodeRed npm install -g --unsafe-perm node-red 安装完成后,你可以通过运行以下命令来启动Node-RED node-red-start2. 配置文件 N…...
万物皆有解法(序)
万物皆有解法(序) 《万物有解》一:解的存在 解:可做解释解答,此文引申为原因。可做解除、解围,此文引申为解法、方法。 先有事物存于世-what,再有原因为何存-why,再有解法如何除去…...
OpenCV相机标定与3D重建(37)计算两幅图像之间单应性矩阵(Homography Matrix)的函数findHomography()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 找到两个平面之间的透视变换。 cv::findHomography 是 OpenCV 库中用于计算两幅图像之间单应性矩阵(Homography Matrix)的…...
从虚拟到现实:AI与AR/VR技术如何改变体验经济?
引言:体验经济的崛起 在当今消费环境中,产品与服务早已不再是市场竞争的唯一焦点,能够提供深刻感知和独特体验的品牌,往往更能赢得消费者的青睐。这种转变标志着体验经济的崛起。体验经济不仅仅是简单的买卖行为,而是通…...
Linux系统之stat命令的基本使用
Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…...
c++ 命名空间
目录 目录 目录 namespace的定义 代码演示 先使用全局域,再使用namespace定义出的域 命名空间中可以定义变量/函数/类型等 命名空间可以嵌套 namespace的使用 指定命名空间访问 using将命名空间中某个成员展开 展开命名空间中全部成员 在c中,由…...
【实验记录】动手实现一个简单的神经网络实验(一)
最近上了“神经网络与深度学习”这门课,有一个自己动手实现调整神经网络模型的实验感觉还挺有记录意义,可以帮我巩固之前学习到的理论知识,所以就打算记录一下。 实验大概是使用LeNet(卷积神经网络)对MINIST数据集做图…...
【2024年最新】BilibiliB站视频动态评论爬虫
废话不多说,直接先放git仓库:GitHub - linyuye/Bilibili_crawler: bilibili爬虫,基于selenium获取oid与cookie,request获取api内容 〇:概念简述 oid:视频/动态的uuid,b站对于发布内容的通用唯…...
清空DNS 缓存
如果遇到修改了host文件,但是IP和域名的映射有问题的情况,可以尝试刷新DNS缓存。 ipconfig/flushdns win建加R建,然后输入cmd,然后回车 然后回车,或者点击确定按钮。 出现如下所示标识清空DNS 缓存成功。...
东土智能交通服务器助力北京市车路云一体化建设
背景及意义 北京高级别自动驾驶示范区自2020年启动建设,至今已经发展建设到3.0阶段,通州区作为3.0阶段扩建的重点区域之一,扩区建设范围共计约175平方公里,涉及18个属地街镇,涵盖580个路口。 作为北京市车路云一体化…...
HarmonyOS NEXT 实战之元服务:静态案例效果---妙语集语
背景: 前几篇学习了元服务,后面几期就让我们开发简单的元服务吧,里面丰富的内容大家自己加,本期案例 仅供参考 先上本期效果图 ,里面图片自行替换 效果图1完整代码案例如下: import { authentication } …...
python基础项目
1.联系人案例 # 导入的模块 from input_util import * import re import csv# 定义一个变量保存文件读取的信息 users {}# 封装读取文件的函数 def reader_file(path_name: str ./python基础/2024-11-15python基础项目/data/a.csv) -> None:try:with open(path_name, enco…...
mysql返回N/A
在写统计图的接口,sql查询一直无数据,给的默认值也没有实现: SELECTifnull( unit.num, 0 ) riskUnitCount,ifnull( EVENT.num, 0 ) riskEventCount,ifnull( measure.num, 0 ) riskMeasureCount FROMtb_companyLEFT JOIN (SELECTrisk.qyid,co…...
C++---------迭代策略与迭代器
一、迭代策略与迭代器 迭代器的概念 迭代器是一种对象,它提供了一种统一的方式来访问容器(如数组、向量、列表等)中的元素,而不暴露容器的内部结构。迭代器的行为类似于指针,可以用于遍历容器中的元素、修改元素以及…...
深入解析 Oracle 的聚合函数 ROLLUP
目录 深入解析 Oracle 的聚合函数 ROLLUP一、ROLLUP 函数概述二、ROLLUP 函数语法三、ROLLUP 实例详解(一)基础分组聚合(二)引入 ROLLUP 函数(三)ROLLUP 与 NULL 值(四)多列复杂分组…...
kipotix4靶机实战
信息收集 1.判断靶机ip 原理:开靶机之前nmap扫一次网段,再开靶机之后扫一次,查看多出来的ip就是靶机ip ip192.168.98.1742.判断端口服务,系统版本 a.确定端口 b.-p指定端口进一步收集 c.信息筛选 1.端口:22,80,139,…...
Java中处理if-else的几种高级方法
前言 在我看来多写几个if-else没啥大不了的,但是就是看起来没啥逼格,领导嫌弃。我根据开发的经历写几个不同的替代方法 一、枚举法替代 我先前写了一篇文章,可以去看看。 通过枚举替换if-else语句的解决方案_枚举代替if else c语言-CSDN博…...
LaTeX 是一种基于标记的排版系统,广泛用于创建高质量的文档,特别是在需要复杂数学公式、表格、文献引用等的场景中
LaTeX 是一种基于标记的排版系统,广泛用于创建高质量的文档,特别是在需要复杂数学公式、表格、文献引用等的场景中。以下是关于 LaTeX 的详细解释: 1. LaTeX 的基本概念 本质:LaTeX 是基于 TeX 的排版系统,提供了更高…...
Go入门篇:(一)golang的安装和编辑工具安装
一、前言 最近我有幸接触到Go语言,深入了解后,发现go语言确实有很多让人惊叹的地方。作为一个有着多年Java编程经验的程序员,我深深地被它所吸引,并且决定记录下我的学习之路,以便与大家分享我的经验和感悟。 与Java不同,Go语言的语法和运行效率都非常高,特别是对于并…...
【10】Selenium+Python UI自动化测试 邮件发送测试报告(某积载系统实例-04)
测试报告需要发送给相关人员,但每次都要在report目录下去复制太麻烦,可以使用邮件模块自动将生成的报告发送给相关人员 1、 新增utils文件夹,用于存放工具文件 在utils下新增sendmail.py文件 代码 sendmail.py import smtplib from email.…...
Playwright爬虫xpath获取技巧
示例一 <button class"MuiButtonBase-root MuiButton-root MuiLoadingButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeLarge MuiButton-containedSizeLarge MuiButton-colorPrimary MuiButton-fullWidth MuiButton-root MuiLoadingButton…...
运算符 - 算术、关系、逻辑运算符
引言 在编程中,运算符是用于执行特定操作的符号。C 提供了多种类型的运算符,包括算术运算符、关系运算符和逻辑运算符等。理解这些运算符及其用法对于编写高效且无误的代码至关重要。本文将详细介绍 C 中的这三种基本运算符,并通过实例帮助读…...
中关村科金外呼机器人智能沟通破解营销难题
当今,传统的营销方式在效率、成本控制、客户管理等方面逐渐显现出局限性,难以满足现代企业的需求。如何提升营销效率、降低运营成本、有效管理客户会员,成为企业的难题。中关村科金外呼机器人通过智能化沟通技术,为企业提供了一站…...
css绘制圆并绘制圆的半径
<div class"item1"></div>.item1 {position: relative;width: 420px;height: 420px;border-radius: 50%; /* 圆形 */color: white; /* 文本颜色 */background-color: rgba(154, 227, 36, 0.4); } .item1::before {content: "";position: absol…...
实现类似gpt 打字效果
1. css的动画(animation) css中实现动画有两种方式:transition过渡动画、 animation自定义动画。 具体的可以看MDN链接:https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation 使用keyframes自定义关键帧动画并未其命名使用自定义动…...
【乐企文件生成工程】根据特定要素动态选择需要生成的发票板式文件实现
乐企版式文件生成工程,涉及到多个票种,不乏特殊票种的生成,如果每个特殊票种都单独写逻辑,那整个代码写起来体量就不得了,如何实现代码逻辑的同时也更优雅的实现代码扩展性呢,您接着往下看。 使用设计模式 工厂模式 1、定义接口InvoiceFileService public interface Inv…...
near-synonym反义词生成(2):Prompt +Bert-MLM(FT)
near-synonym之反义词生成方法二 near-synonym, 中文反义词/近义词/同义词(antonym/synonym)工具包. 方法一为(neg_antonym): Word2vec -> ANN -> NLI -> Length 方法二为(mlm_antonym): Prompt Bert-MLM(FT) Beam-Search 项目地址 github: https://github.com/yon…...
dockfile 配置 /etc/apt/source.list.d/debian.list 清华镜像
docker:3.12.7 镜像使用的是 debian 系统,比 ubuntu 更轻量。debian 系统内,apt 镜像源列表位于 /etc/apt/source.list.d/debian.list(作为对比,ubuntu 的镜像列表位于 /etc/apt/source.list,二者语法相同)…...
DAY38|动态规划Part06|LeetCode:322. 零钱兑换、279.完全平方数、139.单词拆分
目录 LeetCode:322. 零钱兑换 基本思路 C代码 LeetCode:279.完全平方数 C代码 LeetCode:139.单词拆分 基本思路 C代码 LeetCode:322. 零钱兑换 力扣题目链接 文字讲解:LeetCode:322. 零钱兑换 视频讲解:动态规划之完全背包,装满背包最…...
Spring事务回滚
Transactional注解 Transactional作用:就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事务。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作。 Transactional注解:我们一般会在业务层当中来控制…...
【目标跟踪】checkpoint文件到底是什么?
说实话,我一直决定计算机视觉是个很玄的东西,里面的很多东西都是看了概念之后云里雾里,今天就把我复现代码时遇到的不懂得讲一讲——checkpoint文件是个啥? checkpoint文件顾名思义就是一个模型检查点文件,用于保存训练…...
hiprint结合vue2项目实现静默打印详细使用步骤
代码地址是:vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑 本地安装包地址:electron-hiprint 发行版 - Gitee.com 1、先安装hipint安装包在本地 2、项目运行npm(socket.…...
apt和apt-get软件包管理工具-debian
apt 和 apt-get 是在基于Debian的Linux发行版(如Ubuntu)中使用的两个软件包管理工具,它们都属于APT(Advanced Package Tool)的前端工具,用于管理软件包的安装、更新、升级和删除。以下是它们的特性和一些比…...
小程序租赁系统开发的优势与实践探索
内容概要 小程序租赁系统开发正在引起广泛关注,特别是在数字化快速发展的今天。很多企业开始意识到,小程序不仅能为他们带来更多的客户,还能极大地提高管理效率。借助小程序,用户在租赁时可以更加方便地浏览和选择产品࿰…...
sheng的学习笔记-AI-模型评估-留出法、交叉验证法、自助法
Ai目录:sheng的学习笔记-AI目录-CSDN博客 评估方法: 数据集可以分为 训练集,交叉验证集,测试集。 训练集相当于自己做作业,验证集相当于考试测试一下自己的实力,测试集就是真刀真枪的干(当你…...
【Unity3D】ECS入门学习(六)状态组件 ISystemStateComponentData
当需要获知组件是否被销毁时,ECS是没有回调告知的,因此可以将组件继承于ISystemStateComponentData接口,这样即使组件的实体被销毁了,该组件本身是不会消失的,所以可以通过在组件实体销毁后,去设置状态组件…...
DVWA靶场第三关 CSRF
CSRF的中文叫:”跨站请求攻击“,它是通过仿照某一个特殊的网页(重置密码)来进行诱惑性攻击。 难度(low级) 审计代码: <?phpif( isset( $_GET[ Change ] ) ) {// Get input$pass_new $_GE…...
工作流审批功能的一些概念
1. 引言 在当今数字化时代,企业与组织的运营效率在很大程度上依赖于高效、精准的工作流审批系统。随着业务日益复杂且多样化,审批流程变得愈加细致和灵活。一个完善的工作流审批系统不仅能确保任务在组织内部有序流转、协调各方资源,还能实现…...
深度学习与图像处理(国产深度学习框架——飞桨官方指定教材)
计算机视觉从小白到大师之路 《深度学习与图像处理(PaddlePaddle版)》这一本就够了 1.引言 随着人工智能技术的飞速发展,各行各业对深度学习、图像处理相关领域的人才需求日益迫切。本书旨在通过系统的理论讲解与丰富的实战案例࿰…...
音视频入门知识(二)、图像篇
⭐二、图像篇 视频基本要素:宽、高、帧率、编码方式、码率、分辨率 其中码率的计算:码率(kbps)=文件大小(KB)*8/时间(秒),即码率和视频文件大小成正比 YUV和RGB可相互转换 ★YUV(原始数据&am…...
计算机网络——期末复习(3)4-6章考试重点
第四章 根据IPv4第1个十进制数值判断,127以下为A类,128~191为B类,192~223为C类不能分配给主机或路由器接口的:A类网络号0和127,主机号全为0或全为1私有地址(Private IP Address)是指一类专门保…...
openfeign自动将Boolean默认为false
最近发现项目服务间,通过openfeign调用API时,为null的Boolean类型,接收端反系列化后变为false了,经查发现是通用组件中做了处理,特记录下。 主要是设置了这个 SerializerFeature.WriteNullBooleanAsFalse Bean Cond…...
如何实现底部导航栏
文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了TextField Widget,本章回中将介绍BottomNavigationBar Widget。闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中将介绍一个新的Widget:BottomNavigationBar,它就是我们经常在App中看到了底部…...
【深度学习基础|pip安装】pip 安装深度学习库常见错误及解决方案,附案例。
【深度学习基础|pip安装】pip 安装深度学习库常见错误及解决方案,附案例。 【深度学习基础|pip安装】pip 安装深度学习库常见错误及解决方案,附案例。 文章目录 【深度学习基础|pip安装】pip 安装深度学习库常见错误及解决方案,附案例。1. 错…...
org.apache.zookeeper.server.quorum.QuorumPeerMain
QuorumPeerMain源代码 package org.apache.zookeeper.server.quorum;import java.io.IOException; import javax.management.JMException; import javax.security.sasl.SaslException; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.audi…...
如何在yolov8中使用ATSS策略
在yolov8中使用的标签匹配策略是TAL,本篇文章解析一下ATSS代码相关实现以及如何把ATSS放到yolov8中使用 看过本专栏中的另外两篇文章的同学应该对v8解析box那一套很熟悉了,ATSS的第一步就是去得到一系列的anchor-box(如果是anchor-based检测方法)或者anchor-point(基…...
常见的邮件协议SMTP和POP3
常见的邮件协议包括SMTP和POP3,SMTP用来发送邮件,POP3用来接收邮件信息。 SMTP SMTP 是一种用于发送电子邮件的协议。它的主要作用是将**电子邮件**从邮件客户端(如 Outlook、Thunderbird)或邮件服务器发送到接收服务器。 SMTP …...
线性代数行列式
目录 二阶与三阶行列式 二元线性方程组与二阶行列式 三阶行列式 全排列和对换 排列及其逆序数 对换 n阶行列式的定义 行列式的性质 二阶与三阶行列式 二元线性方程组与二阶行列式 若是采用消元法解x1、x2的话则得到以下式子 有二阶行列式的规律可得:分…...
cin/cout性能问题讨论和优化⽅法
样例解析: 在上面的两个案例中,我们发现虽然代码的逻辑是相同的,唯一的不同点在于scanf和cout的使用区别,一份超时一份ac,这是为什么呢?是否有可行的优化方法呢? 背景知识: 在 C 中…...