Skip to content

Repository files navigation

Screen Monitor(屏幕状态视觉感知插件)

screen_monitor 是为 Neo-MoFox 定制的屏幕上下文感知插件。它可以在后台观察用户屏幕变化,通过截图或短视频调用视觉模型分析当前活动,并将近期观测整理成可被工具调用的摘要。

当前版本采用 窗口切换触发 + 冷却控制 + 周期观察 + 低变化自动停止 的保守消费模型,目标是在提供屏幕上下文的同时尽量减少 VLM 调用和 token 消耗。

核心特性

  • 窗口变化触发:后台轮询当前活动窗口标题,检测到窗口切换后触发首次屏幕分析。
  • 冷却保护:通过 monitor.cooldown_seconds 限制两次首次触发之间的最小间隔,避免频繁调用视觉模型。
  • 周期观察模式:首次分析后进入周期模式,按 monitor.periodic_interval_seconds 持续捕获屏幕。
  • 低变化自动停止:画面连续多次变化低于阈值时自动退出周期模式,回到待机状态。
  • 截图/视频双模式:支持单帧截图分析,也支持短视频录制后调用视频理解模型。
  • 局部变化检测:差异检测采用分块最大值评分,角落通知、局部弹窗等变化更容易被捕捉。
  • 观测缓存与状态落盘:最近观测保存在进程内缓存,并将最新分析状态写入 data/screen_monitor,便于排查和后续恢复能力扩展。
  • 工具可读摘要:提供 get_screen_observation 工具,供 LLM 查询当前屏幕活动和近期变化轨迹。
  • 敏感信息模糊化提示:默认提示词要求模型不要转写账号、密码、验证码、证件号、手机号、地址、付款码等敏感内容。

平台与依赖

Python 依赖

这是一个第三方插件,不建议为了安装插件依赖去修改主项目的 pyproject.toml。插件所需 Python 包应优先写在 manifest.jsonpython_dependencies 中,由 Neo-MoFox 的插件依赖解析器按 config/core.tomlplugin_deps.install_command 自动安装。

当前声明的插件依赖为:

"python_dependencies": [
	"mss",
	"pillow",
	"httpx"
]

如果关闭了插件依赖自动安装,或需要手动排查依赖问题,可以在当前项目环境中执行:

uv pip install mss pillow httpx

不推荐使用 uv add 安装第三方插件依赖,因为它会把依赖写入主项目依赖清单,更适合内置功能或核心项目依赖。

系统依赖

  • 截图模式依赖 mss,当前实现主要面向桌面环境。
  • 窗口切换监听使用 Windows API:ctypes.windll.user32
  • 视频模式依赖系统已安装 ffmpeg,并且当前录制实现使用 Windows 的 gdigrab 设备。

FFmpeg 获取入口:

FFmpeg 官方主要提供源码;普通 Windows 用户通常不需要克隆源码仓库自行编译,下载预编译包并把 bin 目录加入 PATH 更直接。

检查 ffmpeg 是否可用:

ffmpeg -version

注意:当前完整后台窗口监听与视频录制流程主要面向 Windows。非 Windows 环境建议先关闭插件,或仅在确认截图能力可用后再做适配。

工作机制

插件启动后,如果 monitor.enabled = true,会创建 ScreenMonitorService 并启动窗口观察器。

整体流程如下:

flowchart TD
		A[插件加载] --> B{monitor.enabled?}
		B -- 否 --> C[记录禁用日志]
		B -- 是 --> D[启动窗口观察器]
		D --> E[轮询活动窗口标题]
		E --> G{窗口标题变化?}
		G -- 否 --> E
		G -- 是 --> H[防抖确认]
		H --> I{冷却结束且非周期模式?}
		I -- 否 --> E
		I -- 是 --> J[执行首次截图/录屏分析]
		J --> K[进入周期模式]
		K --> L[按间隔捕获屏幕]
		L --> M{连续低变化达到阈值?}
		M -- 是 --> N[退出周期模式]
		M -- 否 --> O[VLM/视频模型分析]
		O --> P[写入缓存和状态落盘]
		P --> L
Loading

待机模式

服务初始状态为 IDLE。此时插件只观察当前活动窗口标题变化,不持续截图。

当检测到窗口标题变化后,会等待 monitor.event_debounce_seconds 秒做防抖确认。如果确认发生切换,并且不在冷却期内,则触发一次屏幕分析。

周期模式

首次分析后,服务进入 PERIODIC 状态。此时插件会每隔 monitor.periodic_interval_seconds 秒进行一次截图或录屏分析。

