1实现原理 · 为什么它能做到
用 patchright(Playwright 的反检测分支)启动**真实 Chrome 的持久化用户目录**,而不是临时浏览器实例——这是它能在多次调用间保持『已登录』的基础。
channel="chrome", # Use real Chrome
认证阶段打开可见浏览器等人工登录,然后把 cookies + localStorage 落盘成 state.json——整条链路的凭证就是这份文件,不是 API key。
context.storage_state(path=str(self.state_file))
运行时把 state.json 里的 cookies 手工注入浏览器上下文,用来绕过 Playwright『session cookie 不写入 user_data_dir』的已知缺陷。
context.add_cookies(state['cookies'])
每次提问都是『一问一浏览器』:新建页面 → 打开用户给的 notebook URL → DNS 级白名单等待(只允许回到 notebooklm.google.com 域,用于确认没被踢到登录页)。
page.goto(notebook_url, wait_until="domcontentloaded")
问题文本用『人化输入』逐字符敲进 textarea,选择器三级回退(含德语 aria-label),输入框定位失败则整条链路失败。
"textarea.query-box-input", # Primary
取答案靠轮询稳定性判定:等 `div.thinking-message` 消失,取响应容器最后一个元素的文本,需连续多次相同才认为回答结束。
thinking_element = page.query_selector('div.thinking-message')
脚本在答案文本后强制拼接一段面向 Claude 的祈使句再返回 stdout,形成『必须继续追问』的闭环。
return answer + FOLLOW_UP_REMINDER
run.py 是官方唯一推荐入口:缺 .venv 时先用系统 Python 跑 setup_environment.py 自举,再用 venv 解释器执行目标脚本。
result = subprocess.run([sys.executable, str(setup_script)])
notebook 库是纯本地 JSON 元数据(url/name/description/topics/use_count),没有向量库或内容缓存——『接地』实际发生在 Google 侧,不在本地。
self.library_file = self.data_dir / "library.json"
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | Google NotebookLM 站点(浏览器自动化目标) |
| network | 提问页 URL 白名单校验(确认未被重定向去登录) |
| network | Google 账号登录态判定(字符串检测,非主动请求) |
| package | patchright(Playwright 反检测分支,浏览器自动化核心) |
| package | python-dotenv(声明为依赖,但代码从未使用) |
| cli | pip 安装依赖(走默认 PyPI) |
| cli | 下载安装 Google Chrome 二进制 |
| cli | 另一条安装路径装的是 chromium(与上一条不一致) |
4风险提醒 风险提醒:红色 · 谨慎使用
- 凭证以明文文件形态落盘,且是完整 Google 会话 cookie — data/browser_state/state.json 由 `context.storage_state()` 写出,包含 cookies 与 localStorage;运行期又被 add_cookies 读回注入。文件权限依赖本机 umask,无加密、无过期策略;任何能读该目录的进程(含同机其它 agent 会话)都可复用你的 Google 登录态。
- 刻意规避自动化识别 + 关闭浏览器沙箱 — 启动参数含 `--disable-blink-features=AutomationControlled`(注释直书 Patches navigator.webdriver)与 `--no-sandbox`,依赖为反检测分支 patchright。这既可能违反目标站点的自动化条款,也意味着浏览器进程在没有沙箱隔离的情况下加载不可信网页内容。
- 首次运行即自动安装与下载(供应链面) — scripts/__init__.py 在导入期执行 `ensure_venv_and_run()`:建 venv、`pip install -q -r requirements.txt`、下载 Chromium;依赖只有版本 pin、无 hash 校验,且 run.py 会以 venv 解释器执行传入的任意脚本路径。
- 外部网页文本未经边界声明即进入模型上下文 — `return answer + FOLLOW_UP_REMINDER` 把 NotebookLM 页面纯文本原样交给宿主;notebook 内的文档内容(可能来自第三方)因此获得指令级可信度,SKILL.md 的追询机制还会推动模型据此继续行动。
- 仓库已归档停止维护,且实现与文档多处不符 — README:'This project is no longer maintained … the repository is archived: no updates, bug fixes or support. It may stop working when the upstream services change.';同时 citation-backed 无实现、.env 配置仅有文档、Chrome/Chromium 路径冲突、数据路径文档与实际不一致。
- 选择器脆弱且含德语回退,NotebookLM 前端一变即全链路失败 — config.py 主选择器为 `textarea.query-box-input`,回退项分别为德语 'Feld für Anfragen' 与英语 'Input for queries' 的 aria-label;响应容器同样硬编码 `.to-user-container .message-text-content`,无版本探测或自愈。
5第二遍独立确认
- [ok] skill.path = '.'(根级 SKILL.md) — 重读 SKILL.md L1-4 确认 frontmatter 位于仓库根文件;glob 根 *.json 无命中(无 plugin.json / marketplace.json),故技能入口就是仓库根。
- [ok] official_desc 逐字 — 重读 SKILL.md L2-4:name: notebooklm、description 为单行完整句,与 README L15 的同一句(README 用连字符 'Drastically reduced hallucinations - answers only from your uploaded documents.')措辞略异,已按 frontmatter 原文收录。
- [ok] 混合认证机制(persistent profile + state.json 注入) — 重读 browser_utils.py(launch_persistent_context 与 `context.add_cookies(state['cookies'])`)与 auth_manager.py(`context.storage_state(path=str(self.state_file))`),行号与首遍一致;AUTHENTICATION.md 的说明标注的脚本行号与实码相差数行(文档漂移),机制描述本身正确。
- [ok] 安全结论:反检测与沙箱关闭 — 重读 config.py 的 BROWSER_ARGS 列表:`'--disable-blink-features=AutomationControlled', # Patches navigator.webdriver` 与 `'--no-sandbox',` 逐字确认;重读 requirements.txt 首段注释确认 'anti-detection' 表述与 patchright pin。三条红色判据均在源码可定位。
- [ok] 安全结论:凭证读取范围 — 独立重扫 os.environ|getenv|API_KEY|token|keychain|.env|ssh|credentials:scripts 下零命中(唯二 token 字样在 requirements.txt 与文档中的 python-dotenv)。凭证只有 state.json 里的 Google cookie,路径由 config.py 常量推导,无 ~/.claude 硬编码。
- [ok] 安全结论:网络外发范围 — 重扫 requests|urllib|socket|http.client 于 scripts:零命中;网络只经浏览器(notebooklm.google.com / accounts.google.com)、pip(PyPI)与 patchright 下载 Chrome(域名不可定位,已记入 unlocated)。
- [ok] 安全结论:写盘与删除边界 — 重读 auth_manager、notebook_manager、cleanup_manager、__init__ 的写路径:state.json / auth_info.json / library.json / .venv,全部位于 <skill>/data 与 <skill>/.venv;删除限定在 data/ 且显式排除 .venv(`Virtual environment (.venv) is never deleted`)。
- [discrepancy] 宣称 vs 实际:citation-backed — 脚本内无任何 citation/来源条目的解析代码,唯一取数路径是响应容器最后一元素的 inner_text;README L65 与 SKILL.md 的 citation 表述实际描述的是 NotebookLM/Gemini 侧行为,本 skill 只做纯文本透传,不能保证引用随答案回到模型上下文。
6结论
edda31a0efaa9b1c…c80722d3f2