news 2026/9/6 18:12:21

Khoj 的 Notion 集成:从连接工作区到本地检索的完整实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Khoj 的 Notion 集成:从连接工作区到本地检索的完整实现解析

Khoj 的 Notion 集成:从连接工作区到本地检索的完整实现解析

【免费下载链接】khojYour AI second brain. Self-hostable. Get answers from the web or your docs. Build custom agents, schedule automations, do deep research. Turn any online or local LLM into your personal, autonomous AI (gpt, claude, gemini, llama, qwen, mistral). Get started - free.项目地址: https://gitcode.com/GitHub_Trending/kh/khoj

本篇围绕 Khoj 的 Notion 集成(Notion Integration)展开:它让你可以直接搜索、聊天问答自己 Notion 工作区里的笔记。读完你将掌握两种接入方式(云端 OAuth 一键授权与自托管 API Key 手动配置)的完整操作步骤,并能从源码层面理解 Khoj 如何将 Notion 页面拉取、分块、写入向量库,最终进入统一的检索与对话流程。

功能定位:把 Notion 变成可检索的数据源

Notion 是人们用来记笔记、协作的知识平台。Khoj 的 Notion 集成会把你的 Notion 工作区作为一个内容源(data source)接入:Khoj 通过 Notion API 拉取你有权限访问的页面,将内容拆分为可检索的条目(Entry),生成嵌入(embeddings)后存入本地数据库。此后,无论是 Web 端的搜索框还是对话(chat),你的 Notion 笔记都会与本地文档、GitHub 代码等数据源一起参与召回。

接入方式有两种:

  • 云端(Khoj Cloud):登录app.khoj.dev后在 Settings 页面连接你的 Notion 工作区,走的是 OAuth 授权流程;
  • 自托管(Self-Hosted):在 Notion 侧创建一个名为 Khoj 的 integration 并拿到 API Key,手动填入本地设置页。

两种方式的最终落点相同:数据库中的NotionConfig记录 + 一次后台索引任务。下面先给出自托管的完整操作步骤,再逐层拆解实现。

自托管接入步骤(可复制操作)

