stitch:在 Jupyter 中实现 Jupyter 内核与 JavaScript 双向通信的官方 Widget 实战指南
2026/9/20 14:10:38 网站建设 项目流程
  • 大模型
  • 提示工程
  • AI Agent

【免费下载链接】guidance

A guidance language for controlling large language models.

项目地址:https://gitcode.com/gh_mirrors/gu/guidance
点击查看免费下载

导读

stitch是 guidance 项目官方仓库中附带的一个 Jupyter Widget 包,它提供一条Jupyter(Python 内核)与 JavaScript 之间的双向通信通道:Python 侧可以通过内核往页面里的 iframe 发送消息,iframe 内的 JavaScript 也可以把消息传回内核,从而在 Notebook 中嵌入可交互的 HTML/JS 界面并与之实时交换数据。读完本文,你将掌握 stitch 的安装与前端扩展配置、StitchWidget的完整属性用法、基于postMessage的双向通信协议,以及如何进行开发者模式安装与调试。


一、stitch 是什么:为"内核 ↔ 页面"搭建双向消息桥

stitch 的核心定位一句话即可概括——"Bidirectional comms for Jupyter and JavaScript."(Jupyter 与 JavaScript 的双向通信)。它不是一个通用的 UI 组件库,而是一个纯通信层组件:StitchWidget在 Notebook 输出区创建一个沙箱 iframe,并以postMessage为媒介,把 Python 内核的字符串消息转发进 iframe,同时把 iframe 内产生的消息回传内核。这一点在 Python 侧类定义中写得非常直白:

"Widget that purely handles communication between an iframe and kernel via postMessage." —— stitch/stitch.py

从源码结构看,该包由三部分协作组成:

组成部分仓库中的位置职责
Python 侧 Widgetstitch/stitch.py定义StitchWidget及其同步属性,作为内核侧收发消息的端口
TypeScript 前端src/widget.ts渲染 iframe、监听message事件、在模型与 iframe 之间转发消息
扩展注册入口stitch/init.py向 Jupyter 声明 labextension / nbextension 的安装路径

需要特别说明的是:缝合(stitch)只负责"通信"本身,不限制 iframe 里运行什么内容。你可以把任意 HTML/JavaScript 塞进srcdoc属性,由它负责解释和处理消息——这正是它适合在 guidance 这类语言模型项目中被用于渲染可视化界面的原因。


二、安装 stitch:pip / conda 两种方式

1. 基础安装

根据 docs/source/installing.rst 与 docs/source/index.rst,最简单的方式是通过 pip 安装:

pip install stitch

或通过 conda 安装:

conda install stitch

(仓库 README.md 中还提及包名guidance-stitch,实际以当前发布渠道为准;文档中 pip 与 conda 两种形式均已列出。)

2. 前端扩展配置(重要)

stitch 是双端组件,Python 包装好后,前端扩展是否注册决定了 widget 能否真正渲染。文档给出如下判定规则:

  • 使用 conda 安装时:前端扩展通常已随包自动配置,上述命令一般可以省略。
  • 使用 pip 安装,且 Notebook 版本 < 5.3 时:必须手动安装并启用前端扩展。

如果是 classic Notebook(区别于 JupyterLab),运行:

jupyter nbextension install [--sys-prefix / --user / --system] --py stitch jupyter nbextension enable [--sys-prefix / --user / --system] --py stitch

其中的--sys-prefix / --user / --system互斥的三选一作用域标志,用于决定扩展装到哪个环境;在 conda 环境中通常应选择--sys-prefix,以确保扩展落在当前虚拟环境对应的 Python 前缀下。

如果是 JupyterLab,则安装 lab 扩展:

jupyter labextension install @guidance-ai/stitch

@guidance-ai/stitch这个前端包名与 Python 侧_frontend.py中声明的module_name完全一致(见 stitch/_frontend.py),JS 包当前版本为0.1.5(见 package.json)。

3. 扩展注册的底层依据

为什么需要手动配扩展?因为 Jupyter 通过约定函数发现前端资源。在 stitch/init.py 中定义了两个注册函数:

  • _jupyter_labextension_paths():返回{"src": "labextension", "dest": "@guidance-ai/stitch"},告诉 JupyterLab 从构建产物labextension目录复制文件到<jupyter path>/labextensions/@guidance-ai/stitch
  • _jupyter_nbextension_paths():返回{"section": "notebook", "src": "nbextension", "dest": "stitch", "require": "stitch/extension"},告诉 classic Notebook 把nbextension目录安装到<jupyter path>/nbextensions/stitch,并以stitch/extension作为 AMD 模块入口。

可以看到,nbextension 的入口模块正是仓库里的 stitch/nbextension/extension.js:它通过requirejs.config@guidance-ai/stitch映射到nbextensions/stitch/index,从而让 Notebook 页面能够加载到 widget 的模型/视图实现。这也是文档要求"安装后必须 enable"的原因——只有启用了扩展,这一映射才会被注入页面。


三、快速上手:三分钟跑通内核 → 页面 → 内核的完整回路

