Files
fengqun/examples/run_real_code_task_acceptance.py
gongzhiyongandOmX 10a980b0bf Establish agent swarm quality evidence
Define Agent and swarm-specific acceptance evidence, move the reports under docs, and make the homepage point to the current standard, live run, model I/O, and handoff evidence.

Constraint: Agent quality standards are configured from industry AI and agent risk references because there is no single accepted swarm-Agent certification standard.

Rejected: Treating py_compile or unittest as the primary quality standard | they are evidence collection tools, not the Agent quality standard itself.

Confidence: high

Scope-risk: moderate

Directive: Keep future standard reports under docs/ and keep secrets in ignored local .env files only.

Tested: git diff --cached --check; python -B -m py_compile swarm_minimal/*.py examples/*.py tests/*.py; python -B -m unittest discover -s tests; python -u -B examples/run_academic_standard_evaluation.py

Not-tested: Did not rerun the full live Azure/NewAPI S07 scenario after moving docs; previous live run 3e8e58ae4e084bc8b90cf5c46f8992f3 passed before the docs relocation.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-16 14:36:47 +08:00

379 lines
14 KiB
Python

from pathlib import Path
from uuid import uuid4
import json
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from swarm_minimal.azure_store import PostgresRedisBlobSwarmStore
from swarm_minimal.config import SwarmConfig
from swarm_minimal.core import Agent, SwarmCoordinator, Task
from swarm_minimal.local_env import load_project_env
from swarm_minimal.newapi_agnet import (
NewApiAgnet,
NewApiChannelConfig,
discover_newapi_models,
select_distinct_models,
)
CODE_TASK = {
"title": "真实代码场景:给 swarm-minimal 做可交付的长任务改造方案",
"description": (
"基于当前 swarm-minimal Python 原型,设计一个代码级补丁方案:"
"让 NewAPI 三模型长任务验收更适合持续运行。方案必须覆盖模型自动发现、"
"长任务超时配置、失败模型重试或降级、Azure 持久化验收,以及测试命令。"
"输出控制在 700 字以内,并必须逐字包含指定测试命令。"
),
"target_files": [
"swarm_minimal/newapi_agnet.py",
"swarm_minimal/azure_store.py",
"examples/run_long_task_acceptance.py",
"tests/test_newapi_agnet.py",
"tests/test_minimal_swarm.py",
"README.md",
],
}
ACCEPTANCE_CRITERIA = [
"3 个 Agnet 必须来自模型发现结果,且模型互不相同。",
"3 个模型任务都必须完成,并写入 PostgreSQL task pool。",
"每个任务必须在 PostgreSQL 和 Redis pheromone score 中都有正分。",
"共享状态必须收敛为 converged。",
"收敛结果必须写入 PostgreSQL,并存在 Blob artifact。",
"Redis Stream 必须新增事件,证明任务事件流被写入。",
"被接受的模型输出必须引用至少 3 个真实目标文件。",
"被接受的模型输出必须包含可执行测试命令。",
"被接受的模型输出必须明确不能写死 NEWAPI_MODEL,要使用模型发现。",
"被接受的模型输出不能建议 NATS 或 Cosmos 作为 MVP 必需依赖。",
]
def main() -> None:
load_project_env(ROOT)
azure_config = SwarmConfig.from_env()
newapi_config = NewApiChannelConfig.from_env()
if newapi_config.timeout_seconds < 120:
newapi_config = NewApiChannelConfig(
base_url=newapi_config.base_url,
api_key=newapi_config.api_key,
model=newapi_config.model,
timeout_seconds=120,
)
store = PostgresRedisBlobSwarmStore(azure_config)
try:
store.ensure_schema()
models = select_distinct_models(discover_newapi_models(newapi_config), count=3)
run_id = uuid4().hex
stream_before = store.redis.xlen("swarm:events")
code_context = build_code_context()
prompt = build_task_prompt(code_context)
store.shared_state[f"run:{run_id}:goal"] = CODE_TASK["title"]
store.shared_state[f"run:{run_id}:status"] = "running"
agents: list[Agent] = []
task_ids: list[str] = []
for index, model in enumerate(models):
capability = f"real_code_model_{index + 1}"
task = Task(kind=capability, input=prompt)
store.add_task(task)
task_ids.append(task.id)
agents.append(build_code_task_agent(newapi_config, model, index + 1, capability))
result = SwarmCoordinator(store=store, agents=agents).run_until_converged(run_id)
report = collect_report(
store=store,
run_id=run_id,
result=result,
selected_models=models,
task_ids=task_ids,
stream_before=stream_before,
)
print(json.dumps(report, ensure_ascii=False, indent=2))
if report["summary"]["status"] != "PASS":
raise SystemExit(1)
finally:
store.close()
def build_code_context() -> str:
return """
--- swarm_minimal/newapi_agnet.py ---
Key surfaces:
- NewApiChannelConfig.from_env reads NEWAPI_BASE_URL / NEWAPI_API_KEY and optional NEWAPI_MODEL.
- discover_newapi_models(config) tries /v1/models, /models, /model.
- select_distinct_models(models, count=3) chooses unique model ids.
- build_model_test_agnets(config, models) creates one Agent per model.
- NewApiAgnet.chat posts to /v1/chat/completions and requires config.model.
--- swarm_minimal/azure_store.py ---
Key surfaces:
- PostgresRedisBlobSwarmStore persists task pool, pheromone scores, shared state, outbox, convergence.
- add_task writes swarm_tasks and swarm_pheromones, then emits task.created.
- complete_task writes task output, PostgreSQL score, Redis sorted-set score, observation, task.done.
- converge writes swarm_convergence and Blob artifact.
--- examples/run_long_task_acceptance.py ---
Key surfaces:
- Loads .env.
- Uses SwarmConfig and NewApiChannelConfig.
- Discovers models and selects 3 distinct models.
- Runs long task with Azure-backed store.
- Verifies model discovery, task pool, scores, shared state, convergence, Redis stream, no fixed model.
--- tests/test_newapi_agnet.py ---
Key surfaces:
- Mock HTTP client validates model discovery and chat payloads.
- Tests three NewAPI Agnets use three different models.
--- tests/test_minimal_swarm.py ---
Key surfaces:
- Validates minimal swarm resources and env parsing/redaction.
""".strip()
def build_task_prompt(code_context: str) -> str:
return (
f"{CODE_TASK['title']}\n\n"
f"任务说明:{CODE_TASK['description']}\n\n"
"目标文件:\n"
+ "\n".join(f"- {item}" for item in CODE_TASK["target_files"])
+ "\n\n验收标准:\n"
+ "\n".join(f"- {item}" for item in ACCEPTANCE_CRITERIA)
+ "\n\n当前代码上下文:\n"
+ code_context
+ "\n\n输出要求:\n"
"- 用中文输出。\n"
"- 控制在 700 字以内。\n"
"- 给出补丁计划,必须引用具体文件路径。\n"
"- 必须逐字包含以下测试命令:\n"
" ./.venv/bin/python -B -m unittest discover -s tests\n"
" ./.venv/bin/python -B examples/run_long_task_acceptance.py\n"
"- 明确说明模型必须自动发现,不允许写死 NEWAPI_MODEL。\n"
"- 如果提到 NATS 或 Cosmos,只能用于说明“不引入/不使用”。\n"
"- 不要包含任何真实密钥。\n"
)
def build_code_task_agent(config: NewApiChannelConfig, model: str, index: int, capability: str) -> Agent:
model_config = NewApiChannelConfig(
base_url=config.base_url,
api_key=config.api_key,
model=model,
timeout_seconds=config.timeout_seconds,
)
agnet = NewApiAgnet(model_config, agent_id=f"real-code-agnet-{index}", capability=capability)
def run(task: Task, shared_state: dict[str, str]) -> tuple[str, float]:
content = agnet.chat(
system_prompt=(
"You are a senior coding agent participating in a swarm acceptance test. "
"Analyze the provided real code context and propose a concrete patch plan. "
"Do not include secrets."
),
user_prompt=(
f"Task kind: {task.kind}\n"
f"Task input:\n{task.input}\n\n"
f"Shared state keys: {', '.join(sorted(shared_state.keys()))}"
),
)
return content, score_code_task_output(content)
return Agent(id=f"real-code-agnet-{index}", capability=capability, run=run)
def score_code_task_output(content: str) -> float:
lowered = content.lower()
checks = [
"swarm_minimal/newapi_agnet.py" in content,
"examples/run_long_task_acceptance.py" in content,
"tests/test_newapi_agnet.py" in content or "tests/test_minimal_swarm.py" in content,
"python" in lowered and "unittest" in lowered,
"discover" in lowered or "模型发现" in content,
"NEWAPI_MODEL" in content,
"timeout" in lowered or "超时" in content,
"retry" in lowered or "重试" in content or "降级" in content,
]
return min(0.97, 0.49 + sum(1 for item in checks if item) * 0.06)
def collect_report(
*,
store: PostgresRedisBlobSwarmStore,
run_id: str,
result,
selected_models: list[str],
task_ids: list[str],
stream_before: int,
) -> dict[str, object]:
task_rows = fetch_task_rows(store, task_ids)
pg_scores = fetch_pg_scores(store, task_ids)
redis_scores = {task_id: store.redis.zscore("swarm:pheromones", task_id) for task_id in task_ids}
stream_after = store.redis.xlen("swarm:events")
shared_state = fetch_shared_state(store, f"run:{run_id}:status")
convergence = fetch_convergence(store, run_id)
artifact_path = convergence["artifact_path"] if convergence else ""
blob_exists = bool(artifact_path and store.container.get_blob_client(artifact_path).exists())
accepted = result.accepted_output
checks = [
{
"name": "model_discovery_three_distinct_models",
"passed": len(set(selected_models)) == 3,
"evidence": selected_models,
},
{
"name": "task_pool_pg_done",
"passed": len(task_rows) == 3 and all(row["status"] == "done" for row in task_rows),
"evidence": task_rows,
},
{
"name": "pheromone_scores_pg_and_redis",
"passed": all(pg_scores.get(task_id, 0) > 0 for task_id in task_ids)
and all((redis_scores.get(task_id) or 0) > 0 for task_id in task_ids),
"evidence": {"pg_scores": pg_scores, "redis_scores": redis_scores},
},
{
"name": "shared_state_converged",
"passed": shared_state == "converged",
"evidence": shared_state,
},
{
"name": "result_convergence_pg_and_blob",
"passed": bool(convergence)
and convergence["completed_tasks"] == 3
and convergence["accepted_score"] >= 0.75
and blob_exists,
"evidence": {"convergence": convergence, "blob_exists": blob_exists},
},
{
"name": "redis_stream_events",
"passed": stream_after - stream_before >= 10,
"evidence": {"before": stream_before, "after": stream_after, "delta": stream_after - stream_before},
},
{
"name": "accepted_output_references_real_files",
"passed": count_referenced_target_files(accepted) >= 3,
"evidence": referenced_target_files(accepted),
},
{
"name": "accepted_output_includes_test_commands",
"passed": "./.venv/bin/python -B -m unittest discover -s tests" in accepted
and "./.venv/bin/python -B examples/run_long_task_acceptance.py" in accepted,
"evidence": "must include exact unittest and long-task commands",
},
{
"name": "accepted_output_keeps_model_discovery",
"passed": ("模型发现" in accepted or "discover" in accepted.lower())
and "NEWAPI_MODEL" in accepted
and ("不" in accepted or "not" in accepted.lower()),
"evidence": "must explicitly reject fixed NEWAPI_MODEL for three-model flow",
},
{
"name": "accepted_output_no_nats_or_cosmos_requirement",
"passed": no_required_nats_or_cosmos(accepted),
"evidence": "MVP must remain PostgreSQL/Redis/Blob based",
},
]
status = "PASS" if all(check["passed"] for check in checks) else "FAIL"
return {
"task": CODE_TASK,
"acceptance_criteria": ACCEPTANCE_CRITERIA,
"summary": {
"status": status,
"run_id": run_id,
"accepted_score": result.accepted_score,
"completed_tasks": result.completed_tasks,
"accepted_task_id": result.accepted_task_id,
"artifact_path": artifact_path,
},
"selected_models": selected_models,
"checks": checks,
"accepted_output_preview": accepted[:900],
}
def referenced_target_files(text: str) -> list[str]:
return [item for item in CODE_TASK["target_files"] if item in text]
def count_referenced_target_files(text: str) -> int:
return len(referenced_target_files(text))
def no_required_nats_or_cosmos(text: str) -> bool:
lowered = text.lower()
if "nats" not in lowered and "cosmos" not in lowered:
return True
negative_markers = [
"不引入",
"不使用",
"无需",
"不要",
"no ",
"without",
"not use",
]
for name in ["nats", "cosmos"]:
if name not in lowered:
continue
if not any(marker in lowered[max(0, lowered.find(name) - 24) : lowered.find(name) + 24] for marker in negative_markers):
return False
return True
def fetch_task_rows(store: PostgresRedisBlobSwarmStore, task_ids: list[str]) -> list[dict[str, object]]:
with store.pg.cursor() as cur:
cur.execute(
"select id, kind, status, claimed_by, score from swarm_tasks where id = any(%s) order by kind",
(task_ids,),
)
return [
{"id": row[0], "kind": row[1], "status": row[2], "claimed_by": row[3], "score": row[4]}
for row in cur.fetchall()
]
def fetch_pg_scores(store: PostgresRedisBlobSwarmStore, task_ids: list[str]) -> dict[str, float]:
with store.pg.cursor() as cur:
cur.execute("select task_id, score from swarm_pheromones where task_id = any(%s)", (task_ids,))
return {row[0]: row[1] for row in cur.fetchall()}
def fetch_shared_state(store: PostgresRedisBlobSwarmStore, key: str) -> str | None:
with store.pg.cursor() as cur:
cur.execute("select value from swarm_shared_state where key = %s", (key,))
row = cur.fetchone()
return row[0] if row else None
def fetch_convergence(store: PostgresRedisBlobSwarmStore, run_id: str) -> dict[str, object] | None:
with store.pg.cursor() as cur:
cur.execute(
"""
select run_id, completed_tasks, accepted_score, accepted_task_id, artifact_path
from swarm_convergence
where run_id = %s
""",
(run_id,),
)
row = cur.fetchone()
if not row:
return None
return {
"run_id": row[0],
"completed_tasks": row[1],
"accepted_score": row[2],
"accepted_task_id": row[3],
"artifact_path": row[4],
}
if __name__ == "__main__":
main()