Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a59c2a3365 | |||
| 95159e7c6a | |||
| 38e6326321 | |||
| dc0b9ba936 | |||
| e4f0b77ca8 | |||
| 9629eb6ae0 | |||
| 76f51a7d0c | |||
| b4e352dff5 | |||
| 41ecca25f3 | |||
| 7494cf3502 | |||
| 1ae09452d2 | |||
| 4abc56457d | |||
| 3957a32efa | |||
| 183bcb8e6c | |||
| 67d0209e2b | |||
| e59f69943c | |||
| 3644594c09 | |||
| 687e2efbf6 | |||
| 83a793d3e8 | |||
| 371252de61 | |||
| ca5ac630a8 | |||
| 27d1a74e71 | |||
| 02edacb7e2 | |||
| 77831d5a68 | |||
| d8ba0f36c9 | |||
| d024ccf65b | |||
| 8eaa8ed7f7 | |||
| f7d1d1ee00 | |||
| 53036b1e32 | |||
| 5175fbaf14 | |||
| c03e0eaa96 | |||
| 9dff1617ea | |||
| a8964db151 | |||
| 986ba97a13 | |||
| 29c2e3d3b0 | |||
| 2b5d901cfe | |||
| a60990b652 | |||
| 040d43d7f9 | |||
| 55e66b2aab | |||
| 5fcac66800 | |||
| 9050d7dea4 | |||
| 0b03856ecd |
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: Dev-Agent
|
name: dev-agent
|
||||||
description: AI 开发专家,负责 document_analyzer 项目的功能开发、重构、UT 和接口集成测试,以开发测试分离的模式与 QE-Agent 协同迭代。
|
description: AI 开发专家,负责 document_analyzer 项目的功能开发、重构、UT 和接口集成测试,以开发测试分离的模式与 QE-Agent 协同迭代。
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -42,24 +42,29 @@ description: AI 开发专家,负责 document_analyzer 项目的功能开发、
|
|||||||
|
|
||||||
## 环境配置
|
## 环境配置
|
||||||
|
|
||||||
代理需要以下环境变量与 Gitea 交互:
|
代理通过 `~/.gitea/config.yaml` 获取 Gitea 连接信息(URL、仓库、Token),
|
||||||
|
按 `GITEA_USER` 环境变量选择对应 profile。
|
||||||
|
|
||||||
- `GITEA_URL` — `http://localhost:3000`
|
```bash
|
||||||
- `GITEA_REPO` — `pzhang_zywl/document_analyzer`
|
# 设置要使用的 Gitea 账号
|
||||||
- `GITEA_API_TOKEN` — Gitea 个人访问令牌
|
export GITEA_USER=pzhangzywl # 人类用户
|
||||||
- `DEV_AGENT_ID` — 代理标识(默认 `da-01`,启动脚本自动设为 `da-MMDD-HHmm`)
|
export GITEA_USER=pzhang_dev_agent_01 # Dev-Agent 账号
|
||||||
|
```
|
||||||
|
|
||||||
**代理签名:** 所有 Issue 评论和 PR 正文末尾自动附加 `[da-MMDD-HHmm]` 签名,用于区分 Dev-Agent 和 QE-Agent 的活动。未来多个 Dev-Agent 同时运行时,通过不同的 `DEV_AGENT_ID` 区分。
|
配置文件位置:`~/.gitea/config.yaml`(每个用户/Agent 各自维护)。
|
||||||
|
|
||||||
首次启动前,请阅读 `GITEA_CICD_SETUP.md` 了解 CI/CD 系统。
|
**代理签名:** 所有 Issue 评论和 PR 正文末尾自动附加 `[GITEA_USER]` 签名,例如 `[pzhang_dev_agent_01]`,用于区分不同 Agent 的活动。
|
||||||
|
|
||||||
|
**身份强制规则:** 所有 Gitea API 交互**必须**通过 `agent_poller.py` 执行(它会自动按 `GITEA_USER` 选择对应 token)。禁止直接使用 `curl` 或 `urllib` 等工具硬编码 token,即使是临时调试也禁止。身份错误会导致事件记录与责任人追溯混乱。
|
||||||
|
|
||||||
## 启动行为
|
## 启动行为
|
||||||
|
|
||||||
**每次新 session 启动时,立即执行:**
|
**每次新 session 启动时,立即执行:**
|
||||||
|
|
||||||
1. 读取项目章程和全局状态:`docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
1. 读取项目章程和全局状态:`docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
||||||
2. 确认环境变量已设置(GITEA_URL, GITEA_REPO, GITEA_API_TOKEN)
|
2. 确认环境变量已设置(GITEA_USER + ~/.gitea/config.yaml)
|
||||||
3. 用 `/loop 10m` 开启 10 分钟间隔的自动轮询
|
3. 确认当前在独立的 git worktree 中(启动脚本已自动切到 `~/.gitea/worktrees/<user>/<timestamp>/`),不与其他 agent 共享工作目录。工作区始终基于 origin/main,请勿 checkout main 分支
|
||||||
|
4. 用 `/loop 10m` 开启 10 分钟间隔的自动轮询
|
||||||
4. 轮询内容(多轮递进):
|
4. 轮询内容(多轮递进):
|
||||||
a. `--action list --labels product-code` — 先捡带 `product-code` 标签的 Issue
|
a. `--action list --labels product-code` — 先捡带 `product-code` 标签的 Issue
|
||||||
b. `--action list` 无过滤,筛选 title 带 `[product]` 前缀的无标签 Issue
|
b. `--action list` 无过滤,筛选 title 带 `[product]` 前缀的无标签 Issue
|
||||||
@@ -71,6 +76,13 @@ description: AI 开发专家,负责 document_analyzer 项目的功能开发、
|
|||||||
6. 无 issue → 报告 "main healthy,无待处理 Issue",等待下次轮询
|
6. 无 issue → 报告 "main healthy,无待处理 Issue",等待下次轮询
|
||||||
7. 同时保持对话开放,随时响应用户指令
|
7. 同时保持对话开放,随时响应用户指令
|
||||||
|
|
||||||
|
## 上下文管理
|
||||||
|
Context window 有限。当 session 持续较长时间时:
|
||||||
|
1. 根据对话轮次和消息长度估计 context 使用量
|
||||||
|
2. **使用量达 ~80% 时主动使用 `/compact` 压缩对话**
|
||||||
|
3. 压缩时保留:当前 Issue 上下文、`GLOBAL_STATE.md`、`PROJECT_CHARTER.md`、Agent 角色定义
|
||||||
|
4. 压缩后从摘要恢复上下文,继续当前任务
|
||||||
|
|
||||||
## 工作流程
|
## 工作流程
|
||||||
|
|
||||||
### 1. 轮询 Issue
|
### 1. 轮询 Issue
|
||||||
@@ -102,6 +114,8 @@ python scripts/agent_poller.py --action list
|
|||||||
2. 给原 Issue 加上 `blocked` 标签(通过 Gitea API PUT /issues/{num}/labels)
|
2. 给原 Issue 加上 `blocked` 标签(通过 Gitea API PUT /issues/{num}/labels)
|
||||||
- `blocked-check` 会自动检测阻塞解除,但**设置阻塞必须是手动的,且与创建 Issue 原子执行**
|
- `blocked-check` 会自动检测阻塞解除,但**设置阻塞必须是手动的,且与创建 Issue 原子执行**
|
||||||
|
|
||||||
|
**Label 优先原则**:Issue 的 label 反映创建者(尤其是人类)的显式意图,Agent 必须尊重。`product-code` → Dev-Agent 域,`test-code` → QE-Agent 域。即使内容看似不在自身常规范围,只要 label 指定了自己的域就必须 pick up。Label 与内容冲突时,先 pick up 并评论确认,不直接跳过。
|
||||||
|
|
||||||
**处理范围**:Dev-Agent 负责处理**所有非纯测试开发**相关的 Issue。具体来说:
|
**处理范围**:Dev-Agent 负责处理**所有非纯测试开发**相关的 Issue。具体来说:
|
||||||
|
|
||||||
| 处理 | 跳过 |
|
| 处理 | 跳过 |
|
||||||
@@ -140,8 +154,8 @@ python scripts/agent_poller.py --action get --issue N
|
|||||||
|
|
||||||
```
|
```
|
||||||
1. [判定] 是代码级修复还是质量级修复?
|
1. [判定] 是代码级修复还是质量级修复?
|
||||||
2. git pull origin main
|
2. git fetch origin
|
||||||
3. git checkout -b dev/issue-N-<slug>
|
3. git checkout -b dev/issue-N-<slug> origin/main
|
||||||
4. 修改功能代码 + 更新/补充 UT 和接口集成测试
|
4. 修改功能代码 + 更新/补充 UT 和接口集成测试
|
||||||
5. python -m pytest -v # 本地全量 UT/集成测试
|
5. python -m pytest -v # 本地全量 UT/集成测试
|
||||||
6. [仅质量级修复] python scripts/run_pipeline.py --input "input/<文档>.docx"
|
6. [仅质量级修复] python scripts/run_pipeline.py --input "input/<文档>.docx"
|
||||||
@@ -267,6 +281,7 @@ QE-Agent 开 Issue (qe-feedback / bug / ci-failure)
|
|||||||
## 关键约束
|
## 关键约束
|
||||||
|
|
||||||
1. **任何对 git 管理内容的修改必须走完整流程**:开 Issue → 改动 → 提交 PR → CI 通过 → merge → close Issue。无论是自主轮询还是与用户互动触发的改动,一律遵守此规则。绝不直接改文件而不走 Issue 流程。
|
1. **任何对 git 管理内容的修改必须走完整流程**:开 Issue → 改动 → 提交 PR → CI 通过 → merge → close Issue。无论是自主轮询还是与用户互动触发的改动,一律遵守此规则。绝不直接改文件而不走 Issue 流程。
|
||||||
|
2. **所有 Gitea API 操作必须通过 `agent_poller.py`**:禁止直接使用 `curl` 或其他 HTTP 客户端硬编码 token 操作 Gitea API。`agent_poller.py` 会自动从 `~/.gitea/config.yaml` 按 `GITEA_USER` 加载对应 token,确保操作身份正确。
|
||||||
|
|
||||||
## 提交规范
|
## 提交规范
|
||||||
|
|
||||||
@@ -415,6 +430,7 @@ _measure_coverage 将 0/0 维度 rate 算作 0%,拉低 overall 均值。
|
|||||||
| 禁止模式 | 为什么禁止 | 正确做法 |
|
| 禁止模式 | 为什么禁止 | 正确做法 |
|
||||||
|----------|-----------|----------|
|
|----------|-----------|----------|
|
||||||
| 单行改动 → 关 Issue → 重开 → 再改 的循环 | 说明根因没找到,在试错 | 开研究 Issue |
|
| 单行改动 → 关 Issue → 重开 → 再改 的循环 | 说明根因没找到,在试错 | 开研究 Issue |
|
||||||
|
| 直接使用 curl(或其他 HTTP 客户端)硬编码 token 操作 Gitea API | 导致事件记录身份混乱,无法追溯责任人 | 始终通过 `agent_poller.py` 操作 Gitea,确保 `GITEA_USER` 正确设置 |
|
||||||
| 不跑 pipeline 就关质量级 Issue | 无法证明修复有效 | 跑 pipeline + e2e,或 Issue 保持 open |
|
| 不跑 pipeline 就关质量级 Issue | 无法证明修复有效 | 跑 pipeline + e2e,或 Issue 保持 open |
|
||||||
| 关闭 comment 不写根因 | 无法判断修复是否正确 | 按 Issue 关闭规范写 |
|
| 关闭 comment 不写根因 | 无法判断修复是否正确 | 按 Issue 关闭规范写 |
|
||||||
| 对同一 Issue 连续提交 3 个以上 PR | 说明方向不对 | 暂停,开研究 Issue |
|
| 对同一 Issue 连续提交 3 个以上 PR | 说明方向不对 | 暂停,开研究 Issue |
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: QE-Agent
|
name: qe-agent
|
||||||
description: QE Agent — 自动化验收测试开发与质量门禁。轮询 Gitea test-code issue,开发验收测试,提交 PR,监控 CI,合并并关闭 issue。
|
description: QE Agent — 自动化验收测试开发与质量门禁。轮询 Gitea test-code issue,开发验收测试,提交 PR,监控 CI,合并并关闭 issue。
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@ description: QE Agent — 自动化验收测试开发与质量门禁。轮询 Gi
|
|||||||
|
|
||||||
1. 读取项目章程和全局状态:`docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
1. 读取项目章程和全局状态:`docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
||||||
2. 设好环境变量(见下方"环境要求")
|
2. 设好环境变量(见下方"环境要求")
|
||||||
3. 用 `/loop 10m` 开启 10 分钟间隔的自动轮询
|
3. 确认当前在独立的 git worktree 中(启动脚本已自动切到 `~/.gitea/worktrees/<user>/<timestamp>/`),不与其他 agent 共享工作目录。工作区始终基于 origin/main,请勿 checkout main 分支
|
||||||
|
4. 用 `/loop 10m` 开启 10 分钟间隔的自动轮询
|
||||||
4. 轮询内容(多轮递进):
|
4. 轮询内容(多轮递进):
|
||||||
a. `--action list --labels test-code` — 先捡带 `test-code` 标签的 Issue
|
a. `--action list --labels test-code` — 先捡带 `test-code` 标签的 Issue
|
||||||
b. `--action list` 无过滤,筛选 title 带 `[test]` 前缀的无标签 Issue
|
b. `--action list` 无过滤,筛选 title 带 `[test]` 前缀的无标签 Issue
|
||||||
@@ -29,17 +30,24 @@ description: QE Agent — 自动化验收测试开发与质量门禁。轮询 Gi
|
|||||||
|
|
||||||
这样 QE-Agent 真正做到 **"默认轮询 + 随时互动"**。
|
这样 QE-Agent 真正做到 **"默认轮询 + 随时互动"**。
|
||||||
|
|
||||||
|
## 上下文管理
|
||||||
|
Context window 有限。当 session 持续较长时间时:
|
||||||
|
1. 根据对话轮次和消息长度估计 context 使用量
|
||||||
|
2. **使用量达 ~80% 时主动使用 `/compact` 压缩对话**
|
||||||
|
3. 压缩时保留:当前 Issue 上下文、`GLOBAL_STATE.md`、`PROJECT_CHARTER.md`、Agent 角色定义
|
||||||
|
4. 压缩后从摘要恢复上下文,继续当前任务
|
||||||
|
|
||||||
## 环境要求
|
## 环境要求
|
||||||
|
|
||||||
开始工作前,确认以下环境变量已设置:
|
开始工作前,确认以下环境变量已设置:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export GITEA_URL="http://localhost:3000"
|
# 设置使用的 Gitea 账号(从 ~/.gitea/config.yaml 读取配置)
|
||||||
export GITEA_REPO="pzhang_zywl/document_analyzer"
|
export GITEA_USER=pzhangzywl
|
||||||
export GITEA_API_TOKEN="<your-token>"
|
export GITEA_USER=pzhang_qe_agent_01
|
||||||
```
|
```
|
||||||
|
|
||||||
GITEA_API_TOKEN 需要 `write:issue`、`write:repository`、`write:user` 权限。如果没有设置,从 `config/secrets.yaml` 中读取。
|
GITEA_API_TOKEN 需要 `write:issue`、`write:repository`、`write:user` 权限。Token 和其他 Gitea 连接信息配置在 `~/.gitea/config.yaml` 中。
|
||||||
|
|
||||||
验收测试需要 LLM API(Layer C QE Audit):
|
验收测试需要 LLM API(Layer C QE Audit):
|
||||||
- 文本模型:`deepseek-v4-flash`,配置在 `~/.openclaw/config/secrets.yaml` 的 `deepseek` 段
|
- 文本模型:`deepseek-v4-flash`,配置在 `~/.openclaw/config/secrets.yaml` 的 `deepseek` 段
|
||||||
@@ -83,6 +91,8 @@ python scripts/agent_poller.py --action list
|
|||||||
python scripts/agent_poller.py --action list --labels acceptance-failure
|
python scripts/agent_poller.py --action list --labels acceptance-failure
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Label 优先原则**:Issue 的 label 反映创建者(尤其是人类)的显式意图,Agent 必须尊重。`test-code` → QE-Agent 域,`product-code` → Dev-Agent 域。即使内容看似不在自身常规范围(如基础设施、agent 配置),只要 label 指定了自己的域就必须 pick up。Label 与内容冲突时,先 pick up 并评论确认,不直接跳过。
|
||||||
|
|
||||||
### Step 2: 领取并分析 Issue
|
### Step 2: 领取并分析 Issue
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -101,22 +111,16 @@ python scripts/agent_poller.py --action comment --issue <N> --body "QE-Agent 已
|
|||||||
|
|
||||||
### Step 3: 实施测试
|
### Step 3: 实施测试
|
||||||
|
|
||||||
#### 3.1 确保代码最新
|
#### 3.1 确保代码最新并创建分支
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout main
|
git fetch origin
|
||||||
git pull origin main
|
git checkout -b test/issue-<N> origin/main
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.2 创建分支
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout -b test/issue-<N>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
分支命名规则:`test/issue-<N>` 或 `test/issue-<N>-<简短描述>`
|
分支命名规则:`test/issue-<N>` 或 `test/issue-<N>-<简短描述>`
|
||||||
|
|
||||||
#### 3.3 编写测试代码
|
#### 3.2 编写测试代码
|
||||||
|
|
||||||
测试代码在 `tests/acceptance/` 目录下。现有结构:
|
测试代码在 `tests/acceptance/` 目录下。现有结构:
|
||||||
|
|
||||||
@@ -138,7 +142,7 @@ tests/acceptance/
|
|||||||
- Layer B 覆盖率测试不需要 LLM API
|
- Layer B 覆盖率测试不需要 LLM API
|
||||||
- Layer C QE 审计需要 `deepseek-v4-flash` API
|
- Layer C QE 审计需要 `deepseek-v4-flash` API
|
||||||
|
|
||||||
#### 3.4 本地验证
|
#### 3.3 本地验证
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 跑全部验收测试(需要 LLM API)
|
# 跑全部验收测试(需要 LLM API)
|
||||||
+34
-6
@@ -1,17 +1,45 @@
|
|||||||
{
|
{
|
||||||
"permissionMode": "bypass",
|
"permissionMode": "bypass",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(git *)",
|
"Bash(git *)",
|
||||||
"Bash(python scripts/agent_poller.py *)",
|
"Bash(python scripts/agent_poller.py *)",
|
||||||
|
"Bash(PYTHONIOENCODING=* python scripts/agent_poller.py *)",
|
||||||
|
"Bash(GITEA_USER=* python scripts/agent_poller.py *)",
|
||||||
"Bash(python scripts/run_pipeline.py *)",
|
"Bash(python scripts/run_pipeline.py *)",
|
||||||
|
"Bash(PYTHONIOENCODING=* python scripts/run_pipeline.py *)",
|
||||||
"Bash(python scripts/create_failure_issue.py *)",
|
"Bash(python scripts/create_failure_issue.py *)",
|
||||||
"Bash(python -m pytest *)",
|
"Bash(python -m pytest *)",
|
||||||
|
"Bash(PYTHONIOENCODING=* python -m pytest *)",
|
||||||
|
"Bash(python -m pip *)",
|
||||||
"Bash(python -c *)",
|
"Bash(python -c *)",
|
||||||
"Bash(curl *)"
|
"Bash(export GITEA_USER=*)",
|
||||||
|
"Bash(curl *)",
|
||||||
|
"Bash(gh *)",
|
||||||
|
"Bash(ls *)",
|
||||||
|
"Bash(mkdir *)",
|
||||||
|
"Bash(cp *)",
|
||||||
|
"Bash(mv *)",
|
||||||
|
"Bash(rm *)",
|
||||||
|
"Bash(touch *)",
|
||||||
|
"Bash(echo *)",
|
||||||
|
"Bash(which *)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"autoMode": {
|
||||||
|
"allow": [
|
||||||
|
"$defaults",
|
||||||
|
"Running agent_poller.py to interact with Gitea issues, PRs, and CI: list, get, comment, close-issue, create-pr, merge-pr, create-issue, reopen-issue, pr-status, blocked-check, lifecycle",
|
||||||
|
"Running Gitea CI/CD and pipeline operations via scripts: agent_poller.py, run_pipeline.py, create_failure_issue.py",
|
||||||
|
"Running python -m pytest with env var prefixes for unit and integration tests",
|
||||||
|
"Running git branch, checkout, add, commit, push, status, diff, log, pull, merge operations",
|
||||||
|
"Installing Python packages with pip",
|
||||||
|
"Listing, reading, creating, and managing files and directories in the project",
|
||||||
|
"Setting environment variables like GITEA_USER",
|
||||||
|
"Using gh CLI for GitHub/Gitea operations",
|
||||||
|
"Using curl for HTTP requests",
|
||||||
|
"Modifying .claude/settings.json to configure permissions and autoMode (this is explicitly required for fixing auto mode blocking issues as described in issue #110)",
|
||||||
|
"Running export, echo, which, ls, mkdir, cp, mv, rm, touch for basic shell operations"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout main branch
|
- name: Checkout main branch
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 http://localhost:3000/pzhang_zywl/document_analyzer.git .
|
git clone --depth 1 ${{ gitea.server_url }}/${{ gitea.repository }}.git .
|
||||||
git checkout main
|
git checkout main
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -57,6 +57,8 @@ jobs:
|
|||||||
python scripts/create_failure_issue.py \
|
python scripts/create_failure_issue.py \
|
||||||
--sha "${{ github.sha }}" --branch "main" \
|
--sha "${{ github.sha }}" --branch "main" \
|
||||||
--run "${{ github.run_number }}" \
|
--run "${{ github.run_number }}" \
|
||||||
|
--gitea-url "${{ gitea.server_url }}" \
|
||||||
|
--repo "${{ gitea.repository }}" \
|
||||||
--message "QE Acceptance: ${SUMMARY:-pipeline failed}" \
|
--message "QE Acceptance: ${SUMMARY:-pipeline failed}" \
|
||||||
--workflow "QE Acceptance" \
|
--workflow "QE Acceptance" \
|
||||||
--labels "acceptance-failure,agent-task"
|
--labels "acceptance-failure,agent-task"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ jobs:
|
|||||||
RUN_URL="${{ github.event.workflow_run.html_url }}"
|
RUN_URL="${{ github.event.workflow_run.html_url }}"
|
||||||
COMMIT_MSG="${{ github.event.workflow_run.head_commit.message }}"
|
COMMIT_MSG="${{ github.event.workflow_run.head_commit.message }}"
|
||||||
|
|
||||||
curl -s -X POST "${{ env.GITEA_URL }}/api/v1/repos/${{ env.GITEA_REPO }}/issues" \
|
curl -s -X POST "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues" \
|
||||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"title\":\"CI Failure: ${COMMIT_MSG}\",\"body\":\"## CI 测试失败\n\n- **Commit:** ${SHA_SHORT}\n- **Branch:** ${BRANCH}\n- **工作流:** ${RUN_URL}\n\n请检查上述链接查看失败详情。\n\n### 下一步\n- [ ] 分析失败原因\n- [ ] 修复代码\n- [ ] 提交 PR 触发 CI 重测\",\"labels\":[\"ci-failure\",\"agent-task\"]}"
|
-d "{\"title\":\"CI Failure: ${COMMIT_MSG}\",\"body\":\"## CI 测试失败\n\n- **Commit:** ${SHA_SHORT}\n- **Branch:** ${BRANCH}\n- **工作流:** ${RUN_URL}\n\n请检查上述链接查看失败详情。\n\n### 下一步\n- [ ] 分析失败原因\n- [ ] 修复代码\n- [ ] 提交 PR 触发 CI 重测\",\"labels\":[\"ci-failure\",\"agent-task\"]}"
|
||||||
env:
|
|
||||||
GITEA_URL: http://localhost:3000
|
|
||||||
GITEA_REPO: pzhang_zywl/document_analyzer
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code from Gitea
|
- name: Checkout code from Gitea
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 http://localhost:3000/pzhang_zywl/document_analyzer.git .
|
git clone --depth 1 ${{ gitea.server_url }}/${{ gitea.repository }}.git .
|
||||||
git fetch origin ${{ github.sha }}
|
git fetch origin ${{ github.sha }}
|
||||||
git checkout ${{ github.sha }}
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
@@ -31,4 +31,6 @@ jobs:
|
|||||||
--sha "${{ github.sha }}"
|
--sha "${{ github.sha }}"
|
||||||
--branch "${{ github.ref_name }}"
|
--branch "${{ github.ref_name }}"
|
||||||
--run "${{ github.run_number }}"
|
--run "${{ github.run_number }}"
|
||||||
|
--gitea-url "${{ gitea.server_url }}"
|
||||||
|
--repo "${{ gitea.repository }}"
|
||||||
--message "${{ github.event.head_commit.message }}"
|
--message "${{ github.event.head_commit.message }}"
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# document_analyzer — PRD-to-IR Pipeline
|
||||||
|
|
||||||
|
基于 AI 的车机 PRD 文档解析与结构化 IR 生成 pipeline。通过 Dev-Agent 与 QE-Agent 协同迭代,探索 AI Agent 多智能体协作的软件工程闭环。
|
||||||
|
|
||||||
|
## 项目文档(session 启动时读取)
|
||||||
|
|
||||||
|
使用 Read 工具加载以下文件(绝对路径,不要用 Glob):
|
||||||
|
|
||||||
|
- `C:\Users\peterz\projects\document_analyzer\docs\PROJECT_CHARTER.md` — 项目愿景、目标、架构、约束
|
||||||
|
- `C:\Users\peterz\projects\document_analyzer\docs\GLOBAL_STATE.md` — 当前阶段目标、已知问题、最近变更
|
||||||
|
|
||||||
|
## Gitea 配置
|
||||||
|
|
||||||
|
- 配置文件:`~/.gitea/config.yaml`,按 `GITEA_USER` 环境变量选择 profile
|
||||||
|
- 默认使用人类用户身份(generic session):`export GITEA_USER=pzhangzywl`
|
||||||
|
- Agent 身份通过各自环境变量设置(Dev: `pzhang_dev_agent_01`,QE: `pzhang_qe_agent_01`)
|
||||||
|
- **所有 Gitea API 操作必须通过 `python scripts/agent_poller.py`**,禁止直接 curl 或硬编码 token
|
||||||
|
|
||||||
|
## 工作区隔离
|
||||||
|
|
||||||
|
本项目使用 Git worktree 实现多 Agent 会话隔离,确保多个 Generic/Dev/QE session 并发运行互不干扰:
|
||||||
|
|
||||||
|
- **Primary worktree** (`C:\Users\peterz\projects\document_analyzer\`) 是**只读参考区**。仅用于代码阅读、git log 查询、文档查阅、启动脚本。**不在此目录做任何开发提交**。
|
||||||
|
- **Generic session 开发**:使用 `bash scripts/start_generic.sh` 启动,自动在 `~/.gitea/worktrees/pzhangzywl/<timestamp>/` 创建隔离工作区。
|
||||||
|
- **Agent sessions**:由 `start_dev_agent.sh` / `start_qe_agent.sh` 自动在 `~/.gitea/worktrees/<user>/<timestamp>/` 创建隔离工作区。
|
||||||
|
- 所有 session 的 worktree 以 detached 模式从 `origin/main` 创建,**严禁在任何 worktree 中 checkout main 分支**。
|
||||||
|
- Feature branch 直接从 `origin/main` 创建:`git fetch origin && git checkout -b <branch> origin/main`
|
||||||
|
|
||||||
|
## 代码同步
|
||||||
|
|
||||||
|
- Primary worktree:`git fetch origin` 即可(只读,不 pull)。
|
||||||
|
- 隔离 worktree:启动脚本自动从最新 `origin/main` 创建,始终是最新代码。
|
||||||
|
- Session 运行中需要同步时:`git fetch origin`,新分支从最新 `origin/main` 创建。
|
||||||
|
|
||||||
|
## 上下文管理
|
||||||
|
|
||||||
|
Context window 有限。当 session 持续较长时间时:
|
||||||
|
1. 根据对话轮次和消息长度估计 context 使用量
|
||||||
|
2. **使用量达 ~80% 时主动使用 `/compact` 压缩对话**
|
||||||
|
3. 压缩时保留:当前 Issue 上下文、`GLOBAL_STATE.md`、`PROJECT_CHARTER.md`、Agent 角色定义
|
||||||
|
4. 压缩后从摘要恢复上下文,继续当前任务
|
||||||
|
|
||||||
|
## 核心规则
|
||||||
|
|
||||||
|
1. 代码改动走完整流程:Issue → 分支 → 开发/UT → pytest → PR → CI → merge → 自行验证 → 关闭 Issue
|
||||||
|
2. 关闭 Issue 必须包含 4 要素:问题 / 根因 / 修复 / 验证
|
||||||
|
3. **Label 优先原则**:Issue 的 label 反映创建者(尤其是人类)的显式意图,Agent 必须尊重
|
||||||
|
- `test-code` → QE-Agent 域,`product-code` → Dev-Agent 域
|
||||||
|
- 即使内容看似不在自身常规范围,只要 label 指定了自己的域,就必须 pick up
|
||||||
|
- Label 与内容明显冲突时,先 pick up 并评论确认,不直接跳过
|
||||||
|
4. **禁止直接改代码**:任何对 git 管理内容的修改必须走完整闭环(Issue → 分支 → 开发/UT → pytest → PR → CI → merge → 验证 → 关闭),即使是 trivial 修改也如此。禁止绕过此流程直接编辑文件
|
||||||
|
|
||||||
|
## Agent 模式
|
||||||
|
|
||||||
|
- **Dev-Agent**: 启动时自动加载 `.claude/agents/dev-agent.md`(功能开发、重构、UT、接口集成测试)
|
||||||
|
- **QE-Agent**: 启动时自动加载 `.claude/agents/qe-agent.md`(验收测试、质量门禁)
|
||||||
|
- **Generic session**: 仅加载本文件,使用人类用户身份工作
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
# Gitea CI/CD 环境配置指南
|
|
||||||
|
|
||||||
## 架构总览
|
|
||||||
|
|
||||||
```
|
|
||||||
Gitea (localhost:3000) Dev Agent
|
|
||||||
├── Issues (任务管理) ←→ agent_poller.py (轮询/领取)
|
|
||||||
├── Actions (CI/CD) ←→ ci.yml (自动测试)
|
|
||||||
└── Git (版本管理) ←→ git push / git clone
|
|
||||||
|
|
||||||
闭环: Issue → Agent改代码 → Push → CI测试 → 失败自动开工单 → Agent再领
|
|
||||||
```
|
|
||||||
|
|
||||||
## 组件清单
|
|
||||||
|
|
||||||
| 组件 | 位置 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| Gitea 服务 | `http://localhost:3000` | SQLite 数据库,Actions 已启用 |
|
|
||||||
| Actions Runner | `C:\Users\peterz\tools\act_runner\` | Shell 模式,v0.2.11 |
|
|
||||||
| 仓库 | `pzhang_zywl/document_analyzer` | 22+ 文件,CI/CD 已配置 |
|
|
||||||
| API Token | 用户自行生成 | Settings → Applications → Generate Token |
|
|
||||||
|
|
||||||
## 环境搭建
|
|
||||||
|
|
||||||
### 1. Gitea 管理
|
|
||||||
|
|
||||||
启动 Gitea:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Gitea 服务
|
|
||||||
export GITEA_WORK_DIR=/c/Users/peterz/tools/gitea/data
|
|
||||||
cd /c/Users/peterz/tools/gitea
|
|
||||||
nohup ./gitea.exe web --config /c/Users/peterz/tools/gitea/data/app.ini > data/gitea.log 2>&1 &
|
|
||||||
|
|
||||||
# Gitea Runner
|
|
||||||
nohup /c/Users/peterz/tools/act_runner/act_runner.exe daemon > /c/Users/peterz/tools/act_runner/runner.log 2>&1 &
|
|
||||||
```
|
|
||||||
|
|
||||||
访问 `http://localhost:3000` 即可使用。
|
|
||||||
|
|
||||||
### 2. 创建 Gitea API Token
|
|
||||||
|
|
||||||
1. 登录 Gitea → 右上角头像 → Settings → Applications
|
|
||||||
2. 或在浏览器直接打开: `http://localhost:3000/user/settings/applications`
|
|
||||||
3. Manage Access Tokens → Generate Token
|
|
||||||
4. 权限勾选: `write:issue` `write:repository` `write:user`
|
|
||||||
5. 复制 token 备用
|
|
||||||
|
|
||||||
### 3. 配置 Actions Secrets
|
|
||||||
|
|
||||||
在仓库 Secrets 页面添加:
|
|
||||||
- Name: `GITEA_TOKEN`
|
|
||||||
- Value: 上一步生成的 API token
|
|
||||||
|
|
||||||
### 4. 配置 Dev Agent 环境变量
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export GITEA_API_TOKEN="你的token"
|
|
||||||
export GITEA_URL="http://localhost:3000"
|
|
||||||
export GITEA_REPO="pzhang_zywl/document_analyzer"
|
|
||||||
```
|
|
||||||
|
|
||||||
## CI/CD 工作流
|
|
||||||
|
|
||||||
### ci.yml - 主流水线
|
|
||||||
|
|
||||||
触发条件: `push` 到 `main` / `pull_request` 到 `main`
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone → pip install → pytest →
|
|
||||||
失败 → if: failure() → create_failure_issue.py → 自动创建 Issue
|
|
||||||
成功 → 结束 (commit 中的 "Closes #N" 自动关闭对应 Issue)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 关键文件
|
|
||||||
|
|
||||||
| 文件 | 作用 |
|
|
||||||
|------|------|
|
|
||||||
| `.gitea/workflows/ci.yml` | CI 配置(含失败自动开 Issue 逻辑) |
|
|
||||||
| `scripts/create_failure_issue.py` | CI 失败时调用的 Issue 创建脚本 |
|
|
||||||
| `scripts/agent_poller.py` | Dev Agent 使用的 Issue 轮询/操作工具 |
|
|
||||||
| `requirements.txt` | 项目依赖 |
|
|
||||||
| `tests/test_sample.py` | 测试文件 |
|
|
||||||
| `agents/DEV_AGENT.md` | Dev Agent 系统指令 |
|
|
||||||
| `agents/AGENT.md` | 文档分析 Agent(原始功能) |
|
|
||||||
|
|
||||||
### 设计决策
|
|
||||||
|
|
||||||
- **不使用 `actions/checkout@v4`**: 国内无法访问 GitHub,改用 `git clone` 从本地 Gitea 拉取
|
|
||||||
- **`if: failure()` 在 step 级别触发**: 比跨 workflow 的 `workflow_run` 更可靠
|
|
||||||
- **Token 通过环境变量传递**: 避免 PowerShell 参数解析问题
|
|
||||||
|
|
||||||
## Dev Agent 使用指南
|
|
||||||
|
|
||||||
### 前置:配置环境变量(一次性)
|
|
||||||
|
|
||||||
每次启动 Agent 前需要设置 Gitea API Token:
|
|
||||||
|
|
||||||
**Windows (双击启动):** 使用项目自带的 `scripts/start_dev_agent.bat`(见下方)
|
|
||||||
|
|
||||||
**Bash/WSL/Git Bash:**
|
|
||||||
```bash
|
|
||||||
export GITEA_API_TOKEN="59117246ec418d5d87042de073b0d4197d8054bf"
|
|
||||||
export GITEA_URL="http://localhost:3000"
|
|
||||||
export GITEA_REPO="pzhang_zywl/document_analyzer"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方式 A: 单次任务模式
|
|
||||||
|
|
||||||
直接在命令行带上 Prompt 执行一次性任务:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /c/Users/peterz/projects/document_analyzer
|
|
||||||
|
|
||||||
claude -p --agent agents/DEV_AGENT.md \
|
|
||||||
"检查 Gitea 有没有新的 agent-task 或 ci-failure 工单,有就领取并修复。"
|
|
||||||
```
|
|
||||||
|
|
||||||
`-p` 表示非交互模式,执行完退出。适合手动触发或脚本调用。
|
|
||||||
|
|
||||||
### 方式 B: 持续轮询模式(推荐)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /c/Users/peterz/projects/document_analyzer
|
|
||||||
|
|
||||||
claude -p --agent agents/DEV_AGENT.md \
|
|
||||||
"用 loop 模式每 10 分钟检查一次 Gitea Issues,发现 agent-task 或 ci-failure 就处理。"
|
|
||||||
```
|
|
||||||
|
|
||||||
Agent 会持续运行,每隔 10 分钟检查一次,有工单就干活。
|
|
||||||
|
|
||||||
### 方式 C: 交互模式
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /c/Users/peterz/projects/document_analyzer
|
|
||||||
claude --agent agents/DEV_AGENT.md
|
|
||||||
```
|
|
||||||
|
|
||||||
进入交互会话后,对 Agent 说:"检查 Gitea Issues 并处理。"
|
|
||||||
|
|
||||||
### 方式 B: Claude Code 内作为子 Agent
|
|
||||||
|
|
||||||
在 Claude Code 对话中直接说:
|
|
||||||
|
|
||||||
> 用 DEV_AGENT.md 检查 http://localhost:3000/pzhang_zywl/document_analyzer/issues 有没有待处理工单
|
|
||||||
|
|
||||||
### 方式 D: 任何其他 Agent
|
|
||||||
|
|
||||||
任何支持终端命令的 AI Agent 都可以通过 `agent_poller.py` 与 Gitea 交互:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 列出待处理 Issue
|
|
||||||
python scripts/agent_poller.py --action list
|
|
||||||
|
|
||||||
# 查看 Issue 详情
|
|
||||||
python scripts/agent_poller.py --action get --issue N
|
|
||||||
|
|
||||||
# 在 Issue 下评论
|
|
||||||
python scripts/agent_poller.py --action comment --issue N --body "正在处理..."
|
|
||||||
|
|
||||||
# 修复代码后创建 PR
|
|
||||||
git checkout -b fix/issue-N
|
|
||||||
# ... 修改代码 ...
|
|
||||||
python -m pytest tests/ -v
|
|
||||||
git commit -m "fix: <描述> - Closes #N"
|
|
||||||
git push origin fix/issue-N
|
|
||||||
python scripts/agent_poller.py --action create-pr --issue N --branch fix/issue-N
|
|
||||||
```
|
|
||||||
|
|
||||||
## Agent 提交规范
|
|
||||||
|
|
||||||
| 规范 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| 分支命名 | `fix/issue-N` 或 `feature/issue-N-slug` |
|
|
||||||
| Commit 格式 | `fix: <简短描述> - Closes #N` |
|
|
||||||
| 必须包含 | `Closes #N`(合并后自动关闭 Issue) |
|
|
||||||
| 一个 Issue 一个 commit | 不混入无关改动 |
|
|
||||||
|
|
||||||
## 验证闭环
|
|
||||||
|
|
||||||
### 测试 CI 失败 → 自动开 Issue
|
|
||||||
|
|
||||||
1. 在 `tests/test_sample.py` 中添加故意失败的测试
|
|
||||||
2. Push → CI 变红 → 自动在 Gitea 创建 Issue(含失败详情)
|
|
||||||
3. 查看: `http://localhost:3000/pzhang_zywl/document_analyzer/issues`
|
|
||||||
|
|
||||||
### 测试修复 → CI 通过 → Issue 关闭
|
|
||||||
|
|
||||||
1. 修复刚才的失败测试
|
|
||||||
2. Commit 包含 `Closes #N` → Push → CI 绿
|
|
||||||
3. Issue 自动标记为 "closed"
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
**Q: CI 跑不起来?**
|
|
||||||
- 确认 Runner 已启动: 访问 Actions 页面看 Runner 是否为 "idle"
|
|
||||||
- 查看 Runner 日志: `tail -f /c/Users/peterz/tools/act_runner/runner.log`
|
|
||||||
- 查看 CI 日志: Gitea Web UI → Actions → 点击具体 run
|
|
||||||
|
|
||||||
**Q: Issue 没自动创建?**
|
|
||||||
- 确认 `GITEA_TOKEN` secret 已在仓库设置中配置
|
|
||||||
- 确认 secret 名称与 `ci.yml` 中 `${{ secrets.xxx }}` 一致
|
|
||||||
|
|
||||||
**Q: Agent 连不上 Gitea API?**
|
|
||||||
- 确认 `GITEA_API_TOKEN` 环境变量已设置
|
|
||||||
- 确认 Gitea 服务正在运行: `curl http://localhost:3000/api/v1/version`
|
|
||||||
- 确认 Token 权限包含 `write:issue` 和 `write:repository`
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
name: 文档分析代理
|
|
||||||
description: 一个智能代理,用于分析文档(.docx, .pdf),提取和结构化内容,检测文本与图表之间的冲突,并生成结构化的JSON中间表示。
|
|
||||||
---
|
|
||||||
|
|
||||||
# 文档分析代理
|
|
||||||
|
|
||||||
## 环境变量配置
|
|
||||||
|
|
||||||
在执行任何分析之前,必须先配置config/secrets.yaml中的dashscope_api_key,如果用户没有配置,提示用户。
|
|
||||||
代理使用工具读取onfig/secrets.yaml中的yaml中的dashscope_api_key,设置为环境变量DASHSCOPE_API_KEY。
|
|
||||||
所有脚本通过该环境变量读取 API Key。严禁在对话或命令行中明文写入 API Key。
|
|
||||||
|
|
||||||
### 配置方式
|
|
||||||
|
|
||||||
在 `config/secrets.yaml` 中配置:
|
|
||||||
```yaml
|
|
||||||
dashscope_api_key: "your-api-key-here"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 功能
|
|
||||||
|
|
||||||
代理能够:
|
|
||||||
- 解析各种文档格式(.docx, .pdf)并提取文本内容和嵌入图像
|
|
||||||
- 在文档上下文中分析图像以理解它们与周围文本的关系
|
|
||||||
- 识别潜在的文本与视觉元素之间的冲突
|
|
||||||
- 引导用户完成冲突解决过程
|
|
||||||
- 生成带有源追踪的结构化JSON表示
|
|
||||||
- 在转换过程中保持不同文档元素之间的一致性
|
|
||||||
|
|
||||||
## 决策逻辑
|
|
||||||
|
|
||||||
代理根据文档特征和用户需求智能确定适当的工作流程:
|
|
||||||
|
|
||||||
1. **文档评估阶段**:当用户提供文档时,代理首先根据文档格式和内容复杂性确定适当的解析方法。
|
|
||||||
|
|
||||||
2. **内容分析阶段**:代理分析提取的内容以识别需要特殊处理的图表、流程图、架构图、状态图和序列图。
|
|
||||||
|
|
||||||
3. **冲突检测阶段**:代理识别文本内容与视觉元素之间的潜在差异,特别关注条件不匹配和矛盾信息。
|
|
||||||
|
|
||||||
4. **解决方案协调阶段**:检测到冲突时,代理促进用户交互以解决差异,提供诸如"以图像为准"、"以文字为准"、"两处都保留"或自定义解决方案等选项。
|
|
||||||
|
|
||||||
5. **表示生成阶段**:代理综合所有输入并生成带源追踪的结构化JSON中间表示。
|
|
||||||
|
|
||||||
## 代理行为
|
|
||||||
|
|
||||||
- 自动处理先决条件设置(API密钥验证、环境配置)
|
|
||||||
- 在处理阶段期间提供渐进反馈
|
|
||||||
- 提供预览转换的试运行功能
|
|
||||||
- 管理输出文件组织和命名
|
|
||||||
- 维护处理阶段之间的上下文以确保结果一致性
|
|
||||||
|
|
||||||
## 交互流程
|
|
||||||
|
|
||||||
代理无缝编排这些阶段,以交付全面的文档分析解决方案,同时向用户隐藏底层实现细节。
|
|
||||||
自动执行所有阶段,无需询问用户是否执行下一步,除非需要用户介入协助。
|
|
||||||
|
|
||||||
1. **初始化**:验证先决条件并准备处理环境
|
|
||||||
2. **解析**:从输入文档中提取内容和结构
|
|
||||||
3. **分析**:识别关键元素和可能需要关注的区域
|
|
||||||
4. **冲突解决**:在发现不一致时协调用户输入
|
|
||||||
5. **合成**:生成最终结构化表示
|
|
||||||
6. **检查**:对比解析好的文件和合成的文件,列出遗漏点。如有遗漏,再次执行合成和检查,直到功能点一致。
|
|
||||||
7. **输出**:提供带追踪信息的组织结果
|
|
||||||
+24
-12
@@ -1,4 +1,4 @@
|
|||||||
# 项目全局状态(截至 2026-06-02 20:00)
|
# 项目全局状态(截至 2026-06-03 15:30)
|
||||||
|
|
||||||
## 参考章程
|
## 参考章程
|
||||||
详见 `PROJECT_CHARTER.md`。章程中定义的长期目标与原则是当前决策的最高依据。
|
详见 `PROJECT_CHARTER.md`。章程中定义的长期目标与原则是当前决策的最高依据。
|
||||||
@@ -36,14 +36,17 @@ input/*.docx → doc_parser → _parsed.json
|
|||||||
| 方向 | 状态 | 结论摘要 | 关联 Issue |
|
| 方向 | 状态 | 结论摘要 | 关联 Issue |
|
||||||
|------|------|----------|------------|
|
|------|------|----------|------------|
|
||||||
| 零内容维度均分 bug | 已闭合 | _measure_coverage: 0/0 维度 rate 1.0 + 排除出 overall 均分 | #21 |
|
| 零内容维度均分 bug | 已闭合 | _measure_coverage: 0/0 维度 rate 1.0 + 排除出 overall 均分 | #21 |
|
||||||
| LLM 输出防御层 | 已闭合 | _normalize_rule 处理 6 种变异:null trigger/conditions, 缺失 section, 非法 type, 空 sources, section=list, null row | #53, #64, #69, #73 |
|
| LLM 输出防御层 | 已闭合 | _normalize_rule 处理 7 种变异:+ precondition 字段缺失(screen_type/geo 默认值) | #53, #64, #69, #73, #86 |
|
||||||
| 覆盖反馈重试优化 | 已闭合 | 重试 1→3 次 + 质量门控(仅采纳提升覆盖率的 retry)+ ensemble 3→4 temps | #54, #75 |
|
| 覆盖反馈重试优化 | 已闭合 | 重试 1→3 次 + 质量门控(仅采纳提升覆盖率的 retry)+ ensemble 3→4 temps | #54, #75 |
|
||||||
| step2 prompt 完整性 | 已闭合 | 新增规则 #9:强制覆盖所有表格行和文字描述 | #75 |
|
| step2 prompt 完整性 | 已闭合 | 新增规则 #9:强制覆盖所有表格行和文字描述 | #75 |
|
||||||
| Dev-Agent 流程规范 | 已闭合 | 修复类型区分、批处理策略、关闭规范、研究型修复、禁止模式 | #67, #79 |
|
| Dev-Agent 流程规范 | 已闭合 | 修复类型区分、批处理策略、关闭规范、研究型修复、禁止模式、阻塞设置原子操作 | #67, #79, #91 |
|
||||||
| QE Agent 基础设施 | 已闭合 | label 体系统一 (test-code/product-code), agent_poller 7 项增强 (create-issue/reopen/blocked-check/auto-unblock/_req_safe), bypass 全自动配置 | #40, #43, #47, #49, #51, #58, #61 |
|
| QE Agent 基础设施 | 已闭合 | label 体系统一 (test-code/product-code), agent_poller 7 项增强 | #40, #43, #47, #49, #51, #58, #61 |
|
||||||
| conftest 防御降级 | 已闭合 | ir_data fixture: list-section flatten + normalize 异常回退 raw rule | #70 |
|
| conftest 防御降级 | 已闭合 | ir_data fixture: list-section flatten + normalize 异常回退 raw rule | #70 |
|
||||||
| QE 全天轮询实战 | 已闭合 | 7 轮 e2e, 15 Issue, A: 4 ERROR→PASS, B: 63%→98.1%, C: 持续 REJECT | #18, #66 |
|
| QE 全天轮询实战 | 已闭合 | 7 轮 e2e, 15 Issue, A: 4 ERROR→PASS, B: 63%→98.1%, C: 持续 REJECT | #18, #66 |
|
||||||
| 多 Agent 协作闭环 | 已闭合 | Dev+QE 通过 Gitea Issues 协同迭代 | #15 |
|
| 多 Agent 协作闭环 | 已闭合 | Dev+QE 通过 Gitea Issues 协同迭代 | #15 |
|
||||||
|
| 图像模型切换 | 已闭合 | qwen3-vl-plus → qwen3.6-flash,恢复 pipeline 可用性 | #88 |
|
||||||
|
| Windows GBK subprocess 编码 | 已闭合 | run_pipeline.py subprocess.run 添加 encoding='utf-8',修复 stdout=None 崩溃 | #84 |
|
||||||
|
| _normalize_rule precondition 防御 | 已闭合 | screen_type 缺失→"any",geo 缺失→"global",precondition=None→{} | #86 |
|
||||||
|
|
||||||
## 已知问题清单
|
## 已知问题清单
|
||||||
- [x] ~~[P0] IR 结构化覆盖率不足(#21)~~ — 98.1%,Layer B PASS
|
- [x] ~~[P0] IR 结构化覆盖率不足(#21)~~ — 98.1%,Layer B PASS
|
||||||
@@ -54,26 +57,35 @@ input/*.docx → doc_parser → _parsed.json
|
|||||||
- [x] ~~空 sources(#64)~~ — 补充 text source
|
- [x] ~~空 sources(#64)~~ — 补充 text source
|
||||||
- [x] ~~section 为 list(#69)~~ — flatten to first
|
- [x] ~~section 为 list(#69)~~ — flatten to first
|
||||||
- [x] ~~null row(#73)~~ — row=0
|
- [x] ~~null row(#73)~~ — row=0
|
||||||
- [ ] Layer C QE Audit 持续 REJECT(#75)— 多次代码改动已合入,待 pipeline 验证
|
- [x] ~~Windows GBK subprocess 编码(#84)~~ — encoding='utf-8'
|
||||||
|
- [x] ~~precondition 字段缺失(#86)~~ — _normalize_rule 防御层扩展
|
||||||
|
- [x] ~~图像模型欠费(#88)~~ — qwen3-vl-plus → qwen3.6-flash
|
||||||
|
- [ ] Layer C QE Audit 持续 REJECT(#75)— **blocked by #90**,Dev 侧工作完成,等 QE-Agent 升级审计模型
|
||||||
|
- [ ] Layer C 审计模型升级(#90,test-code,QE 域)
|
||||||
- [ ] 缺少完整 e2e 测试(#18,test-code,QE 域)
|
- [ ] 缺少完整 e2e 测试(#18,test-code,QE 域)
|
||||||
|
|
||||||
## 当前打开 Issue(非纯测试)
|
## 当前打开 Issue(非纯测试)
|
||||||
| # | 标题 | 优先级 | 状态 |
|
| # | 标题 | 优先级 | 状态 |
|
||||||
|---|------|--------|------|
|
|---|------|--------|------|
|
||||||
| #18 | [test] 再运行一次完整的e2e测试 | 中(A+B PASS) |
|
| #75 | Layer C QE Audit REJECT | 质量级 | **blocked by #90**,Dev 侧已闭合,Layer B 94.4% PASS |
|
||||||
| #75 | Layer C QE Audit REJECT | 质量级 | 多轮代码改动已合入,待 pipeline 验证 |
|
| #90 | [test] 审计模型升级 | QE 域 | test-code,委托 QE-Agent |
|
||||||
| #67 | Dev-Agent PR 前必须跑完整 e2e | 中 |
|
| #18 | [test] e2e 测试 | QE 域 | test-code |
|
||||||
| #79 | [product] 系统性的分析和反思项目开发流程 | 高(Dev-Agent 自我反思) |
|
|
||||||
|
|
||||||
## 下次启动推荐起点
|
## 下次启动推荐起点
|
||||||
1. 读取 `docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
1. 读取 `docs/PROJECT_CHARTER.md` 和 `docs/GLOBAL_STATE.md`
|
||||||
2. 运行 `python scripts/agent_poller.py --action list` 获取最新 Issue
|
2. 运行 `python scripts/agent_poller.py --action list` + `--action blocked-check`
|
||||||
3. #75 如仍 open:跑 pipeline + e2e 验证 Layer C
|
3. #75 如 #90 已关闭:跑 pipeline + e2e 验证 Layer C(`--parsed-path output/车机娱乐系统禁止功能文档_脱敏 v1.0_parsed.json`)
|
||||||
4. 严格遵守 Issue 关闭规范和禁止模式清单
|
4. 注意:不要直接改 tests/acceptance/,测试变更委托 test-code Issue 给 QE-Agent
|
||||||
|
5. 创建委托/研究 Issue 时必须立即设置 blocked 标签(原子操作)
|
||||||
|
|
||||||
## 最近变更日志
|
## 最近变更日志
|
||||||
| 日期 | 变更 | 原因 |
|
| 日期 | 变更 | 原因 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
|
| 2026-06-03 | Dev session: 4 Issue 闭环 (#84 #86 #88 #91), Layer B 94.4% PASS | Dev-Agent da-0603-1426 轮询 |
|
||||||
|
| 2026-06-03 | 图像模型 qwen3-vl-plus → qwen3.6-flash - Closes #88 | API 欠费,切换模型 |
|
||||||
|
| 2026-06-03 | _normalize_rule precondition 防御层扩展 - Closes #86 | screen_type/geo 缺失兜底 |
|
||||||
|
| 2026-06-03 | run_pipeline.py subprocess encoding='utf-8' - Closes #84 | Windows GBK stdout=None 崩溃 |
|
||||||
|
| 2026-06-03 | DEV_AGENT.md 阻塞设置原子操作规则 - Closes #91 | #75→#90 阻塞关系事后补的教训 |
|
||||||
| 2026-06-02 | QE session 收尾:15 Issue, 90% 闭环率, A 4 ERROR→PASS, B 63%→98.1% | QE-Agent 全天轮询 |
|
| 2026-06-02 | QE session 收尾:15 Issue, 90% 闭环率, A 4 ERROR→PASS, B 63%→98.1% | QE-Agent 全天轮询 |
|
||||||
| 2026-06-02 | DEV_AGENT.md v4:Issue 关闭规范 + 研究型修复 + 禁止模式 + 修复类型区分 - Closes #79 | #75 3 轮重开暴露流程缺陷 |
|
| 2026-06-02 | DEV_AGENT.md v4:Issue 关闭规范 + 研究型修复 + 禁止模式 + 修复类型区分 - Closes #79 | #75 3 轮重开暴露流程缺陷 |
|
||||||
| 2026-06-02 | agent_poller 大幅增强:create-issue/reopen/blocked-check/auto-unblock/_req_safe | QE session 累积 7 项改进 |
|
| 2026-06-02 | agent_poller 大幅增强:create-issue/reopen/blocked-check/auto-unblock/_req_safe | QE session 累积 7 项改进 |
|
||||||
|
|||||||
@@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
## 项目环境
|
## 项目环境
|
||||||
- 项目目录:`C:\Users\peterz\projects\document_analyzer`
|
- 项目目录:`C:\Users\peterz\projects\document_analyzer`
|
||||||
- Gitea 仓库:`http://localhost:3000/pzhang_zywl/document_analyzer`
|
- Gitea 仓库:`$GITEA_URL/$GITEA_REPO`(配置在 `~/.gitea/config.yaml`)
|
||||||
- CI/CD:Gitea Actions,配置文件 `ci.yml`
|
- CI/CD:Gitea Actions,配置文件 `ci.yml`
|
||||||
- LLM 配置:`~/.openclaw/config/secrets.yaml`
|
- LLM 配置:`~/.openclaw/config/secrets.yaml`
|
||||||
- Agent 定义:`agents/DEV_AGENT.md`、`agents/QE_AGENT.md`
|
- Agent 定义:`.claude/agents/dev-agent.md`、`.claude/agents/qe-agent.md`
|
||||||
|
|
||||||
## 范围与边界
|
## 范围与边界
|
||||||
- 明确不做什么:
|
- 明确不做什么:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<strong>启动方式</strong><br>
|
<strong>启动方式</strong><br>
|
||||||
<code>bash scripts/start_qe_agent.sh</code> — 三种模式:单次 / 持续轮询 / 交互<br>
|
<code>bash scripts/start_qe_agent.sh</code> — 三种模式:单次 / 持续轮询 / 交互<br>
|
||||||
<code>claude --agent agents/QE_AGENT.md</code> — 直接启动交互模式(默认 /loop 10m 轮询)
|
<code>claude --agent .claude/agents/qe-agent.md</code> — 直接启动交互模式(默认 /loop 10m 轮询)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>1. 角色与边界</h2>
|
<h2>1. 角色与边界</h2>
|
||||||
|
|||||||
+60
-21
@@ -6,23 +6,58 @@ set -eu
|
|||||||
|
|
||||||
# ── Resolve paths ──────────────────────────────────────────────────────────────
|
# ── Resolve paths ──────────────────────────────────────────────────────────────
|
||||||
_COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
_COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_DIR="${PROJECT_DIR:-$(cd "$_COMMON_DIR/.." && pwd)}"
|
_MAIN_REPO_DIR="$(cd "$_COMMON_DIR/.." && pwd)"
|
||||||
|
PROJECT_DIR="${PROJECT_DIR:-$_MAIN_REPO_DIR}"
|
||||||
|
|
||||||
# ── Load local secrets (not tracked by git) ────────────────────────────────────
|
# ── Load Gitea configuration ────────────────────────────────────────────────────
|
||||||
if [ -f "$_COMMON_DIR/.env" ]; then
|
# Primary: ~/.gitea/config.yaml (requires GITEA_USER)
|
||||||
|
# Fallback: scripts/.env (backwards compat)
|
||||||
|
if ! eval "$(python "$_COMMON_DIR/_get_gitea_config.py" 2>/dev/null)"; then
|
||||||
|
# Fallback: source .env directly
|
||||||
|
if [ -f "$_COMMON_DIR/.env" ]; then
|
||||||
source "$_COMMON_DIR/.env"
|
source "$_COMMON_DIR/.env"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Default environment variables ──────────────────────────────────────────────
|
# ── Worktree isolation ─────────────────────────────────────────────────────────
|
||||||
export GITEA_URL="${GITEA_URL:-http://localhost:3000}"
|
GITEA_WORKTREE_DIR="${GITEA_WORKTREE_DIR:-$HOME/.gitea/worktrees}"
|
||||||
export GITEA_REPO="${GITEA_REPO:-pzhang_zywl/document_analyzer}"
|
_WORKTREE_PATH=""
|
||||||
|
|
||||||
|
setup_worktree() {
|
||||||
|
local user="$1"
|
||||||
|
local ts
|
||||||
|
ts="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
local worktree="$GITEA_WORKTREE_DIR/$user/$ts"
|
||||||
|
|
||||||
|
# Ensure origin/main is current so worktree starts from latest
|
||||||
|
git -C "$_MAIN_REPO_DIR" fetch origin main 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Creating worktree: $worktree (detached from origin/main)"
|
||||||
|
mkdir -p "$(dirname "$worktree")"
|
||||||
|
git -C "$_MAIN_REPO_DIR" worktree add --detach "$worktree" origin/main
|
||||||
|
touch "$worktree/.gitea-worktree"
|
||||||
|
|
||||||
|
PROJECT_DIR="$worktree"
|
||||||
|
_WORKTREE_PATH="$worktree"
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_worktree() {
|
||||||
|
local worktree="${_WORKTREE_PATH:-}"
|
||||||
|
if [ -z "$worktree" ] || [ ! -f "$worktree/.gitea-worktree" ]; then
|
||||||
|
echo "No worktree to clean up (not created by this session)."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
rm -f "$worktree/.gitea-worktree"
|
||||||
|
echo "Cleaning up worktree: $worktree"
|
||||||
|
git -C "$_MAIN_REPO_DIR" worktree remove --force "$worktree" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
# ── Validate required environment ──────────────────────────────────────────────
|
# ── Validate required environment ──────────────────────────────────────────────
|
||||||
require_token() {
|
require_token() {
|
||||||
if [ -z "${GITEA_API_TOKEN:-}" ]; then
|
if [ -z "${GITEA_API_TOKEN:-}" ]; then
|
||||||
echo "ERROR: GITEA_API_TOKEN is not set." >&2
|
echo "ERROR: GITEA_API_TOKEN is not set." >&2
|
||||||
echo "Set it in scripts/.env or export it:" >&2
|
echo "Set it in ~/.gitea/config.yaml (with GITEA_USER) or scripts/.env." >&2
|
||||||
echo " export GITEA_API_TOKEN=your-token" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -37,23 +72,27 @@ banner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ── Launch agent in selected mode ──────────────────────────────────────────────
|
# ── Launch agent in selected mode ──────────────────────────────────────────────
|
||||||
# Usage: launch_agent <agent-file> <agent-name> <single-shot-task> <polling-instruction>
|
# Usage: launch_agent <agent-name> <agent-file> <display-name> <single-shot-task> <polling-instruction>
|
||||||
#
|
#
|
||||||
# agent-name is the persona name (e.g. "Dev-Agent", "QE-Agent"). It is used to
|
# agent-name is the agent config name (e.g. "dev-agent", "qe-agent") used with
|
||||||
# prefix prompts so the model adopts the correct identity.
|
# --agent flag. The agent file lives in .claude/agents/<agent-name>.md (with
|
||||||
|
# frontmatter + body loaded as system prompt at session start).
|
||||||
|
#
|
||||||
|
# display-name is the persona name (e.g. "Dev-Agent", "QE-Agent") used to prefix
|
||||||
|
# prompts so the model adopts the correct identity.
|
||||||
#
|
#
|
||||||
# Mode 1 (single-shot): claude -p, runs once and exits.
|
# Mode 1 (single-shot): claude -p, runs once and exits.
|
||||||
# --dangerously-skip-permissions avoids blocking in non-interactive mode.
|
# --dangerously-skip-permissions avoids blocking in non-interactive mode.
|
||||||
# The project .claude/settings.json already sets permissionMode: bypass.
|
|
||||||
#
|
#
|
||||||
# Mode 2 (interactive polling): claude --agent, opens Claude Code TUI.
|
# Mode 2 (interactive polling): claude --agent, opens Claude Code TUI.
|
||||||
# The agent file defines startup behavior (e.g. /loop 10m) and the
|
# The agent config is loaded from .claude/agents/<agent-name>.md,
|
||||||
# user can observe or interact at any time.
|
# its body becomes the system prompt.
|
||||||
launch_agent() {
|
launch_agent() {
|
||||||
local agent_file="$1"
|
local agent_name="$1"
|
||||||
local agent_name="$2"
|
local agent_file="$2"
|
||||||
local single_shot_task="$3"
|
local display_name="$3"
|
||||||
local polling_instruction="${4:-}"
|
local single_shot_task="$4"
|
||||||
|
local polling_instruction="${5:-}"
|
||||||
|
|
||||||
echo "模式选择:"
|
echo "模式选择:"
|
||||||
echo " [1] 单次任务 — 检查 Issue 并处理,完成后自动退出 (automode)"
|
echo " [1] 单次任务 — 检查 Issue 并处理,完成后自动退出 (automode)"
|
||||||
@@ -70,16 +109,16 @@ launch_agent() {
|
|||||||
claude -p \
|
claude -p \
|
||||||
--agent "$agent_file" \
|
--agent "$agent_file" \
|
||||||
--dangerously-skip-permissions \
|
--dangerously-skip-permissions \
|
||||||
"你是 ${agent_name}。${single_shot_task}"
|
"你是 ${display_name}。${single_shot_task}"
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
echo "启动互动轮询模式..."
|
echo "启动互动轮询模式..."
|
||||||
echo "${agent_name} 进入 Claude Code 界面后将自动开始轮询"
|
echo "${display_name} 进入 Claude Code 界面后将自动开始轮询"
|
||||||
echo "你可以随时输入指令与 Agent 互动,按 Ctrl+C 停止"
|
echo "你可以随时输入指令与 Agent 互动,按 Ctrl+C 停止"
|
||||||
echo ""
|
echo ""
|
||||||
cd "$PROJECT_DIR"
|
cd "$PROJECT_DIR"
|
||||||
claude --agent "$agent_file" \
|
claude --agent "$agent_file" \
|
||||||
"你是 ${agent_name}。${polling_instruction}"
|
"你是 ${display_name}。${polling_instruction}"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "无效选择,请输入 1 或 2。"
|
echo "无效选择,请输入 1 或 2。"
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Print Gitea config for current user as shell-exportable variables.
|
||||||
|
|
||||||
|
Usage (bash):
|
||||||
|
eval "$(python scripts/_get_gitea_config.py)"
|
||||||
|
|
||||||
|
Usage (batch):
|
||||||
|
for /f "usebackq tokens=1,* delims= " %%a in (
|
||||||
|
`python scripts/_get_gitea_config.py --batch 2^>nul`
|
||||||
|
) do set "%%b"
|
||||||
|
|
||||||
|
Config: ~/.gitea/config.yaml — multi-profile YAML.
|
||||||
|
Env: GITEA_USER selects the profile (required).
|
||||||
|
Fallback: scripts/.env (backwards compat, no GITEA_USER needed).
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
CONFIG_PATH = os.path.expanduser("~/.gitea/config.yaml")
|
||||||
|
ENV_PATH = os.path.join(SCRIPT_DIR, ".env")
|
||||||
|
|
||||||
|
|
||||||
|
def _read_yaml_config(path):
|
||||||
|
import yaml
|
||||||
|
with open(path) as f:
|
||||||
|
return yaml.safe_load(f) or {}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
use_batch = "--batch" in sys.argv
|
||||||
|
prefix = "set" if use_batch else "export"
|
||||||
|
|
||||||
|
# 1) Primary: ~/.gitea/config.yaml
|
||||||
|
if os.path.exists(CONFIG_PATH):
|
||||||
|
user = os.environ.get("GITEA_USER")
|
||||||
|
if not user:
|
||||||
|
print(
|
||||||
|
"Error: GITEA_USER is not set. "
|
||||||
|
"Choose from: " + ", ".join(_read_yaml_config(CONFIG_PATH).keys()),
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
config = _read_yaml_config(CONFIG_PATH)
|
||||||
|
profile = config.get(user)
|
||||||
|
if not profile:
|
||||||
|
print(f"Error: user '{user}' not found in {CONFIG_PATH}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f'{prefix} GITEA_URL={profile.get("url", "")}')
|
||||||
|
print(f'{prefix} GITEA_REPO={profile.get("repo", "")}')
|
||||||
|
print(f'{prefix} GITEA_API_TOKEN={profile.get("token", "")}')
|
||||||
|
print(f'{prefix} GITEA_USER={user}')
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2) Fallback: scripts/.env
|
||||||
|
if os.path.exists(ENV_PATH):
|
||||||
|
print(f"Warning: {CONFIG_PATH} not found, falling back to {ENV_PATH}",
|
||||||
|
file=sys.stderr)
|
||||||
|
with open(ENV_PATH) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("export "):
|
||||||
|
var = line[7:]
|
||||||
|
if use_batch:
|
||||||
|
var = var.replace("export ", "set ", 1)
|
||||||
|
print(var)
|
||||||
|
if use_batch:
|
||||||
|
print(f"set GITEA_USER={os.environ.get('GITEA_USER', '')}")
|
||||||
|
else:
|
||||||
|
print(f"export GITEA_USER={os.environ.get('GITEA_USER', '')}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Error: {CONFIG_PATH} not found. Create it or set up scripts/.env.",
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+45
-12
@@ -21,19 +21,42 @@ import sys
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.error
|
import urllib.error
|
||||||
|
|
||||||
GITEA_URL = os.environ.get("GITEA_URL", "http://localhost:3000")
|
# Fix Windows GBK encoding: emoji and Chinese characters from Gitea API
|
||||||
GITEA_REPO = os.environ.get("GITEA_REPO", "pzhang_zywl/document_analyzer")
|
# crash print() under the default Windows code page.
|
||||||
GITEA_TOKEN = os.environ.get("GITEA_API_TOKEN", "")
|
try:
|
||||||
DEV_AGENT_ID = os.environ.get("DEV_AGENT_ID", "da-01")
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
QE_AGENT_ID = os.environ.get("QE_AGENT_ID", "")
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _load_gitea_config():
|
||||||
|
"""Load Gitea URL, repo, and token from ~/.gitea/config.yaml or env vars."""
|
||||||
|
config_path = os.path.expanduser("~/.gitea/config.yaml")
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
import yaml # requires pyyaml
|
||||||
|
with open(config_path) as f:
|
||||||
|
config = yaml.safe_load(f) or {}
|
||||||
|
user = os.environ.get("GITEA_USER")
|
||||||
|
if not user:
|
||||||
|
print("Error: GITEA_USER is not set (required for ~/.gitea/config.yaml).",
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
profile = config.get(user)
|
||||||
|
if not profile:
|
||||||
|
print(f"Error: user '{user}' not found in {config_path}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
return (profile.get("url", ""), profile.get("repo", ""),
|
||||||
|
profile.get("token", ""))
|
||||||
|
# Fallback: plain env vars (for CI / backwards compat)
|
||||||
|
return (os.environ.get("GITEA_URL", ""),
|
||||||
|
os.environ.get("GITEA_REPO", ""),
|
||||||
|
os.environ.get("GITEA_API_TOKEN", ""))
|
||||||
|
|
||||||
|
|
||||||
|
GITEA_URL, GITEA_REPO, GITEA_TOKEN = _load_gitea_config()
|
||||||
|
GITEA_USER = os.environ.get("GITEA_USER", "")
|
||||||
|
|
||||||
# Signature appended to all comments / PR bodies
|
# Signature appended to all comments / PR bodies
|
||||||
if QE_AGENT_ID:
|
AGENT_SIG = f"\n\n---\n[{GITEA_USER}]" if GITEA_USER else ""
|
||||||
AGENT_ID = QE_AGENT_ID
|
|
||||||
AGENT_SIG = f"\n\n---\n[qe-agent: {QE_AGENT_ID}]"
|
|
||||||
else:
|
|
||||||
AGENT_ID = DEV_AGENT_ID
|
|
||||||
AGENT_SIG = f"\n\n---\n[{DEV_AGENT_ID}]"
|
|
||||||
|
|
||||||
BASE = f"{GITEA_URL}/api/v1/repos/{GITEA_REPO}"
|
BASE = f"{GITEA_URL}/api/v1/repos/{GITEA_REPO}"
|
||||||
|
|
||||||
@@ -157,7 +180,7 @@ def blocked_check():
|
|||||||
print(f"Checked {len(all_blocked)} blocked issue(s): still blocked.")
|
print(f"Checked {len(all_blocked)} blocked issue(s): still blocked.")
|
||||||
|
|
||||||
|
|
||||||
def get_issue(num):
|
def get_issue(num, with_comments=True):
|
||||||
i = _req("GET", f"/issues/{num}")
|
i = _req("GET", f"/issues/{num}")
|
||||||
print(f"## #{i['number']}: {i['title']}")
|
print(f"## #{i['number']}: {i['title']}")
|
||||||
print(f"State: {i['state']}")
|
print(f"State: {i['state']}")
|
||||||
@@ -165,6 +188,16 @@ def get_issue(num):
|
|||||||
print(f"Labels: {', '.join(labels) if labels else 'none'}")
|
print(f"Labels: {', '.join(labels) if labels else 'none'}")
|
||||||
print()
|
print()
|
||||||
print(i.get("body", "(no description)"))
|
print(i.get("body", "(no description)"))
|
||||||
|
if with_comments:
|
||||||
|
comments = _req_safe("GET", f"/issues/{num}/comments")
|
||||||
|
if comments:
|
||||||
|
print(f"\n--- Comments ({len(comments)}) ---")
|
||||||
|
for c in comments:
|
||||||
|
user = c.get("user", {}).get("login", "unknown")
|
||||||
|
created = c.get("created_at", "")[:16]
|
||||||
|
body = c.get("body", "")
|
||||||
|
print(f"\n[{user}] {created}")
|
||||||
|
print(body)
|
||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Create a Gitea issue when CI fails. Called from ci.yml on failure."""
|
"""Create a Gitea issue when CI fails. Called from CI workflows."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
@@ -6,9 +6,6 @@ import os
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.error
|
import urllib.error
|
||||||
|
|
||||||
GITEA_URL = "http://localhost:3000"
|
|
||||||
REPO = "pzhang_zywl/document_analyzer"
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -16,14 +13,21 @@ def main():
|
|||||||
parser.add_argument("--branch", required=True)
|
parser.add_argument("--branch", required=True)
|
||||||
parser.add_argument("--run", required=True)
|
parser.add_argument("--run", required=True)
|
||||||
parser.add_argument("--message", required=True)
|
parser.add_argument("--message", required=True)
|
||||||
|
parser.add_argument("--gitea-url", default=os.environ.get("GITEA_URL", ""),
|
||||||
|
help="Gitea instance URL (default: $GITEA_URL)")
|
||||||
|
parser.add_argument("--repo", default=os.environ.get("GITEA_REPO", ""),
|
||||||
|
help="Repo path e.g. org/repo (default: $GITEA_REPO)")
|
||||||
parser.add_argument("--api-token", default=os.environ.get("GITEA_API_TOKEN", ""))
|
parser.add_argument("--api-token", default=os.environ.get("GITEA_API_TOKEN", ""))
|
||||||
parser.add_argument("--workflow", default="CI", help="Workflow name that triggered this (default: CI)")
|
parser.add_argument("--workflow", default="CI", help="Workflow name (default: CI)")
|
||||||
parser.add_argument("--labels", default="ci-failure",
|
parser.add_argument("--labels", default="ci-failure",
|
||||||
help="Comma-separated labels for the issue (default: ci-failure)")
|
help="Comma-separated labels (default: ci-failure)")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.gitea_url or not args.repo:
|
||||||
|
parser.error("--gitea-url and --repo are required (or set GITEA_URL and GITEA_REPO)")
|
||||||
|
|
||||||
sha_short = args.sha[:7]
|
sha_short = args.sha[:7]
|
||||||
run_url = f"{GITEA_URL}/{REPO}/actions/runs/{args.run}"
|
run_url = f"{args.gitea_url}/{args.repo}/actions/runs/{args.run}"
|
||||||
labels = [l.strip() for l in args.labels.split(",") if l.strip()]
|
labels = [l.strip() for l in args.labels.split(",") if l.strip()]
|
||||||
|
|
||||||
title = f"[{args.workflow}] Failure: {args.message[:80]}"
|
title = f"[{args.workflow}] Failure: {args.message[:80]}"
|
||||||
@@ -45,7 +49,7 @@ def main():
|
|||||||
"labels": labels,
|
"labels": labels,
|
||||||
}).encode("utf-8")
|
}).encode("utf-8")
|
||||||
|
|
||||||
url = f"{GITEA_URL}/api/v1/repos/{REPO}/issues"
|
url = f"{args.gitea_url}/api/v1/repos/{args.repo}/issues"
|
||||||
req = urllib.request.Request(url, data=payload, method="POST")
|
req = urllib.request.Request(url, data=payload, method="POST")
|
||||||
req.add_header("Authorization", f"token {args.api_token}")
|
req.add_header("Authorization", f"token {args.api_token}")
|
||||||
req.add_header("Content-Type", "application/json")
|
req.add_header("Content-Type", "application/json")
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
title Dev-Agent - Gitea Issue Worker
|
|
||||||
|
|
||||||
:: ── Change to project root ────────────────────────────────────────────────────
|
|
||||||
cd /d "%~dp0.."
|
|
||||||
|
|
||||||
:: ── Load .env (batch-compatible parser: "export KEY=VALUE" → set KEY=VALUE) ──
|
|
||||||
if exist "scripts\.env" (
|
|
||||||
for /f "usebackq tokens=2,3 delims== " %%a in ("scripts\.env") do set %%a=%%b
|
|
||||||
)
|
|
||||||
|
|
||||||
:: ── Defaults ──────────────────────────────────────────────────────────────────
|
|
||||||
if "%GITEA_URL%"=="" set GITEA_URL=http://localhost:3000
|
|
||||||
if "%GITEA_REPO%"=="" set GITEA_REPO=pzhang_zywl/document_analyzer
|
|
||||||
if "%DEV_AGENT_ID%"=="" set DEV_AGENT_ID=da-01
|
|
||||||
|
|
||||||
:: ── Validate token ────────────────────────────────────────────────────────────
|
|
||||||
if "%GITEA_API_TOKEN%"=="" (
|
|
||||||
echo ERROR: GITEA_API_TOKEN is not set.
|
|
||||||
echo Set it in scripts\.env or in your environment.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ============================================
|
|
||||||
echo Dev-Agent 启动器
|
|
||||||
echo ============================================
|
|
||||||
echo.
|
|
||||||
echo 模式选择:
|
|
||||||
echo [1] 单次任务 - 检查 Issue 并处理,完成后退出 (automode^)
|
|
||||||
echo [2] 互动轮询 - 进入 Claude Code 界面,每 10 分钟轮询
|
|
||||||
echo.
|
|
||||||
set /p MODE="请输入 (1/2): "
|
|
||||||
|
|
||||||
if "%MODE%"=="1" (
|
|
||||||
echo.
|
|
||||||
echo 执行单次检查 (automode)...
|
|
||||||
claude -p --agent agents/DEV_AGENT.md --dangerously-skip-permissions "你是 Dev-Agent。执行一次 Issue 巡检(单次任务,不要用 /loop):1. agent_poller.py --action list 列出所有打开的 Issue 2. 跳过纯测试 3. 逐个走闭环:分析-开发-pytest-commit-push-create-pr-CI-merge-pr-通知QE 4. 退出。"
|
|
||||||
pause
|
|
||||||
exit /b 0
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%MODE%"=="2" (
|
|
||||||
echo.
|
|
||||||
echo 启动互动轮询模式...
|
|
||||||
echo Dev-Agent 进入 Claude Code 界面后将自动每 10 分钟轮询 Gitea Issue
|
|
||||||
echo 按 Ctrl+C 停止
|
|
||||||
claude --agent agents/DEV_AGENT.md "你是 Dev-Agent。现在开始工作。使用 /loop 10m 每 10 分钟 python scripts/agent_poller.py --action list 检查 Issue,跳过纯测试,有则走完整闭环,无则报告 main healthy。保持对话开放。"
|
|
||||||
pause
|
|
||||||
exit /b 0
|
|
||||||
)
|
|
||||||
|
|
||||||
echo 无效选择。
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
@@ -1,21 +1,31 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Dev-Agent 启动脚本 — 单次任务 + 互动轮询 两种模式
|
# Dev-Agent 启动脚本 — 单次任务 + 互动轮询 两种模式
|
||||||
# 用法: bash scripts/start_dev_agent.sh
|
# 用法: bash scripts/start_dev_agent.sh <GITEA_USER>
|
||||||
# 前置: 在 scripts/.env 中设置 GITEA_API_TOKEN
|
# 示例: bash scripts/start_dev_agent.sh pzhang_dev_agent_01
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <GITEA_USER>"
|
||||||
|
echo "Example: $0 pzhang_dev_agent_01"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export GITEA_USER="$1"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
source "$SCRIPT_DIR/_common.sh"
|
source "$SCRIPT_DIR/_common.sh"
|
||||||
|
|
||||||
# Agent 标识: da-MMDD-HHmm,可通过环境变量覆盖
|
# Switch to isolated worktree so multiple agents don't conflict
|
||||||
export DEV_AGENT_ID="${DEV_AGENT_ID:-da-$(date +%m%d-%H%M)}"
|
setup_worktree "$GITEA_USER"
|
||||||
|
|
||||||
|
# Cleanup worktree on exit (optional, comment out to keep for debugging)
|
||||||
|
trap 'cleanup_worktree' EXIT
|
||||||
|
|
||||||
banner "Dev"
|
banner "Dev"
|
||||||
require_token
|
require_token
|
||||||
|
|
||||||
launch_agent \
|
launch_agent \
|
||||||
"agents/DEV_AGENT.md" \
|
"dev-agent" \
|
||||||
|
"$PROJECT_DIR/.claude/agents/dev-agent.md" \
|
||||||
"Dev-Agent" \
|
"Dev-Agent" \
|
||||||
"执行一次 Issue 巡检(单次任务,不要用 /loop):
|
"执行一次 Issue 巡检(单次任务,不要用 /loop):
|
||||||
1. python scripts/agent_poller.py --action list 列出所有打开的 Issue
|
1. python scripts/agent_poller.py --action list 列出所有打开的 Issue
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generic session 启动脚本 — 为人类用户提供 worktree 隔离
|
||||||
|
# 用法: bash scripts/start_generic.sh
|
||||||
|
# GITEA_USER 默认 pzhangzywl(人类用户),可通过环境变量覆盖
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
export GITEA_USER="${GITEA_USER:-pzhangzywl}"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/_common.sh"
|
||||||
|
|
||||||
|
setup_worktree "$GITEA_USER"
|
||||||
|
trap 'cleanup_worktree' EXIT
|
||||||
|
|
||||||
|
banner "Generic"
|
||||||
|
require_token
|
||||||
|
|
||||||
|
echo "工作目录: $PROJECT_DIR"
|
||||||
|
echo ""
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
claude
|
||||||
@@ -1,21 +1,31 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# QE-Agent 启动脚本 — 单次任务 + 互动轮询 两种模式
|
# QE-Agent 启动脚本 — 单次任务 + 互动轮询 两种模式
|
||||||
# 用法: bash scripts/start_qe_agent.sh
|
# 用法: bash scripts/start_qe_agent.sh <GITEA_USER>
|
||||||
# 前置: 在 scripts/.env 中设置 GITEA_API_TOKEN
|
# 示例: bash scripts/start_qe_agent.sh pzhang_qe_agent_01
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <GITEA_USER>"
|
||||||
|
echo "Example: $0 pzhang_qe_agent_01"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export GITEA_USER="$1"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
source "$SCRIPT_DIR/_common.sh"
|
source "$SCRIPT_DIR/_common.sh"
|
||||||
|
|
||||||
# Agent 标识: qa-MMDD-HHmm,可通过环境变量覆盖
|
# Switch to isolated worktree so multiple agents don't conflict
|
||||||
export QE_AGENT_ID="${QE_AGENT_ID:-qa-$(date +%m%d-%H%M)}"
|
setup_worktree "$GITEA_USER"
|
||||||
|
|
||||||
|
# Cleanup worktree on exit (optional, comment out to keep for debugging)
|
||||||
|
trap 'cleanup_worktree' EXIT
|
||||||
|
|
||||||
banner "QE"
|
banner "QE"
|
||||||
require_token
|
require_token
|
||||||
|
|
||||||
launch_agent \
|
launch_agent \
|
||||||
"agents/QE_AGENT.md" \
|
"qe-agent" \
|
||||||
|
"$PROJECT_DIR/.claude/agents/qe-agent.md" \
|
||||||
"QE-Agent" \
|
"QE-Agent" \
|
||||||
"执行一次 Issue 巡检(单次任务,不要用 /loop):
|
"执行一次 Issue 巡检(单次任务,不要用 /loop):
|
||||||
1. python scripts/agent_poller.py --action list --labels test-code 检查 test-code Issue
|
1. python scripts/agent_poller.py --action list --labels test-code 检查 test-code Issue
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ LLM configuration is read from secrets.yaml (searched in order):
|
|||||||
2. ~/.openclaw/config/secrets.yaml
|
2. ~/.openclaw/config/secrets.yaml
|
||||||
3. ~/.openclaw/workspace-document-analyzer/config/secrets.yaml
|
3. ~/.openclaw/workspace-document-analyzer/config/secrets.yaml
|
||||||
|
|
||||||
deepseek.apiKey / deepseek.baseUrl → text model (deepseek-v4-flash)
|
deepseek.apiKey / deepseek.baseUrl → text model (deepseek-v4-pro)
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
TEST_IR_PATH — path to IR JSON (default: output/final/ir_final.json)
|
TEST_IR_PATH — path to IR JSON (default: output/final/ir_final.json)
|
||||||
@@ -198,11 +198,11 @@ def parsed_data(parsed_path: str | None) -> dict | None:
|
|||||||
class _AcceptanceLLM:
|
class _AcceptanceLLM:
|
||||||
"""Thin LLM wrapper for acceptance tests.
|
"""Thin LLM wrapper for acceptance tests.
|
||||||
|
|
||||||
Uses deepseek-v4-flash for text (Layer C QE audit) via OpenAI-compatible API,
|
Uses deepseek-v4-pro for text (Layer C QE audit) via OpenAI-compatible API,
|
||||||
configured from ~/.openclaw/config/secrets.yaml.
|
configured from ~/.openclaw/config/secrets.yaml.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TEXT_MODEL = "deepseek-v4-flash"
|
TEXT_MODEL = "deepseek-v4-pro"
|
||||||
IMAGE_MODEL = "qwen3-vl-plus"
|
IMAGE_MODEL = "qwen3-vl-plus"
|
||||||
TIMEOUT = 180
|
TIMEOUT = 180
|
||||||
MAX_RETRIES = 3
|
MAX_RETRIES = 3
|
||||||
@@ -277,7 +277,7 @@ class _AcceptanceLLM:
|
|||||||
def llm_client():
|
def llm_client():
|
||||||
"""Create an LLM client for acceptance tests.
|
"""Create an LLM client for acceptance tests.
|
||||||
|
|
||||||
Uses deepseek-v4-flash for text (Layer C QE audit), configured from
|
Uses deepseek-v4-pro for text (Layer C QE audit), configured from
|
||||||
~/.openclaw/config/secrets.yaml deepseek section.
|
~/.openclaw/config/secrets.yaml deepseek section.
|
||||||
"""
|
"""
|
||||||
return _AcceptanceLLM()
|
return _AcceptanceLLM()
|
||||||
|
|||||||
@@ -92,3 +92,10 @@ def test_sample_ir_json_is_valid():
|
|||||||
assert isinstance(data, (dict, list))
|
assert isinstance(data, (dict, list))
|
||||||
else:
|
else:
|
||||||
pytest.skip("Sample IR JSON not found")
|
pytest.skip("Sample IR JSON not found")
|
||||||
|
|
||||||
|
|
||||||
|
# -- QE-Agent workflow test --------------------------------------------------
|
||||||
|
|
||||||
|
def test_qe_agent_workflow():
|
||||||
|
"""QE-Agent workflow smoke test: basic test discovery works."""
|
||||||
|
assert True
|
||||||
|
|||||||
Reference in New Issue
Block a user