如果画面连续 monitor.terminate_after_low_change_count 次低于 monitor.diff_threshold,说明屏幕基本稳定,插件会退出周期模式并回到待机。

工具调用模式

LLM 调用 get_screen_observation 工具时,video 模式会即时录屏并分析,确保拿到最新一段操作变化;screenshot 模式才会优先读取 tool.cache_reuse_seconds 时间窗口内的缓存观测。若截图缓存不存在或过旧,则即时截图并分析当前屏幕。

随后工具会读取最近若干条有效观测,并使用 model.summary_task 对近期活动进行整合摘要。工具结果会按 tool.status_detail_level 追加可选状态块,帮助用户确认本次来源是缓存还是新捕获、请求模式和实际链路是否一致、缓存是否仍有效。

当前体验状态

当前插件的基础体验已经比较完整:后台窗口变化监听、截图/录屏分析、进程内观测缓存、最新状态落盘和工具读取都已经形成闭环。插件更适合承担“近期屏幕上下文感知”的角色,而不是单纯作为一次性的截图工具。

从日常使用角度看,当前体验特点如下:

  • 视频模式体验最佳video 能观察到短时间内的变化过程,适合理解“用户刚刚怎么操作到当前状态”;当 ffmpeg 或视频接口不可用时,插件会明确提示并回退到 screenshot 链路。
  • 截图模式稳定兜底screenshot 链路更轻、更通用,适合作为视频依赖不可用时的可靠后备。
  • 后台打扰较少:窗口切换触发、冷却时间、周期观察和低变化自动停止能减少频繁模型调用,避免一直截图或持续消耗 token。
  • 运行容错较好:不支持 Windows 活动窗口 API 的环境会跳过后台窗口监听;窗口标题读取、锁屏检测、ffmpeg 检测和视频压缩失败都会尽量安全返回,不让后台任务轻易崩掉。
  • 第三方依赖边界清晰:Python 依赖声明在 manifest.json,手动兜底使用 uv pip install,避免把第三方插件依赖写入主项目依赖清单。
  • 专项测试已覆盖主要路径:当前插件专项测试覆盖截图、视频捕获、缓存、工具和服务流程,能帮助避免小改破坏主链路。

仍然会影响体验的地方主要集中在“首次配置”和“视频 provider 兼容性”:

  • 首次配置门槛偏高:依赖、模型任务、桌面捕获权限、ffmpeg 和 provider 视频格式任一环节异常,都可能表现为“插件没观察到东西”。
  • 视频链路依赖更多:视频录制依赖系统 ffmpeg 与 Windows gdigrab,视频请求还依赖供应商兼容 video_url 入参格式。插件会做 ffmpeg 检查并保留最近错误,便于定位。
  • 状态可解释性已增强:工具返回可按 off / basic / detailed 控制状态颗粒度;默认 basic 会显示来源、捕获链路和最近观测缓存情况。
  • 视频直连接口仍需关注 provider 差异_video_api.py 仍绕过框架标准 LLMRequest,但外部 API 错误响应日志已截断,避免大段响应刷屏。

后续如果继续小步优化,建议优先级为:

  1. 继续适配更多供应商的视频入参格式,让 video 模式更稳。
  2. 在配置 UI 中进一步解释 ffmpeg、provider、视频模型任务之间的关系。
  3. 增加更明确的后台健康检查入口,例如一次性输出截图链路、视频链路、模型任务和缓存状态。

配置示例

推荐在插件配置中使用如下结构:

[monitor]
enabled = true
retention_seconds = 7200
save_screenshot = false
monitor_index = 1
image_max_width = 1024
image_max_height = 1024
jpeg_quality = 80
diff_threshold = 2.0
diff_grid_size = 6
log_enabled = true

# 窗口切换与状态机控制
event_debounce_seconds = 2
cooldown_seconds = 600
periodic_interval_seconds = 180
terminate_after_low_change_count = 2

# 捕获模式:screenshot 或 video;最佳体验建议 video,依赖不可用时会回退 screenshot
capture_mode = "video"

# 视频模式配置,仅 capture_mode = "video" 时使用
video_duration = 5.0
video_fps = 5
video_compress_threshold_mb = 8.0

