惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
Latest news
Latest news
T
The Blog of Author Tim Ferriss
D
DataBreaches.Net
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Latest
Security Latest
宝玉的分享
宝玉的分享
S
Schneier on Security
Blog — PlanetScale
Blog — PlanetScale
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
MyScale Blog
MyScale Blog
B
Blog RSS Feed
N
Netflix TechBlog - Medium
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 热门话题
Apple Machine Learning Research
Apple Machine Learning Research
T
Tenable Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
Google DeepMind News
Google DeepMind News
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
博客园 - Franky
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
Secure Thoughts
T
Threat Research - Cisco Blogs
D
Docker
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 【当耐特】
PCI Perspectives
PCI Perspectives
GbyAI
GbyAI
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
V
Vulnerabilities – Threatpost
F
Fortinet All Blogs
罗磊的独立博客
Engineering at Meta
Engineering at Meta
Y
Y Combinator Blog
SecWiki News
SecWiki News
A
Arctic Wolf
小众软件
小众软件
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary

鸟窝

鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 鸟窝 LLM 究竟是如何工作的? Go 实验特性详解 amd64 微架构级别对 Go 程序性能提升多少? Loop Engineering 实践:一次批量实现 8 个 issue,完成夔牛工具的开发 Loop Engineering 实践:我把 RDMA 开发库移植到 Go 语言,花费 239 块钱 傻瓜式RDMA高性能网络开发:从零跑到 400 Gb每秒 百度物理网络监控工具开源第二弹:毫秒级监控工具 baize,让你的网络问题无处遁形 百度网络监控工具开源第三弹:lidar — 不只是 pingmesh 别只盯着gopacket了,看看这个强大的网络库 大厂的内部工具居然开源了! 一窥百度物理网络秒级监控定位的秘密 如何构建你自己的 Agent 运行时 寻找你代码中的臭味:一个让 AI 帮你嗅出架构腐化的开源 Skill 套壳不丢人!我用Go+AI搓了一个Agent统一编排框架,ClaudeCode-Codex-Pi全被我包了 告别死锁和陈旧语法、告别性能瓶颈:三个开源 Skill,新手Gopher 秒变 Go 语言大神 SPEC和PRD的区别 SPEC和方案设计有什么区别 从需求到上线,让 AI 管理你的整个研发流程! antigravity-cli 使用 CLIProxyAPI, 让最新的 Codex 能够支持国内的各大模型 Clawpatch + codex-review:AI 代码审查工具链的正确打开方式 拆解Manus:沙盒架构深度解析 40+ Claude Code Tips: From Basics to Advanced Ralph 实验:构建 SQLite UI 了解 Manus Sandbox - 您的云计算机 拆解Manus:真正有用的深度报告的生成 Claude Code 使用 我给每个模型服务商『捐』了10块钱,只为了... Go之禅 - 基于Rob Pike思想的Go语言哲学 Codex CLI vs Gemini CLI vs Claude Code:谁是最佳选择? goskills:Claude Skills 功能强大,为我所用 一行代码使用 Claude Skill 和 deepseek langchain + MCP:如虎添翼 Linux 中网络包的一生 godotenv 库介绍 Codex CLI vs Gemini CLI vs Claude Code: Which is the Best? 使用Linux 30年了,我都不知道 ping 8.8 还能这么用? 从 AI 哪里挣钱? Go sync 包近两年发展综述 deepseek-v3.2-exp的闪电索引器
Notex:一个开源 NotebookLM 替代方案的实现
by smallnest · 2026-01-01 · via 鸟窝

cover

起源

01-起源

Google 的 NotebookLM 上线后,不少人体验了它的文档问答和内容生成功能。它很好用,但有个问题:数据需要上传到 Google 的服务器。对于一些敏感的内部文档,这不太合适。

于是就想做一个开源版本:数据存在本地,想用什么模型就自己配置。一个元旦假期,基本功能就跑起来了。

项目叫 Notex,代码在 GitHub 上,今天聊聊它的实现。

