跳转到内容

Notebook 工具运行时内部机制

本文档描述了当前 notebook 工具的实现及其与内核支持的 Python 运行时之间的关系。

关键区别:notebook 是一个 JSON notebook 编辑器,而非 notebook 执行器。它直接编辑 .ipynb 单元格源码;它不会启动或与 Python 内核通信。

  • 支持对 .ipynb 文件执行 action: edit | insert | delete 操作。
  • 相对于会话工作目录解析路径(resolveToCwd)。
  • 加载 notebook JSON,验证 cells 数组,验证 cell_index 边界。
  • 在内存中应用源码编辑,并使用 JSON.stringify(notebook, null, 1) 将完整 notebook JSON 写回。
  • 返回文本摘要 + 结构化 detailsactioncellIndexcellTypetotalCellscellSource)。

此工具中不存在内核生命周期:

  • 无网关获取
  • 无内核会话 ID
  • execute_request
  • 无来自内核通道的流式数据块
  • 无富显示捕获(image/png、JSON 显示、状态 MIME)

类 Notebook 的执行路径 (src/tools/python.ts + src/ipy/*)

Section titled “类 Notebook 的执行路径 (src/tools/python.ts + src/ipy/*)”

当代理需要运行单元格式的 Python 代码(顺序单元格、持久状态、富显示)时,该流程通过 python 工具执行,而非 notebook

内核模式、重启/取消行为、数据块流式传输和输出制品截断都在该路径中实现。

2) Notebook 单元格处理语义(notebook 工具)

Section titled “2) Notebook 单元格处理语义(notebook 工具)”

content 被拆分为带换行符保留的 source: string[]

  • 每个非末尾行保留尾部 \n
  • 末尾行不强制添加尾部换行符

这符合 notebook JSON 约定,避免后续编辑时意外的行连接。

  • edit
    • 替换 cells[cell_index].source
    • 保留现有 cell_type
  • insert
    • [0..cellCount] 位置插入
    • cell_type 默认为 code
    • 代码单元格初始化 execution_count: nulloutputs: []
    • markdown 单元格仅初始化 metadata + source
  • delete
    • 移除 cells[cell_index]
    • 在 details 中返回已移除的 source 以供渲染器预览

以下情况将抛出硬错误:

  • notebook 文件缺失
  • 无效 JSON
  • cells 缺失或非数组
  • 索引越界(插入和非插入操作有不同的有效范围)
  • edit/insert 操作缺少 content

这些错误在上游成为 Error: 工具响应;渲染器使用 notebook 路径 + 格式化的错误文本。

3) 内核会话语义(实际存在的位置)

Section titled “3) 内核会话语义(实际存在的位置)”

内核语义在 executePython / PythonKernel 中实现,适用于 python 工具。

PythonKernelMode

  • session(默认)
    • 内核缓存在 kernelSessions 映射中
    • 最多 4 个会话;溢出时驱逐最旧的
    • 每 30 秒进行空闲/死亡清理,5 分钟后超时
    • 每会话队列序列化执行(session.queue
  • per-call
    • 为请求创建内核
    • 执行
    • 始终在 finally 中关闭内核

python 工具仅在多单元格调用的第一个单元格传递 reset;后续单元格始终以 reset: false 运行。

在会话模式(withKernelSession)中:

  • 通过心跳检测内核死亡(每 5 秒进行 kernel.isAlive() 检查)或执行失败。
  • 运行前检测到死亡状态将触发 restartKernelSession
  • 执行时崩溃路径重试一次:重启内核,重新运行处理程序。
  • 同一会话中 restartCount > 1 将抛出 Python kernel restarted too many times in this session

启动重试行为:

  • 共享网关内核创建在遇到 HTTP 5xx 的 SharedGatewayCreateError 时重试一次。

资源耗尽恢复:

  • 检测 EMFILE/ENFILE/“Too many open files” 类型的故障
  • 清除已跟踪的会话
  • 调用 shutdownSharedGateway()
  • 重试内核会话创建一次

内核启动从执行器接收可选的环境变量映射:

  • PI_SESSION_FILE(会话状态文件路径)
  • ARTIFACTS(制品目录)

PythonKernel.#initializeKernelEnvironment(...) 随后在内核内部运行初始化脚本以:

  • os.chdir(cwd)
  • 将环境变量条目注入 os.environ
  • 如果缺失,将 cwd 添加到 sys.path 开头

含义:

  • 读取会话或制品上下文的预置辅助函数依赖于 Python 进程状态中的这些环境变量。

5) 流式/数据块和显示处理(内核支持路径)

Section titled “5) 流式/数据块和显示处理(内核支持路径)”

内核客户端按执行处理 Jupyter 协议消息:

  • stream -> 文本块传递至 onChunk
  • execute_result / display_data ->
    • 按 MIME 优先级选择显示文本:text/markdown > text/plain > 转换后的 text/html
    • 结构化输出单独捕获:
      • application/json -> { type: "json" }
      • image/png -> { type: "image" }
      • application/x-xcsh-status -> { type: "status" }(不产生文本输出)
  • error -> 回溯文本推送至数据块流 + 结构化错误元数据
  • input_request -> 发出 stdin 警告文本,发送空 input_reply,标记已请求 stdin
  • 完成等待 execute_reply 和内核 status=idle 两者

取消/超时:

  • 中止信号触发 interrupt()(REST /interrupt + 控制通道 interrupt_request
  • 结果标记 cancelled=true
  • 超时路径在输出中添加 Command timed out after <n> seconds 注释

src/session/streaming-output.ts 中的 OutputSink 被内核执行路径(executeWithKernel)使用:

  • 对每个数据块进行清理(sanitizeText
  • 跟踪总行数/输出行数和字节数
  • 可选的制品溢出文件(artifactPathartifactId
  • 当内存缓冲区超过阈值(除非覆盖,否则为 DEFAULT_MAX_BYTES)时:
    • 标记为已截断
    • 在内存中保留尾部字节(UTF-8 安全边界)
    • 可将完整流溢出到制品接收器

dump() 返回:

  • 可见输出文本(可能经过尾部截断)
  • 截断标志 + 计数
  • 制品 ID(用于 artifact://<id> 引用)

python 工具将此元数据转换为结果截断通知和 TUI 警告。

notebook 工具不使用 OutputSink;它没有流/制品截断管道,因为它不执行代码。

  • 调用视图:包含操作 + notebook 路径 + 单元格/类型元数据的状态行
  • 结果视图:
    • details 派生的成功摘要
    • cellSource 通过 renderCodeCell 渲染
    • markdown 单元格设置语言提示 markdown;其他单元格无显式语言覆盖
    • 折叠代码预览限制为 PREVIEW_LIMITS.COLLAPSED_LINES * 2
    • 通过共享渲染选项支持展开模式
    • 使用以宽度 + 展开状态为键的渲染缓存

错误渲染假设:

  • 如果第一个文本内容以 Error: 开头,渲染器将其格式化为 notebook 错误块。

Python 渲染器(用于实际执行输出)

Section titled “Python 渲染器(用于实际执行输出)”

内核支持的执行渲染期望:

  • 每个单元格的状态转换(pending/running/complete/error
  • 可选的结构化状态事件部分
  • 可选的 JSON 输出树
  • 截断警告 + 可选的 artifact://<id> 指针

此渲染器行为与 notebook JSON 编辑结果无关,但两者都复用共享的 TUI 原语。

如果”普通 Python 工具”指的是 python 执行路径:

  • python 在内核中执行代码,按模式持久化状态,流式传输数据块,捕获富显示,处理中断/超时,并支持输出截断/制品。
  • notebook 仅执行确定性的 notebook JSON 变更;无执行、无内核状态、无数据块流、无显示输出、无制品管道。

如果工作流需要两者兼备:

  1. 使用 notebook 编辑 notebook 源码
  2. 通过 python(手动传递代码)执行代码单元格,而非通过 notebook

当前实现不提供单一工具来同时变更 .ipynb 并通过内核上下文执行 notebook 单元格。