文档给出的自托管流程如下,每一步都对应了明确的验证点:

  1. 登录 Notion,进入My Integrations页面,创建一个名为Khoj的新 integration,复制生成的API Key(internal integration secret,形如ntn_...secret_...)。
  2. 在 Notion 中把你希望被索引的workspace(工作区)共享给刚创建的 Khoj integration——这一步决定 Khoj 能“看见”哪些页面,未共享的页面不会被拉取。
  3. 打开本地 Khoj 的设置页(默认地址http://localhost:42110/settings#notion),在 Notion 配置项中粘贴上一步的 API Key,点击Save
  4. 在设置页点击Configure,触发对 Notion 工作区的索引。

完成后即可开始搜索与聊天。文档同时提醒:请确保已经配置好 聊天设置(即选择并配置好一个对话模型),否则索引完成也无法发起 chat。

从源码结构看,Save 与 Configure 两步分别对应两个后端动作:Save 把 token 写入NotionConfig;Configure(或在保存 token 的同时)以后台任务形式调用configure_content(user, {}, False, SearchType.Notion),只索引 Notion 这一种内容类型,避免重复处理其他数据源。

连接凭证如何存储与读写

数据模型

Notion 凭证存储在 Django 模型 NotionConfig 中:

class NotionConfig(DbBaseModel): token = models.CharField(max_length=200) user = models.ForeignKey(KhojUser, on_delete=models.CASCADE)

要点:

  • token最大长度 200 字符,每个用户一条记录,随用户级联删除;
  • token 是 Notion API 的 Bearer 凭证,后续所有 Notion API 请求都携带它。

数据库层的读写封装在 src/khoj/database/adapters/init.py 中:set_notion_config(token, user)负责写入,get_user_notion_config(user)负责读取当前用户的配置。

设置页对应的 API 端点

设置页背后的 REST 端点定义在 src/khoj/routers/api_content.py 与 set_content_notion:

  • GET /api/content/notion:读取当前用户配置,把NotionContentConfig(token=...)(定义见 src/khoj/utils/rawconfig.py,其中唯一字段就是token: str)序列化为 JSON 返回给前端,未配置时 token 为空串;
  • POST /api/content/notion:接收前端提交的NotionContentConfig,调用set_notion_config持久化;若 token 非空,则立即通过background_tasks触发configure_content(user, {}, False, SearchType.Notion)——这也是设置页点击 Save 后索引自动开始的原因,接口本身不阻塞等待索引完成。

因此自托管路径完全不需要额外环境变量:只要用户在设置页填入有效 API Key,索引流程就能走通。

云端 OAuth 授权流程(源码视角)

云端部署下,用户无需手动管理 API Key。相关实现集中在 src/khoj/routers/notion.py,依赖三个环境变量:

  • NOTION_OAUTH_CLIENT_ID/NOTION_OAUTH_CLIENT_SECRET:Notion OAuth 应用的客户端凭证;
  • NOTION_REDIRECT_URI:授权回调地址。

流程分两半:

1. 生成授权链接。get_notion_auth_url 在三个环境变量齐全时构造https://api.notion.com/v1/oauth/authorize链接(response_type=codestate参数携带当前用户 UUID),该 URL 随用户配置数据(notion_oauth_url字段)下发到前端设置页。若任一环境变量缺失则返回None,前端自然不会展示 OAuth 入口——这也是自托管环境下默认走 API Key 手动配置路径的原因。

2. 回调换 token。GET /api/auth/callback(即 notion_auth_callback)处理授权回跳:

  • 校验codestate参数,并用会话中已认证的用户替代 state 传参来识别身份,同时校验state == user.uuid作为 CSRF 防护,不匹配则返回 400;
  • 删除该用户旧的NotionConfig,再以Basic base64(client_id:client_secret)认证头向https://api.notion.com/v1/oauth/token提交grant_type=authorization_code换取access_token
  • access_token写入NotionConfig,记录 owner / workspace_id / workspace_name / bot_id 日志;
  • 通过background_tasks异步执行configure_content(user, {}, False, SearchType.Notion)开始索引,随后 302 跳回设置页(config_page)。

可以推断:OAuth 拿到的 token 与自托管手填的 internal integration token 在后续流程中是等价的,二者都只作为 Bearer 凭证使用,因此索引与检索代码完全不感知授权方式差异。

索引流水线:Notion 页面如何变成可检索条目

索引核心是 src/khoj/processor/content/notion/notion_to_entries.py 中的NotionToEntries(继承自TextToEntries)。入口由 configure_content 调度:当客户端没有随请求发送任何文档时,它从数据库取出该用户的NotionConfig,并调用text_search.setup(NotionToEntries, None, regenerate=..., user=user, config=notion_config)完成拉取、分块与嵌入更新。

初始化与 API 会话

构造器(L48-L80)做三件事:

  • 建立requests.Session,请求头固定为Authorization: Bearer <token>Notion-Version: 2022-02-22
  • 定义不支持的块类型(bookmark、divider、child_database、template、callout、unsupported)——命中这些类型时返回空字符串,即被静默跳过;
  • 定义展示型块类型(paragraph、heading_1~3、bulleted/numbered_list_item、to_do、toggle、child_page 等),命中时文本前后补换行,保证分块后的语义边界。

全量拉取与分页

process()(L82-L116)通过 Notion 的 search 端点遍历工作区:

while True: result = self.session.post( "https://api.notion.com/v1/search", json=self.body_params, # 初始 {"page_size": 100} ).json() responses.append(result) if not result.get("has_more", False): break else: self.body_params.update({"start_cursor": result["next_cursor"]})

即每页 100 条、用start_cursor翻页直至has_more为 false。对每条结果:

  • object == "database"的直接跳过(源码中留有TODO: Handle databases注释,说明数据库型内容当前版本不作为条目索引);
  • object == "page"的交给process_page()处理。

页面分块策略

process_page()(L118-L174)按块(block)遍历页面内容,分块规则是“标题即边界”:

  • 遇到heading_1/2/3块时,把此前累积的raw_content先落成一个Entry,并更新当前 heading;
  • 其他块若已有 heading 上下文,会先通过process_heading()写入<b>标题</b>作为上下文前缀,再追加块文本;
  • 富文本链接被渲染为<a href='...'>文本</a>保留锚点信息;
  • 若块带子节点(has_children),递归调用get_block_children()拉取/v1/blocks/{block_id}/children并继续拼接,实现嵌套内容的展开。

每个Entryfile字段是页面的 Notion URL(即结果中“来源文件”展示的是该页面链接),heading为页面标题。页面标题的提取(get_page_content)按title → Title → Name → Page → Event的顺序在页面属性中回退查找,找不到则记 warning 并将该页跳过。

切分与入库

拉取完成后(L114-L116):

  • 先经TextToEntries.split_entries_by_max_tokens(current_entries, max_tokens=256)将超长条目切成不超过 256 token 的片段;
  • 再调用update_entries_with_ids(),内部以DbEntry.EntryType.NOTION/DbEntry.EntrySource.NOTIONkey="compiled"执行增量的嵌入更新——新增内容补嵌入、失效内容删除旧嵌入,重复点击 Configure 不会产生重复条目。

这意味着 Notion 内容在数据库中的身份标记是统一的EntryType.NOTION,与 Markdown、Org、PDF 等来源并列。

检索与对话如何消费 Notion 数据

索引完成后,Notion 条目进入统一的检索体系:

  • 内容类型枚举在 src/khoj/utils/config.py 中声明为SearchType.Notion = "notion"
  • 检索器通过 src/khoj/search_type/text_search.py 中的映射表把SearchType.Notion关联到EntryType.NOTION,因此指定 notion 类型检索时只召回 Notion 来源条目,all类型则与其他来源混合召回;
  • 设置页还会依据 enabled_content_sources 判断用户是否已启用 notion 数据源(基于该用户 Entry 表中出现过的 file source),用于前端展示当前已连接的数据源状态。

至此,一篇 Notion 页面经历的完整链路是:Notion API 拉取 → 按标题分块并保留 heading 上下文 → 256 token 切分 → 生成/更新向量 → 以 EntryType.NOTION 参与搜索与 chat 召回

边界与注意事项

结合源码可以确认以下限制,配置前值得了解:

  1. Database 类型内容暂不索引:search 端点返回的database对象被显式跳过(源码注释为 TODO),Notion 数据库视图中的内容需要落到普通页面才可被检索;
  2. 部分块类型被忽略:bookmark、divider、child_database、template、callout 等类型不产生文本,页面上的 callout 提示等内容不会进入检索语料;
  3. 页面必须有可识别标题:属性中找不到title/Title/Name/Page/Event任一字段时该页被跳过(见 get_page_content);
  4. 权限边界由 Notion 侧决定:Khoj 只能看到已共享给 integration 的 workspace,撤销共享后下次索引即不再包含对应页面;
  5. 索引是增量且幂等的:基于compiled内容比对做增删(update_entries_with_ids),可放心重复点击 Configure 触发同步。

参考路径汇总:数据源文档 notion_integration.md、索引实现 notion_to_entries.py、内容 API api_content.py、OAuth 路由 notion.py、数据模型 models/init.py、调度逻辑 helpers.py。

【免费下载链接】khojYour AI second brain. Self-hostable. Get answers from the web or your docs. Build custom agents, schedule automations, do deep research. Turn any online or local LLM into your personal, autonomous AI (gpt, claude, gemini, llama, qwen, mistral). Get started - free.项目地址: https://gitcode.com/GitHub_Trending/kh/khoj

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 18:10:47

四臂螺旋天线馈电设计:从传统方案到新型技术路线

简介&#xff1a;一份新型馈电形式的四臂螺旋天线设计文献&#xff0c;源自《西安邮电大学学报》&#xff0c;面向天线设计、射频工程及卫星通信领域的研发人员与研究生。针对传统四臂螺旋天线底部馈电网络体积大、难以小型化的问题&#xff0c;该文献提出采用单同轴电缆经轴心…

作者头像 李华
网站建设 2026/9/6 18:06:28

数字工厂规划蓝图这样画:从现状诊断到实施路径的顶层设计方法

简介&#xff1a;数字工厂规划蓝图报告以69页详实内容&#xff0c;面向制造企业数字化转型负责人、智能制造规划人员及工厂管理者&#xff0c;系统梳理大制造领域从项目准备、需求分析到蓝图规划、实施落地的全流程方法论。报告聚焦工艺、计划、生产、物流、采购、质量六大核心…

作者头像 李华
网站建设 2026/9/6 18:04:50

WeChatMsg:四步把微信聊天记录导出成HTML、Word、CSV

WeChatMsg&#xff1a;四步把微信聊天记录导出成HTML、Word、CSV 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChat…

作者头像 李华
网站建设 2026/9/6 17:58:52

安桥TX-NR575E功放实战指南:从接线到AccuEQ校准全解析

简介&#xff1a;安桥功放TX-NR575E高级版中文使用说明书是一份面向TX-NR575E家庭影院功放用户的完整中文文档&#xff0c;内容覆盖一般规格、HDMI高级设置、初始设置、聆听模式、网络与蓝牙功能、多区域控制以及故障排除等模块&#xff0c;既适合普通用户按需查阅&#xff0c;…

作者头像 李华
网站建设 2026/9/6 17:57:00

基于电路交换的NoC路由器设计:原理、架构与FPGA实现

简介&#xff1a;一篇面向片上网络&#xff08;NoC&#xff09;与路由器设计人员的技术文献&#xff0c;系统介绍基于电路交换的NoC路由器设计与实现&#xff0c;针对传统总线结构下的片上通信瓶颈&#xff0c;给出面向无线通信等有保障服务场景的完整方案。资源为1个PDF文件&a…

作者头像 李华