一份目录,两张脸
klink 的控制面有两张脸:Python client KLinkClient,以及 MCP server 暴露的同名工具。二者都来自 live plugin 的 method registry,不维护一份会漂移的手写清单。全部工具的功能、参数与示例见 MCP 工具参考;本页讲的是把这些工具串起来的交互流程。
from klink import KLinkClient
with KLinkClient() as c:
print([m["name"] for m in c.methods()["methods"]])
SEND 选择记忆 —— “这块区域”到底指哪
人和 agent 协作最大的摩擦,是“我说的‘这块’是哪块”。klink 用 SEND 解决它:你在 KLayout 里选中几何,点插件工具栏的 SEND(或 agent 调 selection.send_context),这次选择就被记成一个稳定 id(如 sel_0006),存进 session 级记忆 .klink/sessions/<id>/interaction_context.jsonl。从此“刚发的”“这里”“那一个”都能解析到精确几何,而不是截图。
| 工具 | 用途 |
|---|---|
interaction.selection.recent | 最近若干次 SEND(默认最新 5,按顺序不按时间)。 |
interaction.selection.latest | 最新一次 SEND。 |
interaction.selection.get | 按 id 读精确记录。 |
interaction.selection.label | 给重要选择加名字/描述。 |
interaction.context | 同时返回 live 选择 + 最近 SEND 记忆。 |
关键区别:selection.get 是当前 live 选择;interaction.* 是明确 SEND 过的持久记忆。因为从 SEND 到那句引用它的话,中间可能隔了好几分钟的版图操作,所以记忆按序号/数量解析,而不是按时间。用 interaction.context 可以同时拿到“当前选择”和“最近 SEND”,让 agent 判断用户指的是哪个。
两个刻意的设计取舍:一是用显式 SEND,而不是被动监听每次点选——只有用户主动发出的选择才记一条 sel_000N,过滤掉探索性点击和误选的噪音;二是记忆放在插件外部(桥侧),插件只保持“此刻”的零层事实(selection.get 和事件流),会话级记忆、id 分配、语言解析都在 MCP 桥的 interaction.*,按会话持久化到磁盘 JSONL。
示例:把我刚发的两块连起来。
# 用户:选中 A → SEND;再选中 B → SEND
interaction.selection.recent limit=2 # → [sel_0007(B), sel_0006(A)]
interaction.selection.label id="sel_0006" label="probe pad"
# agent 用这两个 id 去 port.mark / structdevice.declare_nets,不靠截图猜坐标
一个 agent 控制多个 KLayout
每个 KLayout 窗口启动时绑定一个端口(第一个空闲的 8765–8799)并注册为一个 session。一个 MCP 桥可以同时驱动全部这些 session——它们是平等 peer,没有“工作端口”或“LVS 端口”的特殊角色,你总是显式说要哪个。
| 工具 | 用途 |
|---|---|
klink.session_list | 枚举正在运行的 session。 |
klink.session_label | 给某个 session 打人类 label / 别名。 |
klink.session_resolve | 把 label / 别名 / active cell / top cell 解析成 session id。 |
klink.session_use | 把桥的主 RPC 目标切到某个 session。 |
klink.session_status | 看某个 session 的记录。 |
klink.session_list
klink.session_label session_id="klayout-8766" label="scratch" aliases=["test"]
klink.session_resolve query="scratch" # → klayout-8766
klink.session_use session_id="klayout-8766"
实践建议:同时开着真实工作版图和 demo/测试窗口时,先给每个窗口打 label,agent 就能用“scratch”这种名字而不是端口号来指代,破坏性操作也不会误伤工作 tab。
跨会话搬运 —— 两阶段、确认安全
在窗口之间搬几何是两阶段的:先 prepare,对目标 session dry-run 通过后,再 commit 才真正写入。这样在写进目标窗口之前就能发现“搬错窗口”“包内容不对”。
klink.transfer_prepare source_session="klayout-8765" target_session="klayout-8766" \
copy_mode="flat_selection" target_cell="IMPORTED" # dry-run 内建
# → package_id + dry-run 报告(目标/cell/layer/统计)
klink.transfer_commit package_id="pkg_0001"
copy_mode:flat_selection(默认)只拍平复制可见几何、不带层级;shallow_instance 只搬实例引用——若目标窗口缺对应子 cell,它会阻断提交而不是静默造一个空壳。目标窗口写入前什么都不会真正写进去,所以搬错目标会在 dry-run 阶段被发现。搬运中还可用 layer_map 重映射图层、translate_um 平移。
8082 端口:klive 兼容显示,完全替代 klive
插件除了 8765 的 RPC,还在 127.0.0.1:8082 起了一个 klive 协议兼容的显示服务。它是原版 klive 的直接替代(drop-in replacement):任何硬编码 localhost:8082 的 gdsfactory / 外部脚本——尤其是 Component.show()——无需改动就能把版图推进 KLayout。你不需要再单独装 klive。
| 能力 | 说明 |
|---|---|
| 协议兼容 | 逐字节兼容 klive 0.4.1 的请求/响应({"gds":…, "keep_position":…, "libraries":…, "technology":…, "lyrdb":…, "l2n":…}),并回 {"version":"0.4.1", "type":"open"|"reload", …}。 |
c.show() 开箱即用 | gdsfactory 的 Component.show() 直接把组件推进 KLayout,已开则 reload、可 keep_position 保住视角。 |
| 不止 GDS | 随 klive 协议一并推送 lyrdb(DRC 标记)和 l2n(网表抽取),一并在浏览器里显示。 |
| 多会话转发 | 单窗口时直接走 pya 载入;多窗口时 8082 是固定入口,把请求转发给注册的目标 session——用 klink.session_set_klive_target / session.mark_klive_target 选定哪个 KLayout 接收 c.show()。 |
| 不干扰 RPC | 即使 8082 被占用起不来,也只影响显示;8765 的 klink RPC 完全不受影响。 |
示例:普通 gdsfactory 脚本,一行 c.show() 推进 KLayout。
import gdsfactory as gf
c = gf.components.mzi()
c.show() # 走 127.0.0.1:8082 → klink 的 klive 兼容服务 → 显示在 KLayout
示例:多窗口时,先指定谁接收显示。
klink.session_set_klive_target session_id="klayout-8766" # scratch 窗口接收 c.show()
# 之后所有 c.show() / import_gf 的显示都落到 klayout-8766
录制 → 可回放脚本
recorder 把一整段工作会话——手工 GUI 编辑和 agent 的 RPC 编辑一视同仁——变成可回放脚本。它不是逐条 RPC 日志,而是 replay-script 生成器:记录足以重建最终 layout 状态的动作,所以一个批量 RPC 或一段 exec.python 可能会展开成逐对象的动作。
| 工具 | 用途 |
|---|---|
recorder.start | 开始录制(可选 output_path 指定输出)。幂等。 |
recorder.status | 是否在录、已记录多少事件/动作、输出路径。随时可调。 |
recorder.stop | 停止并写脚本,返回统计 + wrote。幂等。 |
停止时写出两个产物:
<name>.py—— 基于KLinkClient的回放脚本,并用# user command:注释标出每步对应的菜单动作。<name>_pya.py—— 独立pya版,可在没装 klink 的 KLayout 里直接跑。
recorder.status # 先确认没在录别人的
recorder.start
# …做一些 typed RPC 编辑 + 手工 GUI 编辑…
recorder.stop # 产出 .py 和 _pya.py
recorder.status,避免覆盖一个正在进行的录制。这样一来,原本一次性的手工修改就变成了能重复运行、能改参数的脚本,agent 也能在此基础上重新生成。
Profiles 与工具发现
--profile 同时按意图(read/write/verify/escape/all)和领域(11 个 token 之一)过滤。默认 read,write,verify,escape;klink.find_tools 在任务中导航更窄的工具。完整说明见 MCP 参考 · Profiles。
python -m klink.mcp --profile read,write,verify,escape # 默认
python -m klink.mcp --profile read,device_photonics
klink.find_tools domain="routing_backends"
klink.status # 解释器 / 能力 / 连接状态
Escape hatch
exec.python 可在 KLayout 内跑受控 pya 片段,适合 typed RPC 暂未覆盖的边角场景;它仍会触发 recorder + layout-diff 检测。但优先级应低于 typed RPC——后者有输入验证、结构化错误、next_action,且在 recorder 里表现为“意图”而非不透明代码。详见 MCP 参考 · Escape hatch。