日习一技 - NotebookLM
type: channel
link: https://i-study-everyday.online/category/notebooklm
description: NotebookLM 学习笔记
items: 学习 SurfSense 的播客生成
items: 再学 SurfSense 的文档问答流程
items: 学习 SurfSense 的文档问答流程
items: 学习 SurfSense 的数据入库流程
items: 学习 SurfSense 的浏览器扩展
items: 学习 SurfSense 的文档管理
items: SurfSense 介绍：NotebookLM 的开源平替
items: NotebookLM 介绍：Google 打造的 AI 笔记助手

学习 SurfSense 的播客生成
type: item
date: 2025-05-25T05:02:00+00:00
description: 经过几天的学习，我们已经基本掌握了 SurfSense 的所有功能，包括添加文档的四种方式，连接器的管理，以及问答流程。并且针对每一个功能，通过剖析源码学习各自的实现原理，比如如何实现文档的解析，如何对文档进行检索，如何实现整个问答流程，等等。不过，还有一个最重要的功能没有讲到。之前有提到，SurfSense 号称是 NotebookLM 的开源平替，而 NotebookLM 的杀手锏便是它的 音频概览（Audio Overview） 功能，用户只需一键点击，就可以将文本内容转换为类似播客的音频讨论，这一功能特别适合那些喜欢听觉学习或需要在通勤、运动等场景下继续学习的用户。SurfSense 同样也实现了类似的功能，可以针对对话内容生成播客，我们今天就来看下这个功能。生成播客在 SurfSense 上产生会话后，就可以点击左侧菜单上的 “View All Chats” 进入会话列表页面，针对每一通会话，我们可以点击 “Generate Podcast” 生成播客：弹出对话框，输入播客名称，然后确认即可：稍等片刻，等后端生成结束，进入 “Podcasts” 页面，就可以看到生成的播客了：下面是我生成的播客，你可以听听效果，是不是有点像那么回事：点击收听生成播客的实现生成播客的代码逻辑位于 podcasts_routes.py 文件中，接口名为 POST /podcasts/generate/：同样地，通过 FastAPI 的 BackgroundTasks 创建一个后台任务来生成播客。任务的实现位于 podcast_tasks.py 文件中 generate_chat_podcast() 函数：async def generate_chat_podcast( session: AsyncSession, chat_id: int, search_space_id: int, podcast_title: str ): initial_state = State( source_content=chat_history_str, ) result = await podcaster_graph.ainvoke(initial_state, config=config)可以看到，又是一个 LangGraph 构建的流程图！它的定义如下：workflow = StateGraph(State, config_schema=Configuration) workflow.add_node("create_podcast_transcript", create_podcast_transcript) workflow.add_node("create_merged_podcast_audio", create_merged_podcast_audio) workflow.add_edge("__start__", "create_podcast_transcript") workflow.add_edge("create_podcast_transcript", "create_merged_podcast_audio") workflow.add_edge("create_merged_podcast_audio", "__end__") graph = workflow.compile() graph.name = "Surfsense Podcaster"这个生成播客的工作流包含两个工作节点：create_podcast_transcript - 生成播客的文本稿create_merged_podcast_audio - 创建合并后的播客音频生成文本稿第一步根据对话内容生成播客的文本稿，其核心仍然是一段系统 Prompt：当前日期: {datetime.datetime.now().strftime("%Y-%m-%d")} <podcast_generation_system> 您是一位专业的播客脚本撰写大师，擅长将各类输入内容转化为两位主持人之间生动、引人入胜且自然流畅的对话。您的核心目标是创作出真实自然的对话内容，完全避免机器人式生硬脚本或刻板的正式感。要实现充满活力的互动效果，而非单纯的信息传递。 - '<source_content>': 待讨论的原始文本内容块。可能是研究发现、文章摘要、详细大纲、与该主题相关的用户聊天记录，或其他相关信息。内容可能是非结构化的，但将作为播客对话的事实依据。 <output_format> 包含交替主持人对话的JSON格式播客文本： { "podcast_transcripts": [ { "speaker_id": 0, "dialog": "主持人0的对话内容" }, { "speak…

再学 SurfSense 的文档问答流程
type: item
date: 2025-05-24T05:00:00+00:00
description: 书接上回，昨天我们提到 SurfSense 的文档问答流程是通过 LangGraph 构建的一个线性工作流实现的，包含三个主要节点：reformulate_user_query：重新表述用户查询，也就是对用户的问题进行改写；write_answer_outline：根据不同的研究模式生成大纲，并为每个大纲生成多个搜索问题；process_sections：针对大纲中的每个章节，调用连接器去搜索，并生成对应章节的内容；如果开启了 LangSmith 可以在控制台页面看到整个图的执行流程：今天对流程中的这几个节点展开聊聊。问题改写问题改写的目的主要有两个：使模糊问题更明确：比如用户的问题是 听说最近武汉的樱花挺不错，打算下周二带家人去看看，不知道那天天气怎么样？，需要把问题改成更明确的 武汉下周二天气怎么样？多轮对话中的指代消解：比如用户先问 合肥明天的天气怎么样？，系统回答之后，用户接着又问 那后天呢？，这时要把问题改写成 合肥后天的天气怎么样？其核心就一段 Prompt：今日日期: {datetime.datetime.now().strftime("%Y-%m-%d")} 您是一位专精于查询优化的高技能AI助手，专为高级研究而设计。 您的主要目标是将用户的初始查询转化为高效的搜索查询。 这个重新表述的查询将用于从多种数据源中检索信息。 **聊天历史上下文:** {chat_history_str if chat_history_str else "没有可用的先前对话历史。"} 如果提供了聊天历史，请分析它以了解用户不断发展的信息需求和他们请求的更广泛背景。利用这种理解来完善当前查询，确保它建立在先前互动的基础上或对其进行澄清。 **查询重构指南:** 您重新表述的查询应该: 1. **增强特异性和细节:** 增加精确度以有效缩小搜索焦点，使查询不那么模糊且更有针对性。 2. **解决歧义:** 识别并澄清模糊的术语或短语。如果一个术语有多种含义，根据上下文将查询引导向最可能的含义。 3. **扩展关键概念:** 纳入核心概念的相关同义词、相关术语和替代表述。这有助于捕获更广泛的相关文档。 4. **分解复杂问题:** 如果原始查询是多方面的，将其分解为核心可搜索组件或重新表述以清晰地解决每个方面。最终输出必须仍然是一个连贯的查询字符串。 5. **优化全面性:** 确保查询的结构能够揭示原始请求的所有基本方面，旨在进行适合研究的彻底信息检索。 6. **保持用户意图:** 重新表述的查询必须忠于用户查询的原始意图。不要引入新主题或显著改变焦点。 **关键约束:** * **简洁和有效性:** 在追求全面性的同时，重新表述的查询必须尽可能简洁。消除所有不必要的冗长。专注于直接有助于有效检索的基本关键词、实体和概念。 * **单一、直接输出:** 仅返回重新表述的查询本身。不要包含任何解释、介绍性短语（例如，"重新表述的查询:"，"这是优化后的查询:"），或任何其他周围文本或markdown格式。 您的输出应该是一个单一的、优化的查询字符串，可以立即用于搜索系统。注意，源码里是英文的，这里我让 Cursor 给我翻译成了中文，主要是参考 Prompt 的思路。生成大纲接下来根据用户的问题生成答案大纲，根据不同的研究模式，生成不同的章节数量：General - 答案包括 1 个章节；Deep - 答案包括 3 个章节；Deeper - 答案包括 6 个章节；并为每个大纲生成多个研究问题。其核心是一段用户 Prompt：现在请为以下查询创建一个回答大纲： 用户查询: {reformulated_query} 章节数量: {num_sections} 请记住将您的回答格式化为完全匹配此结构的有效JSON： { "answer_outline": [ { "section_id": 0, "section_title": "章节标题", "questions": [ "此章节要研究的问题1", "此章节要研究的问题2" ] } ] } 您的输出必须是此格式的有效JSON。不要包含任何其他文本或解释。加上一段系统 Prompt（做了一些删减，去掉了输出格式和示例）：您是一位专门从事信息结构化的专家研究助手。您的任务是根据用户的查询创建一个详细且逻辑清晰的研究大纲。此大纲将作为生成全面研究报告的蓝图。 - user_query (字符串): 用户想要研究的主要问题或主题。这指导整个大纲创建过程。 - num_sections (整数): 最终研究报告应该包含的不同章节的目标数量。这有助于控制大纲的粒度和结构。 <instructions> 1. **分解`user_query`:** 识别用户请求中的关键概念、实体和核心信息。 2. **确…

学习 SurfSense 的文档问答流程
type: item
date: 2025-05-23T04:57:00+00:00
description: 昨天，我们学习了在 SurfSense 中添加文档之后的入库流程，包括总结、分块、向量化等，现在一切准备就绪，到了对文档进行问答的时候了。文档问答页面点击左侧菜单中的 "Researcher" 进入问答页面：输入框的最左边是选择数据源，在 SurfSense 中被称为 连接器（Connectors），可以是我们上传的文件、添加的网页、添加的 Youtube 视频、或者通过浏览器插件抓取的内容：SurfSense 还支持添加其他的连接器，比如搜索引擎 Tavily、聊天工具 Slack、项目管理工具 Linear、知识库 Notion 等等：关于其他的连接器我们后面再看，暂时先不管。输入框的中间用于选择 搜索模式（Search Mode），分 Full Document 和 Document Chunks 两种：Full Document - 根据用户问题检索文档表，找出最相关的文档，返回文档全文，这种模式适用于文档多且单个文档比较小的场景；Document Chunks - 根据用户问题检索分块表，找出最相关的分块，返回一个个的文档片段，这种模式适用于文档较大的场景；输入框的最右边是选择 研究模式（Research Mode），分 General、Deep 和 Deeper 三种，这三种模式会影响检索的数据量和答案的丰富度：General - 检索 10 条数据，答案包括 1 个章节；Deep - 检索 20 条数据，答案包括 3 个章节；Deeper - 检索 30 条数据，答案包括 6 个章节；体验文档问答我们以之前的 MRAG 文档作为示例，体验下 SurfSense 的问答功能。首先，连接器选 File，搜索模式选 Document Chunks，研究模式选 General，然后输入一个简单的问题 What is MRAG?：下面首先会出现一个终端，实时输出 SurfSense 的处理过程，从图中可以看出 SurfSense 接到用户查询后，开始生成答案大纲，生成了 1 个章节和 5 个研究问题，然后针对这些问题分别进行搜索，搜索的结果紧接着终端显示在下面：最后根据搜索结果回答用户：从结果来看，回答的结构比较清晰，符合预期，且带有引用来源，看上去也很靠谱。不过仔细看还是能发现不少问题，比如：引用的文档片段看不到完整内容，也无法跳转到原文对应的位置，导致溯源功能形同虚设；另外答案也不全面，比如第二节只提到了 MRAG 1.0 和 MRAG 2.0 却丢掉了 MRAG 3.0 的内容；第三节甚至出现了 Mixed Language Models (MLLMs) 这样的幻觉错误，连 MLLM 的全称都搞错了。文档问答的实现原理尽管存在很多问题，但并不妨碍我们去看它的实现原理。考虑到这是一个很新的项目，在社区也很活跃，还在不断地演进，相信后面会越来越完善。好了，我们继续看代码。问答接口为 POST /chat，位于 surfsense_backend/app/routes/chats_routes.py 文件中：核心逻辑是下面的 stream_connector_search_results() 函数，这是一个流式处理搜索结果的函数，用于将搜索结果和回答内容实时传输给客户端，点进去看它的实现：async for chunk in researcher_graph.astream( initial_state, config=config, stream_mode="custom", ): if isinstance(chunk, dict) and 'yeild_value' in chunk: yield chunk['yeild_value'] yield streaming_service.format_completion()很显然，这是一个 生成器函数，通过 researcher_graph.astream() 不断产生内容，并通过 yield 关键字返回，最终通过 FastAPI 的 StreamingResponse 实现流式输出。Python 中的生成器函数Python 中的 生成器函数（Generators） 是一种特殊的函数，它使用 yield 关键字来返回值，而不是普通的 return 返回。当函数中包含 yield 语句时，这个函数就变成了生成器函数，调用生成器函数不会立即执行函数体，而是返回一个生成器对象，可以通过 next() 或 for 循环从生成器函数取值。def simple_generator(): yield 1 yield 2 yield 3 for value in simple_generator(): print(value)生成器函数的核心特点是惰性求值和状态保持。当我们从生成器…

学习 SurfSense 的数据入库流程
type: item
date: 2025-05-22T04:55:00+00:00
description: 花了两天时间，我们学习了 SurfSense 文档管理功能，学习了上传文件、添加网页、添加 Youtube 视频、浏览器插件四种添加文档的方式，以及每种添加方式的实现原理。不过目前还只是数据摄入部分，我们今天继续研究下后面的流程，看看这些文档是如何入库的。数据入库流程我们总结下几种添加文档的方式，可以看出，最终的结果都是转为纯文本或 Markdown 格式：拿到文本之后的入库流程几乎是一样的，如下：主要分三个步骤：对整个文档进行总结和向量化；将文档分块，对每个分块进行向量化；将文档和分块数据入库；文档总结SurfSense 使用长文本大模型对完整文档做总结，从配置文件可以知道，默认的模型是 Google 的 Gemini 2.0 Flash，上下文窗口达到 100 万 token，可以塞进去一部《红楼梦》：LONG_CONTEXT_LLM="gemini/gemini-2.0-flash"文档总结的代码如下：summary_chain = SUMMARY_PROMPT_TEMPLATE | config.long_context_llm_instance summary_result = await summary_chain.ainvoke({"document": file_in_markdown}) summary_content = summary_result.content这里通过 LangChain 的 LCEL（LangChain Expression Language）语法 创建一个 摘要生成链（summary chain），将提示词模版和大模型调用链接在一起，其中 SUMMARY_PROMPT_TEMPLATE 的定义如下：SUMMARY_PROMPT_TEMPLATE = PromptTemplate( input_variables=["document"], template=SUMMARY_PROMPT )后面的 long_context_llm_instance 定义如下：LONG_CONTEXT_LLM = os.getenv("LONG_CONTEXT_LLM") long_context_llm_instance = ChatLiteLLM(model=LONG_CONTEXT_LLM)这里有一个挺长的文档总结 Prompt 可以参考，见 surfsense_backend/app/prompts/__init__.py 文件。LCEL 语法上面的 | 连接符有点类似 Linux 命令行中的管道，是 LangChain 的一种特色写法，表示数据会从左侧的提示模板流向右侧的大模型调用。要注意的是，这并不是 Python 的原生特性，而是 LangChain 的语法糖。在 Python 中 | 运算符通常用于：位运算（bitwise OR）集合的并集操作类型注解中的联合类型（Union types）而 LangChain 是通过 重载（overload） 这个运算符，使其具有了新的含义，这种设计灵感来自于 Linux 中的管道操作符，通过这种管道语法使得代码更加简洁和可读，让数据处理的流程更加清晰。在 Python 中重载运算符是通过实现特殊方法（也称为魔术方法或双下划线方法）来实现的，比如下面这些：class MyClass: def __add__(self, other): # + pass def __sub__(self, other): # - pass def __mul__(self, other): # * pass def __truediv__(self, other): # / pass def __eq__(self, other): # == pass def __lt__(self, other): # < pass def __gt__(self, other): # > pass def __or__(self, other): # | pass def __and__(self, other): # & pass可以看到，要重载 | 运算符，需要实现 __or__ 方法。对于 LangChain 来说，它的几个核心类，比如 PromptTemplate 或 BaseChatModel，都统一继承自 Runnable 接口，我们打开 Runnable 源码，就可以发现 LangChain 中 | 连接符的奥秘所在：向量化接着我们计算整个文档的向量：summary_embedding = config.embedding_model_instance.embed(summary_content)其中 embedding_model_instance 定义如下：EM…

学习 SurfSense 的浏览器扩展
type: item
date: 2025-05-21T04:50:00+00:00
description: 昨天体验了 SurfSense 的文档管理功能，包括上传文件、添加网页、添加 Youtube 视频这三种添加文档的方式。其中，添加网页是通过 Firecrawl 或 Playwright 实现的，这两种方式其实都有弊端，比如不能访问内部网页，不能绕过身份认证，等。于是，SurfSense 还提供了另一种添加文档的方式 —— 浏览器扩展，通过浏览器扩展，我们可以添加任何网页，包括那些受到身份验证保护的网页。构建 SurfSense 浏览器扩展首先进入扩展目录，复制 .env.example 文件配置环境变量：$ cd surfsense_browser_extension $ cp .env.example .env环境变量比较简单，保持默认值即可：PLASMO_PUBLIC_API_SECRET_KEY = "surfsense" PLASMO_PUBLIC_BACKEND_URL = "http://127.0.0.1:8000"然后通过 pnpm install 安装依赖：$ pnpm install注意安装后可能会有下面这样的警告信息：╭ Warning ───────────────────────────────────────────────────────────────────────────────────╮ │ │ │ Ignored build scripts: @swc/core, esbuild, lmdb, msgpackr-extract, sharp. │ │ Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts. │ │ │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯它的意思是，在安装期间有一些依赖项需要运行脚本，这个默认是不允许的，我们需要手动执行 pnpm approve-builds 命令来允许。$ pnpm approve-builds命令会弹出提示，选择要运行哪些脚本，按 全选即可：最后构建浏览器扩展：$ pnpm build这个命令默认构建的是 Chrome 扩展，也支持构建其他浏览器的扩展：$ pnpm build --target=firefox $ pnpm build --target=edge构建好的 Chrome 扩展位于 ./build/chrome-mv3-prod 目录。Plasmo 框架SurfSense 的浏览器扩展是使用 Plasmo 框架开发的：这是一款功能强大的浏览器扩展 SDK，功能特性如下：Plasmo 将各大浏览器扩展抽象成统一标准，使用 Plasmo 构建浏览器扩展，可以不用操心扩展的配置文件，以及不同浏览器构建时的一些奇怪特性等。安装 SurfSense 浏览器扩展接下来，打开 Chrome 浏览器的扩展程序页面 —— chrome://extensions/：开启 “开发者模式”，点击 “加载已解压的扩展程序”，然后选择构建好的 Chrome 扩展目录（./build/chrome-mv3-prod），扩展即安装完成。在浏览器的扩展栏可以看到 SurfSense 的小图标，点击小图标弹出扩展的配置页面：在这里需要填写 SurfSense 的 API KEY，这个可以在平台左下角点击账号进行查看：填上 API KEY 之后进入扩展的使用页面：使用 SurfSense 浏览器扩展进入 SurfSense 扩展的使用页面后，首先需要选择将网页推送到哪个搜索空间，下面可以看到三个按钮：Clear Inactive History - 清空不活跃的网页缓存，指的是那些已经关闭的网页标签Save Current Page - 将当前网页内容保存到缓存中Save to SurfSense - 将缓存的网页内容推送到 SurfSense 的搜索空间中我们拿 SurfSense 的 Github 页面做个示例：https://github.com/MODSetter/SurfSense首先点击 Save Current Page 保存网页到缓存，然后点击 Save to SurfSense 将网页推送到 SurfSense 的搜索空间中，刷新文档列表页面如下：代码实现SurfSense 扩展的主要逻辑位于 routes/pages/HomePage.tsx 文件：三个按钮对应的函数分别是：clearMemsaveCurrSnapShotsaveDatamessage其中 saveCurrSnapSho…

学习 SurfSense 的文档管理
type: item
date: 2025-05-20T04:43:00+00:00
description: 昨天给大家介绍了一款号称是 NotebookLM 的开源平替的项目 —— SurfSense，经过一些前期准备工作，我们在本地已经成功部署了它，并通过 Google OAuth 成功登录：接下来，我们再来看看它的各项功能特性，顺便研究下每个功能是如何实现的。首先当然是它的文档管理功能。一个小插曲昨天注册 Unstructured.io 之后，会进入一个 Contact Sales 页面，无法进入 Platform 页面，也拿不到 API KEY，后来就放弃了。今天抱着试一试的态度，通过 F12 审查这个页面的源码，看看能不能绕过这个限制，突然发现这个页面只是一层全屏弹框：直接在 "Elements" 里删除这个弹框的 HTML 就能看到控制台页面了：进而也拿到了 API KEY，新用户有 14 天的免费试用期。文档管理接着我们继续看 SurfSense 的文档管理功能。首次进入 SurfSense 的控制台页面时，需要手动创建一个 搜索空间（Search Space）：点击创建好的空间，进入文档管理页面：在左侧菜单中可以看到，我们有三种方式来添加文档：上传文件、添加网页、添加 Youtube 视频。上传文件SurfSense 支持数十种不同的文件格式，我们还是拿《A Survey of Multimodal Retrieval-Augmented Generation》这篇 MRAG 的论文 PDF 作为示例：点击上传后，要稍等一会，在后端可以看到不断地在刷日志，处理结束后刷新文档列表，如下（可以看到 SurfSense 为文档生成了一份摘要）：上传文件的实现上传文件的代码逻辑位于 documents_routes.py 文件中，接口名为 POST /documents/fileupload：通过 FastAPI 的 BackgroundTasks 创建一个后台任务对文件进行处理。根据不同的文件后缀分别处理：.md, .markdown对于 Markdown 格式的文本，直接读取文件内容。.mp3, .mp4, .mpeg, .mpga, .m4a, .wav, .webm对于音频文件，通过 LiteLLM 的 atranscription() 方法，调用 STT（Speech-to-Text，语音转文本）服务，在 .env 文件中可以看到默认的 STT 是 OpenAI 的 whisper-1 服务。STT_SERVICE="openai/whisper-1"其他类型对于其他类型的文件，统一使用 LangChain 的 UnstructuredLoader 进行处理，实际上就是调 Unstructured.io 的接口：loader = UnstructuredLoader( file_path, mode="elements", post_processors=[], languages=["eng"], include_orig_elements=False, include_metadata=False, strategy="auto", ) docs = await loader.aload()LangChain 支持大量的文档处理器，被称为 Document loaders，如果 Unstructured.io 的接口不可用，也可以考虑换成其他的：https://python.langchain.com/docs/integrations/document_loaders/添加网页SurfSense 支持添加网页文档，输入 URL 自动抓取网页的内容：比如这里我对 Google 的 A2A 比较感兴趣，就将 A2A 的这篇博客丢进去：https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/同样的，等后台处理一会，刷新文档列表，如下：添加网页的实现添加网页的代码逻辑同样位于 documents_routes.py 文件中，接口名为 POST /documents/：通过 FastAPI 的 BackgroundTasks 创建一个后台任务对网页进行处理：if config.FIRECRAWL_API_KEY: crawl_loader = FireCrawlLoader( url=url, api_key=config.FIRECRAWL_API_KEY, mode="scrape", params={ "formats": ["markdown"], "excludeTags": ["a"], }, ) else: crawl_loader = AsyncChromiumLoader(urls=[url], headl…

SurfSense 介绍：NotebookLM 的开源平替
type: item
date: 2025-05-19T04:38:00+00:00
description: 昨天我给大家介绍了一款 AI 笔记助手 —— NotebookLM，使用它可以方便地对我们的资料进行处理，支持智能问答和引用，一键生成学习指南、脑图、FAQ 等内容格式，以及将文本转换成播客，极大提升了学习的灵活性和便捷性。今天再给大家介绍一个 Github 上最近比较热门的开源项目 —— SurfSense，它号称是 NotebookLM 的开源平替：SurfSense 不仅实现了 NotebookLM 的各项功能，而且高度可定制，同时也更加的开放。比如它支持数十种不同类型的文件格式；支持连接各种外部数据源 Tavily、LinkUp、Slack、Linear、Notion、YouTube、GitHub 等；支持对接 150+ 不同的大模型，6000+ 不同的嵌入模型以及几乎所有主流的重排序模型。下面我就带大家一起，在本地把 SurfSense 跑起来看看。安装前准备根据官方的 Prerequisites 文档，在安装之前，我们需要做一些准备工作：安装 PGVectorSurfSense 平台使用 PostgreSQL 数据库，同时需要安装 pgvector 扩展支持向量类型，用于文档的向量检索，可以直接通过 Docker 安装：$ docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg17注意必须设置一个初始密码，否则启动报错。开启 Google People APISurfSense 默认使用 Google OAuth 认证来实现登录，需要开通 Google People API 服务。首先进入 Google API Library搜索 People API：点击进去后，点击 "Enable" 开启 Google People API 服务：然后进入 "Credentials" 页面，创建一个凭据（注意凭据类型选 "OAuth client ID"）：在填写表单时，客户端名称随便填，主要是下面两个参数要填对：Authorised JavaScript originshttp://localhost:8000Authorised redirect URIshttp://localhost:8000/auth/google/callback如下图所示：创建成功后，得到 Client ID 和 Client secret 两个配置：获取 Unstructured.io API KEYSurfSense 使用 Unstructured.io 的 API 进行文档解析，不过当前 Unstructured.io 注册后会进入一个 Contact Sales 页面，无法进入 Platform 页面，也拿不到 API KEY，这一步暂时跳过。获取 LangSmith API KEY（可选）这一步是可选的，但是监控 LLM 调用是推荐做法。LangSmith 是 LangChain 开发的免费开源的 LLM 可观测平台，我们可以自己独立部署，也可以直接免费使用在线服务：获取 Firecrawl API KEY（可选）SurfSense 通过 Firecrawl 来爬取 URL 内容，我们可以免费注册，注册后有免费的额度：配置大模型最后，SurfSense 通过 LiteLLM 调用大模型，所以我们必须有可调用的大模型服务，按 LiteLLM 的规范配置好对应的环境变量，比如 OPENAI_API_KEY 或 GEMINI_API_KEY 等，参考 LiteLLM 的文档：https://docs.litellm.ai/docs/providersSurfSense 安装一切准备就绪，接下来就可以安装 SurfSense 了。尽管官方提供了两种安装方式：Docker 安装 和 手工安装，但是由于项目比较新，变动比较频繁，推荐使用手工安装，过程更可控。首先，克隆代码：$ git clone https://github.com/MODSetter/SurfSense.gitSurfSense 包含前后端两个部分，需要分别安装。安装后端进入后端目录，复制 .env.example 文件配置环境变量，基本上就是上一节我们得到的 API KEY 等信息：$ cd surfsense_backend $ cp .env.example .env然后通过 uv sync 安装依赖：$ uv syncuv 是一个极速的 Python 包和项目管理工具，uv sync 是它的一个命令，用于更新项目的环境，确保项目环境中的依赖与 uv.lock 文件一致。另外，它会检查虚拟环境（.venv）是否存在，如果不存在，将自动创建它。依赖安装完毕后，通过 uv run…

NotebookLM 介绍：Google 打造的 AI 笔记助手
type: item
date: 2025-05-18T04:33:00+00:00
description: 在当今信息爆炸的时代，我们每天都面临着海量的数据和知识。如何高效地处理、理解和应用这些信息，成为了现代学习者、研究者和专业人士的重要挑战。为了解决这一问题，Google 推出了一款革命性的 AI 工具 —— NotebookLM，这是一款基于最新的 Gemini 大模型构建的研究与笔记助手，旨在为用户提供个性化的 AI 研究助手体验。NotebookLM 的核心理念是 Think Smarter, Not Harder（巧思胜苦干），与传统的笔记工具不同，NotebookLM 不仅能够存储信息，还能深入理解内容，建立知识连接，并基于用户提供的可信资料生成有价值的洞察。核心特性NotebookLM 具备如下几个核心特性：多样化资料上传和处理：基于 Gemini 2.0 的多模态理解能力，NotebookLM 能够处理各种类型的资料，包括 PDF 文件、网站、YouTube 视频、音频文件、Google 文档或 Google 幻灯片等；智能问答与深度洞察：基于上传的资料，NotebookLM 能够回答用户的各种问题，提供深度洞察。它的回答都基于用户提供的资料，并附带精确引用，显示信息来源，确保了回答的可靠性和可验证性；一键生成多种内容格式：NotebookLM 能够自动生成多种实用的内容格式，包括内容摘要、常见问题解答(FAQ)、时间线、简报文档和脑图等；随时随地边听边学：全新的 “音频概览” 功能可将文本内容转换为类似播客的音频讨论，便于移动学习，提升学习体验；下面带大家一起体验下 NotebookLM 的基本功能。添加数据源创建一个新笔记，第一步需要添加笔记的数据源：这里支持多种途径添加数据源：上传文件：支持上传 PDF、TXT、Markdown 以及音频等格式的文件；Google Drive：支持导入 Google Drive 中的文档和幻灯片；链接：支持输入网页或 Youtube 视频地址；粘贴文本：支持手工复制粘贴添加数据源内容；另外，右上角还有一个 "Discover sources" 的功能，可以输入你感兴趣的话题进行检索：如果你第一次使用这个工具，可能一时想不到什么话题，可以尝试点击下面的 "I'm feeling curious"，NotebookLM 会随机搜索一些话题供你参考。下面是我输入 "多模态检索增强生成" 后搜索的结果：可以看到搜索的质量还蛮高的，对于手头没有资料又想学习某个主题的用户非常友好。不过我这里暂时不用这些，而是使用最近在看的一篇关于多模态 RAG 的综述论文《A Survey of Multimodal Retrieval-Augmented Generation》：https://arxiv.org/abs/2504.08748将这篇论文的 PDF 上传后，NotebookLM 自动生成了笔记的标题和简要概述，如下：知识问答NotebookLM 的整个面板分成三个部分：SourcesChatStudio"Sources" 部分我们仍然可以继续添加新的数据源，"Chat" 部分可以对我们上传的数据进行提问：问答的结果支持点击数字角标进行溯源：内容生成NotebookLM 还支持根据你添加的数据源生成多种实用的内容格式，包括：脑图（Mind map）学习指南（Study guide）简报文档（Briefing doc）常见问题（FAQ）时间线（Timeline）音频概览最后是音频概览（Audio Overview）功能，它可以说是 NotebookLM 的杀手锏，也是它广受好评的主要原因。用户只需一键点击，就可以将文本内容转换为类似播客的音频讨论，这一功能特别适合那些喜欢听觉学习或需要在通勤、运动等场景下继续学习的用户。NotebookLM 默认生成英文播客，但就在最近，Google 宣布已支持中文等超过 50 种不同语言的音频生成，可以在用户配置中修改输出语言选项来生成中文音频。下面是我生成的关于 多模态检索增强生成综述 的播客，感兴趣的朋友可以试听下：英文版中文版可以听出来，英文的效果非常丝滑，中文的语速有点慢，但总体也是不错的，在通勤或运动时听听还是挺不错的。此外，对于英文音频，NotebookLM 还有一个额外的 交互模式（Interactive mode），用户可以在收听播客的时候随时打断，加入主持人的讨论，跟听电台一样，体验感爆棚：小结NotebookLM 是一款基于 Gemini 大模型的 AI 笔记助手，旨在帮助用户高效处理和理解信息。其核心理念是 巧思胜苦干，通过多样化的资料处理、多模态理解能力和深度洞察，为用户提供个性化的研究体验。关键特性包括支持多种数据源上传，智能问答功能提供可靠的回答和引用，一键生成学习指南、脑图、FAQ 等内容格式，以及将文本转换成播客的音频概览功…