TOUCHSTONE试金石

AI agent 驱动的自动化测试平台 · Free-style testing

把测试交给 agent,
跑在真实代码上。

Touchstone 是一个 AI agent 驱动的自动化测试平台。 你建好项目、挂上被测代码目录与工作目录,它就以多轮会话驱动 agent 在案例库里生成并执行用例、把失败聚类成 bug 报告;这一切都在一张开发看板上调度——过程可观测、可插话、可中断。

  • 6类任务
  • 7个阶段
  • 5列看板
  • 1轮 = 1 次会话
  • 1个项目 1 条队
  • 0状态轮询
touchstone-dsh 串行:同时只跑一个任务 实时
待开发2
给导出接口补边界用例
#392
开始
把压测方案改成 run.py 驱动
#393
添加任务…Enter 落待开发
正在开发2
任务 #96 测试·运行中 回归
登录接口:补并发重复提交的用例
#391会话运行中

第 3 轮 · 12 次工具调用

等待区 · 按队列顺序
修掉重复登录的误报
#388排队中
阻塞1
并发提交同一会话
#390🤔 等待回答
agent 的提问

「并发提交同一会话时,期望是拒绝,还是幂等放行?」

幂等放行拒绝先只测单线程

已推到飞书 · 手机上点一下也能答

待审核2
重复登录仍放行
#386🌿 worktree

会话空闲 · 等你过目

导出案例库索引
#385已作答·待送达
已完成2
登录接口补并发用例
#379

复测通过 · 会话随之归档

清理构建缓存
#377

这一屏就是平台的开发看板:一张卡就是一次 dsh 会话,测试任务和看板卡片排在同一条队里——一个项目同时只跑一件事,轮到的卡才真正起会话。 五列状态 · 一条队列

看板The board

看板不是便签墙,是操作面

开发看板把「谁在跑、谁在等、谁卡住了、谁干完了」摆成五列。它和测试任务共用同一条队列:一个项目同时只跑一件事。

一张卡就是一次会话

卡在「正在开发」=会话在跑;卡落「阻塞」=agent 停下来问了句话;卡到「待审核」=会话空闲,活干完了等你过目。点开卡片就是完整会话窗口:思考、工具调用、返回都在,能插话、能压缩、能从某一轮 fork。

卡片和任务抢同一条队

测试任务、看板卡片、会话消息排在同一条队列里,开发列的上下顺序就是队序。没轮到的卡显示「排队中」,轮到才真正起会话;急就点「⚡ 强制」跳过队列,风险自担。

agent 会停下来问你

提问和审批都走同一条作答队列:会话挂起时卡落「阻塞」,答案送达才接着跑。飞书会推一张卡片到你手机上——回一句「作答 390 1」或「同意 390」就能答,等待期间运行位让出来,队首照常起跑。

待开发todo

还没有会话。点「开始」入队排队,或在「开始 ▾」里选「🌿 在新 worktree 中开始」——那张卡立刻跑,不进队列、不占运行位,改动落在独立工作树里,主仓库不受影响。

正在开发doing

上半是运行位区,下半是等待区,中间一条「等待区 · 按队列顺序」。会话在跑的卡可以「立即注入」插话;排队中的卡可以「停止」(取消排队、移入待审核)。

阻塞blocked

agent 提问或请求审批,会话挂在这里等你。作答、批准后自动续跑;一直等到你答为止,超时兜底只是每 60 秒对一次状态。

待审核review

会话空闲,改动和结论摆在这里等你判断。「通过」移入已完成,「打回」连同一句理由退回——打回不是撤销,是把卡放回开发侧接着改。

已完成done

搬进这一列的卡,对应的 dsh 会话一起归档;在 dsh 里归档主会话,这张卡也会自己搬进来。想接着干就「重开」,会话随之取消归档、卡回待审核。

  • 五列之间有门禁

    拖进「正在开发」即入队;父卡没干完会拦一下,确认「强制开始(自担风险)」才放行;依赖成环直接拒绝。项目可以设「串行:同时只跑一个任务」或「并行:最多 5 个并行」。

  • 卡离开开发列就出队

    没有单独的「释放占用」动作:卡被拖走、被停止、被删除,它在队列里的那一行当场终态化,运行位立刻让给下一个。

  • 删除只是移进回收站

    删掉的卡进回收站,能还原回原来的列和位置;只有回收站里的「彻底删除」不可恢复。清空回收站是一键的,也会一并删掉评论。

  • 描述里能贴图,也能从 Jira 建卡

    卡片描述里可以直接粘贴图片和文件(单个 10MB 上限,走鉴权读取);配好 Jira 凭据后,「导入我的待办」会把指派给你且未解决的 50 条按 key 去重建成卡。

  • 平台不替你提交代码

    卡干完就停手,提交由你或者「自动提交守卫」这个可选资产来做。看板上的每个会话也都能单独切权限档:逐条确认、自动通过、完全自主。

六类任务Six task types

六类任务,六种产物

每类任务留下的东西不一样:有的留下用例,有的只留下一份报告或一次发压记录。左列是任务类型,右列是它落在磁盘上的产物。

探索Explore

从一个还不认识的代码库开始:先摸清模块、给出 P0/P1/P2 功能分级,再按分级生成去重用例、逐条执行,把失败按根因聚类成 bug 报告。测什么由 agent 自主决定,覆盖状态逐轮回写。

留下 用例目录分级 INDEX.md · 轮次 ROUNDS.md失败聚类后的 bug 报告
回归Regression

不生成新用例,重跑存量用例。可以只跑某个提交日期范围改动涉及的部分,也可以整库重跑;每条用例的结论写回它自己的状态文件。

留下 执行记录(每次请求与返回)状态回写 status.md
压测Stress

先用一轮 agent 出方案包——目标与范围、被测接口、加压模型、指标定义、图表设计、复现命令;然后由平台照着方案发压,跑几次就有几份报告,每次都按运行键切分日志与指标。

留下 方案包 plan.md / run.py / charts.json每次发压一份 指标 jsonl + 报告
修复Fix

从报告分析开始:定位根因、给出方案,必要时按项目约定重新部署、再复测。改动未经构建部署时不会假装验证过,状态如实写「已修复(未验证)」。

留下 修复记录(文件:行号 + 摘要)复测通过的 结论
复测Retest

挑一份已有的 bug 报告,范围三选一:只复测、先重新部署再复测、只部署。关联用例全部固化了 verify.py 时走平台脚本复测,秒级出结论。

留下 复测结论(写回报告)全部通过时自动标 已修复
修例Reject

报告被你判为不成立时,按拒绝理由回头改关联用例:断言错了就改,因误报而不成立的就删;教训写进 PITFALLS.md,后续生成用例前必读,同类误报不再犯。

留下 改过的用例沉淀 PITFALLS.md 误报教训录
一轮One round

一轮 = 一次会话

任务不是「跑一个脚本」,而是把一轮工作交给一个常驻会话。会话活在 dsh 宿主进程里,一轮就是一次 followup,等它把这一轮做完。

建任务选类型、起点与终点
统一队列一个项目一条队
dshdriver一轮翻译成一次调用
常驻会话agent 在宿主里干活
结果落库turn/end 到达即收口
↻ 下一轮从同一个会话继续,上下文续接,不重启会话、不重读整库。

一个项目,一次只跑一件事

同一项目的任务、看板卡片、会话消息排在同一条队里,谁在跑谁占运行位。不同项目并行,上限 6。会话挂起等你作答时会主动让出运行位,队首照常起跑。

会话常驻,不轮询

没有本地 agent 子进程,也没有状态轮询:一轮 = 一次 followup + 等一次 SSE 的 turn/end。会话在 dsh 宿主内常驻,sid 在轮次开始就精确入库。

跑着也能插话

「发送」把消息排到队尾等下一轮,「立即注入」用 steer 插进当前轮最近的 step 边界。会话里能看到完整的思考、工具调用与结果。

七阶段Seven stages

任务的七个阶段

每个任务在轨道上选一个起点和一个终点。轨道上的先后是真实的先后——这是一条流水线,不是七张标签。

01测试用例生成gen_case
02测试execute
03生成报告report
04报告分析analyze
05问题修复fix
06重新部署deploy
07复测retest
探索 Explore
回归 Regression
修复 Fix
复测 Retest
压测 · 修例 不落阶段列:压测是「一轮 agent 出方案 + N 次平台发压」,修例是固定一轮的改用例

实心段是默认范围,淡段是可选的终点延伸。探索与回归的终点一旦越过「生成报告」,平台会自动追加一个后段任务,接着做分析、修复、部署、复测——不需要你再建一次任务。

遗留物What remains

一轮跑完,磁盘上多了什么

