The standalone prototype should be the root-level project shape for fengqun while preserving the existing planning documents already at the root. This keeps README, examples, tests, and the Python package directly discoverable without deleting the prior docs. Constraint: User clarified that swarm-minimal is the repository root, but other existing root files must remain. Rejected: Deleting existing root docs | They are part of the fengqun repository context and were explicitly protected. Confidence: high Scope-risk: narrow Directive: Keep secrets in ignored .env only; do not commit live credentials. Tested: python3 -B -m unittest discover -s tests; git diff --check; secret-pattern scan showed only placeholders/test values/task-id false positives. Not-tested: Remote web UI rendering after push.
109 lines
3.7 KiB
Python
109 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import json
|
|
import sys
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from swarm_minimal.core import ConsensusAgent, ConsensusSwarm, ConsensusVote
|
|
|
|
|
|
def main() -> None:
|
|
agents = build_agents()
|
|
result = ConsensusSwarm(
|
|
agents,
|
|
threshold=0.70,
|
|
min_margin=0.25,
|
|
max_rounds=4,
|
|
evaporation=0.9,
|
|
).run("选择 swarm-minimal 的最小持久调度收敛策略")
|
|
|
|
report = {
|
|
"status": "PASS" if result.converged and len(result.rounds) >= 2 else "FAIL",
|
|
"convergence_definition": {
|
|
"not_this": "不是所有任务跑完后简单取最高 score",
|
|
"this": "多 Agnet 根据共享候选分数多轮投票,信息素式累积证据,直到 leader_share 和 margin 同时越过阈值",
|
|
"threshold": 0.70,
|
|
"min_margin": 0.25,
|
|
"evaporation": 0.9,
|
|
},
|
|
"agents": [
|
|
{"id": agent.id, "role": agent.role, "weight": agent.weight}
|
|
for agent in agents
|
|
],
|
|
"accepted_candidate": result.accepted_candidate,
|
|
"accepted_score": result.accepted_score,
|
|
"rounds": [
|
|
{
|
|
"round": item.index,
|
|
"leader": item.leader,
|
|
"leader_share": round(item.leader_share, 4),
|
|
"margin": round(item.margin, 4),
|
|
"converged": item.converged,
|
|
"candidate_scores": {key: round(value, 4) for key, value in item.candidate_scores.items()},
|
|
"votes": [
|
|
{
|
|
"agent": vote.agent_id,
|
|
"role": vote.role,
|
|
"candidate": vote.candidate,
|
|
"confidence": vote.confidence,
|
|
"evidence": vote.evidence,
|
|
}
|
|
for vote in item.votes
|
|
],
|
|
}
|
|
for item in result.rounds
|
|
],
|
|
}
|
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
if report["status"] != "PASS":
|
|
raise SystemExit(1)
|
|
|
|
|
|
def build_agents() -> list[ConsensusAgent]:
|
|
return [
|
|
ConsensusAgent(
|
|
id="architecture-agnet",
|
|
role="architecture",
|
|
weight=1.0,
|
|
vote=lambda scores, state, round_index: ConsensusVote(
|
|
agent_id="architecture-agnet",
|
|
role="architecture",
|
|
candidate="lease_based_pg_queue",
|
|
confidence=0.44 if round_index == 1 else 0.67,
|
|
evidence="PostgreSQL task pool gives durable leases and recovery.",
|
|
),
|
|
),
|
|
ConsensusAgent(
|
|
id="reliability-agnet",
|
|
role="reliability",
|
|
weight=1.2,
|
|
vote=lambda scores, state, round_index: ConsensusVote(
|
|
agent_id="reliability-agnet",
|
|
role="reliability",
|
|
candidate="lease_based_pg_queue",
|
|
confidence=0.38 if round_index == 1 else 0.72,
|
|
evidence="Lease expiry, retry, and outbox recovery need durable state.",
|
|
),
|
|
),
|
|
ConsensusAgent(
|
|
id="latency-agnet",
|
|
role="latency",
|
|
weight=0.8,
|
|
vote=lambda scores, state, round_index: ConsensusVote(
|
|
agent_id="latency-agnet",
|
|
role="latency",
|
|
candidate="redis_only_queue" if round_index == 1 else state.get("active_candidate", "lease_based_pg_queue"),
|
|
confidence=0.60 if round_index == 1 else 0.58,
|
|
evidence="Starts from Redis latency, then follows shared evidence after round 1.",
|
|
),
|
|
),
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|