1. LangGraph 与 Open Notebook 的完美结合
在当今的文档处理领域,Open Notebook 通过巧妙运用 LangGraph 框架,实现了复杂工作流的高效管理。LangGraph 作为 LangChain 生态系统中的状态图框架,专门用于构建复杂的、有状态的 AI 应用程序。这种架构选择让 Open Notebook 在处理文档时展现出显著优势。
1.1 为什么选择 LangGraph?
传统文档处理方式与 LangGraph 方式的对比:
| 对比维度 | 传统方式 | LangGraph 方式 | 优势体现 |
|---|---|---|---|
| 执行流程 | 线性函数调用 | 状态图工作流 | 可视化、可调试 |
| 逻辑组织 | 硬编码逻辑 | 声明式配置 | 灵活、可扩展 |
| 执行效率 | 串行执行 | 并行+条件执行 | 高性能 |
| 测试维护 | 难以测试 | 节点独立测试 | 可维护性高 |
| 错误处理 | 错误传播 | 节点级错误处理 | 容错性强 |
在实际应用中,这种架构差异带来的性能提升非常明显。以一个典型的文档处理流程为例,传统串行方式可能需要顺序执行内容提取、保存、转换等多个步骤,而 LangGraph 可以将这些步骤并行化,显著缩短整体处理时间。
1.2 Open Notebook 的 LangGraph 架构全景
Open Notebook 的 LangGraph 架构包含多个精心设计的工作流:
code复制source_graph ← 文档处理主工作流
├── content_process ← 内容提取节点
├── save_source ← 数据保存节点
└── transform_content ← 内容转换节点
transformation_graph ← 内容转换子工作流
└── run_transformation ← AI 转换执行
ask_graph ← 智能问答工作流
├── agent ← 问题分析和策略制定
├── provide_answer ← 并行向量搜索
└── write_final_answer ← 答案综合
chat_graph ← 对话工作流
└── call_model ← 模型调用
source_chat_graph ← 基于源文档的对话
└── source_chat_agent ← 上下文感知对话
这种模块化设计使得每个功能组件都能独立开发和测试,同时又可以通过工作流灵活组合,满足各种复杂的文档处理需求。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心工作流深度解析
2.1 文档处理主工作流 (source_graph)
作为 Open Notebook 最核心的工作流,source_graph 负责处理用户上传的各种文档。其完整实现如下:
python复制# open_notebook/graphs/source.py
import operator
from typing import Any, Dict, List, Optional
from content_core import extract_content
from langgraph.graph import END, START, StateGraph
from langgraph.types import Send
class SourceState(TypedDict):
content_state: ProcessSourceState
apply_transformations: List[Transformation]
source_id: str
notebook_ids: List[str]
source: Source
transformation: Annotated[list, operator.add]
embed: bool
async def content_process(state: SourceState) -> dict:
"""提取和处理文档内容"""
content_settings = ContentSettings(
default_content_processing_engine_doc="auto",
default_content_processing_engine_url="auto",
default_embedding_option="ask",
auto_delete_files="yes",
youtube_preferred_languages=["en", "pt", "es", "de", "nl", "en-GB", "fr", "hi", "ja"]
)
content_state: Dict[str, Any] = state["content_state"]
content_state["url_engine"] = content_settings.default_content_processing_engine_url or "auto"
content_state["document_engine"] = content_settings.default_content_processing_engine_doc or "auto"
content_state["output_format"] = "markdown"
# 配置语音转文字模型
try:
model_manager = ModelManager()
defaults = await model_manager.get_defaults()
if defaults.default_speech_to_text_model:
stt_model = await Model.get(defaults.default_speech_to_text_model)
if stt_model:
content_state["audio_provider"] = stt_model.provider
content_state["audio_model"] = stt_model.name
except Exception as e:
logger.warning(f"Failed to retrieve speech-to-text model configuration: {e}")
# 调用 content-core 库进行实际内容提取
processed_state = await extract_content(content_state)
return {"content_state": processed_state}
async def save_source(state: SourceState) -> dict:
"""保存处理后的源文档到数据库"""
content_state = state["content_state"]
# 获取现有源文档
source = await Source.get(state["source_id"])
if not source:
raise ValueError(f"Source with ID {state['source_id']} not found")
# 更新源文档内容
source.asset = Asset(url=content_state.url, file_path=