# 截图分析提示词
prompt = """
请理解这张屏幕截图,并总结用户当前正在做什么、关注什么、所处的场景状态。
重点关注:正在使用的程序或网页、可见文字主题、当前操作焦点、可能的意图或上下文。
请输出一段适合后续摘要参考的简洁中文描述,偏向“用户现在在做什么”而不是逐项念图。
不要输出过多琐碎 UI 细节,不要机械罗列控件。
如果画面中可能含有账号、密码、验证码、身份证号、手机号、家庭住址、付款码等敏感信息,
不要转写具体内容,只需模糊化描述。
"""

# 视频分析提示词
video_prompt = """
以下是一段屏幕录制视频,记录了用户在这段时间内的屏幕操作。
请观察视频中的变化,结合时间维度分析:正在使用的程序或网页、可见文字主题、
操作焦点变化、可能的意图或上下文。
请输出一段简洁中文描述,偏向“用户现在在做什么”而不是逐项念图。
不要输出过多琐碎 UI 细节。如果含有敏感信息,只需模糊化描述。
"""

[model.screenshot]
model_task = "vlm"
models = []
temperature = 0.3
max_tokens = 800

[model.video]
model_task = "video"
models = []
temperature = 0.3
max_tokens = 1500

[model]
summary_task = "utils"
recent_count = 5
summary_prompt = """
你是一个屏幕观察摘要助手。请根据以下屏幕观察信息,生成一段简洁的整合摘要,帮助理解用户近期活动和时间跨度。
要求:
1. 按时间顺序总结这段时间里发生了什么变化,并明确写出关键时间点或时间范围
2. 先写变化轨迹,再写当前正在做什么
3. 用2-3句话描述,突出阶段变化、连续性、起止时间和当前所处阶段,而不是只写单帧画面

请输出整合摘要:
"""

[tool]
get_observation_description = """
查看用户近期屏幕观察状态。
调用此工具可以获取用户近期屏幕观察的整合摘要,适用于需要了解用户当前屏幕状态或近期活动的场景。
返回:整合后的屏幕观察摘要,包含当前活动和近期状态概述。
"""
cache_reuse_seconds = 30
status_detail_level = "basic"

[debug]
log_tool_result = false

关键配置说明

monitor.capture_mode

捕获模式:

  • screenshot:截图模式,使用框架标准图片 VLM 流程。
  • video:短视频模式,使用 ffmpeg 录制屏幕,并通过视频理解接口分析。它能保留操作过程,是本插件的最佳体验模式。

视频模式下如果 ffmpeg 不可用,插件会回退到截图模式。

tool.cache_reuse_seconds

截图模式下,工具调用复用最近观测缓存的秒数。默认 30 秒。

video 模式不使用这个 30 秒短期复用窗口;每次工具调用都会重新录制一小段视频,这样才能体现 video 模式“看见过程变化”的最佳体验。

  • 值越大:更省模型调用,结果更稳定,但可能不够“此刻”。
  • 值越小:更频繁即时捕获,更新更及时,但消耗更高。
  • 设为 0:截图模式下也每次工具调用都重新捕获。

tool.status_detail_level

工具返回结果中追加状态信息的颗粒度:

  • off:只返回屏幕摘要,不追加状态块。
  • basic:默认值,追加来源、捕获模式、最近观测和缓存剩余时间。
  • detailed:在 basic 基础上追加最近错误、参考观测条数、工具缓存策略和 video 最优体验提示。

capture_mode = "video" 但实际走了 screenshot,状态块会明确显示“请求 video,实际 screenshot”,方便快速判断双链路是否正常。

monitor.cooldown_seconds

窗口切换触发首次分析后的冷却时间。冷却期内新的窗口切换不会再次触发首次分析。

默认值为 600 秒,即 10 分钟。

monitor.periodic_interval_seconds

进入周期模式后,两次捕获之间的间隔。

默认值为 180 秒,即 3 分钟。

monitor.diff_threshold

画面变化阈值。值越小越敏感,越容易触发分析;值越大越容易跳过变化较小的画面。

monitor.diff_grid_size

差异检测分块数量。插件会取变化最明显区域的平均差异值作为分数。

  • 较小:更稳定,不容易误触发。
  • 较大:更敏感,适合捕捉局部弹窗、角落通知等变化。

monitor.terminate_after_low_change_count

周期模式中,连续多少次画面变化低于阈值后退出周期模式。

默认值为 2

monitor.retention_seconds

观测结果保留时间。过期观测不会继续参与工具摘要。

默认值为 7200 秒,即 2 小时。

model.screenshotmodel.video

截图和视频可以使用不同模型配置:

  • models 非空时,按模型名列表解析模型,并覆盖 model_task
  • models 为空时,使用 model_taskconfig/model.toml 中查找任务对应模型。

