基于企业作息与事件驱动理念的多角色 AI Agent 调度框架。
打破传统 Agent while(true) 循环,解决"长任务 Context 爆炸、状态不可恢复、Token 成本失控、权限无隔离"问题。
每个角色拥有一台属于自己的个人电脑(Podman 容器,同一自定义桥接网络内互通), 文件、任务、笔记、MCP 工具全部落在自己电脑上,权限天然隔离。
┌──────────────────────────────────────────────────────────┐
│ TimeEventBus (时间 × 事件 深度绑定) │
│ TimeManager 并入 EventBus: │
│ - 时钟/Tick/天 (1 Tick = 10 分钟, 上班 0~60 Tick) │
│ - 3 层过滤管线 (状态掩码 → 显著性 → 唤醒) │
│ - 事件调度表: register_event(ev, tick) 定时触发 │
│ - 快进: 全角色空闲 ≥1 分钟 → 跳到下一事件 Tick │
└──────────────┬───────────────────────────────────────────┘
│ SHIFT_START/SHIFT_END/TASK_DUE 事件
▼
┌──────────────────────────────────────────────────────────┐
│ EventDispatcher (事件分发器) │
│ trigger(event) → fan-out to ALL roles │
│ Each role runs Layer 1-2-3 filter independently │
└──────────────┬───────────────────────────────────────────┘
│ PASS events become Tasks
▼
┌──────────────────────────────────────────────────────────┐
│ RolePool (角色线程池) │
│ ThreadPoolExecutor — 每个角色独立线程 │
│ Priority Queue (heapq) — CRITICAL > HIGH > NORMAL │
└──────────────┬───────────────────────────────────────────┘
│ Task execution (原生 function calling)
▼
┌──────────────────────────────────────────────────────────┐
│ AgentRole + 个人电脑 + MCP 工具 + talk │
│ LLM(Task) → tool_calls → execute → role:tool 回喂 │
│ 电脑: podman 容器 maf-<role> (上班开机/下班关机) │
│ MCP 服务器跑在容器内, 授权 /home/agent │
│ talk: inter-role communication │
└──────────────────────────────────────────────────────────┘
TimeManager 已合并进 EventBus(TimeEventBus(EventBus) 子类),既是时间源又是事件总线:
- 统一注册入口
register_event(event, tick=None):tick=None→ 立即投递(进 3 层过滤管线)tick=N→ 存入事件调度表,时间线程到点自动投递
- 作息事件自动触发:每天 Tick 0 →
SHIFT_START(上班),Tick 60 →SHIFT_END(下班) - 定时任务:
schedule_task只保存任务列表;当天任务直接注册事件,隔天任务目标天上班时自动加载 - 兼容别名
TimeManager = TimeEventBus,旧引用不破坏
0 Token 消耗拦截低价值事件:
| 层 | 名称 | 机制 | Token |
|---|---|---|---|
| Layer 1 | State Mask | OFF_DUTY 状态拦截非 EMERGENCY 事件 | 0 |
| Layer 2 | Salience Evaluator | 关键词匹配 + 优先级加权计算显著性 | 0 |
| Layer 3 | Wake | 通过前两层的事件唤醒 Agent 工作流 | 按需 |
真实时间模式下不必干等:全部角色空闲(无任务处理/排队)持续 1 分钟后,
时钟自动跳到下一个事件 Tick(定时事件 / 定时任务 / 下班;已下班则跳到次日上班)。
set_idle_checker / set_fast_forward(enabled, idle_seconds) 可配。
- 每个角色独立线程 + 独立锁 + 独立 LLM 实例
- 优先级任务队列(heapq):CRITICAL(10) > HIGH(6) > NORMAL(3) > LOW(1)
RolePool.add_role_and_start()动态入职(HR 招聘即上岗)RolePool.remove_role()离职(自动关机 + 移出团队)
- 请求带
tools声明 +tool_choice:"auto",判定靠响应message.tool_calls结构化字段 - 工具结果以
role:"tool"+tool_call_id回喂,循环无轮次上限 max_tokens默认无上限(长内容 JSON 不被截断成非法 JSON;None时不传该字段)- 不再使用文本协议(```tool_call 块 + 正则),
_parse_tool_calls仅作兼容保留
每个角色一台独立电脑,默认 Podman 容器(镜像 node:22-alpine,名 maf-<role_id>):
- 容器挂载宿主机目录
data/computers/<role>↔ 容器内/home/agent(同一份文件,双向可见) - 上班自动开机(SHIFT_START)、下班自动关机(summary 总结后)
- 同一自定义桥接网络
maf-net:电脑间可互相通信(lan_devices工具查人名/电脑名/IP) ComputerManager(全局单例)管理分配/销毁- 降级:本机无 podman 时自动用
LocalComputer(data/computers/<role>目录模拟) - 另有
SSHComputer(远程主机,需显式指定 host)
- 每台电脑一个独立 MCP filesystem 服务器,通过
podman exec -i在容器内启动, 授权目录 =/home/agent(与 LLM 看到的工作目录字面一致,无路径空间错位) - 容器创建时自动
npm install -g @modelcontextprotocol/server-filesystem预装 DEFAULT_MCP_GROUPS = ("file_ops",):角色加入/新入职时自动把文件操作工具装到个人电脑MCPManager(全局共享):mcp_search/mcp_list/mcp_add/mcp_remove/mcp_my_tools供 LLM 自助安装其它工具组的工具
| 工具类 | 工具 | 说明 |
|---|---|---|
| memory | summary / write_note / edit_note / list_notes / read_note | 记忆 + 下班总结(自动关机) |
| time | get_time / take_rest | 作息 |
| task | create_task / list_tasks / edit_task / delete_task | 定时任务(Tick 提醒,持久化到电脑 tasks/) |
| computer | run_command / computer_status / lan_devices / reboot | 个人电脑操作 |
| mcp_manager | mcp_search / mcp_list / mcp_add / mcp_remove / mcp_my_tools | MCP 自助管理 |
| talk / list_roles | 角色间通信 | pool.start() 自动注入 |
| MCP file_ops | read_file / write_file / edit_file / ... | 默认 MCP 组,自动安装到电脑 |
专属工具:CEO 有 talk_to_client(甲方交流),HR 有 post_job_posting / list_candidates。
HR 发布招聘 → 后台 RoleFactory 生成新人 → 立即加入运行中团队并启动 worker
(add_role_and_start)→ 新人自动获得全部默认工具 + MCP file_ops + 独立电脑。
无面试环节;入职后 HR 通知 COO。
- 每天下班调
summary保存当日总结(_summary_day_<N>.md,落在角色电脑 notes/) - 第二天
build_system_prompt()自动注入[昨日总结](严格早于今天的最近一篇) - 前提是电脑已开机 —— SHIFT_START 上班自动开机保证了这点
管理层(默认角色):
| 角色 | 姓名 | 职责 |
|---|---|---|
| CEO | 林总 | 接收用户需求→战略目标→汇总报告 |
| COO | 陈总 | 拆解目标→盘点员工→发起招聘 |
| HR | 王人事 | 招聘申请→发布招聘→新人即入职(无面试) |
| CFO | 钱财 | 预算批复→Token 限额→高风险审批(模板保留,暂不启用) |
技术团队:architect(王建国) / fullstack_dev(李明) / reviewer(张伟) / qa_engineer(刘洋) / ops_engineer(赵强)
业务团队:content_marketer(陈静) / data_analyst(孙晓) / support_agent(周梅)
用人需求 → LLM 生成角色配置 → 新 AgentRole → 入职上岗。自动分配不重名人名(24 人名字库)。
maf_scheduler/
├── src/
│ ├── core/
│ │ ├── types.py # Event, AgentState, Priority 等数据类型
│ │ ├── event_bus.py # EventBus: 3 层过滤 + 事件调度表 (_tick_schedule)
│ │ ├── time_manager.py # TimeEventBus: 时间(时钟/Tick/天) + 事件总线 + 快进
│ │ ├── roles.py # AgentRole + RolePool(多角色线程池 + 动态入职/离职)
│ │ ├── agent_system.py # 统一管理: TimeEventBus + RolePool + 事件分发
│ │ ├── computer.py # Computer 基类 + Podman/Local/SSH + ComputerManager
│ │ ├── note_store.py # 笔记 + 每日总结 (按天存储, 走角色电脑)
│ │ ├── role_templates.py # 12 个预定义角色模板
│ │ ├── role_factory.py # LLM 驱动动态创建角色
│ │ ├── dispatcher.py # 事件广播到所有角色
│ │ ├── tools.py # ToolRegistry: 工具注册 + to_openai_tools
│ │ └── llm.py # DeepSeek API 客户端 (原生 function calling)
│ ├── python_tools/ # Python 工具类 (DEFAULT_TOOLKITS)
│ │ ├── memory_toolkit.py # summary (总结+下班+关机) / 笔记
│ │ ├── time_toolkit.py # get_time / take_rest
│ │ ├── task_toolkit.py # 定时任务 CRUD (持久化到电脑 tasks/)
│ │ ├── computer_toolkit.py# run_command / computer_status / lan_devices / reboot
│ │ ├── mcp_manager.py # MCPManager: mcp_search/add/remove (安装到个人电脑)
│ │ ├── mcp_toolkit.py # MCPServer: 服务器连接 (支持自定义启动命令)
│ │ ├── hr_toolkit.py # post_job_posting (招聘即入职) / list_candidates
│ │ ├── client_toolkit.py # talk_to_client (甲方交流, CEO 专属)
│ │ └── talk_toolkit.py # talk / list_roles (角色间通信)
│ ├── storage/
│ │ └── ambient_buffer.py # 潜意识事件暂存
│ ├── workflow/ # 工作流 (预留)
│ ├── config/
│ │ └── mcp_group_rules.json # MCP 服务器与工具分组配置
│ ├── main.py # 主入口: 多日循环 (自动进入第二天)
│ └── mcp_demo.py # MCP 工具调用演示
└── data/
├── main_run.log # 运行日志
└── computers/<role>/ # 角色电脑 (宿主机侧挂载目录)
- Python 3.10+,
pip install -r requirements.txt(或使用项目自带.venv/) - podman(每角色电脑的容器运行时;缺失时自动降级本地目录模拟)
- DeepSeek API Key(环境变量
DEEPSEEK_API_KEY)
cd maf_scheduler
source .venv/bin/activate
# 设置 API Key (必填, 源码不再硬编码)
export DEEPSEEK_API_KEY="sk-..."
# 运行完整作息演示 (多日循环, 自动进入第二天)
python src/main.pyfrom src.core.agent_system import AgentSystem
from src.core.types import Event, Priority
# 统一管理 TimeEventBus + RolePool + 事件分发
system = AgentSystem(role_ids=["CEO", "COO", "HR"])
system.start() # 启动角色线程 + 时间线程 (Tick 0 / 第 1 天)
# 投递事件 (SHIFT_START/SHIFT_END 由时间线程自动触发)
system.trigger(Event(source="github", event_type="new_pr",
priority=Priority.HIGH, payload={"pr_number": 188}))
# 注册定时事件 (指定 Tick 触发; 不传 tick 则立即投递)
system.time_manager.register_event(
Event(source="meeting", event_type="standup", priority=Priority.HIGH),
tick=30,
)
print(system.describe()) # 第 X 天, Tick Y (上班中/已下班)
system.stop()from src.core.dispatcher import EventDispatcher
from src.core.roles import RolePool
from src.core.role_templates import get_template
from src.core.time_manager import TimeEventBus
pool = RolePool()
pool.add_role(get_template("CEO"))
tm = TimeEventBus()
tm.set_event_sender(lambda ev: EventDispatcher(pool).trigger(ev))
tm.start()
pool.start()coo = pool.get_role("COO")
coo.talk_to("HR", "请发布招聘: 需要一位精通 Rust 的后端工程师", "HIGH")
# → HR 队列收到: [FROM COO(陈总)] 请发布招聘...hr = pool.get_role("HR")
result = hr._tools.call_tool("post_job_posting", {
"requirement": "需要一位精通 Rust 的后端工程师, 熟悉 gRPC 和 PostgreSQL",
})
# → 后台生成新人 → 立即加入团队并上岗 (add_role_and_start)
# → 返回新人完整档案: role_id/姓名/技能, 状态 "已加入团队并上岗"dev = pool.get_role("CEO")
print(dev._tools.call_tool("lan_devices", {}))
# → 内网电脑设备:
# - 林总 (CEO) | 电脑 maf-CEO | 10.89.0.2
# - 陈总 (COO) | 电脑 maf-COO | 10.89.0.3| 变量 | 默认值 | 说明 |
|---|---|---|
| DEEPSEEK_API_KEY | (必填) | DeepSeek API 密钥 |
| DEEPSEEK_MODEL | deepseek-v4-flash | 模型名称 |
| DEEPSEEK_THINKING | true | 是否启用思考模式 |
| DEEPSEEK_BASE_URL | https://api.deepseek.com | API 地址 |