Agent开发——屏幕获取

Agent开发——屏幕获取,让AI识别你屏幕的信息

前言

动机来源:首先,毫无疑问的是Neuro-Sama,在Vedel打游戏的时候能够识别屏幕信息并做出吐槽,其实Neuro早在两年前就已经能打游戏了,不清楚使用的是视觉识别还是直接读游戏内存【Neuro】AI对杀戮尖塔的顶级理解.
此外,也来源于–ai妹妹无信息知晓敌人数量与位置,一个叫 妹居物语 的AI陪伴应用的桌宠模式,也能够做到上述Neuro-sama的功能.

本质上来说这实现起来并不复杂

原理

目前市面上的很多AI都已经有了多模态的功能,他们不单单是LLM,同时集成了图像识别的功能,现在实现起来就简单多了.

实现上述功能的流程图如下:

img1

即正常的Tool调用流程,重点在于Tool的实现.

实现

屏幕截取

调用个库截取一下即可,真正的问题在于你截取了屏幕之后如何传递给Agent

参见阿里云 千问模型的文档 图像与视频理解

其中说到,有两种方法,一种是传base64,一种是本地传路径,但是本地传路径的方法不适用于OpenAI方式,所以我们只能采用base64传图.

下面代码也在图片截取之后做了转base64的操作,函数返回值也是对应的base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import base64
import mimetypes
import os

import numpy as np
from PIL import ImageGrab, Image
import cv2
from . import pic_resize

import tkinter as tk

def pic_cap():
root = tk.Tk()
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()

img = ImageGrab.grab(bbox=(0, 0,screen_width, screen_height)) #获取当前分辨率宽高
print(img.size[1], img.size[0])
img = np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0], 3)
print("屏幕获取成功!")
img=cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
cv2.imwrite('screenshot1.jpg', img)

# 截图压缩,节省token,视情况使用
pic_size = pic_resize.pic_compress('screenshot1.jpg', 'screenshot1.jpg', target_size=200)
print("图片压缩后的大小为(KB):", pic_size)

if os.path.exists("screenshot1.jpg"):
mime = mimetypes.guess_type("screenshot1.jpg")[0] or "image/png"
with open("screenshot1.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
# 使用 data URI 字符串,API 要求 image_url.url 是字符串
image_repr = f"data:{mime};base64,{b64}"
else:
image_repr = "screenshot1.jpg"

return image_repr

图片压缩部分代码,节省些token吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from io import BytesIO

import cv2
import numpy as np

def pic_compress(pic_path, out_path, target_size=199, quality=90, step=5, pic_type='.jpg'):
# 读取图片bytes
with open(pic_path, 'rb') as f:
pic_byte = f.read()

img_np = np.frombuffer(pic_byte, np.uint8)
img_cv = cv2.imdecode(img_np, cv2.IMREAD_ANYCOLOR)

current_size = len(pic_byte) / 1024
print("图片压缩前的大小为(KB):", current_size)
while current_size > target_size:
pic_byte = cv2.imencode(pic_type, img_cv, [int(cv2.IMWRITE_JPEG_QUALITY), quality])[1]
if quality - step < 0:
break
quality -= step
current_size = len(pic_byte) / 1024

# 保存图片
with open(out_path, 'wb') as f:
f.write(BytesIO(pic_byte).getvalue())

return len(pic_byte) / 1024

Tool

首先,何时调用此Tool.
因为我们是小项目并且是用的外部API,所以我们在此将屏幕识别的触发设定为:用户在对话框输入”看看我在做什么”这类的被动方式.

想要做到我们在前言提到的效果,可以使用轮询的方式主动不间断获取屏幕画面,当然这样的Token开支是巨大的.

具体的实现思路为:
Agent检测到用户批准”屏幕识别”的意图 -> 触发Tool -> 截图并返回Base64图像信息 -> 调用第三方Agent(这里为我们创建的Agent二号机)获取图像分析结果 -> 带着结果返回给Agent

调用第三方Agent(这里为我们创建的Agent二号机)获取图像分析结果:
这样做而不直接把Base64喂给原Agent的原因是,直接将Base64作为Tool的结果返回,会被识别为str而不是图像( 千万不要这样做 会直接把token用光)
目前只想到这样用另一个Agent去做一个识别文本的中转,不清楚是否有更好的实现方法

Prompt

1
2
3
4
5
get_screenshot_tool
- 核心能力:无入参,截取用户屏幕并根据屏幕图片内容和用户输入以及上下文进行对话;
- 出参:图片的详细描述
- 使用场景:当用户谈到涉及“看看我的屏幕”,“结合我的屏幕”,“看我玩的这个游戏”,“你看看这个网页”等涉及要求你获取屏幕信息的场景;
- 示例:user:“猜猜我在电脑上干什么”,调用工具识别用户桌面,并结合出参结合上下文与人设与用户对话;

Tool实现

这里的agent_nopic为Agent二号机,专门用于做图像识别结果中转

为什么不直接用原有的Agent做这次图像识别?
因为原有的Agent带着get_screenshot_tool方法,而在输入涉及”用户屏幕”等词的时候,有有可能会调用该Tool导致循环嵌套,所以做中段识别应该交给一个什么Tool也没有的Agent来做.
agent_nopic就是我新创建的不含任何Tool的Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@tool(description="捕获用户桌面并响应")
def get_screenshot_tool():

# 调用截图模块,返回 data URI 字符串
img_data_uri = pic_cap()

msg = {"role": "user", "content": [
{"type": "text", "text": "这是用户屏幕的截图,详细描述图片上的内容,仅仅是描述即可,不要有其他任何多余的输出,仅输出'这张图片描述了xxxxxx'即可"},
{"type": "image_url", "image_url": {"url": img_data_uri}},
]}
llm.messages.append(msg)

res = llm.agent_nopic.invoke({
"messages":llm.messages
})

latest_message = res["messages"][-1]
if latest_message.content:
res=latest_message.content.strip()

print("图像识别Tool中段输出:"+res)

return res

使用效果

这是我触发屏幕识别时的截图

img2

tool调用:

1
2
3
4
5
6
7
8
tool_calls=[
{
'name': 'get_screenshot_tool',
'args': {'input': '看看我的屏幕'},
'id': '17b3c2b3-e250-4a2e-932a-ff4635ac70f1',
'type': 'tool_call'
}
]

中段图像识别输出:

1
2
3
4
5
6
7
8
9
10
ToolMessage(
content='这张图片 展示了用户浏览一个技术主题的网页,
左侧是“文章目录”和音乐播放器界面(显示歌单),
右侧是正文内容关于神经科学主题——主要讲述 RAG 技术和向量
数据库在学习及 AI 记忆处理中的应用。屏幕底部任务栏显示时间为 11:32,
日期为 2026 年 3 月 16 日。',
name='get_screenshot_tool',
id='ee93efb4-d8f3-4d38-b5b1-1c632ebe2369',
tool_call_id='17b3c2b3-e250-4a2e-932a-ff4635ac70f1'
)

最终输出

1
2
3
好呀!Master 刚才是在看关于神经科学和 RAG 技术的网页哦。
页面里提到了向量数据库怎么帮 AI 记得住记忆呢,好像还讲了怎么用 LLM 来生成知识库,
感觉挺有意思的~ Master 对这方面感兴趣吗?