2026/6/12 9:41:50
网站建设
项目流程
衡水制作网站,投票网站开发的背景和意义,新乡商城网站建设价格,seo模拟点击工具将LangGraph工作流转换为LangFlow可视化流程
在构建AI智能体时#xff0c;我们常常面临一个两难#xff1a;用代码写逻辑清晰但难以协作#xff0c;用图形工具方便展示却受限于灵活性。尤其是当使用 LangGraph 编排复杂的有状态工作流时#xff0c;虽然能实现精细控制——比…将LangGraph工作流转换为LangFlow可视化流程在构建AI智能体时我们常常面临一个两难用代码写逻辑清晰但难以协作用图形工具方便展示却受限于灵活性。尤其是当使用 LangGraph 编排复杂的有状态工作流时虽然能实现精细控制——比如带记忆的问答、循环反思机制或条件跳转——但每次调试都要重跑脚本非技术人员几乎无法参与设计过程。有没有一种方式既能保留 LangGraph 的强大能力又能像搭积木一样“看见”整个流程答案是肯定的LangFlow。它是一个基于前端界面的低代码平台专为 LangChain 生态打造支持通过拖拽组件和连线来构建 AI 应用。更重要的是它允许开发者自定义组件这意味着你可以把已有的 LangGraph 节点封装成可视化模块真正实现“一次开发多端复用”。本文就以一个典型的文档问答智能体为例带你一步步将原本纯代码的 LangGraph 工作流重构为可在 LangFlow 中运行的可视化流程。过程中会涉及组件封装、数据传递、对象序列化等关键工程技巧全是真实项目中踩过坑后总结出的最佳实践。我们的目标流程并不复杂但却涵盖了 RAG检索增强生成的核心环节加载本地 PDF 文档切分为文本块chunk生成嵌入并存入向量数据库FAISS接收用户提问执行检索 LLM 回答显示结果并维护会话历史原生 LangGraph 实现如下from typing import TypedDict, Annotated from langgraph.graph import StateGraph from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import FAISS from langchain_core.prompts import PromptTemplate from operator import add import os os.environ[OPENAI_API_KEY] sk-... # 状态定义 class QAState(TypedDict): pdf_path: str raw_documents: list chunks: Annotated[list, add] vectorstore: any question: str answer: str history: Annotated[list, add] # 节点函数 def load_document(state: QAState) - QAState: loader PyPDFLoader(state[pdf_path]) docs loader.load() return {raw_documents: docs} def split_text(state: QAState) - QAState: splitter RecursiveCharacterTextSplitter(chunk_size1000, chunk_overlap100) chunks splitter.split_documents(state[raw_documents]) return {chunks: chunks} def create_vectorstore(state: QAState) - QAState: embeddings OpenAIEmbeddings() vs FAISS.from_documents(state[chunks], embeddings) return {vectorstore: vs} def generate_answer(state: QAState) - QAState: retriever state[vectorstore].as_retriever() relevant_docs retriever.invoke(state[question]) prompt PromptTemplate.from_template( Use the following context to answer the question.\n\n Context: {context}\n\nQuestion: {question} ) llm ChatOpenAI(modelgpt-3.5-turbo) response llm.invoke( prompt.format(context\n.join([d.page_content for d in relevant_docs]), questionstate[question]) ) return {answer: response.content, history: [fQ: {state[question]}, fA: {response.content}]} # 构建图 workflow StateGraph(QAState) workflow.add_node(load_document, load_document) workflow.add_node(split_text, split_text) workflow.add_node(create_vectorstore, create_vectorstore) workflow.add_node(generate_answer, generate_answer) workflow.set_entry_point(load_document) workflow.add_edge(load_document, split_text) workflow.add_edge(split_text, create_vectorstore) workflow.add_edge(create_vectorstore, generate_answer) app workflow.compile() # 执行 result app.invoke({ pdf_path: ./sample.pdf, question: What is the main idea of this document?, chunks: [], history: [] }) print(Answer:, result[answer])这段代码逻辑清晰但在团队协作中很快就会遇到瓶颈产品经理看不懂节点连接关系测试人员没法自己改参数验证效果甚至连开发者自己想调整 chunk 大小都得重新启动服务。这时候LangFlow 的价值就体现出来了——我们需要做的是把每个函数节点变成可拖拽的 UI 组件。自定义组件开发从函数到可视化模块LangFlow 支持通过继承Component类来自定义节点。推荐按照功能拆分目录结构langflow/components/docqa/ ├── __init__.py ├── document_loader_component.py ├── text_splitter_component.py ├── vectorstore_creator_component.py ├── rag_generator_component.py └── history_display_component.py文档加载组件这个组件对应原来的load_document函数负责读取 PDF 文件路径并输出原始文档列表。# langflow/components/docqa/document_loader_component.py from langflow.custom import Component from langflow.io import Output, StrInput from langflow.schema import Data from langchain_community.document_loaders import PyPDFLoader class DocumentLoaderComponent(Component): display_name Document Loader description Load PDF document from file path icon file-text inputs [ StrInput( namepdf_path, display_namePDF File Path, value./sample.pdf, infoPath to the PDF file to load ), ] outputs [ Output(display_nameLoaded Documents, nameoutput, methodload_pdf), ] def load_pdf(self) - Data: try: loader PyPDFLoader(self.pdf_path) documents loader.load() data_dict { documents: [doc.dict() for doc in documents], count: len(documents), source: self.pdf_path } self.status fLoaded {len(documents)} pages from {self.pdf_path} return Data(datadata_dict) except Exception as e: self.status fError loading PDF: {str(e)} return Data(data{error: str(e)})这里的关键在于输出格式统一为Data(datadict)这样下游组件可以通过.data[xxx]安全访问字段。同时加入错误处理确保流程不会因单个节点失败而崩溃。文本分割组件接下来是切分文本的节点接收上一步的文档对象并按配置大小进行拆分。# langflow/components/docqa/text_splitter_component.py from langflow.custom import Component from langflow.io import Output, DataInput, IntInput from langflow.schema import Data from langchain_text_splitters import RecursiveCharacterTextSplitter import json class TextSplitterComponent(Component): display_name Text Splitter description Split documents into smaller chunks icon scissors inputs [ DataInput( namedocument_input, display_nameDocuments, infoOutput from Document Loader ), IntInput( namechunk_size, display_nameChunk Size, value1000, infoMaximum size of each chunk ), IntInput( namechunk_overlap, display_nameOverlap Size, value100, infoNumber of overlapping characters between chunks ), ] outputs [ Output(display_nameText Chunks, nameoutput, methodsplit_text), ] def split_text(self) - Data: if hasattr(self.document_input, data): doc_data self.document_input.data else: doc_data self.document_input docs doc_data.get(documents, []) from langchain_core.documents import Document parsed_docs [Document(**d) for d in docs] splitter RecursiveCharacterTextSplitter( chunk_sizeself.chunk_size, chunk_overlapself.chunk_overlap ) chunks splitter.split_documents(parsed_docs) output_data { chunks: [c.dict() for c in chunks], chunk_count: len(chunks), config: { chunk_size: self.chunk_size, overlap: self.chunk_overlap } } self.status fSplit into {len(chunks)} chunks return Data(dataoutput_data)注意这里的类型还原操作从字典重建Document对象。这是因为在 LangFlow 中传输的对象会被自动序列化/反序列化所以我们不能假设输入一定是原始类型必须做好兼容处理。向量库创建组件这一步最棘手如何把一个 FAISS 实例传给下一个节点毕竟 FAISS 内部包含 NumPy 数组和模型引用标准 JSON 是无法序列化的。解决方案就是pickle base64双重编码# langflow/components/docqa/vectorstore_creator_component.py from langflow.custom import Component from langflow.io import Output, DataInput, BoolInput from langflow.schema import Data from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS import pickle import base64 class VectorStoreCreatorComponent(Component): display_name Vector Store Creator description Create FAISS vector store from text chunks icon database inputs [ DataInput( namechunks_input, display_nameText Chunks, infoChunks from Text Splitter ), BoolInput( namepersist_store, display_namePersist to Disk, valueFalse, infoSave vector store to disk ), ] outputs [ Output(display_nameVector Store, nameoutput, methodcreate_vectorstore), ] def create_vectorstore(self) - Data: if hasattr(self.chunks_input, data): chunk_data self.chunks_input.data else: chunk_data self.chunks_input from langchain_core.documents import Document chunks [Document(**c) for c in chunk_data[chunks]] embeddings OpenAIEmbeddings() vectorstore FAISS.from_documents(chunks, embeddings) if self.persist_store: vectorstore.save_local(./faiss_index) serialized pickle.dumps(vectorstore) encoded base64.b64encode(serialized).decode(utf-8) output_data { vectorstore_b64: encoded, chunk_count: len(chunks), embedding_model: text-embedding-ada-002 } self.status fFAISS index created with {len(chunks)} chunks return Data(dataoutput_data)这一招在实际项目中非常实用。只要保证运行环境一致Python 版本、依赖版本就可以安全地跨组件传递复杂对象。当然不建议长期存储 pickle 数据仅用于流程内部临时传递。RAG 回答生成组件最后是核心的问答节点它需要反序列化 FAISS 实例执行检索并调用 LLM 输出答案。# langflow/components/docqa/rag_generator_component.py from langflow.custom import Component from langflow.io import Output, DataInput, MessageTextInput from langflow.schema import Message from langchain_openai import ChatOpenAI from langchain_core.prompts import PromptTemplate import pickle import base64 class RAGGeneratorComponent(Component): display_name RAG Generator description Generate answer using retrieval-augmented generation icon message-square inputs [ DataInput( namevectorstore_input, display_nameVector Store, infoFAISS vector store with embeddings ), MessageTextInput( namequestion, display_nameUser Question, infoThe question to ask about the document ), ] outputs [ Output(display_nameAnswer, nameoutput, methodgenerate_response), ] def generate_response(self) - Message: vs_data self.vectorstore_input.data if hasattr(self.vectorstore_input, data) else self.vectorstore_input encoded_vs vs_data[vectorstore_b64] decoded base64.b64decode(encoded_vs.encode(utf-8)) vectorstore pickle.loads(decoded) retriever vectorstore.as_retriever(k4) docs retriever.invoke(self.question) prompt PromptTemplate.from_template( Answer the question based on the context below.\n\n Context:\n{context}\n\nQuestion: {question}\n\nAnswer: ) llm ChatOpenAI(modelgpt-3.5-turbo) full_prompt prompt.format( context\n.join([d.page_content for d in docs]), questionself.question ) response llm.invoke(full_prompt) answer_text response.content self.status Answer generated return Message(textanswer_text)这里返回的是Message类型可以直接在 LangFlow 的聊天预览窗口中显示内容体验更接近真实应用。结果展示组件为了完整闭环再加一个简单的结果显示组件# langflow/components/docqa/history_display_component.py from langflow.custom import Component from langflow.io import Output, MessageInput from langflow.schema import Message class HistoryDisplayComponent(Component): display_name History Display description Display conversation history icon list inputs [ MessageInput( namemessage, display_nameMessage, infoMessage to display in history ), ] outputs [ Output(display_nameFormatted History, nameoutput, methodshow_history), ] def show_history(self) - Message: text f Final Answer:\n\n{self.message.text} self.status History updated return Message(texttext)别忘了在__init__.py中注册所有组件否则 LangFlow 不会识别它们# langflow/components/docqa/__init__.py from .document_loader_component import DocumentLoaderComponent from .text_splitter_component import TextSplitterComponent from .vectorstore_creator_component import VectorStoreCreatorComponent from .rag_generator_component import RAGGeneratorComponent from .history_display_component import HistoryDisplayComponent __all__ [ DocumentLoaderComponent, TextSplitterComponent, VectorStoreCreatorComponent, RAGGeneratorComponent, HistoryDisplayComponent ]完成之后启动 LangFlowpython -m langflow run --host 0.0.0.0 --port 7861 --env-file .env进入 UI 后你会在左侧看到docqa分组里面包含了刚才定义的所有组件。现在可以像拼图一样把它们连起来[Document Loader] ↓ [Text Splitter] ↓ [Vector Store Creator] ↓ [ RAG Generator ] ← [User Input: Message Text] ↓ [History Display]配置好 PDF 路径和问题文本点击运行整个流程就会自动执行。更重要的是你可以随时修改 chunk_size 或更换模型无需动一行代码。这种转换方式带来了几个显著优势开发效率提升原型阶段不再需要反复写print()调试中间结果协作门槛降低产品、运营也能参与流程设计迭代速度加快参数调整即时生效支持 A/B 测试知识沉淀更好组件可复用形成团队内部的“AI 积木库”。当然也要清醒认识到局限性LangFlow 目前对复杂控制流如循环、条件分支的支持还不够完善生产级部署仍需回归代码模式。理想的工作流应该是——先用 LangFlow 快速验证想法确认可行后再用 LangGraph 实现高可靠性服务。随着 LangFlow 社区快速发展未来很可能支持直接导入.json流程文件生成 LangGraph 代码甚至具备版本管理和组件市场功能。掌握这套技能不仅是提高个人效率的捷径更是迈向 AI 应用低代码化、民主化的重要一步。不妨现在就动手试试把你第一个 LangGraph 工作流转成可视化流程让 AI 开发真的变得像搭积木一样简单。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考