总览与约定
MCP 工具名是稳定的 namespace.verb。每个工具都归属唯一一个领域,同一个领域 token 既是 klink.find_tools domain=<token> 的导航键,也是 --profile <token> 的过滤器。
阅读约定
- 参数表里
name*表示必填,其余为可选。 - 坐标:带
_um后缀是微米(最自然);带_dbu是整数数据库单位。micron = dbu × layout.dbu,dbu来自layout.info。 - 图层写法三选一:图层索引
int、"L/D"字符串(如"1/0"),或{layer, datatype}对象。 - 类型列说明工具跑在哪:rpc 在 KLayout 插件内执行;local 在 MCP 进程内执行。其余徽章: read 只读 · write 写入 · verify 验证 · escape 逃生舱 · mutates 会改动版图(可 Ctrl+Z 撤销)· long 长耗时(走独立超时)。
shape.insert_boxes / shape.insert_many / instance.insert_many / instance.insert_pcell_many。单件插入只用于调试单个对象。
动态发现工具
目录是实时查询的,不要死记——它由 live plugin 的 method registry 生成,不存在会漂移的手写清单。Python 侧:
from klink import KLinkClient
with KLinkClient() as c:
print([m["name"] for m in c.methods()["methods"]]) # meta.methods
MCP 侧,tools/list 会公布每一个工具,而 klink.find_tools 负责导航:
klink.find_tools # 无参 → 领域索引(每个领域一行)
klink.find_tools domain="routing_backends" # 该领域的工具 + 详细用法
klink.find_tools query="lvs route" # 跨全部工具的关键词排序匹配
不确定用哪个工具时,klink.guide 会报告当前打开了什么、磁盘上已有哪些意图状态(declared nets / LVS 报告 / spec),并给出每个可用意图的字面调用。
Profiles 过滤
--profile 沿两条正交轴过滤 MCP 暴露的工具——意图与领域。默认是 read,write,verify,escape。
| 意图 Profile | 暴露什么 |
|---|---|
| read | 只读探查:layout.info、cell.list、shape.query、view.*、pcell.*、recorder。 |
| write | 版图编辑:shape.insert_*、cell.create、layer.ensure、instance.insert*、edit.undo。 |
| verify | 运行检查:drc.run、lvs.run。 |
| escape | 逃生舱:exec.python、exec.reset、events.*。 |
all | 全部,不过滤。 |
python -m klink.mcp --profile read,write,verify,escape # 默认
python -m klink.mcp --profile read,device_photonics # 混合两轴
python -m klink.mcp --profile routing_backends # 只暴露一个领域
所有本地工具在任意意图 profile 下都始终包含;核心导航工具 klink.find_tools / klink.status / klink.reconnect 永远在。旧别名仍有效:basic→read、draw→write、advanced→escape、drc→verify。
1 · 连接、自检、发现与视图 connection_and_view · 30 tools
不确定时从这里开始。 klink.status 报告连接、当前 session、解释器和可选能力;klink.guide 报告打开了什么 + 磁盘意图状态 + 下一步字面调用;klink.find_tools 按领域/关键词导航其余工具;klink.reconnect 恢复断掉的连接。本域还带着 MCP 侧的 session 注册辅助工具(klink.session_*、klink.transfer_*)——klink 用一个 MCP 桥驱动多个 KLayout session,session 是平等 peer,显式传你要的那个。View 工具是读为主的画布导航:新建的 cell 在 view.show_cell 之前不可见;view.new_tab 打开一次性 scratch tab;view.show_25d 从显示列表打开原生 2.5D 视图;子实例显示成名字方框时用 view.hier_levels 调大层级深度。截图(view.screenshot)只是用户要的产物,绝不是 agent 的验证步骤,优先用几何查询。破坏性:view.close_tab——只对一次性测试 tab 用。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
hello | rpc | client, protocol | 介绍 client,拿到 server 信息+能力列表。推荐每条新连接的第一次调用。 |
klink.find_tools | local | domain, query | 按领域或关键词发现工具。无参→领域索引;domain→该域工具+用法;query→排序匹配。 |
klink.guide | local | — | 报告已打开内容、磁盘上的意图状态,以及每个可用意图的字面调用和建议下一步。不知道该干嘛就调它。 |
klink.reconnect | local | — | 关掉过期 client 并尝试重连 KLayout。 |
klink.session_label | local | aliases, description, label*, session_id* | 给注册的 session 附人类 label 和别名。 |
klink.session_list | local | include_stale | 从本地注册表枚举可发现的 KLayout/klink session。 |
klink.session_resolve | local | query* | 把 id / label / 别名 / active cell / top cell 解析成一个 session。 |
klink.session_set_klive_target | local | session_id* | 指定 klive 兼容 8082 入口用的 session(gdsfactory c.show() 落点)。 |
klink.session_status | local | include_stale, session_id | 返回一条 session 记录,默认当前 MCP session。 |
klink.session_use | local | session_id* | 把 MCP 桥的主 RPC 目标切到指定 session。 |
klink.status | local | — | MCP 桥连接状态、当前 session、解释器与上次连接错误。排障第一步。 |
klink.transfer_commit | local | dry_run, package_id* | 提交 transfer_prepare 生成的包。 |
klink.transfer_prepare | local | copy_mode, layer_map, source_session*, target_cell, target_session*, translate_um | 构建两 session 间的 flat-selection 搬运包并在目标 dry-run。 |
meta.debug_signals | rpc | fire | SignalHub 诊断日志;fire="selection_changed" 可触发合成事件测投递链路。 |
meta.methods | rpc | — | 返回完整 RPC 方法目录(描述 + JSON schema),可直接喂给 LLM function-calling。 |
meta.ping | rpc | — | 存活探针,回显 params + trace id,用于测往返延迟。 |
view.activate_tab | rpc | index* | 按索引切换当前 tab;之后所有单 layout RPC 都作用于该 tab。 |
view.close_tab | rpc | view_index | 按索引关闭一个 layout tab(不传则关当前)。 |
view.hier_levels | rpc | max, min | 读取/设置视图显示的层级深度(min/max);密集子实例显示成名字方框时调大 max。 |
view.highlight | rpc | boxes_um, circles_um, clear, color, expire_s, halo, line_width, polygons_um | 在视图上画临时高亮标记(boxes/polygons/circles)——只是叠加层,不碰版图/选择/undo。 |
view.highlight_clear | rpc | — | 立即清除全部 klink 高亮标记。 |
view.list_tabs | rpc | — | 列出该窗口所有 layout tab(索引、标题、文件、active cell、当前 tab)。 |
view.new_tab | rpc | cell_name, dbu | 打开一个带全新 top cell 的空 layout tab 并设为当前;返回 previous_current_index 供之后恢复。 |
view.screenshot | rpc | bbox_dbu, bbox_um, height_px, mode, path, width_px | 渲染 PNG。mode=base64 内嵌 data URL(给有视觉的 LLM);mode=path 存盘返回绝对路径。仅用户请求时用。 |
view.show_25d | rpc | cell, displays*, generator | 从显示列表(每种材料的层+z 范围)打开 KLayout 原生 2.5D 挤出视图;z 高度由调用方提供(工艺事实)。 |
view.show_cell | rpc | cell*, zoom_fit | 把当前 cellview 显示的 top cell 切成指定 cell(新建 cell 靠它才可见),默认 zoom-fit。 |
view.show_lvsdb | rpc | kind, path* | 把存盘的 LVS/netlist 数据库载入 Netlist Browser 并显示,交互 cross-probe。kind=lvs 读 .lvsdb,kind=l2n 读 .l2n。 |
view.viewport | rpc | — | 报告当前视口:可见 bbox(微米与 dbu)、视图像素尺寸、cellview 索引。 |
view.zoom_box | rpc | bbox_dbu, bbox_um | 缩放到恰好显示给定 bbox(微米或 dbu)。 |
view.zoom_fit | rpc | — | 整幅版图适配视口(等价 GUI 的 Zoom Fit)。 |
示例。
klink.status
klink.guide
view.list_tabs
# Python client 等价
from klink import KLinkClient
with KLinkClient() as c:
print(c.hello(client="my-script"))
print(c.layout_info(verbosity="summary"))
print(c.ping(nonce=42))
# session 注册表 + 两阶段搬运(先 prepare + dry-run,看清目标/cell/layer/统计再 commit)
klink.session_list
klink.session_label session_id="klayout-8766" label="scratch" aliases=["test"]
klink.session_resolve query="scratch"
klink.transfer_prepare source_session="klayout-8765" target_session="klayout-8766" copy_mode="flat_selection"
klink.transfer_commit package_id="pkg_0001"
2 · 多会话注册表与跨会话搬运(plugin 侧) multi_session_transfer · 7 tools
session/transfer 的 plugin 侧一半:session.label_set 和 session.mark_klive_target 直接在某个 KLayout 窗口里操作共享注册表;transfer.pending_set/status/clear 和 transfer.paste_pending 在本窗口持有并应用一个已审阅的 flat-selection 包;transfer.import_cell_tree_package 用原生 Cell.copy_tree 从 GDS/OAS 包导入一棵 cell 树。MCP 侧编排(klink.session_*、klink.transfer_prepare/commit)在 connection_and_view 域——两阶段、确认安全的搬运流程从那里开始。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
session.label_set | rpc | aliases, description, label*, session_id* | (plugin 侧)在共享注册表里设 label 和别名。 |
session.mark_klive_target | rpc | — | (plugin 侧)把本窗口标记为 klive/gdsfactory 兼容的 8082 目标。 |
transfer.import_cell_tree_package | rpc | dry_run, path*, source_cell* | 用 KLayout 原生 Cell.copy_tree 从 GDS/OAS 包导入一棵 cell 树;重名以 $N 解决。 |
transfer.paste_pending | rpc | clear_after, dry_run | 把挂起的 flat-selection 包粘入本窗口(包内已含最终目标层与坐标)。 |
transfer.pending_clear | rpc | — | 清掉挂起包(不写几何)。 |
transfer.pending_set | rpc | package* | 把已审阅的 flat-selection 包存进本窗口。 |
transfer.pending_status | rpc | — | 本窗口挂起搬运包的状态。 |
3 · 几何与单元结构 geometry_authoring · 50 tools
核心绘图面。先读:layout.info、cell.list/cell.tree、layer.list/layer.display_list、shape.query、instance.query、pcell.*、library.list。生成式版图永远不要一次 RPC 画一个对象,用批量 RPC。画之前先 layer.ensure。编辑都包在事务里,edit.undo/redo/status 操作它们。本域除基础绘图外还带:geometry.boolean/cell_xor/density(几何差异与覆盖率检查)、cell.fill_region(dummy fill/测试结构平铺)、cell.flatten、layer.load_lyp/save_lyp/set_style/set_visible(视图样式)、library.list/refresh/register_file、pcell.convert_to_static、shape.change_layer/transform、instance.transform。破坏性的 layout.clear、cell.delete 只对一次性测试 cell 用,除非用户明确要求,别碰用户的工作 tab。
读取(geometry, not pixels)
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
layout.info | rpc | verbosity | 当前 layout 快照:视图数、active cellview、top cell、源文件、dbu、top-cell 列表、已注册 layer/datatype。想快速了解当前状态就用它。 |
cell.list | rpc | limit, name_prefix, offset, top_only, with_bbox | 扁平分页列出 cell。发现有哪些 cell 用它,层级用 cell.tree。 |
cell.tree | rpc | max_depth, max_nodes, root | 从某 cell 起的层级树(受 depth/nodes 限);每个节点的 instances 数说明它被父实例化几次。 |
layer.list | rpc | — | 列出所有已定义图层:layer_index(其他 RPC 用的句柄)、layer/datatype、可选 name,附 dbu_um。 |
layer.display_list | rpc | — | 列出当前视图的图层显示项(visible、颜色、抖动图案、name)——layer.list 的视图侧对应物。 |
shape.query | rpc | bbox_dbu, cell*, kinds, layers, limit | 读一个 cell 的形状(不递归)为 JSON。强烈建议用 layers+bbox_dbu 缩小范围并分页(默认 500,最大 5000)。truncated=true 表示还有更多。 |
instance.query | rpc | bbox_dbu, bbox_um, child, limit, parent* | 列父 cell 的直接子实例:child 名、bbox、变换、array、PCell 元数据、子 cell 各层形状计数。 |
pcell.libraries | rpc | — | 列可用 PCell 库(Basic 恒在;PDK 注册自己的)。 |
pcell.list | rpc | library | 列某库(默认 Basic)里全部 PCell 名。 |
pcell.info | rpc | library, pcell* | 描述一个 PCell 的参数(name/type/default/description/choices),据此构造 instance.insert_pcell 的 params。 |
library.list | rpc | — | 列出本 KLayout 进程里注册的全部库(Basic、salt/PDK、运行期注册的)。 |
写入 · 单元与图层
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
cell.create | rpc | name | 新建 cell;重名时 KLayout 追加 $1…(返回生效名)。不保证幂等,每次调用建新 cell。 |
cell.rename | rpc | allow_suffix, cell*, new_name* | 重命名;重名默认报错(allow_suffix=true 才自动加后缀)。 |
cell.delete | rpc | cell*, recursive | 删 cell。recursive=true 连带删孤儿子 cell;默认残留 ghost 引用。破坏性。 |
cell.flatten | rpc | cell*, dry_run, levels, prune | 把一个 cell 的层级拍平成普通形状;dry_run 可预览,prune 删除孤儿子 cell。 |
cell.fill_region | rpc | boxes_um, cell*, circles_um, column_step_um, exclude_layers, exclude_margin_um, fc_bbox_um, fill_cell*, origin_um, polygons_um, region_layers, row_step_um | 在一个区域(boxes/polygons/circles/region_layers,减去 exclude_layers)里平铺一个 fill cell——KLayout 的 Fill Utility,用于 dummy fill、器件阵列、测试结构。 |
layer.ensure | rpc | datatype, layer*, name | 确保 GDS 层存在(缺则在事务里建),返回 layer_index。纯 upsert,可反复调。 |
layer.set_style | rpc | color, dither_pattern, fill_color, frame_color, layer*, line_width | 设置某图层在当前视图里的显示样式(颜色/填充/边框/抖动/线宽)——只改显示,不动版图数据。 |
layer.set_visible | rpc | exclusive, layers*, visible | 在当前视图里显示/隐藏图层;exclusive=true 只显示列出的那些层。 |
layer.load_lyp | rpc | path* | 一次调用把 KLayout .lyp 图层属性文件(颜色/填充/可见性)载入当前视图。 |
layer.save_lyp | rpc | path* | 把当前视图的图层属性(颜色/填充/可见性)存成 .lyp 文件。 |
library.refresh | rpc | library | 重新求值某个(或全部)库的内容,例如某 PCell 重新注册之后调用。 |
library.register_file | rpc | description, name, path*, technology | 把一个版图文件注册成运行期库,其 cell 就能通过 instance.insert* 按名字放置。 |
写入 · 形状(批量优先)
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
shape.insert_boxes | rpc | boxes_dbu, boxes_um, cell*, datatype, dry_run, layer, layer_index | 批量:一次 RPC、一个事务插入很多矩形到同一 cell/层。生成式版图首选。 |
shape.insert_many | rpc | cell*, dry_run, items* | 批量:一次插入混合形状(box/polygon/path/text),每项带自己的层选择器和几何字段。 |
shape.insert_box | rpc | bbox_dbu, bbox_um, cell*, datatype, layer, layer_index | 单个轴对齐矩形。调试单对象用。 |
shape.insert_polygon | rpc | cell*, datatype, layer, layer_index, points_dbu, points_um | 单个多边形(仅外壳,暂不支持洞),≥3 点,自动闭合。 |
shape.insert_path | rpc | begin_ext_dbu, begin_ext_um, cell*, datatype, end_ext_dbu, end_ext_um, layer, layer_index, points_dbu, points_um, round_ends, width_dbu, width_um | 单条 path(中心线+宽度),可选端延伸与圆头。 |
shape.insert_text | rpc | cell*, datatype, layer, layer_index, position_dbu, position_um, size_dbu, size_um, string* | 单个文本标签(非几何注记,不产生掩模)。 |
shape.delete | rpc | all_layers, bbox_dbu, bbox_um, cell*, datatype, dry_run, kinds, layer, layer_index, layers, limit | 按声明式选择器删形状(层 + 可选 bbox + 可选 kinds)。dry_run=true 先看计数。整批一个事务,可一次 undo。 |
shape.change_layer | rpc | bbox_um, cell*, from_layer*, to_layer* | 把一个 cell 里的形状从一个图层搬到另一个图层(可选只搬 touching 某 bbox 的)。 |
shape.transform | rpc | bbox_um, cell*, layers, limit, mirror, move_um, rotation | 原地移动/旋转/镜像已有形状(按 layers 和/或 bbox 过滤,至少给一个)。 |
写入 · 实例与 PCell(批量优先)
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
instance.insert_many | rpc | dry_run, items*, parent* | 批量:一次插入很多已存在子 cell 实例。每项含 child + 变换/array 字段。 |
instance.insert_pcell_many | rpc | dry_run, items*, parent* | 批量:一次插入很多 PCell 实例(library/pcell/params/变换/array)。 |
instance.insert | rpc | array, child*, klink_id, library, magnification, mirror, parent*, position_dbu, position_um, rotation | 把 child 放进 parent。位置微米/dbu,旋转度数,可选 array 建网格。 |
instance.insert_pcell | rpc | array, klink_id, library, magnification, mirror, params, parent*, pcell*, position_dbu, position_um, rotation | 从库构建一个 PCell 变体 cell(如 Basic.CIRCLE/TEXT)再插一个实例。先 pcell.info 查参数。相同 params 复用同一变体。 |
instance.delete | rpc | all, bbox_dbu, bbox_um, child, dry_run, limit, parent* | 按声明式选择器删实例(对子 cell 本身非破坏,只去引用)。一个事务。 |
instance.transform | rpc | bbox_um, child, mirror, move_um, parent*, rotation | 移动/旋转/镜像已放置的实例(按 child 和/或 bbox 过滤);零匹配报错。 |
pcell.register_fitted | rpc | fit_table*, name* | 运行期从 fit table 注册一个拟合器件 PCell(klink_transistor_pcell_fit_v1),落到 klink_structdevice 库,无需重载插件。新器件族零插件改动。 |
pcell.convert_to_static | rpc | cell*, prune_variant | 把一个 PCell 变体转成静态 cell,并把所有实例重定向到它;之后几何就冻结了,不能再改参数。 |
写入 · Layout 级与编辑历史
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
layout.show_file | rpc | keep_position, mode, path*, technology | 载入 GDS/OAS(已开则 reload)。mode=replace 当前视图 / new 新 tab。录制时合并为一行。 |
layout.save_file | rpc | cellview_index, path* | 存盘。扩展名决定格式:.gds/.gds2 GDSII,.oas/.oasis OASIS。 |
layout.import_file | rpc | create_other_layers, layer_map, on_conflict, path* | 把一个版图文件合并进当前 layout,带图层重映射与同名 cell 冲突策略(rename/add/overwrite/skip)。 |
layout.clear | rpc | cellview_index | 破坏性:清空整幅 layout(所有 cell/形状/层级)。 |
edit.undo | rpc | — | 撤销最近一次可撤销操作(klink RPC、Macro IDE 编辑或 GUI 编辑)。返回前后栈快照。 |
edit.redo | rpc | — | 重做,与 edit.undo 配对。 |
edit.status | rpc | debug | 报告当前 undo/redo 可用性;debug=true 暴露 Manager 内省字段。 |
几何检查(只读报告)
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
geometry.boolean | rpc | a*, b*, op*, write_to | 两个 {cell, layer} 源之间的布尔运算(and/or/xor/not);报告多边形数/面积,可选把结果写入 write_to。 |
geometry.cell_xor | rpc | cell_a*, cell_b*, layers, only_differing | 两个 cell 逐层几何差异(纯报告,不写任何东西)——校验“我的改动是否只改了我想改的”的工具。 |
geometry.density | rpc | cell*, layer*, window_um | 一个 cell 里某图层的覆盖面积密度(面积/窗口面积);做 dummy fill 决策前的预检查。 |
示例。
# 1) 读
layout.info verbosity="summary"
cell.list top_only=true
# 2) 建 cell + 确保层
cell.create name="MYBLOCK"
layer.ensure layer=1 datatype=0 name="M1"
# 3) 批量画一排矩形(微米)——不要循环单插!
shape.insert_boxes cell="MYBLOCK" layer="1/0" boxes_um=[[0,0,10,4],[20,0,30,4],[40,0,50,4]]
# 4) 混合形状一次插入
shape.insert_many cell="MYBLOCK" items=[
{"kind":"box","layer":"1/0","bbox_um":[0,10,50,14]},
{"kind":"path","layer":"2/0","points_um":[[0,20],[50,20]],"width_um":2},
{"kind":"text","layer":"63/0","position_um":[0,26],"text":"MYBLOCK","size_um":4}
]
# 5) 让它可见(新 cell 在 show_cell 前不可见)
view.show_cell cell="MYBLOCK"
# 放置 Basic.CIRCLE PCell(先查参数)
pcell.info library="Basic" pcell="CIRCLE"
instance.insert_pcell parent="MYBLOCK" library="Basic" pcell="CIRCLE" \
params={"l":"1/0","r":5.0,"n":64} position_um=[100,0]
# 一次放一个 8×8 网格
instance.insert parent="TOP" child="MYBLOCK" position_um=[0,0] \
array={"rows":8,"cols":8,"pitch_x_um":60,"pitch_y_um":40}
4 · 选择与 SEND 交互记忆 selection_and_send_memory · 10 tools
两件不同的事。selection.* 是 KLayout 里当前的实时选择:selection.get、selection.set_box(替换当前选择)、selection.clear、selection.send_context(agent 侧显式 SEND)。interaction.* 是用户明确 SEND 过(工具栏 SEND,记成 sel_0006 这样的 id)的持久 session 记忆:interaction.selection.latest/recent(默认最新 5,按顺序不按时间裁剪)/get/label,以及 interaction.context(当前选择+最近记忆一起返回)。当用户说“刚发的”“这块区域”“这里”“那一个”时用它们——按顺序/数量解析,不是按时间;把用户措辞绑到这些 id/查询,而不是绑到截图。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
interaction.context | local | include_current_selection | 同时返回当前 KLayout 选择 + 最近持久 SEND 记忆。 |
interaction.selection.clear_session | local | confirm* | 确认后清掉本 MCP session 的持久交互上下文。 |
interaction.selection.get | local | id* | 按稳定 id 读一条存储选择。 |
interaction.selection.label | local | description, id*, label | 给某条存储选择加/改 label 和描述。 |
interaction.selection.latest | local | — | 本 MCP session 记忆里最新一次 SEND。 |
interaction.selection.recent | local | limit | 最近若干次 SEND(默认最新 5,按顺序不按时间裁剪)。 |
selection.clear | rpc | — | 清空当前选择。 |
selection.get | rpc | limit | 当前视图的选择,每项是 shape(层+bbox)或 instance(目标 cell+bbox)。空选择返回空列表,不是错误。 |
selection.send_context | rpc | max_items, source | 把当前非空选择作为 selection_sent 事件显式发出(agent 侧 SEND)。不在 plugin 存记忆。 |
selection.set_box | rpc | bbox_dbu, bbox_um, cell*, include_instances, layers, limit | 选中 cell 内 bbox 与给定框相交的所有形状(默认全层),替换当前选择,返回选中数。 |
示例。
# 用户在 KLayout 里选中 A,点 SEND;再选中 B,点 SEND
interaction.selection.recent limit=2 # → [sel_0007(B), sel_0006(A)]
interaction.selection.label id="sel_0006" label="probe pad"
# 现在按 id 去 mark port / 声明 net,而不是靠截图猜位置
5 · Port 与 Anchor(路由标记) ports_and_anchors · 18 tools
Port 是网络端点(klink_Port PCell:带 net + 朝向 + 宽度)。Anchor 是路由约束(klink_Anchor PCell),kind 为 waypoint_region / bend_region / corridor(普通 corridor 是必经通道;给它 choice_group=BUS 才变成可选通道)。port.mark/list/update/transform/set_layer/unmark/delete_all/repair_names,anchor.*(+ anchor.repair_ids)同名动词对应。port.mark_many 一次 RPC/一个 undo 步在一个 cell 里标很多 port(先校验再写)——生成式 port 阵列用它而不是循环 port.mark。port.harvest_blackbox 按 waveguide stub 约定从 live 的 gdsfactory/PDK blackbox 实例位置派生 Port(这个偏硅光的工具归在本域)。路由工具默认 port_layer=999/99、anchor_layer=999/1。Keepout 不是 anchor 类型——把你自己设计的 obstacle_layers 传给路由工具;klink 不带默认 keepout 层(900/0 是 klink 保留 keepout 层,structdevice 内部用它)。这些是路由后端的输入:先 mark Port+Anchor,再调 routing.*。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
port.set_layer | rpc | layer* | 配置本 layout 的默认 Port 标记层。 |
port.mark | rpc | access_mode, cell*, center_dbu, center_um, label, layer, name, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_um | 在 cell 里建一个 klink_Port PCell 实例(一个端点)。 |
port.mark_many | rpc | access_mode, cell*, items*, label, layer, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_um | 一次调用/一个 undo 步在一个 cell 里建很多 Port PCell;插入前整体校验(一项错则全部拒绝)。 |
port.list | rpc | cell*, layer, sort | 列 cell 里的 Port(给 layer 则只列该标记层的)。 |
port.update | rpc | access_mode, cell*, label, layer, name*, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_um | 按不可变 name 更新单个 Port。 |
port.transform | rpc | access_mode, cell*, label, layer, names, net, orientation, port_type, rotate_delta, selection, show_label, slide_allowed, slide_edge, target_layer, width_um | 按 names 或 GUI 选择批量更新 Port 参数。 |
port.repair_names | rpc | cell*, layer, prefix | 修复重复/空 Port 名(主要给经 GUI PCell 面板手插、绕过唯一性检查的)。 |
port.harvest_blackbox | local | cell*, clear, nets, port_layer, stub_size_um*, tags*, wg_layer* | 按 waveguide stub 约定(波导层上的小 stub 盒)从 PDK blackbox 实例派生 Port。Port 由 live 实例位置得来——移动实例后重跑刷新,再路由。 |
port.unmark | rpc | cell*, name* | 按 name 删一个 Port。 |
port.delete_all | rpc | cell*, layer | 删 cell 里全部 Port。 |
anchor.set_layer | rpc | layer* | 配置默认 Anchor 标记层。 |
anchor.mark | rpc | cell*, center_dbu, center_um, height_um, id, kind, label, layer, mode, name, net, orientation, path_points, priority, radius_um, required, show_label, width_um | 建一个 klink_Anchor PCell 实例(路由约束)。 |
anchor.list | rpc | cell*, layer, sort | 列 cell 里的 Anchor。 |
anchor.update | rpc | cell*, height_um, id*, kind, label, layer, mode, net, new_id, orientation, path_points, priority, radius_um, required, show_label, width_um | 按不可变 id 更新单个 Anchor。 |
anchor.transform | rpc | cell*, height_um, ids, kind, label, layer, mode, names, net, orientation, path_points, priority, radius_um, required, selection, show_label, width_um | 按 ids 或选择批量更新 Anchor。 |
anchor.repair_ids | rpc | cell*, layer, prefix | 修复重复/空 Anchor id(GUI 手插的)。 |
anchor.unmark | rpc | cell*, id* | 按 id 删一个 Anchor。 |
anchor.delete_all | rpc | cell*, layer | 删 cell 里全部 Anchor。 |
示例。
port.mark cell="NET1" name="A" center_um=[0,0] orientation="E" width_um=2 net="sig"
port.mark cell="NET1" name="B" center_um=[80,20] orientation="W" width_um=2 net="sig"
anchor.mark cell="NET1" kind="waypoint_region" center_um=[40,40] radius_um=6 net="sig"
port.list cell="NET1"
routing.tapered_hybrid_cell cell="NET1" angle_mode="manhattan" obstacle_layers=["10/0"]
6 · 路由后端 routing_backends · 9 tools
都读一个 cell 里的 Port/Anchor PCell 并写出路由几何。按拓扑/质量选后端:routing.tapered_hybrid_cell 是主要的 path+patch 后端;routing.tapered_polygon_cell 写连续 taper 多边形(正式后端,不是备选);routing.steiner_cell 处理多端网络(>2 port);routing.damped_* 系列显式加额外障碍间距;routing.global_channel_cell 是在 hybrid 几何之上的全局决策路由(候选 sink 分配+corridor 容量负载均衡);routing.multilayer_escape_cell 用 bridge 层+via 给被墙挡住的网络逃逸;routing.gdsfactory_ports 用命名 gdsfactory 策略路由 Port 标记(需解释器里有 gdsfactory)。永远检查结构化结果:ok=false、obstacle_hit_count>0、sibling 重叠、route_count 偏少都算失败。障碍处理要传你自己设计的 obstacle_layers(无默认)。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
routing.damped_polygon_cell | local | anchor_layer, angle_mode, cell*, clear, corner_style, damping_distance_um, obstacle_layers, port_layer, route_layer, spacing_um | damped 连续 taper 多边形版。 |
routing.damped_segment_cell | local | anchor_layer, angle_mode, cell*, clear, damping_distance_um, obstacle_layers, port_layer, spacing_um | 显式远离障碍的 damped 段后端(用 hybrid 输出 + 额外软间距)。 |
routing.damped_steiner_cell | local | anchor_layer, angle_mode, cell*, clear, damping_distance_um, obstacle_layers, port_layer, root_ports, route_layer, spacing_um | damped 多端 trunk/branch 版。 |
routing.gdsfactory_ports | local | all_two_port_nets, allow_crossing, auto_taper, backbone_um, bundle_gather_um, cell*, clear, collision_check_layers, cross_section, distance_um, end_straight_um, gf_route_layer, min_straight_taper_um, net, obstacle_bboxes_um, output_mode, pair_by, path_length_match, port_layer, radius_um, resolution_um, route_layer*, route_width_um, router, sbend_fallback, separation_um, sort_ports, source, source_orientation, source_prefix, start_straight_um, steps, taper, target, target_orientation, target_prefix, waypoints_um | 读 cell 的 Port 标记,用一种命名 gdsfactory 策略路由再写回。需 MCP 解释器里有 gdsfactory。 |
routing.global_channel_cell | local | anchor_layer, angle_mode, cell*, clear, obstacle_layers, port_layer, safe_distance_um, spacing_um | 更强的全局决策路由:障碍感知候选 sink 分配 + corridor 容量负载均衡,再复用 hybrid 几何。 |
routing.multilayer_escape_cell | local | bridge_layer*, cell*, clear, obstacle_layers, port_layer, route_layer*, spacing_um, via_layer* | 用主层 + bridge 层 + via 盒子给被墙挡住的成对网络逃逸(窄;不建模 via enclosure)。 |
routing.steiner_cell | local | anchor_layer, cell*, clear, obstacle_layers, port_layer, root_ports, route_layer | 多端网络(>2 port)的直角 Steiner/总线树。星形网否则要拆成 2-port。 |
routing.tapered_hybrid_cell | local | anchor_layer, angle_mode, cell*, clear, obstacle_layers, port_layer, spacing_um | 主要的 path+patch 后端。angle_mode:any / manhattan / fortyfive。 |
routing.tapered_polygon_cell | local | anchor_layer, angle_mode, cell*, clear, corner_style, obstacle_layers, port_layer, route_layer, spacing_um | 连续 taper 多边形(正式后端,不是备选),corner_style miter/bevel/round。 |
routing.gdsfactory_ports 的 router 策略(选错参数会报错并指出哪些 router 支持它,绝不静默忽略):
| router | 用途 |
|---|---|
bundle(默认) | 带间距的 Manhattan river 路由;也支持 waypoints/steps、radius_um、start/end_straight_um、path_length_match 等长、collision_check_layers。 |
electrical | bundle + 金属默认 + 直角尖角 + 电学端口类型。 |
sbend | 横向偏移、面对面端口的平滑 S 过渡。 |
all_angle | 非 Manhattan bundle(可选 backbone_um 脊线)。 |
single | 每对独立 Manhattan 路由。 |
dubins | 每对基于圆弧的任意朝向路由。 |
astar(实验) | 绕 obstacle_bboxes_um 的网格 A*;gf 的 astar 脆弱,klink 会校验结果,穿墙则报错而不是返回坏路由。可靠避障请用 klink 自己的 tapered_hybrid/damped + obstacle_layers。 |
示例。
# klink 自带的主路由,Manhattan,避开你的 keepout 层
routing.tapered_hybrid_cell cell="BLOCK" angle_mode="manhattan" spacing_um=20 obstacle_layers=["900/0"]
# 需要更大障碍间距时
routing.damped_segment_cell cell="BLOCK" damping_distance_um=15 obstacle_layers=["900/0"]
# 多端网络(>2 port)
routing.steiner_cell cell="BLOCK" route_layer="1/0"
# gdsfactory river bundle,等长匹配
routing.gdsfactory_ports cell="BLOCK" route_layer="1/0" router="bundle" \
separation_um=5 radius_um=10 path_length_match=true
ok、obstacle_hit_count、sibling overlap 和 route_count。真正的“完成”只有 live LVS match=True。7 · DRC 与 LVS 验证 drc_and_lvs_verification · 2 tools
两个都是长耗时、纯 pya、领域无关的逃生舱式检查。drc.run 跑你提供的任意 DRC DSL(Ruby)脚本——脚本内异常作为结果返回,不会让 RPC 失败。lvs.run 是连通性对应物:把 live 版图抽成器件网表,和你提供的参考网表比对,写 .lvsdb 并(默认显示)在 Netlist/LVS 浏览器打开做 cross-probe。一个 P&R/器件阶段只有在真实 live LVS match=True 时才算 DONE——离线 fixture 和 marker 计数都不能替代。structdevice 流程优先用 structdevice.lvs_check。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
drc.run | rpc | code*, input_layout, output_rdb, result_mode, stderr_limit, stdout_limit, top_cell | 在 KLayout 集成 Ruby DRC 引擎跑任意 DRC DSL。含 source() 则 standalone,否则对当前 layout 交互式跑。 |
lvs.run | rpc | cell*, conductors*, devices*, out_lvsdb, reference*, show, vias | 抽取(按 devices 配置的 per-cell 器件抽取器 + 你传的导体层)→ 与参考网表比对 → 写 .lvsdb 并显示。 |
示例。
# DRC:M1 最小间距 0.2um(交互模式,对当前 layout)
drc.run script="""
m1 = input(1, 0)
m1.space(0.2.um).output("M1_space", "M1 spacing < 0.2um")
"""
# LVS:抽取导体层,和参考 SPICE 比对,打开浏览器
lvs.run cell="BLOCK" conductors=["1/0","3/0"] vias=["2/0"] \
devices={...} reference={"spice":"ref.spice"} out_lvsdb="block.lvsdb" show=true
8 · 自定义器件网表 → 自动 P&R → LVS device_structdevice · 6 tools
这是器件无关的自定义器件 P&R 流。“器件”=任何带任意参数集 + 端子的 cell;klink 不假设参数名/数量,也没有器件词汇表。器件库、工艺 profile、端子来源都是示例/PDK 数据显式传入——工具不带任何工艺,缺工艺时返回有指导性的错误(“写/跑一个 example”),绝不猜。structdevice.build_from_netlist 是一次调用的主流程(确认门控:先调一次拿 proposal,带 confirm token 再调才真正 build);路由跑在 flexdr 引擎上,物理模型很紧凑:器件自己的金属层同时当路由层用。structdevice.declare_nets/connect_nets/lvs_check/spec_write 是 SEND 驱动的交互路径。structdevice.register_pcell 封装了更底层的 plugin RPC pcell.register_fitted(列在 geometry_authoring 域)。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
structdevice.build_from_netlist | local | cell*, cols, confirm, mode, netlist*, rows, session | 一次调用的主流程:给器件级网表 → 派生 floorplan → 单趟多层路由 → 绘制 → 器件 LVS 验证一个全新 cell。确认门控:先不带 confirm → 返回 proposal;带 confirm token 再调 → 真正 build。 |
structdevice.connect_nets | local | cell*, conductors, min_spacing_um, min_width_um, route_layer, route_width_um, session, via_cell, vias | 把每个已声明但未连接的网络布线并验证:任何 mismatch 全部撤销。 |
structdevice.declare_nets | local | cell*, conductors, recent_sends*, vias | 从 SEND 声明电学网络:一次 SEND 框住 ≥2 个端子 = 一个声明网络(持久化)。示例驱动。 |
structdevice.lvs_check | local | cell*, conductors, mode, session, vias | net 级对账;mode=device/both 另跑器件级 NetlistComparer。 |
structdevice.register_pcell | local | diff_report, fit_table*, name*, session | 运行期从 fit table 注册拟合器件 PCell,零插件改动零重载。 |
structdevice.spec_write | local | cell*, conductors, device_class, layer_roles*, session, vias | 把 live cell 投影成 klink.spec.json 事实文件。 |
示例。
# 第 1 步:不带 confirm → 拿 proposal(网格 rows×cols、行距、路由层、器件混合)
structdevice.build_from_netlist cell="RINGOSC" netlist={
"instances":[{"name":"INV0","device":"inv_x1"}, ...],
"nets":[{"name":"a","terminals":["INV0/in","INV2/out"]}, ...],
"groups":[]
} mode="3L"
# → needs_confirmation + proposal + next_action(confirm=...)
# 第 2 步:同样参数 + confirm token → 真正 place/route/draw/LVS
structdevice.build_from_netlist cell="RINGOSC" netlist={...} mode="3L" confirm="CONFIRM-xyz"
# SEND 驱动的交互连线:用户为每个网络 SEND 一次(框住该网络的所有端子)
structdevice.declare_nets recent_sends=3 cell="BLOCK" conductors=["1/0","3/0"] vias=["2/0"]
structdevice.connect_nets cell="BLOCK" route_layer="3/0" route_width_um=0.5
structdevice.lvs_check cell="BLOCK" mode="both"
structdevice.spec_write cell="BLOCK" layer_roles={"1/0":"gate","3/0":"metal1"}
9 · 成像(剖面 / 3D / SEM / Blender) imaging · 4 tools
全部在 klink 侧运行(不涉及插件);重依赖是可选的,缺失时错误会指名确切的 pip install 命令。Recipe/VisualStack 实例是示例自有的——klink 只提供机制。imaging.xsection_run 沿显式切割线,按 .pyxs 工艺配方(引擎 klayout_pyxs)做工艺剖面;steps=true + 配方里的 '# klink-step: <name>' 标记可给每个工艺步骤各出一张剖面。imaging.render3d 生成 GLB + 自包含交互查看器 HTML;mode=fast 按 visual-stack 声明挤出,mode=process 用剖面引擎扫过全片让工艺曲率(LOCOS、CMP)真实;fraction<1 暴露真正的剖切截面。imaging.sem_top 渲染确定性的 SEM 风格俯视图 PNG(灰度+假彩色)。imaging.blender 用无头 bpy 子进程做论文级渲染——mode=die 给 render3d 的 GLB 抛光,mode=figure 按 GDS+stack 以 1:1 版图坐标建器件图(lattice 层变成原子结构图案)。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
imaging.blender | local | basename, camera, cell, gds, glb, lattice_a_um, mode*, output_dir*, overwrite, samples, session, slabs, stack, timeout_s, transparent | 用无头 bpy 子进程做论文级 Blender 渲染。mode=die 给 render3d 的 GLB 加胶片感透明底+阴影接收;mode=figure 按 GDS+stack 以 1:1 版图比例建器件图(lattice 层→原子结构图案)。 |
imaging.render3d | local | basename, cell, exclude, fraction, gds, mode, output_dir*, overwrite, recipe, session, slices, stack* | 生成 3D 模型(GLB)+ 一个离线可用的自包含交互式查看器 HTML。mode=fast 按 visual-stack 声明挤出;mode=process 用剖面引擎扫过全片,曲率(LOCOS/CMP)是真实的。 |
imaging.sem_top | local | basename, cell, corner_radius_um, gds, layers, output_dir*, overwrite, seed, session, stack*, width_px | 按 visual-stack 声明生成 SEM 风格俯视图 PNG(灰度+假彩色),带颗粒/扫描线/暗角;确定性(可设种子)。 |
imaging.xsection_run | local | basename, below_um, cell, cut_um*, delta_dbu, depth_um, exclude, extend_um, gds, height_um, output_dir*, overwrite, recipe*, render, session, show, stack, steps | 沿一条显式切割线,按 .pyxs 工艺配方(引擎 klayout_pyxs)做工艺剖面;steps=true 时每个标记的工艺步骤各出一张剖面。 |
10 · 纳米器件(Hall bar / EBL / flake) device_nanodevice · 2 tools
nanodevice.hallbar 用一次调用完成整个流程:从 HallBarSpec(bar 长宽、contact 数、contact/pad 尺寸、pitch、gap)算并画整个器件(bar + N 对称接触臂 + pad + Port 标记 + label),然后把实际路由交给通用 router 把 contact 接到 pad(开启重叠校验,可选 EBL writefield 墙当 keepout),提交到一次性 cell(支持 dry_run)。失败返回 problems/next_action,什么都不改。nanodevice.detect_commit 从预算 traces.json 提交 flake trace 为多边形,或对显微图像做实时检测(需 cv2 + numpy)。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
nanodevice.detect_commit | local | cell, coordinate, dry_run, image, pixel_size_um, session, traces_path | 加载(或检测)纳米器件 flake trace 并作为多边形提交进 live cell。traces_path 走预算 traces.json;image+pixel_size_um 走实时检测(需 cv2+numpy)。 |
nanodevice.hallbar | local | cell, dry_run, route_layer, session, spacing_um, spec, writefield | 一次调用生成 + 路由 + 校验 + 提交一个 Hall bar:从 spec 生成几何 + Port/Anchor,用 klink router 把 contact 接 pad。 |
示例。
nanodevice.hallbar cell="HB1" spec={
"bar_length_um":60, "bar_width_um":8, "contact_count":6,
"contact_width_um":4, "pad_size_um":40, "pitch_um":24, "gap_um":6
} route_layer="1/0" dry_run=true
nanodevice.detect_commit cell="FLAKE" traces_path="out/traces.json"
11 · 硅光(gdsfactory 导入 / 连接 / 重路由) device_photonics · 3 tools
光子电路流,需 MCP 解释器里有 gdsfactory。两个端口来源、一个交互循环:photonics.import_gf 一次把成品 gdsfactory 脚本接管进循环——器件实例变成真实 KLayout cell+实例,routed 连接坍缩成器件级网络,per-device port 模板持久化进 spec,网络由 klink 路由。port.harvest_blackbox(列在 ports_and_anchors 域——从 live blackbox 实例位置派生 Port)是另一个端口来源;移动实例后重跑它,再路由。photonics.connect 读最近 N 次 SEND 当端口对,自动命名网络,持久化,重新 harvest,再用 gdsfactory 路由。photonics.reroute 在用户移动组件后重路由一个 cell(读持久 net 表)。多端光学网络不按星形路由——先插入显式 splitter/MMI/Y-branch,再路由拆出来的两端网络。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
photonics.connect | local | cell, radius_um, recent_sends*, route_layer, separation_um, stub_size_um, wg_layer, width_um | 连接用户刚 SEND 的端口:读最近 N 次 SEND → 变成端口对 → 自动命名网络 → 持久化 → 重新 harvest → gdsfactory 路由。 |
photonics.import_gf | local | cell, component, port_layer, route, route_layer, script_path*, session | 一次接管成品 gdsfactory 脚本:导入器件实例(批量 RPC),把 routed 连接坍缩成器件级网络,持久化 port 模板 + net 表,用 klink 路由。 |
photonics.reroute | local | cell*, route_layer, session, stub_size_um, wg_layer | 用户移动组件后重路由:读持久 net 表 → 从 live 实例位置重新 harvest → 路由 → 写回。多端光学网络需先放 splitter。 |
示例。
# 1) 一次接管成品脚本(脚本里 c = build_mzi(); 供 klink 取用)
photonics.import_gf script_path="my_mzi.py" cell="MZI" route_layer="1/0"
# 2) 在 KLayout GUI 里拖动某个 phase shifter……然后:
photonics.reroute cell="MZI" # 光学 + 金属一起重画,保住你的拖动
# 也可从 blackbox stub 约定派生端口后再连(这个工具列在 ports_and_anchors 域)
port.harvest_blackbox cell="MZI" tags=["gc","mmi"] wg_layer="1/0" stub_size_um=0.5
photonics.connect recent_sends=4 cell="MZI" radius_um=10 separation_um=5
12 · L-Edit 桥(文件交换 RPC) bridge_ledit · 3 tools
前提是用户把 example_template/ledit_bridge/ledit_bridge.cpp 以源码方式装载进 L-Edit(Tools → Macro → Load Macro…,零编译)。传输是 %LOCALAPPDATA%\klink\ledit_bridge\<namespace> 下的 JSON 文件交换,单命名空间——同时只保留一个装宏的 L-Edit。ledit.status 是发现与握手——任何异常先调它,错误信息直接写明下一步。ledit.import_selection 现拉用户当前 L-Edit 选区到新的 KLayout 落地 cell。ledit.push_cell 把 KLayout 平坦 cell 推回 L-Edit(追加式,重新生成用新 target cell)。更深的 T-Cell 工作流走 Python API klink.bridges.ledit;详见 《L-Edit 桥》指南。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
ledit.import_selection | local | namespace, session, target_cell | 把用户当前在 L-Edit 里的选区导入 KLayout 新落地 cell(每次现拉,绝不用陈旧几何)。能力匹配转换:box→box、wire→path、circle→CIRCLE PCell、其余轮廓兜底 polygon;层连名带号迁移;不可转换对象如实列出。 |
ledit.push_cell | local | cell*, ledit_cell, namespace, session | 把 KLayout 平坦 cell 推回 L-Edit(box/path→wire/polygon;层带名建立并盖 GDS 号)。子实例计入 skipped.instance 不静默丢。L-Edit 侧 draw 是追加式:重新生成请换新 ledit_cell。 |
ledit.status | local | namespace | 发现与分诊:宏活性(心跳年龄)、设计是否打开、宏版本与能力表、当前 .tdb 与 cell。任何 ledit.* 异常先叫它——错误信息直接写明下一步。 |
13 · Escape hatch(pya exec、事件、recorder) escape_hatch · 9 tools
优先用 typed RPC。 exec.python 跑原始 pya,仅用于没有 typed RPC 覆盖的操作 / 调试 / 紧凑一次性(exec.reset 清空其命名空间)——它仍会触发 recorder + layout-diff 检测。events.*(channels/status/subscribe/unsubscribe)是桥为 SEND 记忆订阅的 live 事件流(通常你改读 interaction.*)。recorder.*(start/stop/status)生成回放脚本(不是字面 RPC 日志);测试前先看 recorder.status,别覆盖用户正在录的东西。
| 工具 | 类型 | 参数 | 功能 |
|---|---|---|---|
events.channels | rpc | — | 列 server 能推的事件通道。事件以 NDJSON 帧投递。 |
events.status | rpc | — | 本连接的订阅 + SignalHub 诊断。 |
events.subscribe | rpc | channels* | 订阅一或多个通道(未知通道静默忽略)。 |
events.unsubscribe | rpc | channels | 退订;传 * 退全部。 |
exec.python | rpc | code*, protect_cellview, reset, result_mode, stderr_limit, stdout_limit | 在 KLayout Qt 主线程跑任意 Python。全 pya + 文件系统访问。预绑定 pya, mw, view, layout。同一连接内状态保留(reset=true 清空)。stdout/stderr 捕获返回。 |
exec.reset | rpc | — | 清空 exec.python 的 per-connection 命名空间。 |
recorder.start | rpc | output_path | 开始把所有改动版图的事件翻译成可回放 Python 脚本。幂等。 |
recorder.status | rpc | — | 当前 recorder 状态(是否在录、事件/动作数、输出路径)。 |
recorder.stop | rpc | output_path | 停止并写脚本;返回统计 + wrote。 |
示例。
recorder.status # 先确认没在录别人的
recorder.start
# …做一些 typed RPC 编辑 + 手工 GUI 编辑…
recorder.stop # 产出 <name>.py 和可独立跑的 <name>_pya.py
# 没有 typed RPC 覆盖时才用逃生舱
exec.python code="print(layout.top_cell().name); print(len(list(layout.each_cell())))"
recorder 产出两个文件:<name>.py(基于 KLinkClient 的回放)和 <name>_pya.py(可在 KLayout 内独立运行的 pya 版)。