# task-center **Repository Path**: powercs12/task-center ## Basic Information - **Project Name**: task-center - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-24 - **Last Updated**: 2026-10-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 任务中心(task-center) 一个人 + 多个项目 + 多台机器:**把 AI 干活的时间从「占用我的键盘」变成「不占用我的键盘」。** 在手机上丢一句需求 → 某个产品的机器领活 → 跑通用 AI worker → 回传状态、事件流、产物和结论 → 主动推给我。 worker 是**通用执行器**:写代码只是其中一类任务,调研、查行情、写作、分析都同样派给它。 ## 现在能做什么 派活之外,三件事让你「敢不管它」: | | 能力 | 入口 | |---|---|---| | **验收** | 跑完自动跑产品声明的 test/build(验收门);改动异常大 / 碰了敏感文件会预警;agent 自己拍的板列成「假设账本」;手机上一键**合并 / 开 PR / 丢弃** | 任务详情页 | | **提问** | agent 遇到真正无法假设的缺口会停下来问你(`waiting`),点候选答案即回答,它接着干 | 任务详情页顶部 / 推送 | | **度量** | 每周派出数、长任务替你「等」了多少分钟、从哪里派的、花费与每日预算 | `/stats` | 再往前:**定时 / webhook 触发**(`/automations`)、**多模型赛马**(`/races/new`,同一需求几个 CLI 同场比)、 **worker 并发**、**worktree 自动回收**、**访问口令**、**Mac 服务化**(`deploy/macos/`)。 ## V2:个人 AI 作战系统 V2 把单任务闭环扩展成「计划 → 执行 → 评审 → 决策 → 复盘」: - **待我决策工作台**:waiting 提问、待评审、失败诊断、计划审批集中在 `/`;手机底部导航直接处理。 - **DAG 计划**:`/plans` 支持 task / approval / review / race / condition / join,失败重试、显式跳过、checkpoint 语义和持久调度。 - **自动评审与诊断**:成功任务可自动派只读评审,失败任务自动派诊断;AI 建议与人工决定分离,required 产品必须人 approve 才能合并/开 PR。 - **执行历史与 tracing**:每次 execution 独立,重跑不覆盖;事件 retry 去重、finish 幂等、trace waterfall。 - **偏好与智能路由**:偏好可检查、撤回、注入 agent/worker;创建/续问支持自动或手动 CLI/model 路由,P0–P3 优先级和产品并发配额。 - **飞书互动卡片**:等待回答、验收、重跑、计划批准、周报发布可在飞书里完成;首个真实私聊身份绑定为单用户 owner,也支持 `TC_FEISHU_OWNER_OPEN_ID` 限定。 - **真实 Ops 作战室**:`/ops` 使用一致性 snapshot + publish cursor + 可恢复 SSE;3D 仍保留原有事件编舞。 - **AI 周报**:`/insights` 先保存 SQL 事实,再生成追加式叙述版本,支持重新生成、发布和 Markdown 导出。 - **产物与归档**:内容寻址快照、跨 worker 下载、hot/cold/sealed 事件生命周期、备份 manifest 与隔离校验。 关键运维变量: | 变量 | 作用 | |---|---| | `TC_ARTIFACT_STORE_DIR` | 内容寻址产物目录,默认 `~/.taskcenter/artifacts` | | `TC_BACKUP_DIR` | 备份 manifest 与恢复验证目录,默认 `~/.taskcenter/backups` | | `TC_FEISHU_OWNER_OPEN_ID` | 可选:限制只有指定飞书身份能成为 owner | 完整设计与边界见 [设计-任务中心-V2](docs/设计-任务中心-V2.md),实际验收记录见 [V2 验收记录](docs/V2-验收记录.md)。 ## 它解决什么 | 阻碍 | 现在的做法 | 有了它之后 | |---|---|---| | **占用** | AI 跑 30 分钟,我的键盘被占住 | 派出去,这 30 分钟归我 | | **串行** | 多个项目只能排队推进 | 一产品一台 VM,天然并行 | | **易失** | 想法到执行之间衰减 | 想到就丢进去 | **主战场不是「手机端好看」,而是「派出去之后我敢不管它」。** 所以推送和可验收的产物(分支 + diff)是核心功能,不是附属功能。完整推演见 [docs/产品定位.md](docs/产品定位.md)。 ## 快速开始 ### 一键本地开发(推荐) 前置条件:Go 1.26+、Node 20+、pnpm,以及 Docker(或一个已有的本地 PostgreSQL)。真正执行 worker 任务还需要安装并登录 `claude` 或 `codex`。 ```bash make dev ``` 打开 **** 即可开发;center / API 在 。脚本会先安装依赖并构建 `frontend/dist`,再构建 center(满足 `go:embed`),随后启动 PostgreSQL、center、worker 和 Vite。center 固定使用 `8787`,因为现有 Vite `/api` 代理指向它。 默认 PostgreSQL 使用 Docker 和持久数据卷;默认 worker 只接 `general` 通用任务,配置、token、日志和工作区都隔离在 `.run/dev/`,**不会读取或覆盖根目录 `worker.toml`**。如果 PATH 里两个 CLI 都没有,脚本会明确警告,界面仍可开发,但派发任务前必须先装好 CLI。按 `Ctrl-C` 只停止本次启动的进程和容器,不删数据卷或任务产物。 可选环境变量: | 变量 | 作用 | |---|---| | `TC_DATABASE_URL` | 指向已有本地库;设置后自动跳过 Docker,例如 `postgres://postgres@127.0.0.1:55432/taskcenter?sslmode=disable` | | `TC_DEV_SKIP_PG=1` | 显式跳过 PostgreSQL 启动;使用 `TC_DATABASE_URL`,未设置时回退到代码默认的 `127.0.0.1:55432/taskcenter` | | `TC_DEV_PG_PORT` | 自动 Docker PostgreSQL 映射到宿主机的端口,默认 `55432` | | `TC_DEV_FRONTEND_PORT` | Vite 端口,默认 `5173` | | `TC_DEV_WORKER_CONFIG` | 使用已有 worker 配置而不生成默认配置;相对路径按仓库根目录解析 | | `TC_DEV_SKIP_WORKER=1` | 不启动 worker,只开发 center / frontend | 运行日志在 `.run/dev/logs/`。下面的 0~6 节是等效的手动分步方式。 ### 0. 依赖 - Go 1.26+ - PostgreSQL 16(本机或 Docker) - Node 20+ / pnpm(只在构建前端时需要) - 每台 VM 上装好至少一个 coding CLI(`claude` / `codex`) ### 1. 起 PostgreSQL ```bash docker run -d --name tc-pg -p 5432:5432 \ -e POSTGRES_PASSWORD=postgres -v tc-pg:/var/lib/postgresql/data postgres:16 createdb -h 127.0.0.1 -U postgres taskcenter ``` > 端口:代码默认连 `127.0.0.1:55432`(本机 brew 装的 PG)。上面 docker 起在 5432, > 需要设 `TC_DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/taskcenter?sslmode=disable`。 ### 2. 构建 ```bash # 前端(只需在改了前端后跑;产物会被编进 center 二进制) cd frontend && pnpm install && pnpm build && cd .. # 两个二进制 go build -o bin/center ./cmd/center go build -o bin/worker ./cmd/worker # 交叉编译一个 Linux worker(部署到 VM 用) GOOS=linux GOARCH=amd64 go build -o bin/worker-linux ./cmd/worker ``` > `migrations/` 和 `frontend/dist/` 都是 **go:embed 编进二进制**的,所以 center 启动时会自动建表, > 部署 = `scp bin/center server:/opt/taskcenter/ && systemctl restart taskcenter`。 ### 3. 起中心 ```bash export TC_DATABASE_URL="postgres://postgres@127.0.0.1:5432/taskcenter?sslmode=disable" export TC_FEISHU_WEBHOOK="" # 可选:对话 agent(不配 key 则 /api/agent/chat 返回 503,其余功能不受影响) export TC_AGENT_API_URL="https://api.deepseek.com" export TC_AGENT_API_KEY="sk-..." export TC_AGENT_MODEL="deepseek-flash" # 可选:工具结果回来后的综合用更强模型(不配就用 TC_AGENT_MODEL) export TC_AGENT_STRONG_MODEL="deepseek-chat" # 可选:访问口令。只绑 127.0.0.1 时不需要;监听 0.0.0.0 / 暴露到公网务必设。 # 设了之后所有 worker 也要配同一个值(worker.toml 的 access_token,或环境变量 TC_ACCESS_TOKEN)。 export TC_ACCESS_TOKEN="" # 可选:每日花费上限(美元)。用尽后暂停派发新任务,排队任务保持 pending,次日自动恢复。 export TC_DAILY_BUDGET_USD="" # 可选:飞书通道。走 lark-cli 长连接,不需要公网 URL export TC_FEISHU_ENABLED="true" # 可选:飞书自定义机器人,留空则只在日志里打印通知 ./bin/center -listen 127.0.0.1:8787 ``` 打开 就是看板。首次启动会自动建表并写入内置产品(`logic-lens` / `demo`,以及通用任务工作区 `general`)。 侧栏「**3D 架构**」(`/flow`)把「飞书/网页 → 中心 → worker → CLI」画成可旋转、可点的三维拓扑;无后端时自动降级为演示数据。 ### 4. 起 worker(每台 VM 一个) ```bash cp worker.toml.example worker.toml $EDITOR worker.toml # 改 center 地址、name、产品→本地目录映射 ./bin/worker -config worker.toml ``` 首次运行会自动注册并把 token 存到 `~/.taskcenter/token`(权限 0600)。 ### 5. 派活 浏览器打开 → 右下角「新建任务」→ 选产品 → 写需求 → 提交。 或者: ```bash curl -X POST http://127.0.0.1:8787/api/tasks -H 'Content-Type: application/json' \ -d '{"product_code":"demo","title":"加个导出","prompt":"给这个项目加一个 CSV 导出功能"}' ``` ### 6. 跟它说话(对话 agent) 配了 `TC_AGENT_API_KEY` 之后可以直接用自然语言派活: ```bash curl -X POST http://127.0.0.1:8787/api/agent/chat -H 'Content-Type: application/json' -d '{"chat_key":"me","text":"给逻辑镜加个 CSV 导出功能"}' ``` 网页端走的是 SSE 版本 `POST /api/agent/chat/stream`,回包前能显示「正在读产物 / 正在找历史任务」; 飞书和脚本用上面的普通 JSON 接口即可。 开飞书通道后,直接在飞书里给 bot 发消息即可。详见 [对话Agent](docs/对话Agent.md)。 **不限于开发任务。** 飞书里说「查上周 A 股尾盘」「调研一下 X」「写一段文案」,对话 agent 会派给通用工作区 worker(`general`),跑完把结果回写到这条会话。前提是 general 有在线 worker —— 服务器 runner 常驻接它,不依赖 Mac 开机。 ## 架构 ``` ┌──────────────────────────────────────────┐ 手机 ──► │ 中心(唯一真相源) │ │ Go 单二进制 · PostgreSQL · 前端已内嵌 │ │ 4 张表 · 4 个 worker 接口 · 7 个用户接口 │ └────▲──────────────────────┬──────────────┘ │ 拉取(短轮询 3s) │ 心跳 15s = 续租约 + 事件 + 取取消 │ ▼ ┌─────────┴────────────────────────────────────┐ │ VM-A worker(products=[logic-lens]) │ │ worktree → CLI agent → 归一化事件 → 分支/diff │ ├──────────────────────────────────────────────┤ │ VM-B worker(products=[product-b]) │ └──────────────────────────────────────────────┘ ``` **所有箭头都是 worker 主动发起的。** 中心没有任何指向 worker 的连接 —— 这是它能穿过 NAT、并且不需要知道任何一台机器地址的根本原因。 | 角色 | 是什么 | 明确不做 | |---|---|---| | **中心** | Go 单进程 + PostgreSQL | 不知道任何本地路径,不执行任何命令 | | **worker** | 每台 VM 一个常驻 Go 进程 | 不监听端口,不直连中心数据库 | | **CLI agent** | claude / codex / … | 不知道中心存在 | ## 目录结构 ``` task-center/ ├── cmd/ │ ├── center/ 中心入口(内嵌前端 + 迁移) │ └── worker/ worker 入口 ├── internal/ │ ├── contract/ ★ 中心与 worker 共享的唯一契约 │ ├── center/ PG 存储 / HTTP / sweeper / 通知 │ ├── agent/ ★ 对话层:provider / 主循环 / 通道 │ │ (和 worker 上那个执行 agent 是两类东西) │ └── worker/ │ ├── adapter/ 各 CLI 的适配与事件归一化 │ ├── config.go 本地配置(产品→目录映射只在本机) │ ├── client.go 中心 HTTP 客户端 │ ├── git.go worktree / commit / push / diff │ ├── outbox.go SQLite 待上报队列 │ └── run.go 主循环与 CLI 执行 ├── migrations/ PostgreSQL DDL(go:embed) ├── deploy/ 服务器部署资产(systemd / env 示例 / 部署说明) ├── frontend/ React 18 + TS + Vite + Ant Design ├── docs/ 产品定位 / 技术方案 / 选型 / 适配 / 变更包 ├── testdata/fixtures/ 真实 CLI 输出(adapter 的回归测试素材) └── poc/ 立项前的 Go 可行性验证(保留作留痕) ``` ## 关键设计决策 | # | 决策 | 理由 | 代价 | |---|---|---|---| | 1 | **拉取**而非推送 | worker 只出网,NAT 后随便放 | 取消要等一个心跳周期(≤15s) | | 2 | **中心是唯一真相源** | 状态在 worker 本地就做不成分布式 | 原始日志只在 VM 上 | | 3 | **短轮询 3s**,不用长轮询 | 任务本身跑 20 分钟,省 3 秒没意义 | 领活延迟 ≤3s | | 4 | **`FOR UPDATE SKIP LOCKED`** 领取 | 一条 SQL 完成选+领+返回,并发天然分流 | 绑定 PostgreSQL | | 5 | **所有时间由 `now()` 生成** | VM 时钟不可信,一个时间来自 worker 租约就会误判 | worker 不能上报时间 | | 6 | **stale 不自动重跑** | AI 干半截的活自动重跑往往更糟 | 需要人工介入 | | 7 | **一任务一 worktree + 一分支** | 失败不影响主分支,重跑天然安全 | 磁盘占用 | | 8 | **事件在 worker 归一化** | 加新 CLI 时中心/前端/数据库都不用动 | 加 CLI 要重部署 worker | | 9 | **中心不知道任何路径** | 躲开「绝对路径写进提示词」那类跨机必然失效的做法 | worker 需要本地映射 | ## 部署到服务器 中心是**单二进制 + 内嵌前端 + 自动迁移**,服务器只需要 PostgreSQL 16。 完整步骤见 [deploy/server/README.md](deploy/server/README.md)。 一台服务器 + 一个 Mac 的典型拓扑: | 位置 | 跑什么 | |---|---| | 服务器 | center + 服务器 runner(dedicated:logic-lens 真产品 + general 通用任务) | | 本地 Mac | 另一个 runner(dedicated,只接演练产品 sim/docs/…) | 要点: - 交叉编译:`make build-linux`(产出 `bin/center-linux` / `bin/worker-linux`) - center 监听 `0.0.0.0:8787` 时,用 `TC_ALLOWED_HOSTS` 放行公网 IP / 反向代理域名 - 真实产品的仓库给 runner 用**独立 clone**,不要把 runner 指向生产目录 - 通用任务(调研、查行情、写作、分析)走服务器 runner 的 `general` 产品(`kind = "scratch"`,每任务一个工作目录),不依赖 Mac 开机 - Claude Code / Codex 都可以指向 DeepSeek:API 模型名 `deepseek-flash` 现在就是 DeepSeek-V4.1-Flash-1017(传版本号字面量会被 400) - 用户接口默认**没有鉴权**(本机个人工具)。公网部署请设 `TC_ACCESS_TOKEN`;监听非回环地址又没设口令时, center 启动会打 WARN。**启用口令时 center 和所有 worker 必须同时配**(见变更包 03 的升级顺序) ## 测试 ```bash # Go:契约 + 适配器 + worker + 中心(含真连 PostgreSQL 的集成测试) export TC_TEST_DSN="postgres://postgres@127.0.0.1:5432/taskcenter_test?sslmode=disable" createdb -h 127.0.0.1 -U postgres taskcenter_test go test ./internal/... -count=1 # 前端 cd frontend && pnpm test && pnpm build # 端到端(真 center + 真 worker + 假 claude/codex + 真 git;用临时库和 18788 端口,不碰在用的服务) scripts/ops-e2e.sh ``` **adapter 的测试用真实 CLI 输出回放**(`testdata/fixtures/`):真跑一次 CLI 要 60~85 秒还烧 token, 不能当回归测试。改成「捕获一次 + 回放无数次」: ```bash # 捕获(会烧 token,偶尔做一次) claude -p "…" --output-format stream-json --verbose > testdata/fixtures/claude-ok.jsonl codex exec --json - < prompt.txt > testdata/fixtures/codex-warn.jsonl ``` 中心侧的集成测试**真连 PostgreSQL** —— `SKIP LOCKED`、`now()`、部分索引在 mock 上根本测不出来。 其中 `TestConcurrentClaimNeverDuplicates` 是整套系统最重要的一条正确性保证。 ## 常见问题 **worker 一直显示离线?** 看 `worker` 的日志有没有「注册成功」。中心判定离线是「90 秒没有心跳」。 **问 bot 一个非开发任务,它会不会只回「我帮不上」?** 现在不会了:任何需要动手的事(查资料、查行情、调研、写作、分析、写代码)都派给 worker。 非代码任务默认派给通用工作区产品 `general`,跑完按来源渠道回传(飞书派出的推回飞书)。 前提是 general 有在线 worker —— 服务器 runner 常驻接它;一个都没有时任务会排队,等 worker 上线再跑。 **任务卡在 running?** worker 挂了一分钟内 sweeper 会把它标成 `stale`,并在手机上推送。**不会自动重跑**,需要你去任务详情点「重跑」。 **任务失败但退出码是 0?** 正常。**退出码不可靠** —— 有些 CLI 会 0 退出但活没干成,也可能非 0 退出但活干成了。 终态以 stream 里的事件为准,退出码只作兜底。这也是为什么 worker 必须解析 stream。 **加一个新的 coding CLI?** 只写一个 `internal/worker/adapter/.go`(约 80~150 行),在 `adapter.New` 里注册, 再改 `worker.toml` 里的 `cli`。**中心、前端、数据库一行都不用改。** ## 文档索引 | 文档 | 内容 | |---|---| | [设计-任务中心-V2](docs/设计-任务中心-V2.md) | V2 全部功能、数据模型、状态机、迁移与验收设计 | | [V2 验收记录](docs/V2-验收记录.md) | 实际测试结果、端到端覆盖与未联调边界 | | [备份与恢复](deploy/server/BACKUP.md) | 备份、校验、恢复与演练运行手册 | | [产品定位](docs/产品定位.md) | 服务谁、价值主张排序、采用率风险、有效性判据 | | [技术方案](docs/技术方案.md) | 角色边界、架构图、4 张时序图、部署拓扑、施工顺序 | | [技术栈选型](docs/技术栈选型.md) | 逐端选型:worker / 中心 / 数据库 / 前端 | | [CLI适配与事件模型](docs/CLI适配与事件模型.md) | 各 CLI 的真实差异、Adapter 接口、实测验证与三个坑 | | [设计-任务中心-V1](docs/设计-任务中心-V1.md) | 数据模型与接口契约 | | [对话Agent](docs/对话Agent.md) | 中心的对话层:与 worker agent 的区别、通道抽象、选项机制、飞书长连接 | | [系统架构](docs/系统架构.md) | 拉取模型、三类工作目录、验收闭环、成本 / 自动化 / 度量 | | [docs/changes/](docs/changes/) | 按变更包组织的交付留痕 |