ThearchyHelios
|
f0fd63f0b9
|
UI 重构:Copilot 进侧栏,三个页面共用一条助手流
用户的原话是「copilot(包括问答)可以是侧边栏的效果」「显示他的简要思考节点,
使得用户知道他大致在想什么,以及用了哪些工具」。
画布右栏从「运行 / 属性 → 时间线 / 成果 / 事件」(两层嵌套 5 个视图)变成
「助手 / 属性」一层。助手栏里是一条流:你让它做什么、它做了什么、结果是什么。
Copilot 建图和跑图在协议上完全无关(SSE 操作流 vs 事件 WebSocket,不共享
seq 和 node_id),但对用户是一件事的两半,decode.ts 把两种流译成同一种 Step
之后才能排进同一条时间轴。
Copilot 不再是弹窗。弹窗的问题不是样式:每改一次图都要重开一次,关掉之后
需求文本就没了,想微调只能重打;而画布左下角那条浮条(z-30)和 Toast
(z-100)、Modal(z-50)三层各自为政,本身也是重复的一份 Copilot 状态渲染。
两个都删了,输入框就在栏里,说完一句接着说下一句。右栏没删——它是发起运行
和处理审批的唯一入口,RunLauncher 原样保留,只是跑起来会自动收起给执行过程
让位。tab 用 hidden 切换而不是卸载,不然点一下「属性」看个节点再切回来,
输入框草稿和展开状态全丢。
问数据页和运行页换成同一个 AssistantStream + 同一个 decode.ts。RunPanel 里
那套平行的 EVENT_META + describe() 删了:留着两套翻译,同一次运行在不同页面
会读出不同的故事,而用户没法判断哪个是真的。同理合并了两个耗时格式化函数
(画布节点显示"0ms",右边同一步什么都不显示,两块还并排放着)。运行页列表
加了内容摘要——原本每行只有"临时图 · 1.2s · 340 tok",十条长得一模一样,
要分清哪条是哪条只能挨个点开。
跑真实运行时发现两个缺陷:
1. 后端丢事件(app/core/bus.py)。WebSocket 先读历史、再订阅,两步之间图
正好跑出来的事件既不在历史里也不在实时流里,永远丢了。而 subscribe() 是
异步生成器,真正注册还要等到第一次迭代,窗口比看上去更大。实测一次跑图
开头的 node.finished/node.started 消失,界面上表现为某个节点永远在转圈、
它的子步骤跑到了顶层。改成先 attach 占位、再读历史,重叠部分按 seq 去重。
tests/test_bus_attach.py 里有一条刻画旧行为的测试,说明这个窗口确实存在。
2. 循环里的多轮审批被算错。我在 S1 按内容去重,但三轮审批的 title 一模一样,
和 LangGraph 的重放无法区分——结果第 2、3 次真实审批被当成重放吞掉,而
三次"你驳回了/你放行了"各自成行。改成开/闭配对:开着就是同一次,闭了才是
新一次;决定折进那条审批行而不是另起一行。真实运行(驳回 → 改写 → 再驳回
→ 改写 → 放行)已收进 fixtures。
验证:三层各守一件事,共 76 条断言全过。check-decode 守翻译(含新的多轮审批
和截断结果集),check-stream 守渲染,新增 check-ui 守"接起来还对不对"——真
浏览器真后端,包括「切走再切回来草稿还在」「唯一的运行入口还在」「原始事件
切得到也切得回」。另外真跑了一次带三轮审批的图和两次 Copilot 生成(60s,
deepseek-v4-pro),截图逐帧看过。后端 78 个测试全过。
|
2026-09-17 18:29:16 +08:00 |
|
ThearchyHelios
|
a16cdf3ab1
|
修两条 critical:文件工具可读任意路径、并发代码节点互相覆盖
## 文件工具可读任意文件(含加密主密钥)
sandbox_session 从请求体直接拼进工作区路径,没有任何净化。传 ".." 就能把
根目录挪出工作区,之后 _resolve 的越界检查是在这个被挪走的根下做的,等于失效:
curl -X POST /api/tools/file_read/run -d '{"sandbox_session":"..","args":{"path":".secret_key"}}'
→ {"ok":true,"content":"8UygDbx9..."} # 加密所有 provider Key 的 Fernet 主密钥
根因是同一段净化逻辑被抄了五遍:四个沙箱后端各一份,文件工具那份漏抄了。
所以提到 config.sanitize_session 统一一份,五处共用;RunToolIn.sandbox_session
再加 pattern 做纵深防御——畸形 session 直接 422,而不是悄悄净化成 default。
## 并发代码节点互相覆盖入口脚本
同一 run 的节点共用一个沙箱会话,而入口脚本写死 main.py。README 主打的
"连线即并行"会让并排的代码节点在同一 superstep 并发执行,都写 main.py 就
互相覆盖——后写的赢,每个节点却都拿到 ok=True 和"自己的"返回值。
8 路并发同 session:7/8 执行了别人的代码,error 全为 None
改成每次执行生成唯一入口脚本(__entry_<uuid>.py),跑完即删。共享工作区语义
保留,跨节点传文件照常。四个后端同构改造,实测 8/16 路并发均 0 串味。
## 顺带修掉两个自己埋的坑
- conda 迁移把沙箱解释器隔离弄丢了:conda 环境 sys.prefix == sys.base_prefix,
于是"干净解释器"挑出来的就是项目环境自己,沙箱代码能 import cryptography
直接解密落库的 Key。改成实际探测候选解释器是否看得见敏感包,都不干净就
退回 -S 关掉 site-packages。README 那句承诺现在重新成立了。
- -I 在 3.11+ 隐含 -P,脚本目录不进 sys.path,代码节点的 files 参数(多文件)
在 seatbelt/bubblewrap 下一直是坏的,import helper 直接 ModuleNotFoundError。
换成 -s -E,隔离意图不变,同目录 import 恢复。
- microvm 的 files_written 取 FsEntry.name(不存在),塞进去的是对象 repr。
改取 path 的尾段。
沙箱回归 16 项通过(两档各 8 项)。
|
2026-09-14 15:22:58 +08:00 |
|
ThearchyHelios
|
7d00c24ab1
|
microVM 沙箱后端:内存限额终于真限得住,但网络关不干净
Seatbelt 在 macOS 上最难受的缺口是 RLIMIT_AS 不被强制——设 256MB 仍能分配
900MB。microVM 不是"更严格的访问控制",是换了一类边界:宿主 Darwin 27.0.0,
VM 里 Linux 6.12.99(libkrunfw),PID 1 是 init.krun,/Users 根本不存在,
free 只看得见 512MB。申请 900MB 会被 OOM killer 杀掉而 VM 照常存活。
实现按实测契约写,不按别的 SDK 的惯例推断:create() 直接返回已启动的
Sandbox,没有 handle/connect 两层;cwd/env/timeout 都是 exec 的原生参数;
超时抛 ExecTimeoutError 而不是返回非零码。
必须写明的缺口:network=false 时 HTTP/HTTPS 与域名解析都断得掉,但 UDP/53
拦不住,手写 DNS 包仍能拿到真实响应。default_egress=DENY、Rule.deny_dns()、
显式 deny UDP、max_connections=0 全试过都堵不上——deny_dns 生成的规则只针对
宿主,DNS 出口在 microsandbox 网络栈里是硬编码放行的。所以 health 里 network
标成 partial 而不是 true,设置页对应显示黄色「部分生效」:拦了一半要是显示成
全绿,比显示成全红更危险。
成本也照实说:首次拉 OCI 镜像实测 54.5s,之后热启动 0.19s、执行 7~30ms。
贵的只是第一次,所以 auto 在镜像没缓存时先用 Seatbelt,不让人干等一分钟;
显式选 strict 档不受此限——那是使用者自己要的硬隔离。
端到端 17 项验证通过:隔离、内存拦截、超时 124、退出码透传、多文件与 env、
会话间互不可见、VM 回收。Seatbelt 那档回归未受影响。
|
2026-09-14 14:10:10 +08:00 |
|
ThearchyHelios
|
dcba9a63e0
|
一次思考在轨迹里只落一条事件
思考流上线时漏了把它登记进 ephemeral 集合,导致每个流式增量都
落库——一条思考链在执行轨迹里被拆成几十条 llm.thinking 事件。
拆成两种事件:llm.thinking.delta 是流式增量,只推给画布做实时
滚动、不落库(与 llm.token 同类);llm.thinking 是调用收尾时落的
单条汇总,携带完整思考内容(截断 6000 字符,完整版在节点输出
工件里)。顺带修好刷新回放:此前回放重现不了思考(增量被隐藏、
汇总不存在),现在一条汇总事件直接恢复全文,时间线上也可展开查看。
验证:确定性假思考模型过完整管道(3 条增量实时推、汇总恰一条且
内容完整);真模型跑模板⑤,轨迹 29 条降到 18 条,llm.thinking
恰好 1 条。adaptive thinking 对简单任务自主跳过思考时两类事件
都不产生,已从工件核实是模型行为而非管道丢失。
|
2026-09-14 10:40:20 +08:00 |
|
ThearchyHelios
|
2df591d05f
|
工件库:内容寻址存储、节点输出与工具快照落盘、运行清单哈希
事件流里的输出是给画布看的截断预览,出具溯源需要完整证据。
新增 data/artifacts/ 内容寻址工件库(sha256 即地址,取回时复验哈希,
篡改即报错);节点完整输出与工具调用快照(query+结果集)自动落盘,
事件只携带工件 id。运行终态对全部事件计算清单哈希存入 Run,
事后对事件流的增删改会与它对不上。
graph_hash 剔除 viewport 后计算——挪画布不改变图的身份。
Run/Workflow/WorkflowVersion/Approval 预置分级与治理列,
init_db 附带 SQLite 轻量列迁移。
|
2026-09-14 10:10:20 +08:00 |
|
ThearchyHelios
|
dd57c12016
|
沙箱改用系统自带的隔离原语,移除 Docker 依赖
跑一段不可信代码不需要一整套容器运行时。macOS 用 Seatbelt
(sandbox-exec,系统自带),Linux 用 bubblewrap;冷启动从 ~250ms
降到 ~25ms,且不再需要装任何东西。
守住三件事:默认断网、家目录不可读、只有工作区可写。策略由内核
强制且对子进程继承——沙箱内 subprocess 出来的 cat 读家目录同样是
Operation not permitted。解释器改用 sys.base_prefix 下那个干净的
Python,沙箱代码因此 import 不到后端的 FastAPI、SQLAlchemy 和凭据
处理代码。
如实标注能力边界,不给虚假的安全感:
- macOS 不强制 RLIMIT_AS,内存限不住(实测设 256MB 仍可分配 900MB),
health 接口申报 enforced 字段,界面把限不住的项划掉
- Seatbelt 是访问控制而非虚拟化:进程表共享,以当前用户身份运行
- bubblewrap 侧多隔离 PID/IPC/挂载视图,内存限额真实生效
顺带修两个 bug:
- LocalSandbox 设 RLIMIT_NPROC=64 会误伤——macOS 上它按 uid 全局
计数,用户已有上千进程时沙箱内任何 fork 都失败,连 bash 都起不来
- 模型所属 provider 被停用时,原先静默换一家继续用原模型名,必然
404 且报错指不到原因;改为直接说清是哪个 provider 被停用了
写 SBPL 的坑记在注释里:(deny default) 会让 Python 直接 SIGABRT,
改用「默认允许 + 精确拒绝」。
7 个模板全部回归通过。
|
2026-09-13 18:04:36 +08:00 |
|
ThearchyHelios
|
964eeaa829
|
AgentLab:可视化 Agent 编排实验台
在画布上拖拽编排 agent 工作流,并实时看着它执行。
后端 FastAPI + LangGraph:把画布 JSON 编译成 StateGraph,15 种节点
(模型/Agent/多Agent、工具/沙箱、分支/循环/子图、记忆/检索/整形、
人工介入/结构校验)。选 LangGraph 是因为可视化编排真正难的是暂停、
恢复和回放,而 checkpointer + interrupt() 正好把这几件事做在了底层。
前端 React + React Flow:节点元数据驱动属性面板与连接桩,运行时
节点高亮、边流动画、卡片内流式 token 全部由同一份事件流驱动。
关键实现:
- Agent 内工具审批用 @task 缓存模型调用,interrupt 重放时不重复计费
- 沙箱保持只读根文件系统,代码经 exec+base64 写入(put_archive 会被
只读容器拒绝),断网、非 root、内存/CPU/超时限额
- 检索为向量与 BM25 混合,本地哈希 embedding 零配置可用
- 分支条件走 AST 白名单求值器;HTTP 工具逐 IP 拦截内网与元数据地址
- Mock provider 让无 API Key 也能跑通完整链路
7 个内置模板全部冒烟通过,含真实模型端到端验证。
|
2026-09-13 17:27:00 +08:00 |
|