Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ src/backend/repos/
# Optional local-only operator notes
/AGENTS.md
/LOCAL.md

.github/copilot-instructions.md
**/*.log
32 changes: 23 additions & 9 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# HALF 架构说明

> **对应版本**:v0.2.1
> **对应版本**:v0.3.0(含自动派发模式)
> 本文档描述当前代码已经实现的系统结构、关键设计决策和公开面。以代码为事实源头;文档与代码不一致时,代码为准。

---

## 一、系统定位

HALF(Human-AI Loop Framework)是一个人机协同多智能体任务管理平台。它面向同时使用多个 AI coding agent(Claude Code、Codex、Copilot、GLM、Kimi 等)的研究者和团队,通过纯人工触发的方式协调任务编排、prompt 分发、状态跟踪和结果归档:系统生成 prompt,负责人手工粘贴到 agent 执行,agent 根据 prompt 修改项目代码仓库,并把计划、任务结果等协作产物写回 HALF 协作仓库,HALF 后台只轮询协作仓库识别完成。整个闭环不调用 agent 平台的非公开接口,不做接口逆向。
HALF(Human-AI Loop Framework)是一个人机协同多智能体任务管理平台。它面向同时使用多个 AI coding agent(Claude Code、Codex 等)的研究者和团队,提供两种任务执行模式:

- **手动模式**:系统生成 prompt,负责人手工粘贴到 agent 执行,agent 把计划、任务结果等协作产物写回 HALF 协作仓库,HALF 后台轮询协作仓库识别完成。整个闭环不调用 agent 平台的非公开接口,不做接口逆向。
- **自动模式**:对配置了 API 凭证的 agent 类型,HALF 后端直接调用其 REST API(Anthropic Messages 格式)触发任务执行,按 DAG 依赖顺序自动推进,无需人工介入。

---

Expand Down Expand Up @@ -53,8 +56,8 @@ HALF(Human-AI Loop Framework)是一个人机协同多智能体任务管理
**三层划分**:

- **前端**:React 18 + TypeScript + Vite SPA,负责页面渲染、交互编辑、手工触发复制操作;通过 JWT Bearer 访问后端 REST API。
- **后端**:FastAPI(Python 3.12)服务,负责鉴权、业务状态维护、prompt 生成、DAG 解析、Git 轮询调度。后端 **不主动调用 agent**
- **存储**:**SQLite 承载系统内部状态**(项目、计划、任务、用户、审计日志等);**Git 协作仓库承载项目协同产物**(每个项目配置一个协作仓库,任务输出、`result.json` 哨兵、`usage.json` 用量记录)。项目代码仓库可与协作仓库相同,也可单独配置;HALF 只轮询协作仓库。SQLite 与 Git 通过轮询桥接。
- **后端**:FastAPI(Python 3.12)服务,负责鉴权、业务状态维护、prompt 生成、DAG 解析、Git 轮询调度,以及自动模式下直接调用外部 Agent API(通过 Agent Runner 层)
- **存储**:**SQLite 承载系统内部状态**(项目、计划、任务、用户、审计日志等);**Git 协作仓库承载项目协同产物**(每个项目配置一个协作仓库,任务输出、`result.json` 哨兵、`usage.json` 用量记录)。项目代码仓库可与协作仓库相同,也可单独配置;手动模式下 HALF 只轮询协作仓库。SQLite 与 Git 通过轮询桥接。

---

Expand Down Expand Up @@ -88,12 +91,23 @@ HALF(Human-AI Loop Framework)是一个人机协同多智能体任务管理

这样同时覆盖单文件和多文件任务,显著降低轮询误判漏判。具体状态流转参见 `task-lifecycle.md`。

### 3.5 人工派发模式(Human-in-the-Loop)
### 3.5 人工派发模式(Human-in-the-Loop,手动模式

MVP 所有任务分发均由项目负责人手工完成——系统生成 prompt,负责人点击"复制 Prompt 并派发"按钮把内容送到剪贴板,再手工粘贴给对应 agent。HALF 不会自动发送任何 prompt。
**手动模式**项目中,所有任务分发均由项目负责人手工完成——系统生成 prompt,负责人点击"复制 Prompt 并派发"按钮把内容送到剪贴板,再手工粘贴给对应 agent。HALF 不会自动发送任何 prompt。

**剪贴板 user-activation 约束**:浏览器要求 `navigator.clipboard.writeText` 必须在用户点击产生的同步执行栈内调用。前端因此采用"预取 prompt + 同步写剪贴板"的编排:选中任务时后台预取最新 prompt 缓存;用户点击时 `copyText` 在任何 `await` 之前直接写剪贴板;写入失败必须显式中止,不得继续调用 `/dispatch`。这避免了"按钮显示已复制但剪贴板里残留的是上一个任务 prompt"的历史 bug。

### 3.6 自动派发模式(Auto-Dispatch)

**自动模式**项目中,Agent 类型配置了 `sdk_type`(目前支持 `claude`),且对应 Agent 实例配置了 `api_base_url` 和加密存储的 `api_key` 后,计划 finalize 或任务完成时,后端自动触发就绪任务的执行,无需人工操作。

- **DAG 并发链式执行**:`services/auto_dispatch.py::run_auto_task` 是一个异步协程,执行完成后调用 `get_ready_auto_tasks` 获取下游就绪任务,通过 `asyncio.gather` 并发运行所有解锁的下游任务,形成自动推进的 DAG 链。
- **Git Worktree 隔离**:每个自动执行的任务获得独立的 per-task 工作目录(`{REPOS_DIR}/{project_id}/tasks/{task_id}/`),基于项目代码仓库(`{project_id}/code/`)的 git worktree 创建,任务完成后删除,避免并发任务互相干扰。
- **两仓库布局**:后端在 `{REPOS_DIR}/{project_id}/` 下维护两个子目录:`collab/`(HALF 协作仓库克隆)和可选 `code/`(项目代码仓库克隆,与协作仓库不同时才独立存在);`tasks/{task_id}/` 为按需创建的 worktree。
- **状态管理**:任务在真正调用 Agent API 前才在 DB 中标记为 `running`(`dispatch_mode=auto`);执行完成后由 `run_auto_task` 直接标记 `completed` 或 `needs_attention`,**不经过 polling_service**。手动模式任务的状态变更仍走轮询路径。
- **失败处理**:API 调用失败时任务进入 `needs_attention`,记录错误原因,不自动重试。可在前端手动重新派发或人工处理。
- **进程内去重**:`_running_auto_tasks: set[int]` 在 asyncio 单线程事件循环中保证同一任务不会被并发执行两次。

---

## 四、技术栈
Expand Down Expand Up @@ -165,11 +179,11 @@ MVP 所有任务分发均由项目负责人手工完成——系统生成 prompt
|---|---|
| `User` | 系统用户(admin / user);`status` 为 `active` 或 `frozen` |
| `Agent` | 用户登记的 AI agent,含多模型配置(`models_json`)、订阅到期、短期/长期重置时间与间隔 |
| `AgentTypeConfig` + `ModelDefinition` + `AgentTypeModelMap` | Agent 类型目录与模型定义的全局配置,由管理员维护 |
| `Project` | 项目。含 `git_repo_url`(HALF 协作仓库)、`project_repo_url`(可选项目代码仓库)、`collaboration_dir`、`planning_mode`、`template_inputs_json`、轮询配置快照、`goal`(任务介绍) |
| `AgentTypeConfig` + `ModelDefinition` + `AgentTypeModelMap` | Agent 类型目录与模型定义的全局配置,由管理员维护;`AgentTypeConfig` 含 `sdk_type` 字段(目前仅 `claude`),用于标识自动模式;API 凭证(`api_base_url`、`api_key_encrypted`,Fernet 加密存储)存在 `Agent` 实例层,每实例独立配置 |
| `Project` | 项目。含 `git_repo_url`(HALF 协作仓库)、`project_repo_url`(可选项目代码仓库)、`collaboration_dir`、`planning_mode`、`template_inputs_json`、轮询配置快照、`goal`(任务介绍)、`is_auto`(是否为自动模式项目,默认 `False`) |
| `ProjectPlan` | 计划。每个项目可有多个 `candidate` 计划和一个 `final` 计划 |
| `ProcessTemplate` | 可复用的流程模版。任务中的 `assignee` 使用抽象槽位 `agent-N`,应用到项目时替换为真实 Agent |
| `Task` | 计划 finalize 后产生的执行任务;`status` 覆盖 `pending / running / completed / needs_attention / abandoned` |
| `Task` | 计划 finalize 后产生的执行任务;`status` 覆盖 `pending / running / completed / needs_attention / abandoned`;`dispatch_mode` 区分手动(`manual`)和自动(`auto`)派发 |
| `TaskEvent` | 任务状态变更事件(dispatched / completed / timeout / manual_complete / abandoned / redispatched / updated / error) |
| `GlobalSetting` | 全局项目参数(轮询间隔、启动延迟、默认超时)+ 规划 prompt 的同机分配引导 |
| `AuditLog` | 密码修改、用户角色/状态变更的操作审计日志 |
Expand Down
54 changes: 18 additions & 36 deletions docs/prd/auto-dispatch-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@

### 智能体执行模式

每个 agent 实例(非 agent 类型)拥有一个**执行模式**字段:
每个 agent 类型拥有一个**执行模式**字段:

| 模式 | 含义 | 默认行为 |
| 模式 | 含义 | 约束 |
|---|---|---|
| **手动模式**(Manual) | 沿用现有流程,系统生成 prompt,操作人手动复制粘贴 | 默认 |
| **自动模式**(Auto) | 系统在任务就绪时自动调用外部 AI API 执行,无需人工介入 | 需配置 API 凭证后方可激活 |
| **手动**(manual) | 所有任务由操作人手动派发 | 只能使用手动模式 agent|
| **自动**(auto) | 任务就绪后自动派发,无需人工介入 | 只能使用自动模式 agent|

---

## 用户故事

**US-1 管理员/用户配置自动模式 agent**
作为一名用户,我可以在"智能体设置"页面为某个智能体类型开启自动模式,填写 API 凭证,并选择调用格式,保存后该 agent 即可被自动派发
作为一名用户,我可以在"智能体设置"页面为某个智能体类型开启自动模式;并在各智能体实例的配置页面填写该实例专属的 API 凭证,保存后该实例即可被自动派发

**US-2 项目负责人查看 agent 模式**
作为项目负责人,在选择参与 agent 时,我可以看到每个 agent 的模式标签("自动"或"手动"),以便了解哪些 agent 需要人工介入。
Expand All @@ -49,29 +49,28 @@

**FR-1.1** 创建或编辑智能体类型时,提供"执行模式"选项:手动(默认)或自动。

**FR-1.2** 当选择"自动"时,需展示并要求填写以下字段
**FR-1.2** 当选择"自动"时,智能体类型本身不存储 API 凭证;每个属于该类型的智能体实例需在实例配置页面单独填写以下字段

| 字段 | 说明 | 必填 |
|---|---|---|
| API Base URL | API 服务地址,如 `https://api.openai.com/v1` | 是 |
| API Key | 鉴权密钥 | 是 |
| API 格式 | 单选:OpenAI Chat Completions / Anthropic Messages | 是 |

**FR-1.3** API Key 在界面上以密码框形式展示(`****`),不允许明文回显。编辑时可选择"重新设置"以覆盖,或留空以保持原值不变。

**FR-1.4** agent 列表卡片上以标签形式展示执行模式:自动模式显示 `自动` 标签,手动模式不显示(沿用当前样式)。

**FR-1.5** 切换模式时需要确认提示:从自动切换回手动时,提示"切换后现有 API 凭证将被清除,确认继续?"。

**FR-1.6** API 凭证(base_url、api_key)存储在 Agent 类型层,在所有引用该智能体类型的智能体实例中共享
**FR-1.6** API 凭证(base_url、api_key)存储在 Agent 实例层,每个智能体实例独立配置各自的 API 凭证,互不影响。自动模式类型下的每个实例在投入使用前必须完成凭证配置

---

### FR-2 项目页面 agent 选择

**FR-2.1** 项目选择参与 agent 的界面中,在 agent 名称旁以标签展示其执行模式("自动"/"手动"),仅供查看,不可在项目页面修改。

**FR-2.2** 无需其他改动,项目页面的 agent 选择逻辑保持不变
**FR-2.2** 项目创建/编辑时,只展示与项目执行模式匹配的 agent:自动模式项目只显示自动模式 agent,手动模式项目只显示手动模式 agent。尝试将不匹配模式的 agent 加入项目时,后端返回 400 错误

---

Expand All @@ -81,7 +80,7 @@

**FR-3.2** 自动派发遵循 DAG 依赖顺序:仅当一个任务的所有前置依赖任务已 `completed` 时,该任务才会被自动派发。

**FR-3.3** 对于混合模式项目,自动 agent 的任务正常自动执行;手动 agent 的任务继续在界面上等待人工操作,两者可并行推进
**FR-3.3** 不支持混合模式项目。项目必须为纯自动或纯手动模式,不允许同一项目中同时存在自动模式 agent 和手动模式 agent。

**FR-3.4** 自动派发的任务在界面上标记为"自动执行中",区别于人工派发后的"运行中"状态(视觉上可保持一致,语义上需可区分,便于后续扩展)。

Expand All @@ -91,24 +90,6 @@

---

### FR-4 外部 API 格式支持

系统以 task prompt 作为输入,通过调用外部 AI 服务的 API 来执行任务。支持以下两种主流接口协议:

**FR-4.1 OpenAI Chat Completions 格式**
- 系统向 `POST {base_url}/chat/completions` 发送请求
- 请求体包含 `model`、`messages`(将 task prompt 作为 user message)
- 支持流式(`stream: true`)和非流式两种响应

**FR-4.2 Anthropic Messages 格式**
- 系统向 `POST {base_url}/messages` 发送请求
- 请求体包含 `model`、`messages`(将 task prompt 作为 user message)、`max_tokens`
- 鉴权使用 `x-api-key` header

**FR-4.3** 两种格式下,task prompt 作为整条 user message 发送给外部服务。系统不维护跨任务的 conversation history(每次调用均为独立对话)。

---

## 非功能需求

**NFR-1 安全性**
Expand All @@ -120,14 +101,13 @@
- 自动派发为新增功能,已有工作流不变。

**NFR-3 可观测性**
- 自动派发的每次调用需记录事件日志(`TaskEvent`),包括:发起时间、API 格式、成功/失败、错误信息(不含 API Key)。
- 自动派发的每次调用需记录事件日志(`TaskEvent`),包括:发起时间、成功/失败、错误信息(不含 API Key)。

---

## 超出范围(Out of Scope)

- 跨任务的会话上下文维护(多轮对话)
- 项目级别覆盖 agent 执行模式
- API 调用失败后的自动重试机制
- 用量统计和费用追踪(可作为后续功能扩展)
- 对现有 `claude_runner.py` / `copilot_runner.py`(SDK 模式)的整合,本次仅引入 REST API 模式
Expand All @@ -139,19 +119,21 @@
### 智能体页面(/agents)

- Agent 卡片:自动模式 agent 展示 `自动` 标签
- 创建/编辑 agent 表单:新增"执行模式"切换 + 自动模式下的凭证配置区域
- 创建/编辑智能体类型表单:新增"执行模式"切换(手动/自动),不含凭证字段
- 创建/编辑智能体实例表单:当所属类型为自动模式时,展示凭证配置区域(API Base URL、API Key)

### 项目创建/编辑页面

- Agent 选择列表:在 agent 名称旁展示模式标签(只读)
- 新增"执行模式"选择(手动/自动,默认手动)
- Agent 选择列表:按项目执行模式过滤,只展示匹配模式的 agent,在 agent 名称旁展示模式标签(只读)

---

## 验收标准

1. 创建一个自动模式 agent,填写有效的 OpenAI 兼容 API 凭证,将其加入项目并 finalize 计划后,对应任务无需人工操作即可进入执行状态,完成后任务状态变为 `completed`。
2. 创建一个自动模式 agent,填写无效的 API Key,任务触发后进入 `needs_attention` 状态,错误信息显示鉴权失败,且响应中不包含 API Key 明文。
3. 一个项目中同时包含手动和自动 agent:自动 agent 的任务自动执行,手动 agent 的任务在界面上等待人工派发,两者正常并行推进,互不影响
1. 创建一个自动模式 agent 类型,再创建一个属于该类型的智能体实例并填写有效的 API 凭证,将其加入项目并 finalize 计划后,对应任务无需人工操作即可进入执行状态,完成后任务状态变为 `completed`。
2. 创建一个自动模式 agent 实例,填写无效的 API Key,任务触发后进入 `needs_attention` 状态,错误信息显示鉴权失败,且响应中不包含 API Key 明文。
3. 尝试将自动模式 agent 加入手动模式项目,或将手动模式 agent 加入自动模式项目时,后端返回 400 错误,操作被拒绝
4. 同一自动模式 agent 被分配多个就绪任务时,这些任务可并发执行,无需等待其中一个完成。
5. 所有现有手动模式 agent 的行为与改动前完全一致。
5. 所有现有手动模式 agent 的行为与改动前完全一致;所有存量项目默认为手动模式,现有工作流不受影响
6. API Key 不出现在任何 API 响应、日志或页面中。
Loading
Loading