包含从项目创建至今的全部代码首次入库: 核心框架: - TaskQueue(SQLite,6 种状态,原子 dequeue) - Executor(asyncio 并发,semaphore 限流,agent_role 路由) - Scheduler(APScheduler,三队列独立调度) - Watchdog(超时检测 + 优先级防饥饿) - Scanner(项目扫描,README/TODO/CLAUDE.md 提取) Agent 体系(20+ 角色): - 项目交付组:architect/developer/tester/productizer - 基础技术组:base-architect/validator/hw/os/kernel/lowlevel/system-tester - 算法组:algo-antishake/position/nav - 洞察组:planner/vision-analyst/media-producer - 市场组:market-pm/sport/elder/safety - 组织层:boss/group-leader/senior-dev/ops - 平台扩展:nrf-dev/esp32-dev - 三专项调研:rtp-researcher/net-researcher/kernel-analyzer 工具层: - ProjectMemory(goals/facts/history SQLite) - VersionPipeline(并行版本流水线 + 反幻觉门控) - EventBus(Redis Streams,5 consumer groups) - DailyReport(append-only 日报 + Boss 决策视图) - AgentScorer(4 维评分:完成度/质量/自主性/协作) - DeviceAgent(SSH rsync + 板端执行) - EnvCollector(本机+SSH 环境采集) - ArchVersion(快照/优化/promote/rollback) Dashboard(React + Vite + Tailwind): - 看板/项目/配置/洞察/日报/Visual 六标签页 - WebSocket 实时更新 - Markdown 渲染(react-markdown + remark-gfm) 测试:314 passed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.6 KiB
Python
92 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import pytest
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
from nmfs_agents.agents.manager import ManagerAgent
|
|
from nmfs_agents.agents.productizer import ProductizerAgent
|
|
from nmfs_agents.agents.architect import ArchitectAgent
|
|
from nmfs_agents.config import AgentsConfig, ProjectConfig, SchedulerConfig, ClaudeConfig, FeishuConfig
|
|
from nmfs_agents.core.queue import TaskQueue, Task
|
|
|
|
|
|
def _make_project(base: Path, name: str, has_issues: bool = True) -> Path:
|
|
p = base / name
|
|
p.mkdir(parents=True)
|
|
if has_issues:
|
|
(p / "README.md").write_text(f"# {name}\n## 已知问题\n- {name} 存在待修复问题\n")
|
|
else:
|
|
(p / "README.md").write_text(f"# {name}\n## 快速开始\nfoo\n")
|
|
(p / "RELEASE.md").write_text("## v0.1\n")
|
|
(p / "pyproject.toml").write_text("[project]\nname='x'\n")
|
|
(p / "tests").mkdir()
|
|
((p / "tests") / "test_a.py").write_text("def test_a(): pass\n")
|
|
return p
|
|
|
|
|
|
def _cfg(tmp_path: Path) -> tuple[AgentsConfig, TaskQueue]:
|
|
q = TaskQueue(db_path=tmp_path / "tasks.db")
|
|
yolo = _make_project(tmp_path, "yolo", has_issues=True)
|
|
emb = _make_project(tmp_path, "embedding", has_issues=False)
|
|
cfg = AgentsConfig(
|
|
projects={
|
|
"yolo": ProjectConfig(path=yolo, mode="report"),
|
|
"embedding": ProjectConfig(path=emb, mode="report"),
|
|
},
|
|
scheduler=SchedulerConfig(max_concurrent=2),
|
|
claude=ClaudeConfig(api_key="fake"),
|
|
feishu=FeishuConfig(),
|
|
devices={},
|
|
)
|
|
return cfg, q
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_manager_full_cycle(tmp_path):
|
|
"""Manager 完整循环:扫描 → 入队 → 执行 → 全部 done。"""
|
|
cfg, q = _cfg(tmp_path)
|
|
manager = ManagerAgent(cfg, queue=q)
|
|
mock_result = MagicMock(status="done", summary="分析完成")
|
|
with patch("nmfs_agents.core.executor.DeveloperAgent") as MockDev:
|
|
MockDev.return_value.run.return_value = mock_result
|
|
stats = await manager.run_cycle()
|
|
assert stats["enqueued"] >= 1
|
|
assert stats["executed"] >= 1
|
|
status = manager.get_status()
|
|
assert status.get("done", 0) >= 1
|
|
|
|
|
|
def test_productizer_evaluates_real_project(tmp_path):
|
|
"""Productizer 评估真实项目结构,高质量项目应得分 >= 50。"""
|
|
cfg, q = _cfg(tmp_path)
|
|
agent = ProductizerAgent(cfg)
|
|
task = Task(project="embedding", type="productize", title="评估",
|
|
priority=3, mode="report", agent_role="productizer", id=10)
|
|
result = agent.run(task)
|
|
assert result.status == "done"
|
|
assert agent.last_report.score >= 50
|
|
|
|
|
|
def test_architect_enqueues_from_two_projects(tmp_path):
|
|
"""Architect 分析两个项目后应提取并入队预研任务。"""
|
|
from nmfs_agents.agents.developer import AgentResult
|
|
cfg, q = _cfg(tmp_path)
|
|
agent = ArchitectAgent(cfg, queue=q)
|
|
task = Task(project="ALL", type="architect", title="全局架构分析",
|
|
priority=2, mode="report", agent_role="architect", id=11)
|
|
|
|
report = "两个项目可整合。\n预研任务:\n- [预研] 视觉语义搜索引擎\n- [预研] 统一推理 SDK"
|
|
verify_ok = MagicMock(returncode=0, stdout="claude 1.0", stderr="")
|
|
with patch("subprocess.run", return_value=verify_ok), \
|
|
patch("nmfs_agents.agents.architect._run_with_log",
|
|
return_value=AgentResult(status="done", summary=report)):
|
|
result = agent.run(task)
|
|
|
|
assert result.status == "done"
|
|
with q._conn() as c:
|
|
count = c.execute(
|
|
"SELECT COUNT(*) FROM tasks WHERE type='architect'"
|
|
).fetchone()[0]
|
|
assert count >= 2
|