[moz] impl(§22): P0 Phase 1 Discussion Broadcast #124

Merged
pangtong-fujunshi merged 1 commits from impl/p0-discussion-broadcast into main 2026-06-22 23:36:16 +00:00
2 changed files with 86 additions and 32 deletions
+43 -30
View File
@@ -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 <your-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 Issueassign 自己)
- 如果有分歧或不确定,在 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:
+43 -2
View File
@@ -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(身份+专长注入)"""