From dc1d444fedb1ba21560bb53915c62dbda90d3b43 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 23 Jun 2026 07:32:46 +0800 Subject: [PATCH] =?UTF-8?q?[moz]=20impl(=C2=A722):=20P0=20Phase=201=20Disc?= =?UTF-8?q?ussion=20Broadcast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DISCUSSION_PROMPT_TEMPLATE 改为 Gitea API(Issue comment + sub Issue 创建) - _build_discussion_prompt 从 must_haves 解析 repo/issue_number 注入模板 - _broadcast_claim 判断 action_type=issue_discussion → 用 discussion prompt - 新增 _get_task_action_type 辅助方法 - Gitea 创建 flow/direct 和 flow/discuss label --- src/daemon/spawner.py | 73 +++++++++++++++++++++++++------------------ src/daemon/ticker.py | 45 ++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 32 deletions(-) diff --git a/src/daemon/spawner.py b/src/daemon/spawner.py index 239c3a2..546bd6b 100644 --- a/src/daemon/spawner.py +++ b/src/daemon/spawner.py @@ -103,7 +103,7 @@ SPAWN_PROMPT_TEMPLATE = """{identity_section} """ -DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与讨论。这是一个四相循环的讨论环节。 +DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与 Gitea Issue 讨论。 ## 讨论主题 @@ -119,14 +119,19 @@ DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与讨论。这是一个四 ## 你必须做什么 -读完需求后,在黑板 comment 回应(必须,不是可选): +读完需求后,在 Gitea Issue comment 回应(必须,不是可选): 1.【定位】这个需求和你有什么关系?你的专业能力能贡献什么? 2.【建议】你对实现方案有什么建议?(技术选型、数据来源、实现路径) -3.【认领】如果你需要参与,创建 sub task 并在 parent task comment 注册: - - 创建 sub task: POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks - body: {{"title": "...", "description": "...", "task_type": "...", "parent_task": "{task_id}", "must_haves": "{{\"capability\": \"...\"}}"}} - - 创建后在 parent task comment: "[你的角色名] 我创建了 sub: 任务名,我负责 简述" +3.【认领】如果你需要参与,创建 sub Issue 并在 parent Issue comment 注册: + - 创建 sub Issue: + ```bash + curl -X POST http://192.168.2.154:3000/api/v1/repos/{repo}/issues \ + -H "Authorization: token " \ + -H "Content-Type: application/json" \ + -d '{{"title": "[moz][sub][parent #{issue_number}] 任务名", "body": "Parent: #{issue_number}\n## 任务\n...", "assignees": ["{agent_id}"]}}' + ``` + - 创建后在 parent Issue comment: "[角色名] 我创建了 sub Issue #N: 任务名,我负责 简述" 4.【风险】如果你发现风险、不合理的假设、或遗漏的环节,直接提出 ⚠️ 每个 agent 必须 comment。即使你认为和自己无关,也要说明原因——这证明你读过并思考过了。 @@ -139,33 +144,28 @@ DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与讨论。这是一个四 例:[关羽] 这个策略需要风控,连亏 3 天应暂停。 例:[赵云] 数据已就绪,2024-08 缺失已补齐。 -## 黑板 API +## Gitea API -- 读黑板:GET http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}?expand=all -- 写 comment:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/comments - body: {{"author": "{agent_id}", "body": "内容"}} -- 创建 sub task:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks -- 认领任务:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{{sub_task_id}}/claim +- 读 Issue: GET http://192.168.2.154:3000/api/v1/repos/{repo}/issues/{issue_number} +- 读 Issue comments: GET http://192.168.2.154:3000/api/v1/repos/{repo}/issues/{issue_number}/comments +- 写 comment: POST http://192.168.2.154:3000/api/v1/repos/{repo}/issues/{issue_number}/comments + body: {{"body": "内容"}} +- 创建 sub Issue: POST http://192.168.2.154:3000/api/v1/repos/{repo}/issues ## 行为准则 -1. **你是自主的。**读黑板、思考、行动,不要等指令。 -2. **不重复别人的工作。**动手前先读黑板看谁在做什么(Separation)。 -3. **保持方向对齐。**你的产出方向和 parent goal 对齐,不确定时 @pangtong-fujunshi(Alignment)。 -4. **产出可共享。**产出写入黑板,让其他人能看到你的成果(Cohesion)。 -5. **不越界。**安全红线不要碰,超出能力的 @ 庞统升级(Boundary)。 -6. **随时讨论。**执行过程中需要协作时 @ 对应 Agent,讨论是灵活的不是固定阶段的。 +1. **你是自主的。** 读 Issue、思考、行动,不要等指令。 +2. **不重复别人的工作。** 动手前先读 Issue comment 看谁在做什么(Separation)。 +3. **保持方向对齐。** 你的产出方向和 parent goal 对齐,不确定时 @pangtong-fujunshi(Alignment)。 +4. **产出可共享。** 产出写入 Gitea Issue/PR,让其他人能看到你的成果(Cohesion)。 +5. **不越界。** 安全红线不要碰,超出能力的 @ 庞统升级(Boundary)。 +6. **随时讨论。** 执行过程中需要协作时 @ 对应 Agent,讨论是灵活的不是固定阶段的。 ## 讨论完成后 -- 如果讨论收敛到可执行的任务,直接创建 sub task -- 如果有分歧或不确定,在黑板上写 comment @ 庞统裁决 -- 标记完成: -```bash -curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status \ - -H 'Content-Type: application/json' \ - -d '{{"status": "done", "agent": "{agent_id}"}}' -``` +- 如果讨论收敛到可执行的任务,直接创建 sub Issue(assign 自己) +- 如果有分歧或不确定,在 Issue comment @pangtong-fujunshi 裁决 +- 标记完成(在 parent Issue comment 写总结) """ @@ -407,9 +407,22 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta def _build_discussion_prompt(self, task_id: str, title: str, description: str, must_haves: str, project_id: str, agent_id: str) -> str: - """构建讨论类 spawn prompt(§3.3 框架 + Boids)""" + """构建讨论类 spawn prompt(§22.2 Gitea API 版本)""" goal_snapshot = description or title - constraints = must_haves or "(无特殊约束)" + + # 从 must_haves 解析 issue 上下文 + repo = "sanguo/sanguo_moziplus_v2" + issue_number = "" + constraints = "(无特殊约束)" + try: + meta = json.loads(must_haves) if must_haves else {} + ctx = meta.get("context", {}) + repo = ctx.get("repo", repo) + issue_number = str(ctx.get("issue_number", "")) + if meta.get("steps"): + constraints = "\n".join(meta["steps"]) + except (json.JSONDecodeError, TypeError): + pass agent_identity = self._inject_agent_identity(agent_id) return DISCUSSION_PROMPT_TEMPLATE.format( @@ -419,8 +432,8 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta task_id=task_id, agent_id=agent_id, agent_identity=agent_identity, - api_host=self.api_host, - api_port=self.api_port, + repo=repo, + issue_number=issue_number, ) def _inject_agent_identity(self, agent_id: str) -> str: diff --git a/src/daemon/ticker.py b/src/daemon/ticker.py index b6a4933..7ced61a 100644 --- a/src/daemon/ticker.py +++ b/src/daemon/ticker.py @@ -1161,10 +1161,43 @@ Parent Task ID: {parent_task.id} if t.id not in self._broadcast_tracker: self._broadcast_tracker[t.id] = BroadcastRound(task_id=t.id) + # 分离 discussion tasks 和普通 claim tasks + discussion_tasks = [] + claim_tasks = [] + for t in broadcastable: + action_type = self._get_task_action_type(t) + if action_type == "issue_discussion": + discussion_tasks.append(t) + else: + claim_tasks.append(t) + spawned = [] for agent_id in idle_agents: - prompt = self._build_claim_prompt( - agent_id, broadcastable, project_id) + prompts_to_send = [] + + # discussion tasks: 每个 task 独立构建 discussion prompt + for t in discussion_tasks: + disc_prompt = self.spawner._build_discussion_prompt( + task_id=t.id, + title=t.title, + description=t.description or "", + must_haves=t.must_haves or "", + project_id=project_id, + agent_id=agent_id, + ) + prompts_to_send.append(disc_prompt) + + # claim tasks: 按原逻辑批量构建 claim prompt + if claim_tasks: + claim_prompt = self._build_claim_prompt( + agent_id, claim_tasks, project_id) + prompts_to_send.append(claim_prompt) + + # 合并 prompt(如果有多种类型) + if not prompts_to_send: + continue + prompt = "\n\n---\n\n".join(prompts_to_send) + try: session_id = await self.spawner.spawn_full_agent( agent_id=agent_id, @@ -1187,6 +1220,14 @@ Parent Task ID: {parent_task.id} return spawned + def _get_task_action_type(self, task) -> str: + """从 task.must_haves JSON 中提取 action_type""" + try: + meta = json.loads(task.must_haves) if task.must_haves else {} + return meta.get("action_type", "") + except (json.JSONDecodeError, TypeError, AttributeError): + return "" + def _build_claim_prompt(self, agent_id: str, tasks: list, project_id: str) -> str: """#03: 广播认领 prompt(身份+专长注入)"""