model.summary_task

工具整合近期观测时使用的摘要模型任务。默认是 utils

如果找不到可用摘要模型,工具会直接返回当前屏幕活动,不再做二次整合。

数据存储

插件使用两层存储:

  • 进程内缓存ScreenObservationStore,最多保留 50 条近期观测,供工具快速读取。
  • 磁盘状态落盘data/screen_monitor/latest_status,保存最新未过期观测文本,主要用于排查、审计和后续恢复能力扩展;当前工具热路径以进程内缓存为准。

如果启用 monitor.save_screenshot = true,截图或视频会保存到:

data/screenshots

该目录最多保留 100 个 screen_monitor_* 文件,超出后会删除最旧文件。

工具:get_screen_observation

插件提供工具组件:

get_screen_observation

用途:让 LLM 查询用户近期屏幕观察状态。

工具执行时:

  1. 检查插件是否启用。
  2. 如果是 video 模式,直接即时录屏并分析。
  3. 如果是 screenshot 模式,优先复用 tool.cache_reuse_seconds 时间窗口内的最新缓存;缓存过旧时即时截图并分析。
  4. 将当前分析结果写入缓存。
  5. 读取最近 recent_count 条未过期观测。
  6. 使用 model.summary_task 生成近期活动整合摘要。
  7. tool.status_detail_level 追加可选状态块。

可选参数:

  • recent_count:读取最近观测条数;为空时使用 model.recent_count

视频模式说明

视频模式目前会绕过框架标准 LLMRequest 视频载荷,直接调用 OpenAI 兼容的 /chat/completions 接口。

原因是当前框架的视频类型序列化存在兼容问题,插件临时使用直连方式构造:

{
	"type": "video_url",
	"video_url": {
		"url": "data:video/mp4;base64,...",
		"fps": 5,
		"max_frames": 25
	}
}

因此视频模式需要注意:

  • 供应商必须兼容上述视频请求格式。
  • config/model.toml 中的视频模型必须配置有效的 api_provider
  • 视频任务默认使用 model.video.model_task = "video"
  • 如果不同供应商的视频入参格式不同,可能需要调整 _video_api.py
  • 如果 ffmpeg 不可用,工具状态会显示 video 请求已回退到 screenshot 链路。

隐私与安全建议

  • 默认不保存截图;除非需要调试,否则建议保持 monitor.save_screenshot = false
  • 如需保存截图或视频,请注意 data/screenshots 可能包含敏感屏幕内容。
  • 默认提示词已要求模型模糊化敏感信息,但这不能替代真正的数据脱敏。
  • 不要在日志或配置中提交明文 API key。
  • 视频模式直连 API 时会读取模型 provider 的 API key,请确保相关配置文件不被提交到公开仓库。

故障排查

插件没有触发分析

检查:

  • monitor.enabled 是否为 true
  • 是否仍在 monitor.cooldown_seconds 冷却期内。
  • 当前是否处于锁屏状态。
  • 日志中是否有 [Screen Monitor] 相关错误。

工具返回“截屏失败”

可能原因:

  • 当前环境没有可访问的桌面会话。
  • mss 无法捕获屏幕。
  • 显示器索引配置不正确。

可尝试:

  • monitor.monitor_index 设置为 1
  • 如果多显示器环境异常,可尝试设置为 0 捕获全部显示器。

视频模式失败

检查:

ffmpeg -version

同时确认:

  • monitor.capture_mode = "video"
  • model.video.model_taskmodel.video.models 已正确配置。
  • 对应 provider 支持视频输入。
  • 日志中是否出现 Video API errorVideo capture failed

模型不可用

检查 config/model.toml 中是否存在:

  • 截图任务:vlm
  • 视频任务:video
  • 摘要任务:utils

也可以在插件配置中显式填写 model.screenshot.modelsmodel.video.models

相关文件

  • plugin.py:插件入口和生命周期管理。
  • config.py:插件配置定义。
  • service.py:后台窗口监听、状态机、周期捕获与分析。
  • _capture.py:截图、录屏和模型解析工具。
  • _video_api.py:视频模式直连 API 请求。
  • tool.pyget_screen_observation 工具实现。
  • observation_store.py:进程内观测缓存。
  • storage.py:磁盘持久化存储。
  • event_handler.py:启动事件日志处理。

About

主人屏幕动态实时视觉监测插件,结合VLM分析提供弱注入背景

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages