dots-agent
一个会主动发现任务、主动积累技能的智能体。
包名 dots-agent,命令是 dots(装完就有)。两种用法:命令行,或者 Windows 桌面窗口。
2.0 起它不止会接活,还能把一条需求从头送到尾:
- 全流程流水线:需求分析 → 原型设计 → 开发 → 测试 → 修复 → 归档,阶段推进看产物不看嘴。 见 全流程流水线。
- 本体库:从公司库(PG / MySQL / Mongo)采元数据,存成一份可以手改的 JSON, 再派生文档和代码;改完一条命令,全流程的文档和代码一起变。见 本体库。
- MCP:飞书、Jira、Sigma、GitLab 的工具直接挂到手上,走同一套审批闸门。见 MCP。
- 微信群 BUG 监控:用本机微信 UI 自动化(pyweixin)扫群,或让桥写 spool; 报错自动去重、进台账、开修复流水线。见 微信群 BUG 监控。
安装
命令行
pip install dots-agent国内镜像(清华、阿里等)同步新包有延迟,刚发布的几天可能报
No matching distribution found。加官方源即可:pip install dots-agent -i https://pypi.org/simple
或者不装,直接在仓库里跑(两种方式命令都通用):
git clone https://github.com/duyongan/dots-agent
cd dots-agent
python -m dots serve桌面版(Windows)
怎么打开
日常使用双击 dots.exe(或者桌面/开始菜单里的 dots 快捷方式)。装好之后它在:
%LOCALAPPDATA%\Programs\dots\dots.exedots-<版本>-setup.exe 只是安装器,装一次就行,不是日常打开方式。
安装
安装包由打包命令生成到 release/(该目录不进仓库):
release/dots-<版本>-setup.exe双击安装。安装器是按用户安装,不需要管理员权限,默认装到
%LOCALAPPDATA%\Programs\dots。安装过程中可以改目录;会创建桌面快捷方式和开始菜单项。
卸载走 Windows 的「设置 → 应用 → 已安装的应用 → dots」。
安装包自带一份嵌入式 Python 和全部依赖(resources/runtime/),机器上没装过 Python 也能跑。
不想安装也可以直接运行 release/win-unpacked/dots.exe。它旁边必须保留 resources/
目录(运行时都在里面),不能只把这个 exe 单独拷走。
自己打包:
cd desktop
python make-runtime.py # 首次,或 dots/ 源码改动后(Windows 没 Bash 也能跑)
npm run dist # 生成 release/dots-<版本>-setup.exe有 Git Bash 的机器也可以用等价的
bash make-runtime.sh; 两个脚本做的事一样(下载嵌入式 Python + pip --target + 拷贝 dots)。
在仓库里开发
cd desktop
npm start开发模式不打包,直接用系统上的 Python;工作目录是仓库根,所以会读仓库根或 ~/.dots/
下的 dot.toml。
快速开始
dots init --global # 配一次,以后 cd 到哪都能敲 dots
dots doctor # 体检一下
dots goal "把 inbox 里的东西整理成一份报告"
dots serve --web # 常驻跑起来,同时开活动视图--global 把配置写到 ~/.dots/,数据和 workspace/ 也跟着放那儿 ——
一份到处用,换个目录不用再 init。不带参数就是 dots init,只写当前目录,
那样每个目录都得配一次。
也可以不带子命令,直接进交互终端。里面直接打字就是和它说话,
/ 开头才是命令:
$ dots
dots> 工作区里现在有什么?帮我看看该先处理哪件事
(它自己去翻文件,然后回你)
dots> /goal "把 inbox 整理成一份周报" ← 交给后台慢慢跑
dots> /ls
dots> /exit
| 输入 | 干什么 |
|---|---|
| 直接打字 | 和它对话。记得上下文,能接着追问 |
/命令 | 命令行那些命令,前面加个 /(见下表) |
/help | 看全部命令 |
/exit | 退出(Ctrl-D、Ctrl-C 也行) |
对话走的是同一个大脑、同一个工作区,但它不是目标:不进 dot ls,
后台 tick 也不碰它,所以不会算进你的目标队列。要让它后台跑,用 /goal。
它想在对话里做 ask 类动作(比如发邮件)时会当场问你 y/n,不走邮件审批。
每条命令都会重新读一遍配置,所以改完 dot.toml 不用退出来重进。
打开 http://127.0.0.1:8765/ 看它在干什么。
想先不配模型试一遍:
dots selftest # 不联网、不调模型桌面界面
一个窗口装下 dots 的全部日常操作:看它在干什么、给它派活、批准它想做的事、跟它说话。
不用开终端,不用背命令。它和命令行版共用同一份配置、同一个数据库、同一个工作区。
第一次启动前必须先有配置
这是最容易卡住的一步:
后端找不到
dot.toml会直接退出,界面顶栏变成「后端已停」,所有按钮变灰。 此时连「生成全局配置」也点不动 —— 那个按钮要求后端已经就绪。
所以第一次使用,先在终端里生成一份全局配置:
pip install -e . # 或按上面的方式安装 dots-agent
dots init --global # 写到 ~/.dots/dot.toml
dots doctor # 体检,确认模型、目录、数据库都没问题dots init 会自动探测环境变量里的模型 key。没探到就手填 ~/.dots/dot.toml 的 [model] 段。
打包版查找配置的顺序:
%APPDATA%\dots-desktop\dot.toml(也就是应用的当前工作目录)~/.dots\dot.toml
你的数据跟着配置文件走:全局配置在 ~/.dots/,数据库和工作区也都在那儿。
启动过程
双击图标后:
- Electron 先画窗口;
- 拉起
python -m dots bridge; - 后端导入 langchain 那一整串依赖,约 10 秒;
- 后端发出
ready,顶栏从「正在启动后端…」变成「值守中」。
这 10 秒里输入框和按钮是灰的。不是卡死。不要在这个时候狂点 —— 虽然界面会拦住, 但后端准备好之前发的命令会堆在管道里,顺序不可控。
如果后端起不来,顶栏显示「后端已停」,同时底部弹出输出面板,里面是 Python 的 stderr 尾部(常见的:「没找到配置」「ModuleNotFoundError」「API key 缺失」)。
界面总览
窗口从上到下五段:
┌────────────────────────────────────────────────────┐
│ 顶栏:dots ● 值守中 每 60s 一轮 [停止] [体检] │
├────────────────────────────────────────────────────┤
│ 待批横幅(只在有事等你时出现) │
├────────────────────────────────────────────────────┤
│ 目标 │ 技能 │ 活动 │
│ (三栏监控区) │
├────────────────────────────────────────────────────┤
│ 对话 │
│ 你/它 的消息…… │
│ [ 说点什么…(回车发送) ] │
├────────────────────────────────────────────────────┤
│ ▴ 全部命令(展开后是一整排中文按钮) │
└────────────────────────────────────────────────────┘窗口最小 720×520。窗口高度不足 640px 时,命令抽屉会自动收起且把手不可点, 保证上面的内容不被挤没。
顶栏:状态与值守开关
后端状态:
| 文字 | 含义 |
|---|---|
| 正在启动后端… | Python 子进程正在导入依赖,按钮全灰 |
| 值守中 | 后端就绪,可以操作 |
| 正在停止… | 正在优雅退出后端 |
| 后端已停 | 进程退出或起不来,详情看输出面板 |
顶栏中间显示:
每 60s 一轮:循环在跑。数字来自[dot] tick_seconds。循环已停:循环没跑,但后端还活着,你仍然可以手动「跑一轮」、聊天、点命令。
右边按钮:
- 停止:请求停下循环。如果它正跑着一个目标,不会立刻停 —— 要等这一轮结束。
按钮会一直是「停止」,直到后端推来
loop_stopped。急着关窗口也没事, 关闭时会先把没跑完的目标放回队列。 - 开始值守:重新启动循环。
循环默认在界面启动时自动开启。它的存在感就是「主动」:不用你喊,它每轮自己找活干。
顶栏最右边「体检」= dots doctor。逐项检查:配置文件、读配置、审批规则、依赖、模型、
工作区可写、数据库、事件源、审批通道、技能库、bot、成本上限。每项失败都会给一句
「下一步怎么做」。
待批横幅:它停下来等你
只有存在挂起审批时才出现,是整个界面唯一用琥珀色强调的地方。
⚠ a7f3 想发邮件给 alice:「周会改到周四」 (还有 2 个)
a7f3 [批准] [拒绝] b91c [批准] [拒绝] …
- 摘要会尽量说人话:
send_email显示收件人和主题,run_shell显示命令, 其它动作显示工具名。 - 每个审批有唯一编号(token)。点「批准」=
dots approve <token>, 「拒绝」=dots reject <token>。 - 决定写进
approvals表,下一轮 tick 自动续跑对应目标。邮件回复和这里点按钮 走的是同一条路径。 - 一直不批:超过
[approval] timeout_hours(默认 72 小时)自动按拒绝处理。 这比无限等待好 —— 一个永远等不到回复的审批就是一个悄悄卡死的目标。
哪些动作需要批,由 dot.toml 的 [approval.rules] 决定:
[approval.rules]
send_email = "ask" # 挂起,等人批
save_skill = "allow" # 直接执行
run_shell = "deny" # 永不执行没写到的动作一律 ask(默认从严)。
目标面板
目标 = 一件要它做的事。每行显示:
| 列 | 含义 |
|---|---|
| 编号 | 目标 id,也是交接/日志里的标识 |
| 状态 | 见下表 |
| 目标原文 | 你写的或它自己派生的任务 |
| 结论 | agent 完成时给的 summary |
| 证据 | 工作区路径、邮件 id、URL 等可核对的东西 |
| 数字 | 这个目标累计消耗的 token |
| 时间 | 最后更新的时间(12s / 3m / 2h / 2d) |
| 日志 | 只看这个目标的活动记录 |
七种状态:
| 界面文字 | 内部值 | 含义 |
|---|---|---|
| 待批准 | pending | agent 自己派生出来的目标,要你点头才跑 |
| 排队中 | ready | 等着被某一轮 tick 领走 |
| 在跑 | running | 正在执行 |
| 停下等你 | interrupted | 撞上审批闸门,等批准/拒绝 |
| 待复核 | pending_review | 它说完成了但没给证据,需要人看一眼 |
| 完成 | done | 完成且给了证据 |
| 失败 | failed | 执行失败 |
「待复核」值得单独说:一个没有证据的结论没法核对,所以不会直接算完成。
面板按钮:
| 按钮 | 等价命令 | 行为 |
|---|---|---|
| 新建目标 | 原生 goal | 弹输入框,写进 goals 表,状态直接 ready(你亲手写进去的,视为授权) |
| 跑一轮 | dots tick | 手动跑一轮就退,调试用。循环运行时按钮置灰;后端也会拦住并发的 tick |
| 全部目标 | dots ls | 输出完整列表(界面里的 60 条只是快照) |
| 活动日志 | dots log | 输出最近 30 条活动 |
「新建目标」走的是 bridge 的原生命令,不是 CLI:不会在输出面板里打印
「跑起来:python -m dots serve」这种在界面里很误导的提示。它固定交给默认 bot dot。
目标列表最多显示最近 60 条,多了会静默截断。
技能面板
技能是 agent 干活时自己总结出来的可复用流程,存在工作区里,每份是一个目录,
里面有 SKILL.md(YAML frontmatter + 正文)。
面板每行显示技能名和一句话描述。技能库是渐进披露的:只有「名字 + 描述」每轮会进 系统提示,正文等真用到时才读。所以描述被硬性限制在 60 字符,超出部分每轮都会被截断。
| 按钮 | 等价命令 | 行为 |
|---|---|---|
| 学习 | dots learn <材料或描述> | 弹输入框;创建一个 origin=cli:learn 的 ready 目标,让 agent 把材料总结成技能。它不会立刻执行,要等循环或手动跑一轮 |
| 技能库 | dots skills | 输出技能表(名称、描述长度、描述) |
| 整理技能 | dots curate | 创建整理目标,让 agent 合并重复、删除没价值的。会真的删改文件,会先弹确认;技能少于 2 份时命令直接说没什么好整理的 |
每行右侧还有两个小按钮:
- 看:
dots skill show <名>,在输出面板里显示SKILL.md全文。 - 删:
dots skill rm <名>,删除整个技能目录。会弹确认,确认按钮写「删掉」。
技能库为空时,面板会提示:它干活时会自己攒,「学习」也能喂材料。
活动面板
完整流水:收了事件、建了目标、被哪条规则拦下、什么时候跑完、循环启停、错误栈。
数据来源有两份,合并后按时间排序,最多显示最近 120 行:
- 数据库
audit表的快照(最近 60 条); - 本次运行期间后端实时推来的
log/fatal/loop/ 协议错误(最多缓存 300 条)。
每行是 时间 | 类型 | 详情。出问题时先看这里。
| 按钮 | 等价命令 | 说明 |
|---|---|---|
| 用量 | dots cost | 今日和累计 token 用量 |
| 自检 | dots selftest | 不联网、不调模型,跑四个模块的 demo() + 全套测试,约 30 秒 |
对话区
直接打字就是跟它说话,回车发送。它用的是同一个大脑、同一个工作区:让它读文件、 查资料、写东西,它会真的动手,不是只描述打算怎么做。
特点:
- 不是流式的:发送后显示「正在想…」,慢模型下可能要等十几秒才出整段回答。
- 有上下文:对话用固定的
thread_id = "chat",checkpoint 存在dot.db里, 关掉应用再打开,之前的对话还在。 - 和后台目标是两条线:聊天不占目标队列,后台 tick 也不碰它。要让一件事在后台慢慢跑, 用「新建目标」。
- 界面里最多保留最近 300 条消息用于显示,更早的仍在 checkpoint 里。
聊天时它如果想做 ask 类动作,会当场弹窗,不走待批队列:
它想干这个
send_email
{ "to": "alice@example.com", "subject": "周会改到周四", ... }
[拒绝] [批准]
- 你在弹窗里做的决定只影响这一次对话。
- 拒绝之后它会记住:同一件事再做,它会引用规则告诉你「被拒了不要重试」。
- 最多等 10 分钟(600 秒)。超时按拒绝处理;如果你在超时后才点「批准」, 界面会明确告诉你「这个审批已经过期了,agent 早按拒绝处理了」,不会假装成功。
- 弹窗期间聊天线程阻塞等待,但读 stdin 的是另一条线程,所以界面不会卡死。
对话功能是懒启动的:第一次发消息才建图,要几秒钟。不聊天就不付这个成本。
命令抽屉:全部按钮
底部「▴ 全部命令」展开后,是 dots 的全部日常子命令。按钮上写中文,不写原始命令名;
鼠标悬停会说明它做什么,并带出对应的 dots xxx。
展开状态记在本地(localStorage),下次打开还是那样。
| 分组 | 按钮 | 实际命令 | 需要参数 | 二次确认 |
|---|---|---|---|---|
| 配置 | 生成配置 | dots init | — | — |
| 配置 | 生成全局配置 | dots init --global | — | ✓ |
| 配置 | 体检 | dots doctor | — | — |
| 配置 | 自检 | dots selftest | — | — |
| 目标 | 新建目标 | 原生 goal | 目标文本 | — |
| 目标 | 跑一轮 | dots tick | — | — |
| 目标 | 全部目标 | dots ls | — | — |
| 目标 | 活动日志 | dots log | — | — |
| 审批 | 批准 | dots approve <编号> | 编号(只用行内/横幅按钮) | — |
| 审批 | 拒绝 | dots reject <编号> | 编号(只用行内/横幅按钮) | — |
| 技能 | 学习 | dots learn <内容> | 材料或一句描述 | — |
| 技能 | 技能库 | dots skills | — | — |
| 技能 | 看技能 | dots skill show <名> | 技能名 | — |
| 技能 | 删技能 | dots skill rm <名> | 技能名 | ✓(按钮写「删掉」) |
| 技能 | 整理技能 | dots curate | — | ✓ |
| 成本 | 用量 | dots cost | — | — |
serve 和 bridge 不在抽屉里:常驻循环由顶栏按钮管;再起一个 worker 会抢同一个数据库。
几个按钮的细节:
- 生成配置:在当前工作目录写一份
dot.toml。打包版的当前目录是%APPDATA%\dots-desktop,开发版是仓库根。文件已存在时不会覆盖。 - 生成全局配置:写到
~/.dots/dot.toml,之后在任何目录都能用。 - 跑一轮:只跑一轮就退。循环正在跑时按钮置灰;即使绕过界面,后端也会因为抢不到 tick 锁而拒绝,避免两个 worker 同时写同一个 checkpoint 和事件表。
- 学习:创建的是 ready 目标,真正执行要等后台循环或手动 tick。
[skills] enabled = false时命令会拒绝。 - 整理技能:真的会删文件和改文件;技能少于 2 份时不会创建目标。
- 批准 / 拒绝:只出现在待批横幅和行内,不出现在抽屉里(抽屉里没有编号可填)。
命令跑在独立的命令线程上,串行排队。一条在跑时点下一条,它会在后面等。 输出以流式事件推到输出面板,面板最终以应答里的完整输出为准。 界面给每条命令 180 秒超时;超时只是界面不再等,后端那条命令可能仍在跑完。
输出面板
命令输出从底部升起,覆盖在界面下方:
- 标题是按钮的中文名;
- 内容是流式追加;
- 跑完显示「退出码 0」或红色「退出码 N」;
- 复制:把全部输出放进剪贴板;
- 关闭:收起面板,不影响命令本身。
「新建目标」不产生输出面板内容(成功时静默,失败时弹面板说明原因)。
界面没覆盖的功能
界面只放了日常操作。下面这些仍然要用命令行(或 dots 交互终端):
| 功能 | 命令 | 为什么不在界面里 |
|---|---|---|
| 常驻循环 + 只读网页活动视图 | dots serve --web [--web-port 8765] | 循环已由顶栏管;web 是另一条监听 |
| 干跑(不真发邮件,审批打终端) | dots serve --dry-run / dots tick --dry-run | 调试用途 |
| 手动跑一轮(带 dry-run) | dots tick --dry-run | 同上 |
| 指定 bot 接活 | dots goal --bot ops "..." | 界面固定用 dot |
| 按状态筛目标 | dots ls --status running | 界面显示全部 |
| 控制日志条数 | dots log -n 100 [--goal <id>] | 界面固定 30 / 按目标查 |
| 交互终端 | 直接敲 dots | 需要真人在终端前 |
| 指定配置文件 | dots -c path/to/dot.toml ... | 界面固定用自动发现的那份 |
交互终端里,/ 开头是命令(如 /ls、/goal "..."),裸文本是和 agent 说话,
和桌面版「对话区」是同一个能力。
网页界面(可选)
不想用桌面窗口,也可以走浏览器:
dots gui它在本机 127.0.0.1 起一个页面并自动打开,四个页签:聊天、活动(目标/审批/流水)、
配置(模型预设 + API Key、事件源、SMTP、审批规则、预算)、技能(增删改查)。
写操作要带页面内生成的随机 token,只监听本机。
源码/pip 模式默认用 ~/.dots/。1.1.x 的 Windows 安装包里也带这个界面
(开始菜单「dots(网页界面)」,无控制台启动器 dots-gui.exe);1.2.0 起的
桌面版是 Electron 窗口,内置的不是这套网页。
命令
| 命令 | 干什么 |
|---|---|
dots | 不带子命令 = 进交互终端。直接打字是对话,命令前加 / |
dots init | 生成 dot.toml,扫环境变量自动配好 [model]。--global 写到 ~/.dots/ |
dots doctor | 体检:配置、密钥、模型、目录、数据库、事件源逐项查 |
dots goal "..." | 下一个目标。--bot <名> 指定交给谁做 |
dots serve | 常驻跑。--web 开活动视图,--once 只跑一轮,--dry-run 不真发邮件 |
dots tick | 手动跑一轮就退(调试用) |
dots ls | 列目标。--status <状态> 过滤 |
dots log | 活动流水。-n 50 条数,--goal <id> 只看一个 |
dots approve <token> | 批准一个挂起的动作 |
dots reject <token> | 拒绝一个挂起的动作 |
dots cost | token 用量 |
dots learn "..." | 把一段材料总结成技能 |
dots skills | 列出技能库 |
dots skill show <名> | 看技能正文 |
dots skill rm <名> | 删掉技能 |
dots curate | 让 agent 整理技能库:合并重复、删掉没价值的 |
dots mcp list | 看接了哪些外部系统、缺哪个环境变量 |
dots mcp add jira|feishu|gitlab|sigma | 把一家现成的 MCP 配置写进 dot.toml |
dots ontology init | 建一份空的 ontology/ontology.json |
dots ontology sync [数据源] | 从公司库采元数据,合并进本体库,再派生文档和代码 |
dots ontology show [实体] | 看实体列表,或某个实体的完整定义 |
dots ontology edit | 用编辑器手改本体库,存盘后自动校验并重新派生 |
dots ontology apply | 按当前本体库重新生成派生文档和代码 |
dots ontology status | 派生层是不是落后于本体库 |
dots sdlc new "..." | 开一条流水线。--track bugfix 走缺陷轨迹,--jira 绑单号 |
dots sdlc ls / dots sdlc show <id> | 列流水线 / 看某条的阶段、产物、历史 |
dots sdlc sync | 把本体库变更同步进所有流水线文档 |
dots sdlc advance <id> | 人工跳过当前阶段(卡住时用,会留痕) |
dots bugs | 缺陷台账(微信群报的 BUG 都在这儿) |
dots bugs fix <id>... | 给一条或多条缺陷开修复流水线 |
dots bugs close <id> | 关掉一条缺陷 |
dots wechat ingest "..." | 手动灌一条群消息(补录 / 联调) |
dots wechat collect | 用本机微信 UI 自动化采一次群消息 |
dots wechat check | 检查 pyweixin / 微信登录 / UI 可见性 |
dots wechat hook --port 8790 | 起本机 webhook,接收桥推来的消息 |
dots wechat status | spool 在哪、读到哪了 |
dots selftest | 不联网不调模型跑一遍自检 |
找配置的顺序:-c 指定的路径 → 当前目录的 ./dot.toml → ~/.dots/dot.toml。
到处能用靠的是最后一个,dots init --global 才会写它。
-c 指了路径就不会再往下兜底 —— 写错名字直接报错,不会偷偷用上全局那份。
配置
dots init 生成一份 dot.toml,所有相对路径都相对这个文件所在目录。
密钥只写环境变量名,不写进文件。
[dot]
[dot]
tick_seconds = 60 # 心跳间隔。这就是「主动」的节拍
workspace = "workspace" # agent 的工作目录 = 沙箱
db = "dot.db"[model]
dots init 会自动探测,一般不用手改。支持的 key:
DeepSeek / Kimi / 通义 / 智谱 / 豆包 / 硅基流动 / Claude / OpenAI / Gemini。
第三方端点要三件套:
[model]
name = "openai:deepseek-chat"
base_url = "https://api.deepseek.com"
api_key_env = "DEEPSEEK_API_KEY"
max_tokens = 4096
temperature = 0.2api_key_env 填的是环境变量名,不是密钥本身。打包版从开始菜单启动时继承的是
系统环境变量,不是你在某个终端里 export 的那个。
[smtp]
审批邮件和汇报从这里发出去。不配也能跑,审批退化成 dots approve。
[smtp]
host = "smtp.example.com"
port = 587
user = "me@example.com"
sender = "me@example.com"
to = "me@example.com" # 审批邮件发给谁(通常就是你自己)
password_env = "DOT_SMTP_PASSWORD"
use_tls = true[sources.*] 事件源
决定「没人说话的时候,什么唤醒它」。至少开一个。
[sources.fs]
enabled = true
paths = ["inbox"] # workspace 下的相对目录
interval = 120[sources.imap]
enabled = true
host = "imap.example.com"
port = 993
user = "me@example.com"
folder = "INBOX"
password_env = "DOT_IMAP_PASSWORD"
interval = 300
verify_tls = true # 自建 Dovecot 用自签证书就设 false[sources.ical]
enabled = true
url = "https://example.com/cal.ics"
interval = 900[approval] 审批
[approval]
subject_prefix = "[dot#"
timeout_hours = 72 # 超时没人批就按拒绝处理。0 = 永远等
[approval.rules]
send_email = "ask" # ask = 挂起等你批
run_shell = "deny" # deny = 永不执行
save_skill = "allow" # allow = 直接放行
# fs_* = "allow" # 支持通配三个值:allow 直接执行、ask 挂起等你、deny 永不执行。
没写到的动作一律 ask。 匹配优先级:精确名 > 通配 > 默认 ask。
[skills] 技能
[skills]
enabled = true
dir = "skills" # workspace 下的相对目录关掉后技能面板为空,「学习」「整理」会被拒绝。
[bots.*] 多 bot
[bots.ops]
prompt = "你是执行侧..."
tools = ["send_email"] # 只给这些工具;留空 = 给全部dot 这个 bot 永远存在,不用配。交接后新 bot 用同一条对话线(同一个 thread_id),
能看到前任做过什么。
[handoff] 交接
[handoff]
max_hops = 3 # 一个目标最多被交接几次,防乒乓[safety] 成本
[safety]
daily_token_budget = 0 # 0 = 不限。填了数字,超了当天就不再跑新目标[sources.wechat] 微信群监控
[sources.wechat]
enabled = false
backend = "spool" # spool | pyweixin
library_path = "" # pyweixin 源码目录(pywechat/src)
python_exe = "" # 采集用哪个解释器;留空 = 当前进程
collect_timeout = 240 # 子进程采集超时(秒)
collect_interval = 300 # pyweixin 采集间隔(秒)
max_pull = 20 # 每个会话一次最多拉多少条
spool = "wechat/inbox.jsonl" # 两种方式共同的落地文件
groups = [] # 空 = 全部会话;["项目A-线上问题"] = 只看这些群
keywords = [] # 空 = 内置的报错关键词
interval = 60
max_batch = 100 # 一次最多读多少条新消息
token_env = "DOT_WECHAT_TOKEN" # webhook 鉴权;留空 = 不校验(仍然只听 127.0.0.1)[mcp] 外部系统
[mcp]
enabled = false
[mcp.servers.jira]
transport = "stdio" # stdio | sse | streamable_http
command = "uvx"
args = ["mcp-atlassian"]
pass_env = ["JIRA_URL", "JIRA_USERNAME", "JIRA_API_TOKEN"]
env = { JIRA_PROJECTS_FILTER = "OPS" } # 固定值写这里,密钥走 pass_env
timeout = 60
prefix = "jira" # 工具名前缀,默认就是 server 名dots mcp add jira 会直接生成这样一段。参数和 URL 里可以写 ${VAR},
连接时才展开 —— 所以像飞书那种必须把 app_secret 放命令行的服务,
dot.toml 里也只留变量名。
[ontology] 本体库
[ontology]
enabled = true
dir = "ontology" # ontology.json + history/
derived = "generated" # 派生产物,整体可重建,别手改
inject_prompt = true # 每轮把实体摘要塞进系统提示
prompt_entities = 40 # 摘要里最多列多少个实体
[ontology.datasources.crm]
kind = "postgres" # postgres | mysql | mongodb
dsn_env = "DOT_CRM_DSN" # 连接串放环境变量
schema = "public" # MySQL 用 database 当 schema
include = [] # 只看这些表/集合,支持通配
exclude = ["alembic_*", "tmp_*", "sys_*"]
sample_size = 50 # mongo 采样条数[sdlc] 流水线
[sdlc]
enabled = true
dir = "projects" # 一条流水线一个子目录
max_fix_rounds = 3 # 测试 FAIL → 修复 → 再测,最多来回几次
auto_from_bug = true # 微信群报的 BUG 自动起修复流水线后台一轮 tick 做了什么
每轮固定四步:
- 收事件:
sources.poll_all()扫描事件源(工作区文件、IMAP 邮件、iCalendar、微信群 spool), 新事件落events表。UNIQUE(source, key)保证同一件事只收一次。 - 路由事件:主题带
[dot#的审批回信写进approvals表;群消息先过 BUG 判定和台账去重; 其它事件建成 ready 目标。 - 处理已决定的审批:超时的按拒绝过期,已批准/拒绝的续跑对应目标。
- 跑一个 ready 目标。目标完成且给了证据后,如果它属于某条流水线,就推进阶段并派下一段。
然后睡 tick_seconds 秒,再下一轮。停止请求能在睡眠中被打断,不用等满一分钟。
几个容易误解的点:
- 界面和命令行没有 IPC。
dots goal插一行、dots approve改一行, 常驻进程下一轮自己看见。GUI 里的按钮同理。 - 被 kill 后卡在
running的目标,下次启动会被recover_stale_runs()放回ready; checkpoint 还在,是接着跑,不是重跑。 - agent 往被监听目录写的东西会被自己的文件源当成新事件。后端会把本轮新增文件预插成
status='self'挡掉,否则它会对着自己的产出无限套娃。
全流程流水线(需求→原型→开发→测试→修复)
一条需求进来,分阶段跑完,每个阶段只干自己那段,产物落盘给下一段读:
需求分析(pm) → 原型设计(designer) → 开发(dev) → 测试(qa) → 修复(fixer) → 归档(pm)dots sdlc new "把订单导出成 CSV,只要已支付的" --jira OPS-42
dots sdlc ls
dots sdlc show <id>
dots serve # 后台每轮推进一步目录长这样(相对 workspace/):
projects/<slug>/
├── pipeline.json 状态机:阶段、轮次、目标 id、历史。人可以改
├── PRD/requirements.md 需求:背景 / 用户故事 / 验收标准 / 非目标 / 待确认问题
├── PRD/questions.md 澄清问题清单,每题带 2-4 个选项
├── design/prototype.md 原型说明:页面 / 交互 / 状态
├── design/prototype.html 能点的静态原型
├── dev/spec.md 技术方案 + 任务拆分
├── dev/tasks/T01-*.md 一个任务一个文件 = 一次做完并自测的最小单元
├── dev/src/ 代码
├── test/report.md 测试报告,最后一个非空行必须是 VERDICT: PASS 或 FAIL
├── fix/fix-log.md 缺陷 / 根因 / 改动 / 回归
└── archive/summary.md 功能点 / 怎么跑 / 已知限制,给以后排查推进靠产物,不靠模型说自己做完了:
- 每阶段有必须落盘的文件和必须包含的小节。缺了就不推进,重派一次; 两次还缺就停下并记 audit —— 悄悄往前推比卡住更糟。
VERDICT: FAIL会进修复阶段,修完再测,max_fix_rounds轮还不收敛就停。- 目标完成时
evidence里的路径和 Jira/MR 单号会留在pipeline.json里, 下一个阶段不用猜上一段干了什么。 - 阶段目标由 worker 自动派发,进程重启、断点续跑都不会把流水线卡在半路。
实在卡住了:
dots sdlc advance <id>人工跳过,会留痕。
缺陷轨迹走同一条流水线,只是从修复开始:
dots sdlc new --track bugfix "导出接口 500" --jira OPS-77
dots bugs fix <id1> <id2> # 多条一起修,先按根因归类本体库
三层,各管各的:
物理层 公司库里的表 / 集合 / 字段。只读。
↓ dots ontology sync
语义层 ontology/ontology.json:实体、字段、关系、枚举、术语。人可以手改。
↓ dots ontology apply
派生层 generated/:数据字典、ER 图、DDL、Python 模型、TS 类型dots ontology init # 建一份空的
dots ontology sync # 采所有数据源并合并
dots ontology sync crm # 只采一个
dots ontology show # 实体列表
dots ontology show orders # 某个实体的完整定义
dots ontology edit # 用 $EDITOR 手改,存盘后自动校验 + 派生
dots ontology apply # 手改 JSON 之后重新生成
dots ontology status # 派生层是否落后JSON 的形状(节选):
{
"version": 1,
"entities": [
{
"id": "orders",
"label": "订单",
"status": "confirmed",
"curated": true,
"source": { "datasource": "crm", "schema": "public", "table": "orders" },
"primary_key": ["id"],
"fields": [
{ "name": "status", "label": "状态", "semantic": "enum",
"enum": ["paid", "unpaid"], "nullable": false }
],
"relations": [
{ "field": "customer_id", "target": "customer", "kind": "many_to_one" }
]
}
],
"enums": [{ "id": "order_status", "values": [{ "value": "paid", "label": "已支付" }] }],
"glossary": [{ "term": "GMV", "definition": "成交总额" }]
}手改规则(有意不对称):
curated: true的实体/字段,下一次sync不会覆盖你的标注、枚举、关系; 库里新增的列只报冲突,不静默加进来。- 没改过的实体:字段跟着库走,新字段自动补。
- 库里消失的表只标
missing,不删 —— 删了人工标注就没了。
改完之后全流程一起变:
generated/下的数据字典、schema.sql、models.py、types.ts、er.mmd全部重写。- 每条流水线的每份 md 里,「数据模型(本体库自动生成)」那一节整段重写。
- 系统提示里的本体摘要跟着变;阶段目标里也带着它,所以 next 阶段拿到的字段名是新的。
dots ontology status会告诉你派生层是不是忘了 apply;手改过派生文件也会被点出来。
MCP:飞书 / Jira / Sigma / GitLab
dots mcp add jira # 写一段现成配置进 dot.toml
dots mcp add feishu
dots mcp add gitlab
dots mcp add sigma
dots mcp list # 看状态、缺哪个环境变量dots mcp add 只会写变量名,凭据要 export:
export JIRA_URL=https://your-team.atlassian.net
export JIRA_USERNAME=you@example.com
export JIRA_API_TOKEN=...工具名统一带前缀(jira__create_issue、gitlab__create_merge_request),
所以审批规则可以按系统写通配:
[approval.rules]
"jira__*" = "ask"
"feishu__send_*" = "ask"
"gitlab__create_*" = "deny"- 没装适配器:
pip install 'dots-agent[mcp]'。没装也不影响别的功能,tick 照跑。 - 一个 server 连不上不影响其它 server,也不影响这一轮 tick。
- 连一次很贵(要起
npx/uvx子进程),同一进程内会缓存。
微信群 BUG 监控
两种采集方式,消息最终都落到同一个 workspace/wechat/inbox.jsonl:
方式一(推荐,直接用本机微信):用 pywechat
里的 pyweixin 做 UI 自动化 —— 扫未读会话 → 打开对应群拉最新消息 → 写进 spool。
[sources.wechat]
enabled = true
backend = "pyweixin"
# pywechat 仓库里的源码目录
library_path = "C:/Users/you/Documents/project/pywechat/src"
# 用哪个解释器跑采集。留空 = 当前进程内 import;填了就起子进程
python_exe = "C:/Users/you/anaconda3/python.exe"
collect_interval = 300 # 每 5 分钟扫一次
max_pull = 20 # 每个群一次最多拉 20 条
groups = ["项目A-线上问题"] # 按微信备注名精确匹配;空 = 全部会话dots wechat check # 微信在跑吗、登录了吗、UI 树可见吗
dots wechat collect # 立刻采一次(--chat 群名 只采一个)
dots wechat status # 看 backend / spool / 上次采集时间要点:
- dot 和 pyweixin 的依赖可以不在同一个 Python 环境。dots 要 langchain,
pyweixin 要 pywinauto/pyautogui;
python_exe填能跑 pyweixin 的那个解释器, 采集就走子进程,崩了也不会带倒常驻 agent。 - 依赖:
pip install 'dots-agent[wechat]'(或按 pywechat 仓库的scripts/requirements.txt装全),微信 4.x + Windows 10/11。 - 微信的 UI 树必须可见(开启 Windows 无障碍/讲述人),否则自动化取不到控件。
collect_interval是独立于 tick 的:UI 自动化会动窗口,不要每轮都扫。- 不用
pyweixin就backend = "spool",消息由桥(Wechaty / wechatferry / 企业微信机器人 / 人工导出)写进同一个文件,下游完全一样。
spool 的格式(一行一条 JSON,msg_id 有就去重、没有就按内容算):
{"group": "项目A-线上问题", "sender": "张三", "text": "订单导出报 500", "msg_id": "abc"}桥不方便写文件时,用内置本机 webhook(只听 127.0.0.1):
export DOT_WECHAT_TOKEN=change-me
dots wechat hook --port 8790
curl -X POST http://127.0.0.1:8790/wechat \
-H "Content-Type: application/json" -H "X-Dots-Token: change-me" \
-d '{"group":"项目A-线上问题","sender":"张三","text":"导出报 500"}'一条群消息进来之后:
- 关键词判定:报错 / 500 / 异常 / traceback / 崩溃 …… 闲聊不进台账。
- 指纹去重:异常类型 + 顶部文件名、错误码、或归一化后的正文骨架。 同一条错换个措辞、换个行号还是同一个指纹 —— 群里十个人报不会开十个目标。
- 进台账:
bugs表记次数、首末时间、样本;dots bugs看全部。 - 派活:新 BUG 自动起一条
bugfix流水线(修 → 测 → 归档); 把[sdlc] auto_from_bug关掉就退化成普通目标。 - 回归:已经修复/关闭的又出现,状态回
open并重新开流水线,不会安静地留在已修复里。
dots wechat ingest "导出报 500" --group 项目A-线上问题 --sender 张三 # 联调用
dots bugs
dots bugs show <id>
dots bugs fix <id1> <id2>
dots bugs close <id>数据放在哪
打包版的数据跟着配置文件走。默认全局配置下:
~/.dots/
├── dot.toml 配置(密钥只写环境变量名)
├── dot.db 目标、事件、审批、缺陷台账、审计、对话 checkpoint
├── dot.db-wal 正常运行时存在
├── dot.db-shm 正常运行时存在
└── workspace/ 它的工作区 = 沙箱
├── inbox/ 往里扔文件,它下一轮自己发现
├── skills/ 它攒的技能,每份一个目录 + SKILL.md
├── ontology/ 本体库:ontology.json + history/ 快照
├── generated/ 本体库派生的文档和代码(整体可重建,别手改)
├── projects/ 流水线,一条一个子目录(PRD/design/dev/test/fix/archive)
└── wechat/ 群消息 spool(inbox.jsonl)在仓库里开发时,如果用的是仓库根的 dot.toml,数据就在仓库根下。
想换位置:改 dot.toml 里的 workspace 和 db(相对路径相对配置文件目录)。
不要删除正在使用的 dot.db:后端、checkpointer、每个请求各开自己的 SQLite 连接,
Windows 上连接没关时文件删不掉(WinError 32)。先关应用再删。
常见用法
让文件触发它
往 workspace/inbox/ 扔个文件,下一轮 tick 它自己就会发现并处理。
printf '把这三件事排个优先级\n- 修打印机\n- 回邮件\n- 整理数据\n' > workspace/inbox/todo.txt
dots tick
dots ls用邮件审批
配好 [smtp] 和 [sources.imap] 后,它想执行 ask 的动作会给你发一封邮件,
主题里带一个编号:
[dot#a7f3] 发邮件给 alice: 周会改到周四
回复这封邮件,第一行写 y / 同意 或 n / 拒绝 即可。
没配邮件也能用 CLI。dots ls 会把等审批的目标和编号直接列出来:
dots ls
# ID STATUS TOKEN TOKENS UPDATED TEXT
# 43e86d interrupted a7f3 29,047 12s 给 alice 发一封邮件...
#
# 1 个在等你。批准:dots approve <编号> 拒绝:dots reject <编号>
dots log --goal 43e86d # 先看它到底想干什么
dots approve a7f3桌面版则在待批横幅里点「批准」/「拒绝」,和邮件回复走的是同一条路径。
攒技能
它干活的时候会自己把可复用的流程记下来。你也可以喂材料:
dots learn "workspace/weekly-template.md 里的周报模板,以后按这个格式写周报"
dots tick
dots skills技能存在 workspace/skills/<名>/SKILL.md,是普通 markdown 文件,可以手改。
桌面版在技能面板里点「学习」「技能库」「看」「删」「整理技能」。
交接给别的 bot
dots goal --bot ops "把报告发给 alice"跑起来之后它也能自己换人,你在 dots log 里会看到 handoff 那一条。
看活动视图
dots serve --web --web-port 8765三张表:待批准、目标(带结论和证据)、最近的流水。10 秒自刷新。
只听本机,没有鉴权,别绑到 0.0.0.0。
常驻跑
dots serveCtrl-C 停。没跑完的目标留在库里,下次启动接着跑。
安全边界
- 界面窗口只加载本地文件,CSP 禁止任何远程脚本/样式/字体。
- 渲染进程
sandbox: true、nodeIntegration: false、contextIsolation: true; preload 只暴露一个白名单 API,不把ipcRenderer交出去。 - 活动视图(
dots serve --web)只听127.0.0.1,没有鉴权,不要改成对外地址。 run_shell是shell=True+ 靠 cwd 兜着,不是沙箱,默认规则是deny。 要用它必须显式在[approval.rules]里放宽,并接受风险。- 所有模型/邮件/工具输出在活动视图里都会被转义渲染;桌面界面一律用
textContent写入,不拼innerHTML。 dot.toml、dot.db、workspace/都在.gitignore里,不要提交。
常见问题
双击后一直显示「正在启动后端…」
正常要 10 秒左右(导入 langchain 一整串依赖)。超过 30 秒还没变,看是否:
- 安装目录被杀毒软件拦了;
%APPDATA%\dots-desktop\不可写;- 输出面板里有 Python 栈。顶栏变成「后端已停」时会自动弹出原因。
顶栏显示「值守中」,但状态是「循环已停」,活动里写着「循环自己崩了」
后端活着,只是 tick 循环崩了。最常见的是从开始菜单启动时进程环境里没有模型 key, 活动面板会写出具体原因,例如:
fatal tick: SystemExit: [model] api_key_env = 'DEEPSEEK_API_KEY' 但那个环境变量是空的
loop 循环自己崩了把 key 设进系统环境变量(不是某个终端的临时 export),重启应用,再点「开始值守」。
顶栏「后端已停」,按钮全灰
说明 Python 子进程退出了。最常见的原因是找不到配置:
dots init --global其它原因看输出面板的 stderr 尾部:依赖缺失、TOML 语法错误、模型 key 不存在等。
提示找不到 API key
密钥必须放在系统环境变量里。从开始菜单启动时,进程继承的是系统环境,
不是你在某个终端里临时 export 的。设置完要重启应用(必要时重启资源管理器/注销)。
点「停止」之后按钮没马上变
循环可能正在跑一个目标。等这一轮结束,后端会推 loop_stopped。
停止期间按钮保持「停止」是刻意的,避免你误以为已经停了。
「跑一轮」是灰的
后台循环正在跑。两个 tick 会抢同一个数据库,所以界面禁用、后端也会拒绝。 先「停止」,再手动跑一轮。
审批挂着不动
- 看横幅里的编号,确认你批的是对的;
- 超过
timeout_hours会自动按拒绝处理; - 如果刚点完,决定要等下一轮 tick 才会续跑目标,不是立即生效。
聊天里点「批准」提示审批已过期
聊天审批只等 10 分钟。超时后 agent 已按拒绝处理,界面不会假装成功。 重新让它做一次即可。
命令一直转圈 / 显示失败但后端还在跑
界面给命令 180 秒超时。selftest 约 30 秒,正常不会超;如果前面排着长命令,
等待时间会累加。超时只是界面不再等,后端那条命令可能仍在跑完。
装完启动报「安装包里没找到自带运行时」
resources/runtime/python/python.exe 缺失,说明打包时 extraResources 没带上
desktop/runtime/。重新执行 python make-runtime.py(或 bash make-runtime.sh)
再 npm run dist。
dots ontology sync 报「缺驱动」或连不上
按库装驱动,都只在你真要采那个库时才需要:
pip install 'psycopg[binary]' # postgres
pip install pymysql # mysql
pip install pymongo # mongodb连接串只从 dsn_env 指的环境变量读。先在同一个终端 export DOT_CRM_DSN=... 再跑,
不要把连接串写进 dot.toml。报「一个实体都没采到」时检查 schema / database
和 include / exclude —— MySQL 的 schema 是库名,不是 public。
改了本体库,但文档/代码没变
dots ontology status 会告诉你派生层落后了没有。改 JSON 后跑 dots ontology apply;
用 dots ontology edit 存盘会自动做这一步(校验不过则不会动派生文件)。
派生文件本身别手改 —— 它整体可重建,手改会在 status 里被标成 hand_edited。
MCP 工具没出现在 agent 手里
按顺序查:
dots mcp list—— 有没有 server、是不是「就绪」、缺哪个环境变量;pip install 'dots-agent[mcp]'—— 适配器装了没;[mcp] enabled = true——dots mcp add会自动打开,手写配置时容易漏;command里的npx/uvx在 PATH 里吗(飞书和 GitLab 要 Node,GitLab/Jira 要 uv);- 工具名有前缀(
jira__create_issue),审批规则别按裸名写。
微信群消息没反应
先看是哪条路:
dots wechat status—— 源开没开、backend 是什么、spool 在哪、读到的偏移是多少;backend = "pyweixin"时先跑dots wechat check:微信没登录 / UI 树不可见 /library_path指错 /python_exe里没装 pywinauto,都会在这一步说清楚;- 消息是不是进了
groups白名单,文本是不是命中了报错关键词; - 同一句话报第二遍不会开新目标(这是故意的):
dots bugs里看计数; - 想联调不接微信:
dots wechat ingest "导出报 500" --group 项目A然后dots tick。
流水线卡在某一阶段
dots sdlc show <id> 看「缺什么」那列:产物文件没落盘或少了必须的小节,阶段就不会推进。
让 agent 再跑一次(dots tick),或者人工补文件;确实已经在别处做完了,
用 dots sdlc advance <id> 跳过并留痕。两次都没落盘的话流水线会被标成 blocked,
不会继续烧 token。
想彻底重来
- 关闭应用;
- 备份或删除
~/.dots/(会丢目标、审批、审计、对话历史、技能和工作区文件); - 重新
dots init --global。
一页速查
启动应用 → 等 10 秒 → 顶栏「值守中」
派活 → 目标面板「新建目标」→ 写一句话
看它干了什么 → 活动面板 / 目标行「日志」
它要发邮件 → 待批横幅点「批准」或「拒绝」
跟它说话 → 底部输入框,回车发送
让它学一套流程 → 技能面板「学习」→ 等下一轮
开一条流水线 → dots sdlc new "需求一句话"
看流水线走到哪 → dots sdlc ls → dots sdlc show <id>
采公司库元数据 → 配 [ontology.datasources.*] → dots ontology sync
手改本体 → dots ontology edit → 存盘自动派生
群里的 BUG → dots wechat check → dots bugs → dots bugs fix <id>
接外部系统 → dots mcp add jira|feishu|gitlab|sigma
换模型 → 改 ~/.dots/dot.toml 的 [model] → 重启应用
出问题 → 顶栏「体检」→ 看输出面板 → 活动面板一个提醒
每次运行都会重新过一遍系统提示和工具描述,固定开销不小 —— 往 inbox/
扔一个文件大约几万 token 起。先让 daily_token_budget = 0 跑一天,
用 dots cost(或桌面版活动面板的「用量」)看真实数字,再决定填多少。
许可
MIT