本项目受open-notebook项目的启发。但是open-notebook缺少信息图、思维导图、幻灯片等高级功能。本项目弥补了这些功能
信息图采用最新的nano banana pro实现
幻灯片采用 宝玉的 《预订本年度最有价值提示词 —— 生成既有质感,又能随意修改文字的完美 PPT》 的方式,生成真正意义上的PPT

功能概览

02-功能概览

Notex 是一个隐私优先的开源知识管理工具,基于 RAG(检索增强生成)技术。它支持多种文档格式,通过 AI 帮助你理解、总结和可视化文档内容。

核心功能

功能分类 具体能力
文档管理 支持 PDF、DOCX、PPTX、Markdown、TXT、HTML 等多种格式的文档上传和解析
AI 对话 基于文档内容的智能问答,回答会标注来源引用,避免 AI 幻觉
内容转换 摘要、FAQ、学习指南、大纲、时间线、术语表、测验、播客脚本等 9 种预设类型
视觉生成 思维导图(Mermaid.js)、信息图(Gemini Nano Banana Pro)、幻灯片/PPT
模型支持 OpenAI(含兼容 API)、Ollama 本地模型、Google Gemini

特色功能详解

1. 幻灯片(PPT)生成

采用两阶段生成流程:

  • 阶段一:使用 Gemini Flash 生成 PPT 大纲,包含:

    • 全局风格指南(字体、色彩、设计美学)
    • 每页的叙事目标、关键内容、视觉元素、布局说明
  • 阶段二:使用 Gemini Pro Image 为每页幻灯片生成配图

最终输出图文并茂的完整 PPT,而非简单的文字大纲。

2. 信息图生成

通过 Prompt Engineering 将文本内容"翻译"为结构化的视觉描述,再调用 Gemini Nano Banana Pro 生成手绘风格的信息图。

适用于:数据可视化、流程说明、概念解释等场景。

3. 思维导图生成

自动将文档结构提炼为 Mermaid.js mindmap 格式,支持:

  • 中心主题、主要分支、细节节点的层级展示
  • 自动缩放和交互式导航
  • 导出为 SVG/PNG

4. 智能问答(RAG)

支持基于文档内容的自然语言问答:

  • 中文/英文自适应分词和检索
  • 回答会标注来源,可追溯原文
  • 支持多轮对话历史

整体架构

03-整体架构

技术栈选型很简单:

1

2

3

后端:Go 1.25 + Gin + SQLite

前端:原生 HTML/CSS/JavaScript

AI:LangChainGo + OpenAI/Ollama + Gemini

为什么不选 Python?因为 Go 编译完就是一个二进制文件,部署起来省事。而且 Go 的并发性能好,后续如果需要处理大量文档也不会成为瓶颈。

目录结构也很清晰:

1

2

3

4

5

6

7

8

9

notex/

├── main.go # 入口

├── backend/

│ ├── agent.go # AI 调用逻辑

│ ├── server.go # HTTP 服务

│ ├── vector.go # 文档索引

│ ├── store.go # 数据持久化

│ └── nanobanana.go # Gemini 图片生成

└── frontend/ # 单页应用(嵌入二进制)

前端通过 //go:embed 编译进二进制,一个文件就能跑起来。

核心模块

1. 文档索引(vector.go)

04-核心模块-向量

RAG 的第一步是把文档切成小块,建立索引。

这里有个问题:中文和英文的分词方式不一样。英文按空格分词就行,中文需要按字符切。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

cjkCount := 0

for _, r := range runes {

if r >= 0x4E00 && r <= 0x9FFF {

cjkCount++

}

}

cjkRatio := float64(cjkCount) / float64(len(runes))

if cjkRatio > 0.3 {

for i := 0; i < len(runes); i += (chunkSize - chunkOverlap) {

}

} else {

words := strings.Fields(text)

}

检索部分目前用的是简单的关键词匹配,没有用向量 Embedding。原因?对于中小规模的文档(几万字以内),关键词匹配够用了,而且快。如果后续文档量大,可以接入向量数据库。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

score := 0.0

if strings.Contains(content, queryLower) {

score += 10.0

}