仓库自带的示例 Notebook examples/introduction.ipynb 演示了 stitch 的完整用法,下面按它的步骤展开讲解。

1. 创建 Widget 并注入页面 HTML

import stitch w = stitch.StitchWidget() w.srcdoc = """ <html style=""> <script> window.addEventListener("message", function(event) { if (event.source === window.parent) { if (event.data.type === "kernelmsg") { document.getElementById("msgview").innerHTML = event.data.content; window.parent.postMessage({type: "clientmsg", content: event.data.content}, "*"); // Save state for offline render window.parent.postMessage({type: "state", content: event.data.content}, "*"); } else if (event.data.type === "init_state") { document.getElementById("msgview").innerHTML = event.data.content; } } }); window.addEventListener("load", function(){ var prevHeight = 0; setInterval(function() { var body = document.body; var html = document.documentElement; var height = html.getBoundingClientRect().height if (height !== prevHeight && html.checkVisibility()) { msg = { type: 'resize', content: { height: height + 'px', width: '100%' } }; window.parent.postMessage(msg, "*"); prevHeight = height; } }, 100); }); </script> <body style=""> <div> MESSAGE: <span id="msgview" style="background-color: #90ee90;"></span> </div> </body> </html> """ w.initial_width = '100%' w.initial_height = 'auto' display(w)

这段代码做了三件事:

  1. 指定srcdoc:iframe 内渲染的完整 HTML 文档,其中内嵌的<script>负责监听来自父页面的消息;
  2. 设置初始尺寸initial_width = '100%'initial_height = 'auto'
  3. display(w):把 widget 渲染进 Notebook 输出区(示例输出的 MIME 类型为application/vnd.jupyter.widget-view+json,说明它被识别为标准的 Jupyter Widget v2 模型)。

注意 iframe 内脚本的职责分工:收到kernelmsg类型消息时更新页面内容并回发两条消息clientmsg用于实时回传、state用于保存离线渲染状态);收到init_state时恢复上次的状态;页面加载后用setInterval每 100ms 检查一次页面高度变化并发送resize消息,实现 iframe 高度自适应内容。

2. 从内核发送消息

w.kernelmsg = """A language model is a probabilistic model of a natural language. ..."""

kernelmsg属性赋值后,消息会经 traitlet 同步机制推送到前端,前端再把消息 postMessage 进 iframe,页面中的msgview立即更新,同时clientmsg/state回传内核。

3. 在内核侧观察回传消息

w.observe(lambda x: print(x['new']), 'kernelmsg') w.kernelmsg = "Wow, a change!"

输出:

Wow, a change!

observe是 ipywidgets traitlet 的观察接口:这里既演示了内核侧对属性变化的响应,也演示了属性被回写后(如 iframe 回传的clientmsgstate)可以在 Python 侧通过同样的机制捕获。示例的最终 widget 状态里clientmsgstate均为"Wow, a change!",证明了一次完整的"内核 → iframe → 内核"回路已经打通。


四、StitchWidget 核心属性详解

StitchWidgetipywidgets.DOMWidget的子类,所有通信字段都通过traitlets.Unicode定义并标记sync=True,意味着它们会在 Python 模型与前端 JS 模型之间自动同步(见 stitch/stitch.py)。各属性如下:

属性默认值说明
kernelmsg""内核 → 客户端消息通道。Python 侧赋值后,前端会把内容以kernelmsg消息 postMessage 进 iframe
clientmsg""客户端 → 内核消息通道。iframe 内发送clientmsg消息后回写此属性
srcdoc"<p>srcdoc should be defined by the user</p>"iframe 渲染的 HTML 源码;赋值后前端会重建 iframe 的srcdoc并立即补发最新的kernelmsg
initial_height"1px"iframe 初始高度 CSS 值,如'auto'
initial_width"1px"iframe 初始宽度 CSS 值,如'100%'
initial_border"0"iframe 初始边框 CSS 值
state""状态快照字段,用于保存/恢复界面状态(如离线渲染场景)

对应的默认值在 TypeScript 前端 src/widget.ts 的defaults()中逐项一致,Python 与 JS 两侧保持同步。单元测试 stitch/tests/test_example.py 也验证了空实例的默认行为:kernelmsg == ""clientmsg == ""srcdoc == "<p>srcdoc should be defined by the user</p>"


五、双向通信协议:前端到底在转发什么

如果想深度定制 iframe 内的交互逻辑,就必须理解前端StitchView处理的消息类型。查看 src/widget.ts 的recvFromClient回调与render()逻辑,可以梳理出完整的消息清单:

父页面(widget 前端)接收 iframe 发来的消息:

消息类型type载荷content前端行为
init_stitchiframe 就绪信号;触发初始化流程,首次渲染时会依次发送init_state与当前kernelmsg
clientmsg任意字符串写入模型clientmsg属性并save_changes()同步回内核
resize{height, width}动态调整 iframe 的宽高 CSS(用于自适应内容高度)
state任意字符串写入模型state属性并同步回内核

父页面发送给 iframe 的消息:

