内容
<div align="center">

### 使用简单语言配置的 Qwen 智能摄像头代理。
*用简单的英语描述需要监视的内容。 AI 代理监视、分类并验证。*





[**快速开始**](#-快速开始) · [**架构**](#️-架构) · [**文档**](#-文档)
</div>
## 🏆 Qwen 云全球黑客马拉松
提交作品,参加 **Qwen 云全球黑客马拉松 — 赛道 5:边缘代理**。
| | |
|---|---|
| **实时应用** | [erlang-vision.duckdns.org](https://erlang-vision.duckdns.org) |
| **演示视频** | *即将推出 — 将链接到此处和 Devpost* |
| **仓库** | [erlang-ai-vision-fullstack](https://github.com/nickyui99/erlang-ai-vision-fullstack) (云 + 应用,此仓库) · [SentinelEdge_LaptopEdge](https://github.com/KennethChua1998/SentinelEdge_LaptopEdge) (边缘桥) · [SentinelEdge_IOT](https://github.com/KennethChua1998/SentinelEdge_IOT) (ESP32-S3 固件) |
| **部署** | 阿里云 `ap-southeast-3` (吉隆坡):ECI 容器 (FastAPI + Caddy),OSS (网页应用 + 媒体),RDS PostgreSQL,ACR — [架构和部署证明](docs/deployment/alibaba_cloud_architecture.md) |
| **团队** | Nicholas Ooi ([@nickyui99](https://github.com/nickyui99)) · Kenneth Chua ([@KennethChua1998](https://github.com/KennethChua1998)) · Fang Wei Lim · Ng Wei Kiat|
### 使用的 Qwen 模型
| 模型 | 运行位置 | 功能 |
|---|---|---|
| `qwen3.7-plus` *(图像验证)* | Qwen 云 (DashScope) | 第三阶段事件验证(视觉 + 工具调用)— 每个事件的图像工作在 plus 级别运行以降低成本(回退到 `qwen3.7-plus-2026-05-26`) |
| `qwen3.7-max` *(聊天 + 文本)* | Qwen 云 (DashScope) | 应用内代理助手(Erlang AI 代理),NL 规则编译器和对话代理生成器(回退到 `qwen3.7-max-2026-05-20` → `qwen3.7-max-preview` → `qwen3.7-max-2026-05-17` → `qwen3.7-max-2026-06-08`) |
| `qwen3.5:0.8b` | **在边缘笔记本** 通过 Ollama | 第二阶段分类:根据用户规则判断每个候选关键帧,本地运行(也是当所有云模型耗尽时的最终视觉回退) |
| `qwen3.5:4b` | **在边缘笔记本** 通过 Ollama | 降级模式权限:当云不可达时,代理工具调用循环(平移,重新快照,重新评估) |
### 为什么 Qwen 至关重要
整个架构围绕 Qwen 家族构建,**从 0.8B 开放权重 VLM 到边缘笔记本** 到 **前沿云模型**,所有模型都使用一种提示样式:
1. **简单语言是产品** — 用户规则通过 Qwen 文本模型成为检测器配置;没有基于表单的规则编辑器。
2. **边缘过滤器需要本地 VLM** — `qwen3.5:0.8b` 在笔记本电脑上分类每个候选帧,这使得系统可以在任何云调用之前丢弃约 99% 的帧(带宽,成本和隐私的优势)。
3. **验证需要视觉 + 工具** — `qwen3.7-plus` 不仅仅查看关键帧;它可以平移摄像头,拍摄新快照,并在规则通过之前检查最近的事件。
4. **离线恢复能力需要相同的小型大脑** — 当云被切断时,`qwen3.5:4b` 在本地运行同类型的代理验证循环。
### 在黑客马拉松期间构建(2026 年 5 月 26 日后)
所有三个仓库都是在黑客马拉松窗口期内从头开始构建的(第一次提交于 2026 年 6 月 11 日):FastAPI 后端和 Flutter 控制台,笔记本边缘管道(YOLO/YAMNet + Ollama Qwen 分类,离线队列,降级模式),ESP32-S3 固件,带有二维码配置和旋转/倾斜功能,阿里云部署,以及 — 在最后一周 — 带有代理应用助手,按需录制,代理即时启用/禁用和浏览器可播放的(H.264)事件剪辑的 MCP 工具服务器。查看 `HACKATHON_SUBMISSION_CHECKLIST.md` 和 git 历史记录以获取完整的记录。
### 🧑⚖️ 评委测试说明
1. **登录** 使用 Devpost 上提供的评委凭证登录实时应用网址(该账户已预先验证 — 无需电子邮件确认)。
2. 仪表板已 **预先设置**:每个用例的摄像头,启用的代理和示例事件,以便立即探索。
3. **零硬件演示**:评委摄像头在服务端模拟 — 后端将预提取的帧流式传输到实时视图并运行真正的 Qwen-Plus 检测 — 所以完整的检测 → 验证 → 警报流程在没有物理设备的情况下运行。
4. 尝试以下操作:
- 用简单的英语创建一个代理(例如 *"如果有人在晚上 9 点后在门口逗留,请提醒我"*)并检查编译后的检测器配置。
- 打开 **Erlang AI 代理** 聊天并询问 *"我有哪些摄像头,哪些代理已启用?"* — 它从 MCP 工具实时数据中回答,可以为你启用代理或拍摄快照。
- 打开一个事件并播放其剪辑;检查已验证事件的审计工具调用。
5. 要完全在本地运行(包括物理设备路径),请参见下面的 **快速开始**。
**预期结果:** 仪表板通过 HTTPS 加载,演示摄像头显示实时帧,合格规则生成带有 Qwen 验证结果和应用内警报的事件。如果实时 Qwen 依赖项不可用,UI 将以降级状态报告,而不是呈现模拟结果作为实时验证。
### 按演示路径划分的预期结果
| 路径 | 从这里开始 | 预期结果 |
|---|---|---|
| **生产评委演示** | 在 [实时应用](https://erlang-vision.duckdns.org) 上使用 Devpost 评委账户登录。 | 预先设置的仪表板和演示摄像头通过 HTTPS 加载;实时帧,已验证的事件,剪辑和应用内警报无需物理硬件即可使用。 |
| **本地无硬件演示** | 按照 **快速开始**,创建评委账户并启用 `DEMO_SIMULATION_ENABLED=true`。 | 后端创建演示账户,发布模拟帧,并在四秒后将第一个合格帧提交给 Qwen。 |
| **物理设备路径** | 完成链接的伴侣仓库中的 LaptopEdge 和 ESP32-S3 设置。 | 桥接器报告 `device listener ready` 和 `backend websocket connected`;仪表板接收实时帧,启用代理可以创建事件和警报。 |
## 🎬 看看它在行动

> 没有硬件?后端提供一个 **零硬件演示模式**,该模式将视频播放到虚拟摄像头并在其上运行真正的 Qwen-Plus 检测。
## 什么是 Erlang AI Vision?
Erlang AI Vision 是一个全栈 AI 摄像头平台。 用简单的语言定义检测规则("如果有人在晚上 10 点后出现在前门,请提醒我"),系统将它们编译成边缘检测器配置,运行摄像头馈送的本地 AI,并将合格事件升级到云视觉模型进行验证。
它跨越三个层级:
| 层级 | 仓库 | 角色 |
|------|------|------|
| **云/应用** | 此仓库 | FastAPI 后端 + Flutter 控制台:身份验证,代理,实时视频,验证 |
| **边缘桥接器** | [`SentinelEdge_LaptopEdge`](https://github.com/KennethChua1998/SentinelEdge_LaptopEdge) | 本地 YOLO/YAMNet 检测 + Ollama Qwen 分类 |
| **摄像头** | [`SentinelEdge_IOT`](https://github.com/KennethChua1998/SentinelEdge_IOT) | ESP32-S3 固件,二维码配置,旋转/倾斜 |
## ✨ 功能
- 🗣️ **自然语言代理** — 描述规则;Qwen 云编译为检测器配置(关键字回退,从不失败)。
- 💬 **对话式代理生成器** — 通过聊天起草和完善规则,预览编译后的检测器。
- 🤖 **MCP 上的 AI 助手** — 应用内的 Erlang AI 代理连接到平台的 MCP 工具服务器,可以检查摄像头,拍摄快照,平移/倾斜,查询事件,获取剪辑,创建/启用代理([详情](#-mcp-工具服务器))。
- 🎥 **实时视频扇出** — 边缘流式传输 JPEG 帧;客户端查看签名 MJPEG/帧 URL。
- 🧠 **两阶段 AI** — 边缘本地 YOLO/Qwen 分类,云 Qwen-Plus 验证高价值事件。
- 🔍 **审计验证工具** — 验证器可以平移摄像头,抓取快照,读取最近事件 — 所有操作均已记录。
- 🕹️ **远程控制** — 通过 WebSocket 中继平移/倾斜/快照。
- 🔔 **实时 + 推送** — SSE 实现实时更新,FCM 实现高优先级警报。
- 🧪 **零硬件演示模式** — 使用真实云检测从视频文件模拟摄像头。
## 🏗️ 架构
```mermaid
flowchart TD
App[Flutter 控制台] <--> BE[FastAPI 后端]
BE <--> Edge[笔记本边缘桥接器]
Edge <--> Cam[ESP32-S3 摄像头]
BE --> Qwen[Qwen 云验证]
Edge --> Local[YOLO / YAMNet / Ollama]
```
> 后端从不直接与局域网摄像头通信。边缘桥接器保持与后端的 outbound 连接,并将命令中继到摄像头。
<details>
<summary>详细数据流程</summary>
```text
Erlang AI Vision Flutter 控制台
-> FastAPI 后端(此仓库)
- 身份验证/会话/设备/代理 API
- 实时 MJPEG 流经纪人
- 边缘命令通过 WebSocket 中继
- 事件/媒体/警报持久性
- Qwen 云验证 + MCP 样式工具
<- 笔记本边缘桥接器(SentinelEdge_LaptopEdge)
- 接收 ESP32 帧/健康/命令
- 运行 YOLO/YAMNet/Ollama 本地管道
- 发布候选事件和媒体元数据
<- ESP32 摄像头固件(SentinelEdge_IOT)
- 捕获 JPEG 帧
- 扫描配对二维码以获取 Wi-Fi + 桥接器地址
- 驱动旋转/倾斜伺服器
```
</details>
## 🔌 MCP 工具服务器
平台的工具作为标准 **模型上下文协议** 服务器(可流式传输的 HTTP)公开 — 默认情况下为 `POST {API_PREFIX}/mcp/` — `http://localhost:8000/api/v1/mcp/`。应用内的 **Erlang AI 代理** 聊天作为普通的 MCP 客户端在每次对话中与其连接,因此它可以做的任何事情,任何外部 MCP 客户端(Claude,IDE,代理框架)使用有效令牌也可以做到。
**工具**(所有工具都限定在已验证用户的自己的数据范围内):
| 组 | 工具 |
|---|---|
| 摄像头 | `list_devices`, `get_device_status`, `get_live_snapshot`, `pan_camera`, `tilt_camera` |
| 事件和媒体 | `query_events`, `get_event_clip`, `list_recordings` |
| 代理 | `list_agents`, `create_agent`, `assign_agent`, `unassign_agent` |
**身份验证**:请求携带 `Authorization: Bearer <token>`,其中令牌是后端签发的短时 `mcp_access` 令牌(聊天服务每次对话都会签发一个;用户端令牌端点在路线图上)。没有有效令牌的请求在到达协议层之前被拒绝。
**防护措施**:每个调用都根据聊天范围自主权表进行权限检查(拒绝紧急升级),摄像头移动被限制在伺服器安全范围内并限速,每个工具调用都审计到 `tool_audit`,每个账户每天的聊天回合次数有限(`CHAT_DAILY_MESSAGE_LIMIT`,默认为 50/天),因为一个代理回合可能会消耗多个 Qwen 调用。如果 MCP 服务器不可达,聊天会优雅地降级为纯文本答案。
## 🚀 快速开始
```powershell
# 1. 后端依赖 + 环境变量
pip install -r backend\requirements.txt
# 预期:以 "Successfully installed ..." 结束(无红色 ERROR 行)
Copy-Item .env.example .env
# 2. 初始化或升级本地 SQLite 模式
python -m alembic -c backend\alembic.ini upgrade head
# 预期: "Running upgrade ..." 行以 head 修订版结束,退出代码 0
# 3. 同时运行后端 + Flutter 网页
.\scripts\start-dev.ps1
# 预期:后端日志 "Uvicorn running on http://0.0.0.0:8000",
# Flutter 日志 "lib\main.dart is being served at http://localhost:8080"
```
API 文档:`http://localhost:8000/docs` · 应用:`http://localhost:8080`
**预期结果:** 应用在 `localhost:8080` 加载,`http://localhost:8000/healthz` 返回 `{"data":{"status":"ok"}}`。
**尝试无硬件演示:**
```powershell
$env:PYTHONPATH="backend"
python scripts\create_judge_account.py # 种子演示登录 + 摄像头 + 代理
# 预期:以 "=== judge demo account ready ===" 和登录凭证结束
```
在 `.env` 中设置 `DEMO_SIMULATION_ENABLED=true` 和 Qwen 视觉模型,然后打开演示摄像头的实时视图。后端在四秒后将第一个帧发送到 Qwen,每分钟最多发送一个帧。
完整设置 → [后端设置](docs/backend/backend_setup.md) · [前端设置](docs/frontend/frontend_setup.md)
## 📚 文档
- [后端架构](docs/backend/architecture.md)
- [API 端点](docs/backend/api_endpoints.md)
- [边缘集成](docs/backend/edge_integration.md)
- [媒体存储](docs/backend/media_storage.md)
- [部署(阿里云)](docs/deployment/alibaba_cloud_architecture.md)
**相关仓库:** `SentinelEdge_LaptopEdge`(边缘管道)· `SentinelEdge_IOT`(ESP32 固件)
---
<div align="center">
在 [MIT 许可证](LICENSE) 下许可 · [第三方通知](THIRD_PARTY_NOTICES.md) · 为 Erlang AI Vision 黑客马拉松而构建。
</div>
<!--
资产:banner.png 已连接(保存在 docs/assets/ 下)。仍然可选:
- demo.gif 10-15s 聊天规则 -> 实时视图 -> 事件触发 -> 警报
-->