白天你把想做但没空做的活儿丢给桌上这只精灵,它按四象限排好队,趁你睡觉把能自动做的都做完。早上醒来——事情变少了、进度变多了,拿不准的攒成一张确认清单等你拍板。
它坐在一把清闲人体工学椅上,靠桌面环境判断你在不在、要不要开工,全程无需手动操作:白天——脚踏上的传感器检测到你落座就开工;夜晚——灯灭(你入睡)它就开始烧 token 替你办事,灯亮(你醒来)就停工并播报晨报。
我们每天和 AI 说话,但它住在别人的服务器里,关掉标签页就消失——它不属于你。如果有一台机器 24 小时待在你桌上、只属于你一个人,它不睡觉、能跑程序、能存你的数据、能在你睡着时把事情办了,那么 Agent 最好的家就不是云,而是一台离你最近、一直醒着的小主机。
| 本地常驻主机 | 云端 Agent |
|---|---|
| ✅ 整夜独占算力 | ❌ 按调用计费、排队 |
| ✅ 能碰真实环境(文件/外设) | ❌ 够不着你的桌面 |
| ✅ 隐私护城河,数据不出这台机器 | ❌ 数据上云 |
- 晚上交班 —— 你把三件事丢给精灵(挑图修图分类 / 把技术文档整理成讲稿大纲 / 盯 GitHub issue),它说「晚安,交给我」。
- 关灯入睡 —— 你关掉房间的灯,这既是「人退场」的仪式,也是精灵的开工信号。灯转值班蓝,开始夜间执行、烧 token。
- 快进一夜 —— token 计数在走,灯从蓝渐变。
- 早上开灯播报 —— 你开灯(醒来)→ 精灵停工、灯转暖光,主动播报晨报:图挑好了在这个文件夹、讲稿大纲已生成、issue 回复草稿等你确认。
- 看得见成果 —— 关键是让评委看到本地文件夹里真的多了东西、确认清单里真有条目。
| 能力 | 状态 | 说明 |
|---|---|---|
| 🗂️ 四象限任务看板 | ✅ | 增删改查、拖拽改象限、多客户端实时刷新 |
| 🌓 环境感知状态机 | ✅ | 脚踏 + 灯光 → 待命 / 白天工作 / 夜间执行 自动推导 |
| 💡 LED 灯语联动 | ✅ | 屏幕精灵灯与实体灯同一套语言,随模式变色 |
| 🔌 硬件连接架构 | ✅ | GPIO 直读传感器 + UART 驱动 T5 面板;MQTT 为可选外部边界(已按重构提案 v1 落地) |
| 🤖 四象限自动分类 | ✅ | ClassifierAgent:LLM 优先(DeepSeek),无 key 时启发式兜底;显式指定的象限不覆盖 |
| 🌙 夜间执行器 | ✅ | 灯灭自动开工:ExecutorAgent 路由 skill,产物真实落盘 data/output/;灯亮收工不留半个产物 |
| 🤝 信任机制(确认清单) | ✅ | 没现成 skill 的活不擅自做,攒进确认清单(呼吸红),早上拍板联动任务状态 |
| 🌅 晨报 | ✅ | 灯亮自动播报:完成/失败/待拍板/token 消耗,前端弹卡 + 落盘 markdown |
| 🧘 久坐提醒 | ✅ | 连续落座 30 分钟(可调)→ 轻提示 + 面板音频,起身自动复位 |
| 🚶 离席开工 | ✅ | 人离开超阈值(可调)→ 自动进入「离席执行」干活(走时 toast 告诉你);坐回弹「离席汇报」 |
| 🧩 Skill 插件系统 | ✅ | BaseSkill 契约 + 首个落地 skill(文档→讲稿大纲),新 skill 注册即用 |
| 🖥️ 交互式启动 CLI | ✅ | 菜单式/命令式双模式,自动开浏览器,端口占用友好处理 |
| 🧪 Harness 测试编排 | ✅ | Phase 1 回归 + Phase 2 契约 + 行为场景 18 项全绿,自带环境编排与报告 |
① 环境感知 → 四种状态(无感,是整个 Demo 的记忆点)
| 传感器 | 组合 | 系统模式 | 精灵状态 |
|---|---|---|---|
| 灯灭 | light=0 |
🌙 夜间执行 | 值班蓝,开始烧 token |
| 灯亮 + 踩脚踏 | light=1, sit=1 |
💼 白天工作 | 冷白,陪你干活 |
| 灯亮 + 未踩脚踏 | light=1, sit=0 |
😴 待命 | 暖光,安静守着 |
| 拿不准的事 | — | 🚨 需拍板 | 呼吸红,等你决定 |
② 四象限任务分类(X 轴 = 截止紧迫度,Y 轴 = 自主度)
| 可自动(有现成 skill) | 需拍板(要你决定) | |
|---|---|---|
| 紧急 | ① 夜间执行 | ② 确认清单 |
| 不紧急 | ③ 排队中 | ④ 待定 |
③ LED 灯语(屏幕 UI 与硬件灯效是同一套叙事)
暖光=休息 · 冷白=工作 · 值班蓝=夜间值班 · 呼吸红=需拍板
🤝 信任机制:它不是擅自做主的老板,是打工仔。拿不准的(这封邮件语气对不对?这条要不要删?)不擅自做,攒进明早确认清单,用呼吸红灯提醒。这解决了「放手让 AI 整夜乱搞」的信任焦虑。
压力/光敏传感器 ──GPIO──> ┌───────────────── Orange Pi 3B ─────────────────┐
│ hardware/gpio_adapter.py (消抖+稳定判定) │
│ │ EnvironmentState │
│ ▼ │
│ domain/ 状态机 StateMachine (唯一权威状态) │
│ │ DomainEvent (revision++) │
│ ▼ │
│ services/orchestrator.py (组合根/事件总线) │
│ ┌──────┼───────────┬─────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ UART WebSocket MQTT(可选) Executor(P2) │
└─────┼──────┼───────────┼─────────────────────────┘
│ │ │
T5 面板 ◄────┘ │ ▼
(MODE_SET等) │ 外部应用/调试/Home Assistant
▼
前端看板 (HTML/CSS/JS 静态托管)
四象限看板 + 精灵灯 + 确认清单
▲
SQLite (aiosqlite, WAL) 本地存储
架构要点(《MQTT 重构提案 v1》):MQTT 是可选的外部集成边界,不是内部硬件链路。内部数据路径固定为
传感器 ──GPIO──> Orange Pi ──UART──> T5。领域层domain/不碰任何网络库,MQTT 可整体关闭,核心照常运行。
| 层 | 技术 | 选型理由 |
|---|---|---|
| 后端 | FastAPI + Uvicorn | 异步、自带 WS、类型安全,单进程跑通全链路 |
| 存储 | SQLite (aiosqlite, WAL) | 零部署、本地隐私、读写并发友好 |
| 硬件-有线 | pyserial-asyncio | 一根 USB 线解决供电+通信,现场最可靠 |
| 硬件-无线 | WebSocket / MQTT (aiomqtt) | WiFi 灵活 / IoT 标准(QoS1+LWT+保留消息) |
| 前端 | 原生 HTML/CSS/JS | 无构建、FastAPI 直接托管,设计师改 CSS 即可换肤 |
| LLM (Phase 2) | 国产模型(DeepSeek 等) | 国内访问无障碍、成本低 |
| 测试 | pytest + 自研 Harness | 单元/链路 + 场景编排双保险 |
只需 Python 3.11+,无需 Node。前端由后端直接托管。
| 脚本 | 作用 |
|---|---|
安装依赖.bat |
首次运行一次,自动装依赖(会检测并提示装 Python) |
启动看板.bat |
起后端 + 自动打开浏览器(日常用) |
一键演示.bat |
MQTT Broker + 后端全链路(现场演示) |
演示剧本.bat |
视频四拍 + 夜场,回车逐拍推进(需看板已在运行;自动热调阈值并恢复) |
# 1. 安装依赖
pip install -r requirements.txt
# 2. 启动(进入交互式菜单,按编号选择)
python cli.py
# 或直接启动看板(后端+前端,自动开浏览器)
python cli.py serve --open
# 3. 浏览器打开(启动后会自动打开)
http://127.0.0.1:8000💡 前端不用单独启动——它由 FastAPI 静态托管,起了后端访问
http://127.0.0.1:8000/就是看板。设计师改frontend/css/style.css后刷新浏览器即可,无需 npm。 (前端开发者若装了 Node,可cd frontend && npm run dev获得热重载,Vite 在 :5173 代理到后端。)
运行 python cli.py 进入交互式菜单(也可直接用子命令):
请选择启动模式:
1 ◈ 启动看板 后端 + 前端, 自动开浏览器 ★ 推荐
2 ✦ 一键演示 Broker + 后端[MQTT] 全链路
3 ☁ MQTT Broker 只启动本地消息中转
4 ⚙ 硬件模拟器 已废弃, 改用 env 注入
5 ✓ 运行测试 全部 / Phase1 / MQTT / Agent
6 ⚑ 环境自检 依赖 / 端口 / 配置
7 ⚡ Harness 测试 Phase1 回归 + Phase2 Agent 契约
8 🎬 演示剧本 视频四拍 + 夜场, 回车推进
0 ✕ 退出
| 命令 | 作用 | 常用示例 |
|---|---|---|
serve |
启动后端(+前端) | python cli.py serve --open --reload |
demo |
一键全链路编排 | python cli.py demo --open |
broker |
本地 MQTT Broker | python cli.py broker --port 1883 |
sim |
硬件模拟器(已废弃,改用 env) | — |
test |
跑 pytest | python cli.py test --agent |
check |
环境自检 | python cli.py check |
harness |
测试编排框架 | python cli.py harness --only phase1 |
env |
注入模拟环境状态 | python cli.py env night / env work / env --light off |
presence |
查看/热调久坐·离席阈值 | python cli.py presence --sedentary 30 --away 10(--reset 恢复) |
play |
演示剧本(回车逐拍推进) | python cli.py play |
reset-db |
清空开发数据库(需先停后端) | python cli.py reset-db --with-output |
🛟 端口被占用? CLI 会友好提示
[o]打开浏览器 / [p]换端口 / [b]返回,不会报错崩溃。
按 《MQTT 重构提案 v1》(权威规范,见 docs/Nightshift_MQTT_重构提案_v1.md):
内部硬件链路:压力/光敏传感器 ──GPIO──> Orange Pi 3B ──UART──> T5 面板
| 边界 | 协议 | 实现 |
|---|---|---|
| GPIO 输入 | 直读数字输入,消抖+稳定判定 → EnvironmentState{sit,light,ready} |
backend/hardware/gpio_adapter.py |
| UART 面板 | T5-Link 板间协议:MODE_SET / ATTENTION_SET / WORK_STATE_SET / DASHBOARD_SET / TASK_LIST / UI_ACTION / AUDIO_PLAY |
backend/hardware/uart_adapter.py |
| MQTT(可选外部边界) | 仅供外部调试/控制/Home Assistant,默认关闭 | backend/integrations/mqtt/ |
MQTT 不是内部总线:GPIO 不经 MQTT 回环,T5 不接 MQTT,MQTT 故障不影响 GPIO/UART/执行器。
MQTT Topic 速查(nightshift/v1/opi/{node_id}/,默认 node_id=opi5b01,详见 docs/MQTT_硬件接入文档.md):
- 后端 → 外部:
availability(retain) /state(retain) /event/telemetry - 外部 → 后端:
command(信封含request_id+reply_to+ttl_ms,幂等)→ 后端回reply/{client_id}
没硬件也能联调(注入模拟环境状态):
curl -X POST http://127.0.0.1:8000/api/dev/environment \
-H "Content-Type: application/json" -d '{"light": false}' # 关灯 -> 夜间执行
curl http://127.0.0.1:8000/api/dev/panel # 看 T5 面板收到的指令
# 演示热调(不用真等 30 分钟): 久坐提醒/离席开工阈值秒级化
curl -X POST http://127.0.0.1:8000/api/dev/presence \
-H "Content-Type: application/json" -d '{"sedentary_seconds": 30, "away_seconds": 10}'python -m pytest tests/test_phase1.py -v # Phase 1 全链路(自包含,自动起后端)
python -m pytest tests/test_mqtt.py -v # MQTT 链路(自带嵌入式 broker)
python -m pytest tests/test_agent.py -v # Agent 层单元测试(纯单元,无需服务器)
python -m pytest tests/ -v # 全部测试一次跑通一个场景驱动的编排框架:自动拉起完整环境 → 执行场景 → 生成报告。
python -m harness # spawn 完整环境,跑全部场景
python -m harness --only phase1 # 只跑 Phase 1 回归
python -m harness --only agent # 只跑 Phase 2 契约
python -m harness --attach http://127.0.0.1:8000 # 连接已运行的后端
python cli.py harness --only phase1 # 经由统一 CLI- Phase 1 回归(
scenarios/phase1_core.py,7 项):健康/状态/CRUD/导入/前端WS/硬件链路/LED下行 —— 必须全绿。 - Phase 2 契约(
scenarios/agent_contracts.py,8 项):Agent 层接口契约(BaseAgent / Classifier / Executor / Scheduler / Reporter / BaseSkill / 多Agent协作)—— 已全部转绿;新 Agent 模块按契约对齐即可。 - Phase 2 行为(
scenarios/agent_behavior.py,3 项):夜循环端到端(灯灭→执行→产物落盘→灯亮→晨报)/ 自动分类 / 确认清单拍板 —— 当前 18 项全绿。 - 报告:控制台彩色汇总 +
harness/reports/*.json+ 自包含*.html。 - 环境用独立临时数据库(
NIGHTSHIFT_DB_PATH),不污染开发数据,退出自动回收。
写一个新场景(放 harness/scenarios/ 任意文件即自动注册):
from harness.scenario import Scenario, scenario
@scenario
class MyScenario(Scenario):
name = "phase2.my_feature"
title = "我的功能"
requires = ("backend.agent.my_module",) # 缺失则 SKIP
async def run(self, ctx):
r = ctx.http.get("/api/xxx")
ctx.check(r.status_code == 200, "应返回 200")| 成员 | 角色 | 你的快速入口 |
|---|---|---|
| 寒 | 全栈(后端+分类器+执行器+前端) | python cli.py serve --reload,看 backend/、harness/ |
| YUANZL | 硬件 / 嵌入式(技术核心) | 读 docs/Nightshift_MQTT_重构提案_v1.md,按 gpio_adapter.py / uart_adapter.py 实现真实适配器 |
| 许新豪 | 设计 / 视觉 / 叙事 | 素材放 frontend/assets/,改 frontend/css/style.css 顶部 :root 变量即可全局换肤 |
| Lucy | PM | 双击 启动看板.bat 看看板;演示数据在 backend/routes/tasks.py 的 MOCK_TASKS |
d:\zhende-zdv\
├── cli.py # 统一启动器(交互菜单 + 子命令)
├── 安装依赖.bat / 启动看板.bat / 一键演示.bat # Windows 双击脚本
├── requirements.txt
├── pytest.ini
│
├── backend/ # FastAPI 后端
│ ├── main.py # 入口(路由 + WebSocket + 静态托管)
│ ├── config.py # 配置(pydantic-settings, NIGHTSHIFT_ 前缀)
│ ├── database.py # SQLite 异步连接(aiosqlite, WAL)
│ ├── models.py # 数据模型 + 建表 SQL + 四象限/模式/LED 枚举
│ ├── ws.py # WebSocket 双通道连接管理器
│ ├── routes/
│ │ ├── tasks.py # 任务 CRUD + mock 导入(Lucy 调数据处)
│ │ └── confirmations.py# 确认清单:列表 + 拍板(Phase 2 信任机制)
│ ├── domain/ # 领域层(唯一权威状态 + 状态机 + 领域事件,不碰网络库)
│ │ ├── models.py # EnvironmentState / SystemState / WorkState
│ │ ├── events.py # DomainEvent 类型
│ │ └── state_machine.py# 模式推导 + revision 维护
│ ├── agent/ # Agent 层(Phase 2,不碰 MQTT/UART/WS,结果交调度层落地)
│ │ ├── base_agent.py # BaseAgent / AgentResult / AgentContext(契约地基)
│ │ ├── llm.py # LLM 接入(DeepSeek 兼容端点 + NullLLM 降级)
│ │ ├── classifier.py # 四象限自动分类(LLM 优先,启发式兜底)
│ │ ├── executor.py # 夜间执行器:任务 -> skill 路由 / 无 skill 转确认
│ │ ├── scheduler.py # NightScheduler 夜循环调度(灯灭开工,灯亮收工)
│ │ ├── reporter.py # 晨报生成(模板 + 可选 LLM 导语)
│ │ └── skills/ # BaseSkill 契约 + doc_outline(首个落地 skill)
│ ├── hardware/ # 硬件物理边界
│ │ ├── gpio_adapter.py # GPIO 直读传感器(消抖+稳定判定,含模拟实现)
│ │ └── uart_adapter.py # UART 驱动 T5 面板(含模拟实现)
│ ├── integrations/
│ │ └── mqtt/ # MQTT 可选外部集成边界
│ │ ├── topics.py # Topic 命名 (nightshift/v1/opi/{node_id}/...)
│ │ ├── schemas.py # 消息 Schema 构造/校验
│ │ ├── client.py # aiomqtt 客户端(线程隔离/LWT/重连)
│ │ ├── publisher.py# availability/state/event 发布
│ │ ├── command_handler.py # command -> 领域 -> reply
│ │ └── dev_broker.py # 本地嵌入式 Broker(免装 mosquitto)
│ └── services/
│ └── orchestrator.py # NightshiftCore 组合根(连接以上所有)
│
├── frontend/ # 前端(FastAPI 静态托管,无需构建)
│ ├── index.html # 单页骨架
│ ├── css/style.css # 设计令牌(设计师改这里)
│ └── js/ # app/api/ws/kanban/status/confirm/report 模块
│
├── harness/ # Harness 测试编排框架(Phase 2 驱动器)
│ ├── config.py environment.py clients.py scenario.py
│ ├── scenarios/ # phase1_core(回归) + agent_contracts(契约) + agent_behavior(行为)
│ ├── report.py runner.py main.py
│ └── reports/ # 生成的 JSON/HTML 报告
│
├── tests/ # pytest(test_phase1 / test_mqtt / test_agent)
├── docs/
│ ├── Nightshift_MQTT_重构提案_v1.md # 连接架构权威规范(队友提交)
│ └── MQTT_硬件接入文档.md # MQTT 集成实用参考
└── data/ # 运行时生成的 SQLite 数据库 + Agent 产物 (output/)
全部通过环境变量覆盖,前缀 NIGHTSHIFT_(见 backend/config.py):
| 变量 | 默认 | 说明 |
|---|---|---|
NIGHTSHIFT_HOST / NIGHTSHIFT_PORT |
127.0.0.1 / 8000 |
服务地址 |
NIGHTSHIFT_DB_PATH |
data/nightshift.db |
SQLite 路径 |
NIGHTSHIFT_SERIAL_ENABLED |
true |
串口通道开关 |
NIGHTSHIFT_SERIAL_BAUDRATE |
115200 |
串口波特率 |
NIGHTSHIFT_MQTT_ENABLED |
false |
MQTT 总开关(false 时核心照常运行) |
NIGHTSHIFT_MQTT_HOST / NIGHTSHIFT_MQTT_PORT |
127.0.0.1 / 1883 |
Broker 地址 |
NIGHTSHIFT_MQTT_NODE_ID / NIGHTSHIFT_MQTT_BASE_TOPIC |
opi5b01 / nightshift/v1 |
节点标识 / 主题命名空间 |
NIGHTSHIFT_LLM_API_KEY |
空 | DeepSeek key;不配则 Agent 走启发式/模板兜底,演示不受影响 |
NIGHTSHIFT_LLM_BASE_URL / NIGHTSHIFT_LLM_MODEL |
https://api.deepseek.com/v1 / deepseek-chat |
OpenAI 兼容端点 / 模型 |
NIGHTSHIFT_AGENT_OUTPUT_DIR |
data/output/ |
Agent 产物落盘目录(大纲/晨报) |
NIGHTSHIFT_SEDENTARY_REMIND_SECONDS |
1800 |
连续落座多久提醒活动(久坐提醒) |
NIGHTSHIFT_AWAY_EXEC_SECONDS |
300 |
离席多久自动开工(演示可热调,见下) |
✅ Phase 1 · 骨架(已完成) 任务看板 / 领域状态机(domain 层)/ LED 灯语 / GPIO+UART 硬件架构 / MQTT 可选外部边界 / 交互式 CLI / Harness 回归
✅ Phase 2 · Agent 核心(已完成)
- LLM 接入层(DeepSeek OpenAI 兼容端点,无 key 自动降级,演示/CI 不依赖网络)
- 四象限自动分类 Agent(LLM 优先 + 启发式兜底,显式象限不覆盖)
- 夜间执行器 + Skill 插件系统(首个落地 skill:文档→讲稿大纲,产物真实落盘
data/output/) - 多 Agent 协作调度(NightScheduler:灯灭开工、灯亮收工,收工不留半个产物)
- 确认清单信任机制(拿不准→呼吸红→拍板联动)+ 晨报(WS 弹卡 + 落盘)
🚧 下一步
- 真实 GPIO / UART 适配器(待 YUANZL 确认硬件库与引脚方案,新增
GPIOAdapter/UARTAdapter子类即可) - 更多 skill(按 BaseSkill 契约注册即用)与晨报语音播报(面板 AUDIO_PLAY)
🔮 非目标(本次不碰,讲「可扩展」) 多数据源全接入(只做 1 个真的,其余 mock)· 多 skill 库(只做 1 个能落地的)· 账号体系 / 云同步 / 复杂权限
| 问题 | 排查 |
|---|---|
| 启动报「端口 10048 被占用」 | 端口已被占用。CLI 会提示 [o]打开浏览器 / [p]换端口 / [b]返回;或 python cli.py serve --port 8001 --open |
| 前端打不开 / 空白 | 前端由后端托管,确认后端已启动,访问 http://127.0.0.1:8000/(不是 5173) |
| 需要装 Node 吗? | 不需要。只有前端开发者想要热重载才可选装 |
| MQTT 连不上 | 先 python cli.py broker 起本地 Broker;Windows 上 MQTT 客户端需 SelectorEventLoop(代码已内置处理) |
| 测试报错连不上后端 | test_phase1.py 现为自包含(conftest.py 自动起后端);test_mqtt.py 自带 broker;两者均可直接 pytest |
| 硬件一直没反应 | 先用 POST /api/dev/environment 验证软件链路,再接真机;检查 GPIO/UART 连接与主题是否匹配 |
Nightshift —— 它不替你创造,而是替你守住创造所需要的环境: 少一点打断,少一点重复劳动,多一点连续的专注和自由。🌙
AdventureX 2026 · 清闲 UberNovo 团队出品