matchCount := 0

for _, r := range queryRunes {

if strings.ContainsRune(content, r) {

matchCount++

}

}

if matchCount > 0 {

charMatchRatio := float64(matchCount) / float64(len(queryRunes))

score += charMatchRatio * 5.0

}

queryWords := strings.Fields(queryLower)

for _, word := range queryWords {

if len(word) > 2 && strings.Contains(content, word) {

score += 2.0

}

}

2. AI 调用(agent.go)

05-核心模块-AI

这块是核心,负责和 LLM 打交道。

使用 LangChainGo 作为抽象层,支持 OpenAI 和 Ollama:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

func createLLM(cfg Config) (llms.Model, error) {

if cfg.IsOllama() {

return ollamallm.New(

ollamallm.WithModel(cfg.OllamaModel),

ollamallm.WithServerURL(cfg.OllamaBaseURL),

)

}

opts := []openai.Option{

openai.WithToken(cfg.OpenAIAPIKey),

openai.WithModel(cfg.OpenAIModel),

}

if cfg.OpenAIBaseURL != "" {

opts = append(opts, openai.WithBaseURL(cfg.OpenAIBaseURL))

}

return openai.New(opts...)

}

Prompt Engineering 是关键。不同的转换类型需要不同的 prompt 模板:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

case "mindmap":

return `你是一位资深的信息架构师和知识管理专家。

请将【文本内容】提炼并转换为 Mermaid.js 的 mindmap 格式。

# 样式规范:

1. 中心主题:root((内容))

2. 主要分支:(内容)

3. 细节节点:[内容]

# 严格规范:

- 严禁使用 graph, LR, --> 等字符

- 节点内容 10 字以内

- 只输出代码块,不解释

来源:{sources}

`

注意这里的 prompt 设计:明确的输出格式要求,加上反面示例("严禁"),能有效减少 LLM 输出格式不稳定的问题。

3. PPT 生成

06-核心模块-PPT

这是比较复杂的 feature。需要两个步骤:

第一步:生成大纲

用 Gemini Flash 生成 PPT 大纲,每页包含:

  • 叙事目标(这张幻灯片要讲什么)
  • 关键内容(标题、要点)
  • 视觉元素(需要什么图)
  • 布局(怎么排版)

第二步:解析和生成图片

1

2

3

4

5

6

7

8

9

10

11

12

13

14

func (a *Agent) ParsePPTSlides(content string) []Slide {

styleStart := strings.Index(content, "<STYLE_INSTRUCTIONS>")

styleEnd := strings.Index(content, "</STYLE_INSTRUCTIONS>")

re := regexp.MustCompile(`(?m)^(?:\s*#{1,6}\s*)?(?:Slide|幻灯片)\s*\d+`)

indices := re.FindAllStringIndex(content, -1)

if strings.Contains(lower, "叙事目标") || strings.Contains(lower, "关键内容") {

slides = append(slides, Slide{Style: style, Content: slideContent})

}

}

然后为每张幻灯片调用 Gemini Pro Image 生成图片:

1

2

3

4

5

for i, slide := range slides {

prompt := fmt.Sprintf("Style: %s\n\nSlide Content: %s", slides[0].Style, slide.Content)

imagePath, err := s.agent.GenerateImage(ctx, "gemini-3-pro-image-preview", prompt)

slideURLs = append(slideURLs, "/uploads/"+filepath.Base(imagePath))

}

4. 信息图生成

07-核心模块-信息图

信息图的核心是让 LLM 把文本内容"翻译"成视觉描述,然后把描述作为 prompt 喂给图像模型。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

case "infograph":

return `# Role

你是一位世界顶级的数据可视化设计师和信息图专家。

# Task

阅读所附文本,设计一张信息图。不要进行总结,

而是描述这张图应该长什么样。你的输出将被直接用作图像生成的 prompt。

# Design Guidelines

1. 核心信息提炼:找出最重要的 3-5 个数据点

2. 视觉隐喻:用形象的比喻(如网络安全用"盾牌和锁")

3. 布局结构:明确定义图的结构

4. 文本限制:极简,只保留标题和关键数据

5. 风格:插画或手绘感

# Output Format

Start with "Infographic illustration created in a soft, hand-drawn digital art style..."

[描述整体布局和背景风格]

[详细描述主要视觉元素]

...

`