消息类型type触发时机
init_statewidget 初始化完成且模型非新建状态时,向 iframe 恢复上次的state(见emit_init_state()
kernelmsg内核侧kernelmsg变化时(change:kernelmsg回调),或srcdoc更新后立即补发一次

此外,render()中还有两个值得注意的实现细节:

  • 沙箱安全:iframe 被显式加上sandbox且只放开allow-scripts(src/widget.ts),iframe 内的脚本可以运行,但无法访问父页面 DOM,从机制上隔离了页面与 Notebook 环境;
  • 事件过滤:所有message事件都先校验event.source === iframe.contentWindow,确保只处理来自本 widget iframe 的消息,避免被页面中其他来源的 postMessage 干扰。

对应地,StitchModel在 src/widget.ts 中声明了与 Python 侧完全一致的_model_name: 'StitchModel'_view_name: 'StitchView',模块名取自 src/version.ts,保证了模型注册的双端匹配。


六、开发者模式安装与调试

如果要在本地修改 stitch 源码(尤其是前端 TypeScript 代码),按照 docs/source/develop-install.rst 的流程操作。

1. 克隆仓库并以可编辑模式安装

git clone https://github.com/guidance-ai/stitch cd stitch pip install -e .

2. 链接安装前端扩展

如果同时开发 JS/前端代码,需要对扩展做符号链接(symlink)安装,这样源码改动即时生效,无需反复复制:

classic Notebook:

jupyter nbextension install [--sys-prefix / --user / --system] --symlink --py stitch jupyter nbextension enable [--sys-prefix / --user / --system] --py stitch

JupyterLab:

jupyter labextension install .

3. 构建与热更新

仓库 package.json 提供了完整的构建脚本体系:

  • jlpm run build:依次执行 TypeScript 编译(tsc)、webpack 打包 nbextension、以及 dev 模式的 labextension 构建;
  • jlpm run watch:并行启动tsc -wwebpack --watchjupyter labextension watch .,配合jupyter lab即可实现前端改动自动重建、浏览器刷新即生效;
  • Python 侧改动则需重启 Notebook 内核才能生效(这一点在 README.md 中有明确说明)。

值得一提的是,开发依赖中把@jupyterlab/builder钉在 4.0.11、并在jupyterlab.sharedPackages中将@jupyter-widgets/base标记为bundled: false, singleton: true(见 package.json),这是为了让 lab 扩展与 Notebook 共享同一份 widget 基础库,避免模型注册冲突——在排查"widget 不显示"类问题时,可优先检查这一依赖对齐关系。


七、验证与常见问题排查

1. 用单元测试验证默认行为

仓库在 stitch/tests/test_example.py 提供了最小化的验证用例:

from ..stitch import StitchWidget def test_example_creation_blank(): w = StitchWidget() assert w.kernelmsg == "" assert w.clientmsg == "" assert w.srcdoc == "<p>srcdoc should be defined by the user</p>"

该用例确认了StitchWidget()无参创建时的默认状态,可作为开发时回归测试的模板。

2. 常见问题定位思路

  • widget 只显示为空白:先确认扩展是否已启用。classic Notebook 运行jupyter nbextension list,JupyterLab 运行jupyter labextension list,检查stitch/@guidance-ai/stitch是否在列;pip 安装且 Notebook < 5.3 时必须手动执行第二节中的 install + enable 命令。
  • 内核消息发不进 iframe:检查srcdoc内是否监听了kernelmsg类型,且事件源校验为event.source === window.parent;同时确认 iframe 脚本是在init_stitch握手之后才运行(前端只在收到该信号后才开始推送消息)。
  • iframe 高度异常:利用resize协议,参照示例在 iframe 内周期性比较内容高度并回传{type: 'resize', content: {height, width}}
  • Python 收不到回传:确认 iframe 回发的是clientmsg/state类型,且内容为可序列化字符串;这两个属性与kernelmsgsrcdoc一样,都依赖 traitlets 的sync=True双向同步链路。

八、小结

stitch 以极简的设计解决了 Jupyter 生态中的一个关键痛点:让任意 HTML/JavaScript 界面与 Python 内核之间拥有可靠的实时双向消息通道。它的使用路径清晰——pip/conda 安装、按环境配置前端扩展、用StitchWidget的几个字符串属性完成收发;它的原理同样清晰——DOMWidget + traitlets 同步属性 + 沙箱 iframe +postMessage协议。无论是做交互式可视化、嵌入式工具面板,还是在语言模型工作流中渲染动态结果,这套模式都可以直接复用。

进一步阅读:完整安装说明见 installing.rst,开发安装说明见 develop-install.rst,可运行示例见 introduction.ipynb,Python 与前端实现分别见 stitch.py 与 widget.ts。

  • 大模型
  • 提示工程
  • AI Agent

【免费下载链接】guidance

A guidance language for controlling large language models.

项目地址:https://gitcode.com/gh_mirrors/gu/guidance
点击查看免费下载

相关推荐

上一篇:SiYuan 加密笔记本深度解析:本地数据加密、密钥管理与隐私保护完全指南
下一篇:【免费下载】 DeepCAD 开源项目教程

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询