Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

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.exe

dots-<版本>-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] 段。

打包版查找配置的顺序:

  1. %APPDATA%\dots-desktop\dot.toml(也就是应用的当前工作目录)
  2. ~/.dots\dot.toml

你的数据跟着配置文件走:全局配置在 ~/.dots/,数据库和工作区也都在那儿。

启动过程

双击图标后:

  1. Electron 先画窗口;
  2. 拉起 python -m dots bridge;
  3. 后端导入 langchain 那一整串依赖,约 10 秒;
  4. 后端发出 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)
日志只看这个目标的活动记录

七种状态:

界面文字内部值含义
待批准pendingagent 自己派生出来的目标,要你点头才跑
排队中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 行:

  1. 数据库 audit 表的快照(最近 60 条);
  2. 本次运行期间后端实时推来的 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 costtoken 用量
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 statusspool 在哪、读到哪了
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.2

api_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 做了什么

每轮固定四步:

  1. 收事件:sources.poll_all() 扫描事件源(工作区文件、IMAP 邮件、iCalendar、微信群 spool), 新事件落 events 表。UNIQUE(source, key) 保证同一件事只收一次。
  2. 路由事件:主题带 [dot# 的审批回信写进 approvals 表;群消息先过 BUG 判定和台账去重; 其它事件建成 ready 目标。
  3. 处理已决定的审批:超时的按拒绝过期,已批准/拒绝的续跑对应目标。
  4. 跑一个 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"}'

一条群消息进来之后:

  1. 关键词判定:报错 / 500 / 异常 / traceback / 崩溃 …… 闲聊不进台账。
  2. 指纹去重:异常类型 + 顶部文件名、错误码、或归一化后的正文骨架。 同一条错换个措辞、换个行号还是同一个指纹 —— 群里十个人报不会开十个目标。
  3. 进台账:bugs 表记次数、首末时间、样本;dots bugs 看全部。
  4. 派活:新 BUG 自动起一条 bugfix 流水线(修 → 测 → 归档); 把 [sdlc] auto_from_bug 关掉就退化成普通目标。
  5. 回归:已经修复/关闭的又出现,状态回 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 serve

Ctrl-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 手里

按顺序查:

  1. dots mcp list —— 有没有 server、是不是「就绪」、缺哪个环境变量;
  2. pip install 'dots-agent[mcp]' —— 适配器装了没;
  3. [mcp] enabled = true —— dots mcp add 会自动打开,手写配置时容易漏;
  4. command 里的 npx / uvx 在 PATH 里吗(飞书和 GitLab 要 Node,GitLab/Jira 要 uv);
  5. 工具名有前缀(jira__create_issue),审批规则别按裸名写。

微信群消息没反应

先看是哪条路:

  1. dots wechat status —— 源开没开、backend 是什么、spool 在哪、读到的偏移是多少;
  2. backend = "pyweixin" 时先跑 dots wechat check:微信没登录 / UI 树不可见 / library_path 指错 / python_exe 里没装 pywinauto,都会在这一步说清楚;
  3. 消息是不是进了 groups 白名单,文本是不是命中了报错关键词;
  4. 同一句话报第二遍不会开新目标(这是故意的):dots bugs 里看计数;
  5. 想联调不接微信:dots wechat ingest "导出报 500" --group 项目A 然后 dots tick。

流水线卡在某一阶段

dots sdlc show <id> 看「缺什么」那列:产物文件没落盘或少了必须的小节,阶段就不会推进。 让 agent 再跑一次(dots tick),或者人工补文件;确实已经在别处做完了, 用 dots sdlc advance <id> 跳过并留痕。两次都没落盘的话流水线会被标成 blocked, 不会继续烧 token。

想彻底重来

  1. 关闭应用;
  2. 备份或删除 ~/.dots/(会丢目标、审批、审计、对话历史、技能和工作区文件);
  3. 重新 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

关于 About

一个会主动发现任务的智能体
ai-agentautomationautonomous-agentsdeepagentslanggraphllmproactivepython

语言 Languages

Python81.9%
JavaScript12.0%
CSS4.5%
HTML0.7%
Shell0.5%
Inno Setup0.4%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
27
Total Commits
峰值: 12次/周
Less
More

核心贡献者 Contributors