这个 prompt 设计的要点是:明确告诉 LLM 它的输出会被用作另一个 prompt,这样 LLM 就会更注意输出的结构化和可用性。

5. 思维导图

08-核心模块-思维导图

思维导图的实现最简单,核心是生成 Mermaid.js 的语法:

1

2

3

4

5

6

7

8

9

10

11

12

13

case "mindmap":

return `将文本转换为 Mermaid.js mindmap 格式。

# 样式规范:

root((中心主题)) # 圆圈

(主要分支) # 圆角矩形

[细节节点] # 矩形

# 严格规范:

- 仅限 mindmap 语法

- 节点内容 10 字以内

- 严禁包含引号

`

前端用 Mermaid.js 自动渲染:

1

2

import mermaid from 'https://s4.zstatic.net/ajax/libs/mermaid/11.4.0/mermaid.min.js';

mermaid.initialize({ startOnLoad: true });

数据流

09-数据流

整个系统的数据流如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

用户上传文档

markitdown 转换为 Markdown(如果需要)

文本分块并索引

存储到 SQLite

用户选择转换类型(PPT/信息图/思维导图)

根据类型构建 prompt

调用 LLM 生成内容

如果需要图片,调用 Gemini 生成

保存为 Note

前端渲染展示

部署和使用

10-部署和使用

本地运行

1

2

3

4

5

6

7

8

9

10

11

12

git clone https://github.com/smallnest/notex.git

cd notex

go mod tidy

export OPENAI_API_KEY=your_key

go run . -server

export OLLAMA_BASE_URL=http://localhost:11434

export OLLAMA_MODEL=qwen2.5:7b

go run . -server

Docker 部署

1

docker-compose up

环境变量

变量 说明 默认值
OPENAI_API_KEY OpenAI API 密钥 -
OPENAI_BASE_URL 自定义 API 地址 -
OPENAI_MODEL 模型名称 gpt-4o-mini
OLLAMA_BASE_URL Ollama 地址 -
OLLAMA_MODEL Ollama 模型 -
GOOGLE_API_KEY Gemini API(PPT/信息图) -
SERVER_PORT 服务端口 8080

扩展性

代码结构支持几种扩展方式:

1. 添加新的转换类型

agent.gogetTransformationPrompt 添加新 case:

1

2

case "new_type":

return `你的 prompt 模板...`

2. 支持新的 LLM

LangChainGo 支持多种模型,只需在 createLLM 添加新的分支。

3. 接入向量数据库

VectorStore 目前是内存实现,可以替换成 Qdrant、Milvus 等。

4. 添加新的文档格式

vector.goneedsMarkitdown 添加新的文件扩展名。

代码量和学习价值

项目核心代码大约 2000 行左右,适合学习:

  1. RAG 的完整实现:从文档处理到检索到生成
  2. Prompt Engineering:如何设计稳定的 prompt
  3. Go Web 开发:Gin 框架、SQLite、并发处理
  4. 前后端分离:单页应用 + REST API
  5. LLM 应用架构:如何组织一个 AI 应用的代码

如果你想学习如何构建 AI 应用,或者想找一个 NotebookLM 的替代方案,这个项目可以作为一个起点。

总结

Notex 不是什么高大上的项目,代码也写得朴素。但它解决了实际问题:让你在本地使用 AI 处理文档,数据不外泄。

核心功能就几个:

  • 文档问答(RAG)
  • 多种内容转换(摘要、FAQ、大纲等)
  • 视觉内容生成(PPT、信息图、思维导图)

技术选型务实地选择了 Go 和原生 JS,没有过多的依赖。部署简单,维护成本低。

如果你感兴趣,可以看看代码,提提 PR,或者直接 fork 改成你自己的工具。


项目地址: https://github.com/smallnest/notex

欢迎 Star、Issue、PR。