Files
qiuruiandClaude Sonnet 4.6 df59fc0c83 feat: NMFS Agents 全量入库(org v2 + dashboard + 多队列 + 组织架构)
包含从项目创建至今的全部代码首次入库:

核心框架:
- 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>
2026-03-10 07:24:19 +08:00

232 lines
10 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""NMFS Agents 启动入口"""
from __future__ import annotations
import argparse
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
def main() -> None:
parser = argparse.ArgumentParser(description="NMFS Agents")
sub = parser.add_subparsers(dest="cmd")
sub.add_parser("scan", help="扫描所有项目,打印发现的任务(不执行)")
sub.add_parser("run", help="扫描 → 入队 → 执行所有任务(一键完成)")
sub.add_parser("run-next", help="执行队列中下一个已入队任务")
sub.add_parser("status", help="查看任务队列状态")
sub.add_parser("start", help="启动定时调度器(每小时自动执行)")
p_av = sub.add_parser("arch-versions", help="查看项目的架构优化版本历史")
p_av.add_argument("project", help="项目名称(如 yolo、mediapipe")
p_ar = sub.add_parser("arch-restore", help="将项目恢复到指定架构版本")
p_ar.add_argument("project", help="项目名称")
p_ar.add_argument("version_id", help="版本 ID(如 v001")
sub.add_parser("goals", help="查看所有项目的长期目标记忆")
sub.add_parser("facts", help="查看所有项目的知识库事实(由 agents 自动记录)")
p_sf = sub.add_parser("set-fact", help="手动写入项目事实")
p_sf.add_argument("project", help="项目名")
p_sf.add_argument("key", help="事实键名,如 test_cmd")
p_sf.add_argument("value", help="事实值")
sub.add_parser("projects", help="查看自动发现的所有项目(含类型/模式)")
sub.add_parser("collect-env", help="收集本机+SSH设备环境信息(模型路径/磁盘/包)")
sub.add_parser("research", help="触发三项基础技术调研任务入队(RTP/网络/内核)")
args = parser.parse_args()
if args.cmd == "scan":
from nmfs_agents.core.scanner import Scanner
from nmfs_agents.config import load_config
cfg = load_config()
tasks = Scanner(cfg).scan_all()
if not tasks:
print("未发现待处理任务")
for t in tasks:
print(f"[{t.project}] {t.title} (priority={t.priority})")
elif args.cmd == "run":
import asyncio
from nmfs_agents.agents.manager import ManagerAgent
from nmfs_agents.config import load_config
cfg = load_config()
stats = asyncio.run(ManagerAgent(cfg).run_cycle())
print(f"扫描入队: {stats['enqueued']} 个,执行完成: {stats['executed']} 个")
elif args.cmd == "run-next":
import asyncio
from nmfs_agents.core.executor import Executor
from nmfs_agents.config import load_config
cfg = load_config()
ran = asyncio.run(Executor(cfg).run_next())
print("已执行" if ran else "队列为空")
elif args.cmd == "status":
from nmfs_agents.agents.manager import ManagerAgent
from nmfs_agents.config import load_config
cfg = load_config()
status = ManagerAgent(cfg).get_status()
if not status:
print("队列为空")
else:
for state, count in status.items():
print(f" {state}: {count}")
elif args.cmd == "start":
from nmfs_agents.core.scheduler import start
start()
elif args.cmd == "arch-versions":
from nmfs_agents.config import load_config
from nmfs_agents.tools.arch_version import ArchVersion
cfg = load_config()
proj_cfg = cfg.projects.get(args.project)
if not proj_cfg:
print(f"项目 {args.project!r} 不在配置中")
else:
av = ArchVersion(proj_cfg.path)
versions = av.list_versions()
if not versions:
print(f"{args.project}: 暂无架构优化版本")
else:
best = av.current_best()
for v in versions:
mark = " ★" if v.version_id == best else ""
passed = "✓" if v.test_passed else ("✗" if v.test_passed is False else "?")
print(f" {v.version_id}{mark} [{v.status:12s}] [{passed}] {v.created_at[:16]} {v.description}")
elif args.cmd == "arch-restore":
from nmfs_agents.config import load_config
from nmfs_agents.tools.arch_version import ArchVersion
cfg = load_config()
proj_cfg = cfg.projects.get(args.project)
if not proj_cfg:
print(f"项目 {args.project!r} 不在配置中")
else:
av = ArchVersion(proj_cfg.path)
av.restore(args.version_id)
print(f"已将 {args.project} 恢复到 {args.version_id}")
elif args.cmd == "goals":
from nmfs_agents.tools.project_memory import ProjectMemory
goals = ProjectMemory().list_goals()
if not goals:
print("暂无项目目标记录(运行 productizer 或 architect 任务后自动写入)")
else:
for proj, g in goals.items():
print(f"\n[{proj}] by {g.updated_by} {g.updated_at[:10]}")
print(f" {g.goal_text}")
elif args.cmd == "facts":
from nmfs_agents.tools.project_memory import ProjectMemory
from nmfs_agents.config import load_config
mem = ProjectMemory()
cfg = load_config()
any_found = False
for proj_name in sorted(cfg.projects):
facts = mem.get_facts(proj_name)
if facts:
any_found = True
print(f"\n[{proj_name}]")
for k, v in facts.items():
print(f" {k}: {v}")
if not any_found:
print("暂无项目事实记录(运行 architect 任务后自动写入)")
elif args.cmd == "set-fact":
from nmfs_agents.tools.project_memory import ProjectMemory
mem = ProjectMemory()
mem.set_fact(args.project, args.key, args.value, role="developer")
print(f"已写入: [{args.project}] {args.key} = {args.value}")
elif args.cmd == "projects":
from nmfs_agents.config import load_config
cfg = load_config()
print(f"{'项目名':20s} {'类型':8s} {'模式':8s} 路径")
print("-" * 70)
for name, p in sorted(cfg.projects.items()):
dw = f" → {p.device_workspace}" if p.device_workspace else ""
print(f"{name:20s} {p.kind:8s} {p.mode:8s} {p.path}{dw}")
elif args.cmd == "collect-env":
from nmfs_agents.config import load_config
from nmfs_agents.tools.env_collector import run_collection
from nmfs_agents.tools.project_memory import ProjectMemory
cfg = load_config()
mem = ProjectMemory()
facts = run_collection(cfg, mem)
# 本机摘要
env_vars = {k: v for k, v in facts.items() if k.startswith("local.envvar.")}
print("\n[本机]")
if env_vars:
print(f" ENV: {len(env_vars)} 条含模型关键词变量")
for k, v in list(env_vars.items())[:3]:
print(f" {k.split('.')[-1]}={v}")
for ext in ("rknn", "onnx", "pt", "pb"):
val = facts.get(f"local.models.{ext}", "")
if val:
cnt = len([p for p in val.split(",") if p])
print(f" *.{ext}: {cnt} 个文件")
# 各设备摘要
device_names: set[str] = set()
for key in facts:
parts = key.split(".")
if len(parts) >= 2 and parts[0] not in ("local", "collected"):
device_names.add(parts[0])
for dev_name in sorted(device_names):
dev_cfg = cfg.devices.get(dev_name)
host_info = f" @ {dev_cfg.host}" if dev_cfg else ""
print(f"\n[{dev_name}{host_info}]")
error = facts.get(f"{dev_name}.error", "")
if error:
print(f" ⚠ {error}")
continue
rknn_val = facts.get(f"{dev_name}.models.rknn", "")
if rknn_val:
cnt = len([p for p in rknn_val.split(",") if p])
print(f" *.rknn: {cnt} 个文件")
disk = facts.get(f"{dev_name}.disk", "")
if disk:
print(f" 磁盘: {disk[:80]}")
pkgs = facts.get(f"{dev_name}.packages", "")
if pkgs:
print(f" 关键包: {pkgs[:80]}")
ts = facts.get("collected_at", "")
print(f"\n已保存到 ProjectMemory (__env__) [{ts}]")
elif args.cmd == "research":
from nmfs_agents.config import load_config
from nmfs_agents.core.queue import TaskQueue, Task
cfg = load_config()
q = TaskQueue()
research_tasks = [
Task(project="research", type="research",
title="RTP 实时传输优化调研",
context="调研 RK3588 平台 RTP 协议栈延迟瓶颈,输出 [调研报告] facts 和 [优化任务]",
priority=3, mode="report", agent_role="rtp-researcher", initiator="user"),
Task(project="research", type="research",
title="网络栈优化调研",
context="调研 RK3588 TCP/UDP 调优参数,输出 [调研报告] facts 和 [优化任务]",
priority=3, mode="report", agent_role="net-researcher", initiator="user"),
Task(project="research", type="research",
title="内核资源调度优化调研",
context="分析 RK3588 多核调度与 NPU 资源竞争,输出 [调研报告] facts 和 [优化任务]",
priority=3, mode="report", agent_role="kernel-analyzer", initiator="user"),
]
for t in research_tasks:
tid = q.enqueue(t)
if tid > 0:
print(f" 入队: [{t.agent_role}] {t.title} → #{tid}")
else:
print(f" 跳过(已在队列): {t.title}")
print(f"\n已入队 {len(research_tasks)} 个基础技术调研任务")
print("执行调研:python scripts/run.py run-next(需在普通终端,非 Claude Code 内)")
else:
parser.print_help()
if __name__ == "__main__":
main()