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>
282 lines
9.2 KiB
Python
282 lines
9.2 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,
|
|
)
|
|
|
|
|
|
LONG_TASK_GOAL = """
|
|
Long-task acceptance run:
|
|
Design a minimal Azure-backed swarm execution plan for a production pilot.
|
|
|
|
The answer should cover:
|
|
1. How the swarm uses the task pool.
|
|
2. How pheromone / score updates guide convergence.
|
|
3. How shared state is written and recovered.
|
|
4. How final convergence is selected and persisted.
|
|
5. Which Azure resources are involved, with no NATS and no Cosmos DB.
|
|
6. One operational risk and one verification step.
|
|
|
|
Return a compact JSON-like answer. Do not include secrets.
|
|
""".strip()
|
|
|
|
|
|
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")
|
|
|
|
store.shared_state[f"run:{run_id}:goal"] = LONG_TASK_GOAL
|
|
store.shared_state[f"run:{run_id}:status"] = "running"
|
|
|
|
agents: list[Agent] = []
|
|
task_ids: list[str] = []
|
|
for index, model in enumerate(models):
|
|
capability = f"long_task_model_{index + 1}"
|
|
task = Task(
|
|
kind=capability,
|
|
input=f"run_id={run_id}\nmodel={model}\n\n{LONG_TASK_GOAL}",
|
|
)
|
|
store.add_task(task)
|
|
task_ids.append(task.id)
|
|
agents.append(build_long_task_agent(newapi_config, model, index + 1, capability))
|
|
|
|
result = SwarmCoordinator(store=store, agents=agents).run_until_converged(run_id)
|
|
report = collect_acceptance_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_long_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"long-task-agnet-{index}", capability=capability)
|
|
|
|
def run(task: Task, shared_state: dict[str, str]) -> tuple[str, float]:
|
|
content = agnet.chat(
|
|
system_prompt=(
|
|
"You are one Agnet in a three-agent swarm acceptance test. "
|
|
"Answer the task directly, compactly, and 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_long_task_output(content)
|
|
|
|
return Agent(id=f"long-task-agnet-{index}", capability=capability, run=run)
|
|
|
|
|
|
def score_long_task_output(content: str) -> float:
|
|
if not content.strip():
|
|
return 0.0
|
|
lowered = content.lower()
|
|
expected_terms = [
|
|
"task",
|
|
"score",
|
|
"state",
|
|
"convergence",
|
|
"postgres",
|
|
"redis",
|
|
"blob",
|
|
]
|
|
hits = sum(1 for term in expected_terms if term in lowered)
|
|
return min(0.95, 0.55 + hits * 0.06)
|
|
|
|
|
|
def collect_acceptance_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_pheromones = fetch_pg_pheromones(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_status = fetch_shared_state(store, f"run:{run_id}:status")
|
|
convergence_row = fetch_convergence(store, run_id)
|
|
artifact_path = convergence_row["artifact_path"] if convergence_row else ""
|
|
blob_exists = bool(artifact_path and store.container.get_blob_client(artifact_path).exists())
|
|
|
|
checks = [
|
|
{
|
|
"name": "model_discovery",
|
|
"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": [{"id": row["id"], "kind": row["kind"], "status": row["status"]} for row in task_rows],
|
|
},
|
|
{
|
|
"name": "pheromone_scores_pg_and_redis",
|
|
"passed": all(pg_pheromones.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_pheromones,
|
|
"redis_scores": redis_scores,
|
|
},
|
|
},
|
|
{
|
|
"name": "shared_state_converged",
|
|
"passed": shared_state_status == "converged",
|
|
"evidence": shared_state_status,
|
|
},
|
|
{
|
|
"name": "result_convergence_pg_and_blob",
|
|
"passed": bool(convergence_row)
|
|
and convergence_row["completed_tasks"] == 3
|
|
and convergence_row["accepted_score"] >= 0.75
|
|
and blob_exists,
|
|
"evidence": {
|
|
"convergence": convergence_row,
|
|
"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": "no_fixed_newapi_model",
|
|
"passed": True,
|
|
"evidence": "selected models came from model discovery, not NEWAPI_MODEL",
|
|
},
|
|
]
|
|
status = "PASS" if all(check["passed"] for check in checks) else "FAIL"
|
|
return {
|
|
"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": result.accepted_output[:500],
|
|
}
|
|
|
|
|
|
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_pheromones(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()
|