产物是普通文件,不是锁在数据库里的记录:可以读、可以 diff、可以进版本库,也可以随时删掉重建派生索引。

  • free_style/ # 案例库,跨轮次跨会话持续积累
    • INDEX.md # 功能分级 · id 游标 · 统计
    • ROUNDS.md # 每轮的角度与结果
    • PITFALLS.md # 误报教训录,生成用例前必读
    • 登录/会话/
      • FS0007_重复登录/
        • case.md # 用例定义(唯一权威)
        • status.md # 通过 / 失败 / 跳过 / 需要复测
        • execution_20261007_0930.md # 本次的请求与返回
  • bug_report/
    • 20261007_0930_FS_重复登录仍放行/
      • bug_report.md # 现象 · 证据 · 根因 · 关联用例
  • .live/live.json # 监控页数据源(阶段 · 进度 · 分级)
  • .web/ # 任务日志 · 压测指标 · 看板会话日志
  • board_media/ # 看板卡片的图片与附件
  • 判重靠案例库,不靠记忆

    新用例入库前先在案例库里查重,id 由根 INDEX.md 的游标统一分配——只有主 agent 能取号,不会两条并线同时用掉一个号。

  • 派生索引随时可重建

    .live/cases.jsonl、similar.jsonl、rag_index.jsonl 都是从案例库派生出来的,删掉不影响任何原始数据。

  • 监控与通知接着产物走

    监控面板读 live.json 展示阶段与进度;看板卡片挂着会话;飞书在任务阻塞、需要作答题时推一张卡片过来,你在手机上点选就能作答。

  • 案例库还能被语义检索

    配置嵌入模型后,复测任务会按改动文件先做一次语义粗筛,把最相关的用例排在前面;没配置就退回关键字匹配,永远不会因此卡住任务。

边界Limits

明确不做的,和只做到一半的

一个测试平台的价值有一半在它敢承认的边界上。下面这些是我们明确不做、或者明确只做到某一程度的。

平台不替你提交代码

修复改完文件就停手。需要自动提交就自己装「自动提交守卫」这个内置资产:默认只拦一下并提示 agent 去提交,只有把 commitOnApproval 打开它才自己提交。

智能体族只有 dsh 插件一族

kimi / claude / opencode / hermes 与裸 dsh CLI 已退场删除。旧绑定在起跑处直接报错并提示改绑,不静默降级成另一种跑法。

两种形态同库互斥

独立站点与 dsh 插件形态默认共用 ~/.touchstone/touchstone.db,靠文件锁保证同一时刻只跑一个:第二个实例启动即退出,并打印占用者是谁。

rewind 与「压缩并新建」是近似

都是从某一轮 fork 出一支新会话,旧会话仍在、可以切回。它们是「从这里另起一支」,不是原地撤销。

没有「模型请求失败」的错误条目

dsh 不提供这条事件通道,所以会话窗口不会显示错误横幅。哪一步没跑通,看的是会话里实际发生的工具调用与结果。

Windows 还没实机验证过 dsh

平台本体在 Windows 与 Linux 功能一致(启停器、端口顺延、路径处理都做了),但 dsh 在 Windows 上的父死感知只剩 stdin 管道这一条通道,尚未实机核验。

上手Quick start

跑起来

不想先装环境?在线 Demo 不用安装、不用准备数据:建项目、建任务、看排队、答 agent 的提问、看用例与 bug 报告长出来,全部在浏览器里跑一遍(静态模拟,不连后端)。

要跑真的,需要 Python 3 与 Node,两种形态二选一。

独立站点

自带前端,默认 127.0.0.1:4601

./touchstone.sh build   # 构建前端
./touchstone.sh start   # 后台启动,日志在 .run/
./touchstone.sh status
# Windows: python touchstone.py start

没有 webui/dist 时 server.py 会直接退出,所以先 build 再 start。首次启动会种下一个 admin 账号:随机初始口令只在启动横幅打印一次,首次登录强制改密。数据库在 ~/.touchstone/touchstone.db,必须放本地盘——SQLite 的文件锁在网盘同步目录里不可用。

dsh 插件形态

内嵌 dsh web,与独立站点二选一

./dsh-plugin/install.sh
# 幂等装到 ~/.dsh/profiles/web
# 重启 dsh web 生效,之后在 dsh 里开面板

这一形态把平台面板嵌进 dsh 的半侧栏:agent 会话就活在宿主进程内,看板卡片、会话窗口、实时监控与独立站点完全一致。两种形态同一时刻只跑一个。