zhl commited on
Commit
ed1d2fa
·
1 Parent(s): f654931

agent.wang first commit.

Browse files
Files changed (7) hide show
  1. README.md +113 -14
  2. __pycache__/openai.cpython-38.pyc +0 -0
  3. analysis_result.json +55 -0
  4. analysis_result.py +198 -0
  5. app.py +558 -0
  6. demo.txt +0 -0
  7. logo.txt +1 -0
README.md CHANGED
@@ -1,14 +1,113 @@
1
- ---
2
- title: Score
3
- emoji: 🔥
4
- colorFrom: blue
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.44.1
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- short_description: Wang Agent Intelligent Body Scoring System
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 王艾卷智能体评分系统 agent.wang
2
+
3
+ ## 项目介绍
4
+
5
+ `agent.wang 王艾卷智能体评分系统` 是一款基于百度千帆 ERNIE-4.5-Turbo-VL 多模态大模型开发的智能体评估工具。该系统能够对智能体网页截图进行自动化分析,从多个维度进行评分并拆解智能体能力,为开发者和产品经理提供客观、全面的智能体评估报告。
6
+
7
+ ## 功能特点
8
+
9
+ - **多维度评分**:从整体评价、设计美感、易用性、功能完整性和响应式设计五个维度进行1-10分制评分
10
+ - **能力拆解**:自动识别智能体的核心功能、优势、劣势、潜在用途和改进方向
11
+ - **结构化输出**:提供JSON格式原始数据和可视化解析结果,方便查看和进一步处理
12
+ - **结果保存**:自动将分析结果保存为JSON文件,便于归档和对比分析
13
+ - **错误处理**:完善的错误提示和处理机制,帮助用户快速定位和解决问题
14
+
15
+ ## 环境要求
16
+
17
+ - Python 3.8+
18
+ - openai 库
19
+
20
+ ## 安装步骤
21
+
22
+ 1. 克隆或下载项目代码到本地
23
+ 2. 安装依赖库:
24
+ ```bash
25
+ pip install openai
26
+ ```
27
+
28
+ ## 百度API密钥获取
29
+
30
+ 1. 访问百度智能云API密钥管理页面:https://console.bce.baidu.com/iam/#/iam/apikey/list
31
+ 2. 登录百度账号(未注册用户需先完成注册和实名认证)
32
+ 3. 点击"创建Access Key"生成API密钥
33
+ 4. 确保已开通千帆大模型服务(新用户可领取免费调用额度)
34
+
35
+ ## 使用指南
36
+
37
+ 1. 准备一张智能体网页的截图,保存为PNG或JPG格式
38
+ 2. 打开`ernie_analysis_with_parsing.py`文件,修改配置参数:
39
+ ```python
40
+ API_KEY = "您的百度智能云Access Key" # 替换为实际API密钥
41
+ IMAGE_PATH = "demo.png" # 替换为您的截图路径
42
+ OUTPUT_FILE = "analysis_result.json" # 结果保存文件名
43
+ ```
44
+ 3. 运行脚本:
45
+ ```bash
46
+ python ernie_analysis_with_parsing.py
47
+ ```
48
+ 4. 查看结果:
49
+ - 控制台将显示原始JSON数据和解析后的结构化结果
50
+ - 分析结果同时会保存到指定的JSON文件中
51
+
52
+ ## 输出结果说明
53
+
54
+ ### 控制台输出
55
+
56
+ 1. **原始JSON结果**:完整展示模型返回的结构化数据,包含页面评分和智能体能力拆解的全部信息
57
+ 2. **解析后的结果**:格式化展示分析结果,包括:
58
+ - 页面评分:各维度得分及评论
59
+ - 智能体能力拆解:核心功能、优势、劣势、潜在用途、改进方向和详细分析
60
+
61
+ ### JSON文件输出
62
+
63
+ 分析结果会保存到指定的JSON文件中,可用于:
64
+ - 结果归档和版本对比
65
+ - 导入其他工具进行进一步分析
66
+ - 集成到报告生成系统
67
+
68
+ ## 常见问题
69
+
70
+ ### API密钥相关问题
71
+
72
+ - **Q: 提示"API密钥验证失败"怎么办?**
73
+ A: 检查API_KEY是否正确填写,确保没有多余空格;确认百度智能云账号已完成实名认证并开通千帆大模型服务;检查账号是否有可用的调用额度。
74
+
75
+ ### 图像相关问题
76
+
77
+ - **Q: 提示"未找到图像文件"如何解决?**
78
+ A: 检查IMAGE_PATH是否正确,确保路径中包含正确的文件名和扩展名;如果使用相对路径,确认图像文件与脚本在同一目录下。
79
+
80
+ ### 结果解析问题
81
+
82
+ - **Q: 模型返回结果格式异常怎么办?**
83
+ A: 系统已内置格式清理功能,会自动处理常见的格式问题。如果仍解析失败,可尝试重新运行脚本。
84
+
85
+ ## 自定义扩展
86
+
87
+ ### 调整评分维度
88
+
89
+ 如需增加或修改评分维度,可修改脚本中messages里的文本内容,例如:
90
+ "text": "请分析这张智能体网页的截图,并完成以下任务:\n\
91
+ 1. 页面评分(每项1-10分,并给出简短评论):\n\
92
+ - 整体评价(overall)\n\
93
+ - 设计美感(design)\n\
94
+ - 易用性(usability)\n\
95
+ - 功能完整性(functionality)\n\
96
+ - 响应式设计(responsiveness)\n\
97
+ - 安全性(security)\n # 新增维度\
98
+ ..."
99
+ ### 修改输出文件路径
100
+
101
+ 如需将结果保存到指定路径,可修改OUTPUT_FILE参数:
102
+ OUTPUT_FILE = "D:/reports/agent_analysis_202409.json" # 绝对路径
103
+ ## 注意事项
104
+
105
+ - 使用前请确保已阅读并遵守百度智能云的服务协议
106
+ - 合理使用API调用额度,避免不必要的频繁调用
107
+ - 对于敏感页面截图,请确保已获得必要的授权
108
+
109
+ ## 联系方式
110
+
111
+ ### 如有任何问题或建议,请联系项目维护团队。
112
+ ### url: https://agent.wang
113
+ ### exmail: zhl@agent.wang
__pycache__/openai.cpython-38.pyc ADDED
Binary file (2.28 kB). View file
 
analysis_result.json ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "页面评分": {
3
+ "overall": {
4
+ "score": 8,
5
+ "comment": "整体页面简洁明了,信息传达清晰,用户体验较好。"
6
+ },
7
+ "design": {
8
+ "score": 8,
9
+ "comment": "设计风格简约,色彩搭配和谐,图标和文字布局合理。"
10
+ },
11
+ "usability": {
12
+ "score": 9,
13
+ "comment": "页面操作简单,用户可以轻松找到所需功能,易用性高。"
14
+ },
15
+ "functionality": {
16
+ "score": 8,
17
+ "comment": "功能明确,能够满足用户识别物体等基本需求。"
18
+ },
19
+ "responsiveness": {
20
+ "score": 8,
21
+ "comment": "在不同设备上应该都能有较好的显示效果,响应式设计良好。"
22
+ }
23
+ },
24
+ "智能体能力拆解": {
25
+ "core_functions": [
26
+ "识别周围物体并提供名称",
27
+ "提供物体的属性信息",
28
+ "提供物体的用途信息",
29
+ "回答用户关于物体的相关问题"
30
+ ],
31
+ "strengths": [
32
+ "功能明确,专注于物体识别和信息提供",
33
+ "操作简单,用户易上手",
34
+ "信息提供较为全面",
35
+ "页面设计简洁,用户体验好"
36
+ ],
37
+ "weaknesses": [
38
+ "功能相对单一,缺乏更多扩展功能",
39
+ "对于复杂物体的识别和信息提供可能存在不足",
40
+ "缺乏用户个性化设置",
41
+ "没有明显的反馈机制,用户无法对识别结果进行评价"
42
+ ],
43
+ "potential_uses": [
44
+ "日常生活中的物体识别,如识别植物、电子设备等",
45
+ "学习场景中,帮助学生识别和了解各种物体",
46
+ "旅游时,识别陌生物体获取相关信息"
47
+ ],
48
+ "improvement_areas": [
49
+ "增加更多功能,如物体分类、相似物体推荐等",
50
+ "提供用户反馈机制,以便改进识别准确率",
51
+ "增加个性化设置,满足不同用户需求"
52
+ ],
53
+ "detailed_analysis": "该智能体网页整体设计简洁,以用户需求为核心,专注于物体识别和信息提供。页面布局合理,操作简单,用户可以轻松输入问题并获取相关信息。然而,功能相对单一,缺乏更多扩展功能,对于复杂物体的识别和信息提供可能存在不足。此外,缺乏用户个性化设置和反馈机制,无法满足不同用户的需求和改进识别准确率。未来可以增加更多功能,提供用户反馈机制和个性化设置,以提升用户体验和智能体的实用性。"
54
+ }
55
+ }
analysis_result.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import json
3
+ from openai import OpenAI
4
+ from openai import APIError, AuthenticationError, NotFoundError
5
+
6
+ def image_to_base64(image_path):
7
+ """将图像文件转换为base64编码字符串"""
8
+ try:
9
+ with open(image_path, 'rb') as image_file:
10
+ image_data = image_file.read()
11
+ return base64.b64encode(image_data).decode('utf-8')
12
+ except FileNotFoundError:
13
+ print(f"错误:未找到图像文件 '{image_path}'")
14
+ return None
15
+ except Exception as e:
16
+ print(f"转换图像时出错:{str(e)}")
17
+ return None
18
+
19
+ def clean_json_response(raw_response):
20
+ """清理模型返回的响应,移除 ```json 和 ``` 代码块标记"""
21
+ # 移除开头的 ```json(不区分大小写)
22
+ if raw_response.startswith('```json') or raw_response.startswith('```JSON'):
23
+ raw_response = raw_response[7:]
24
+ # 移除结尾的 ```
25
+ if raw_response.endswith('```'):
26
+ raw_response = raw_response[:-3]
27
+ # 去除首尾多余空格/换行
28
+ return raw_response.strip()
29
+
30
+ def parse_analysis_result(result):
31
+ """解析分析结果并以结构化方式展示"""
32
+ if not result:
33
+ return
34
+
35
+ print("\n" + "="*50)
36
+ print("📊 图像分析结果解析")
37
+ print("="*50)
38
+
39
+ # 解析页面评分
40
+ print("\n【页面评分】")
41
+ scoring = result.get("页面评分", {})
42
+ for key, value in scoring.items():
43
+ if key.endswith("_comment"):
44
+ # 处理评论(对应前面的评分项)
45
+ score_key = key.replace("_comment", "")
46
+ print(f" - {score_key}: {scoring.get(score_key, 'N/A')}分 - {value}")
47
+
48
+ # 解析智能体能力
49
+ print("\n【智能体能力拆解】")
50
+ capabilities = result.get("智能体能力拆解", {})
51
+
52
+ # 核心功能
53
+ print("\n核心功能:")
54
+ for i, func in enumerate(capabilities.get("core_functions", []), 1):
55
+ print(f" {i}. {func}")
56
+
57
+ # 优势
58
+ print("\n优势:")
59
+ for i, strength in enumerate(capabilities.get("strengths", []), 1):
60
+ print(f" {i}. {strength}")
61
+
62
+ # 劣势
63
+ print("\n劣势:")
64
+ for i, weakness in enumerate(capabilities.get("weaknesses", []), 1):
65
+ print(f" {i}. {weakness}")
66
+
67
+ # 潜在用途
68
+ print("\n潜在用途:")
69
+ for i, use in enumerate(capabilities.get("potential_uses", []), 1):
70
+ print(f" {i}. {use}")
71
+
72
+ # 改进方向
73
+ print("\n改进方向:")
74
+ for i, area in enumerate(capabilities.get("improvement_areas", []), 1):
75
+ print(f" {i}. {area}")
76
+
77
+ # 详细分析
78
+ print("\n【详细分析】")
79
+ print(capabilities.get("detailed_analysis", "无详细分析信息"))
80
+
81
+ print("\n" + "="*50)
82
+
83
+ def analyze_image_with_ernie(api_key, image_path):
84
+ """使用百度ERNIE-VL模型分析图像并返回结果"""
85
+ # 转换图像为base64
86
+ base64_string = image_to_base64(image_path)
87
+ if not base64_string:
88
+ return None
89
+
90
+ try:
91
+ # 初始化客户端
92
+ client = OpenAI(
93
+ base_url='https://qianfan.baidubce.com/v2',
94
+ api_key=api_key
95
+ )
96
+
97
+ # 构建消息
98
+ messages = [
99
+ {
100
+ "role": "user",
101
+ "content": [
102
+ {
103
+ "type": "text",
104
+ "text": "请分析这张智能体网页的截图,并完成以下任务:\n\
105
+ 1. 页面评分(每项1-10分,并给出简短评论):\n\
106
+ - 整体评价(overall)\n\
107
+ - 设计美感(design)\n\
108
+ - 易用性(usability)\n\
109
+ - 功能完整性(functionality)\n\
110
+ - 响应式设计(responsiveness)\n\
111
+ 2. 智能体能力拆解:\n\
112
+ - 核心功能(core_functions):列出3-5个主要功能\n\
113
+ - 优势(strengths):列出3-4个主要优势\n\
114
+ - 劣势(weaknesses):列出3-4个潜在劣势\n\
115
+ - 潜在用途(potential_uses):列出3-4个可能的应用场景\n\
116
+ - 改进方向(improvement_areas):列出2-3个可以改进的地方\n\
117
+ - 详细分析(detailed_analysis):一段详细的综合分析\n\
118
+ 【输出要求】:仅返回纯JSON字符串,不要包含任何代码块标记(如```json、```),确保JSON结构可直接被Python的json.loads()解析。"
119
+ },
120
+ {
121
+ "type": "image_url",
122
+ "image_url": {"url": f"data:image/jpeg;base64,{base64_string}"}
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+
128
+ # 发送请求
129
+ print("正在分析图像,请稍候...")
130
+ response = client.chat.completions.create(
131
+ model="ernie-4.5-turbo-vl",
132
+ messages=messages,
133
+ temperature=0.8,
134
+ top_p=0.2,
135
+ extra_body={"penalty_score": 1}
136
+ )
137
+
138
+ # 处理响应
139
+ raw_content = response.choices[0].message.content
140
+ cleaned_content = clean_json_response(raw_content)
141
+
142
+ try:
143
+ result = json.loads(cleaned_content)
144
+ return result
145
+ except json.JSONDecodeError as e:
146
+ print(f"错误:清理后的内容仍不是有效JSON格式,错误详情:{str(e)}")
147
+ print(f"清理后内容:{cleaned_content}")
148
+ return None
149
+
150
+ except AuthenticationError:
151
+ print("错误:API密钥验证失败,请检查您的百度智能云API密钥是否正确")
152
+ return None
153
+ except NotFoundError:
154
+ print("错误:未找到指定的模型,请确认模型名称是否正确")
155
+ return None
156
+ except APIError as e:
157
+ print(f"API请求失败:{str(e)}")
158
+ return None
159
+ except Exception as e:
160
+ print(f"发生未知错误:{str(e)}")
161
+ return None
162
+
163
+ def save_analysis_result(result, output_file="analysis_result.json"):
164
+ """保存分析结果到JSON文件"""
165
+ if result:
166
+ try:
167
+ with open(output_file, 'w', encoding='utf-8') as f:
168
+ json.dump(result, f, ensure_ascii=False, indent=2)
169
+ print(f"\n分析结果已保存到 {output_file}")
170
+ except Exception as e:
171
+ print(f"保存结果时出错:{str(e)}")
172
+
173
+ if __name__ == "__main__":
174
+ # 配置
175
+ API_KEY = os.getenv('baidu_api_key') # 替换为实际API密钥
176
+ IMAGE_PATH = "demo.png" # 替换为实际图像路径
177
+ OUTPUT_FILE = "analysis_result.json" # 结果保存文件名
178
+
179
+ # 检查API密钥
180
+ if API_KEY in ["您的百度智能云API密钥", "请替换为您的百度API密钥"]:
181
+ print("⚠️ 请先替换代码中的API_KEY为您自己的百度智能云API密钥!")
182
+ print("获取地址:https://console.bce.baidu.com/iam/#/iam/apikey/list")
183
+ else:
184
+ # 执行分析
185
+ analysis_result = analyze_image_with_ernie(API_KEY, IMAGE_PATH)
186
+
187
+ # 解析并展示结果
188
+ if analysis_result:
189
+ # 打印原始JSON(便于调试)
190
+ print("\n📄 原始分析结果(JSON):")
191
+ print(json.dumps(analysis_result, ensure_ascii=False, indent=2))
192
+
193
+ # 解析并格式化展示
194
+ parse_analysis_result(analysis_result)
195
+
196
+ # 保存结果到文件
197
+ save_analysis_result(analysis_result, OUTPUT_FILE)
198
+
app.py ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import base64
3
+ import json
4
+ import time
5
+ from openai import OpenAI, APITimeoutError, APIError
6
+ import io
7
+ import logging
8
+ import os
9
+
10
+ # 尝试导入PIL(pillow的实际包名),并处理导入错误
11
+ try:
12
+ from PIL import Image
13
+ PIL_AVAILABLE = True
14
+ except ImportError:
15
+ PIL_AVAILABLE = False
16
+ logging.warning("未找到pillow库,图片处理功能将不可用")
17
+
18
+ # 配置日志
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
+ API_KEY = os.getenv('baidu_api_key')
22
+ # 初始化OpenAI客户端(替换为你的百度千帆API密钥)
23
+ client = OpenAI(
24
+ base_url='https://qianfan.baidubce.com/v2',
25
+ api_key=API_KEY # 请替换为实际密钥
26
+ )
27
+
28
+ # ---------------------- Base64图片读取函数 ----------------------
29
+ def read_base64_from_file(file_path):
30
+ """从指定文本文件读取Base64编码(处理空文件、路径错误等异常)"""
31
+ try:
32
+ # 检查文件是否存在
33
+ if not os.path.exists(file_path):
34
+ logging.warning(f"Base64文件不存在:{file_path}")
35
+ return None
36
+
37
+ # 读取文件内容(去除空行和空格)
38
+ with open(file_path, 'r', encoding='utf-8') as f:
39
+ base64_str = f.read().strip()
40
+
41
+ # 验证Base64有效性(简单检查长度是否为4的倍数)
42
+ if len(base64_str) % 4 != 0:
43
+ logging.error(f"{file_path} 中Base64编码无效(长度非4的倍数)")
44
+ return None
45
+
46
+ return base64_str
47
+ except Exception as e:
48
+ logging.error(f"读取{file_path}失败:{str(e)}")
49
+ return None
50
+
51
+ def base64_to_pil_image(base64_str):
52
+ """将Base64字符串转换为PIL Image对象(用于Gradio渲染)"""
53
+ try:
54
+ if not base64_str:
55
+ return None
56
+
57
+ # 解码Base64为字节流
58
+ image_bytes = base64.b64decode(base64_str)
59
+ # 转换为PIL Image
60
+ image = Image.open(io.BytesIO(image_bytes))
61
+ return image
62
+ except Exception as e:
63
+ logging.error(f"Base64转图片失败:{str(e)}")
64
+ return None
65
+
66
+ # ---------------------- 核心工具函数 ----------------------
67
+ def compress_image(image_path, max_size=(1024, 1024)):
68
+ """图片压缩处理,增加pillow可用性检查"""
69
+ if not PIL_AVAILABLE:
70
+ raise Exception("未安装pillow库,请执行 `pip install pillow` 安装后重试")
71
+
72
+ try:
73
+ with Image.open(image_path) as img:
74
+ # 处理透明背景(转为白色背景)
75
+ if img.mode in ('RGBA', 'LA'):
76
+ background = Image.new(img.mode[:-1], img.size, (255, 255, 255))
77
+ background.paste(img, img.split()[-1])
78
+ img = background
79
+ # 按比例缩小图片(不拉伸)
80
+ img.thumbnail(max_size, Image.Resampling.LANCZOS)
81
+ # 保存到内存并转Base64
82
+ img_byte_arr = io.BytesIO()
83
+ img.save(img_byte_arr, format='JPEG', quality=80)
84
+ return base64.b64encode(img_byte_arr.getvalue()).decode('utf-8')
85
+ except Exception as e:
86
+ logger.error(f"图片处理失败: {str(e)}")
87
+ raise Exception(f"图片处理失败: {str(e)}")
88
+
89
+ def clean_json_response(raw_content):
90
+ """清理模型返回的响应,移除代码块标记,确保JSON可解析"""
91
+ # 移除开头的 ```json/```JSON 标记
92
+ if raw_content.startswith('```json') or raw_content.startswith('```JSON'):
93
+ raw_content = raw_content[7:]
94
+ # 移除结尾的 ``` 标记
95
+ if raw_content.endswith('```'):
96
+ raw_content = raw_content[:-3]
97
+ # 去除首尾多余空格/换行
98
+ return raw_content.strip()
99
+
100
+ def format_analysis_result(result_json):
101
+ """将JSON结果格式化为结构化的Markdown文本,用于页面展示"""
102
+ if not isinstance(result_json, dict):
103
+ return "⚠️ 分析结果格式异常,无法结构化展示"
104
+
105
+ # 初始化Markdown内容
106
+ md_content = "# 智能体截图分析报告\n\n"
107
+
108
+ # 1. 页面评分模块
109
+ scoring = result_json.get("页面评分", {})
110
+ if scoring:
111
+ md_content += "## 一、页面评分(1-10分)\n"
112
+ md_content += "| 评价维度 | 得分 | 评价说明 |\n"
113
+ md_content += "|----------------|------|---------------------------|\n"
114
+
115
+ # 处理每个评分项(匹配 "维度" 和 "维度_comment")
116
+ score_items = ["overall", "design", "usability", "functionality", "responsiveness"]
117
+ item_names = {
118
+ "overall": "整体评价",
119
+ "design": "设计美感",
120
+ "usability": "易用性",
121
+ "functionality": "功能完整性",
122
+ "responsiveness": "响应式设计"
123
+ }
124
+
125
+ for item in score_items:
126
+ score = scoring.get(item, "N/A")
127
+ comment = scoring.get(f"{item}_comment", "无")
128
+ md_content += f"| {item_names[item]} | {score} | {comment} |\n"
129
+ md_content += "\n"
130
+
131
+ # 2. 智能体能力拆解模块
132
+ capabilities = result_json.get("智能体能力拆解", {})
133
+ if capabilities:
134
+ md_content += "## 二、智能体能力拆解\n"
135
+
136
+ # 核心功能
137
+ core_funcs = capabilities.get("core_functions", [])
138
+ if core_funcs:
139
+ md_content += "### 1. 核心功能\n"
140
+ for i, func in enumerate(core_funcs, 1):
141
+ md_content += f"- **{i}.** {func}\n"
142
+ md_content += "\n"
143
+
144
+ # 优势
145
+ strengths = capabilities.get("strengths", [])
146
+ if strengths:
147
+ md_content += "### 2. 优势\n"
148
+ for i, strength in enumerate(strengths, 1):
149
+ md_content += f"- **{i}.** {strength}\n"
150
+ md_content += "\n"
151
+
152
+ # 劣势
153
+ weaknesses = capabilities.get("weaknesses", [])
154
+ if weaknesses:
155
+ md_content += "### 3. 劣势\n"
156
+ for i, weakness in enumerate(weaknesses, 1):
157
+ md_content += f"- **{i}.** {weakness}\n"
158
+ md_content += "\n"
159
+
160
+ # 潜在用途
161
+ potential_uses = capabilities.get("potential_uses", [])
162
+ if potential_uses:
163
+ md_content += "### 4. 潜在用途\n"
164
+ for i, use in enumerate(potential_uses, 1):
165
+ md_content += f"- **{i}.** {use}\n"
166
+ md_content += "\n"
167
+
168
+ # 改进方向
169
+ improvement_areas = capabilities.get("improvement_areas", [])
170
+ if improvement_areas:
171
+ md_content += "### 5. 改进方向\n"
172
+ for i, area in enumerate(improvement_areas, 1):
173
+ md_content += f"- **{i}.** {area}\n"
174
+ md_content += "\n"
175
+
176
+ # 详细分析
177
+ detailed_analysis = capabilities.get("detailed_analysis", "无")
178
+ if detailed_analysis != "无":
179
+ md_content += "### 6. 详细分析\n"
180
+ md_content += f">{detailed_analysis}\n\n"
181
+
182
+ # 若结果不完整,补充提示
183
+ if not scoring and not capabilities:
184
+ md_content += "⚠️ 未获取到有效分析结果,请检查图片内容或重试"
185
+
186
+ return md_content
187
+
188
+ def analyze_image(input_image):
189
+ """AI分析核心函数:返回【结构化Markdown结果】+【原始JSON】+【状态】"""
190
+ # 初始化返回值(结构化结果、原始JSON、状态)
191
+ structured_result = ""
192
+ raw_json = ""
193
+ status = "就绪"
194
+
195
+ # 依赖检查
196
+ if not PIL_AVAILABLE:
197
+ status = "❌ 缺少依赖"
198
+ structured_result = "未安装pillow库,请关闭应用并执行 `pip install pillow` 安装后重试"
199
+ yield structured_result, raw_json, status
200
+ return
201
+
202
+ # 图片检查
203
+ if not input_image:
204
+ status = "请先上传图片"
205
+ structured_result = "请点击左侧「上传截图」区域,选择JPG/PNG格式的智能体网页截图"
206
+ yield structured_result, raw_json, status
207
+ return
208
+
209
+ try:
210
+ # 1. 图片准备阶段
211
+ status = "正在准备图片..."
212
+ structured_result = "🔄 正在读取并准备图片文件..."
213
+ yield structured_result, raw_json, status
214
+ time.sleep(0.1)
215
+
216
+ # 2. 图片压缩阶段
217
+ status = "正在压缩图片..."
218
+ structured_result = "🔄 正在压缩图片(确保分析效率,不影响质量)..."
219
+ yield structured_result, raw_json, status
220
+ image_base64 = compress_image(input_image)
221
+ time.sleep(0.1)
222
+
223
+ # 3. 请求发送阶段
224
+ status = "正在发送分析请求..."
225
+ structured_result = "🔄 正在向AI模型发送分析请求(约3-10秒)..."
226
+ yield structured_result, raw_json, status
227
+
228
+ # 构建AI请求(明确输出格式要求,减少清理工作)
229
+ messages = [
230
+ {
231
+ "role": "user",
232
+ "content": [
233
+ {
234
+ "type": "text",
235
+ "text": "请分析这张智能体网页的截图,并严格按照以下格式返回JSON:\n\
236
+ {\n\
237
+ \"页面评分\": {\n\
238
+ \"overall\": 分数(1-10),\n\
239
+ \"overall_comment\": \"简短评价\",\n\
240
+ \"design\": 分数(1-10),\n\
241
+ \"design_comment\": \"简短评价\",\n\
242
+ \"usability\": 分数(1-10),\n\
243
+ \"usability_comment\": \"简短评价\",\n\
244
+ \"functionality\": 分数(1-10),\n\
245
+ \"functionality_comment\": \"简短评价\",\n\
246
+ \"responsiveness\": 分数(1-10),\n\
247
+ \"responsiveness_comment\": \"简短评价\"\n\
248
+ },\n\
249
+ \"智能体能力拆解\": {\n\
250
+ \"core_functions\": [\"功能1\", \"功能2\", ...],\n\
251
+ \"strengths\": [\"优势1\", \"优势2\", ...],\n\
252
+ \"weaknesses\": [\"劣势1\", \"劣势2\", ...],\n\
253
+ \"potential_uses\": [\"用途1\", \"用途2\", ...],\n\
254
+ \"improvement_areas\": [\"改进1\", \"改进2\", ...],\n\
255
+ \"detailed_analysis\": \"详细综合分析文本\"\n\
256
+ }\n\
257
+ }\n\
258
+ 【注意】:仅返回纯JSON,不要包含代码块标记(如```json)、解释文本等额外内容!"
259
+ },
260
+ {
261
+ "type": "image_url",
262
+ "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}
263
+ }
264
+ ]
265
+ }
266
+ ]
267
+
268
+ # 4. AI分析阶段
269
+ status = "AI正在分析(约3-10秒)..."
270
+ structured_result = f"🔄 AI正在深度分析图片内容...\n\n当前进度:\n- 图片已上传:✅\n- 模型已接收:✅\n- 分析中:⌛"
271
+ yield structured_result, raw_json, status
272
+
273
+ # 调用ERNIE-VL模型
274
+ response = client.chat.completions.create(
275
+ model="ernie-4.5-turbo-vl", # 简化模型名,避免版本兼容性问题
276
+ messages=messages,
277
+ temperature=0.2,
278
+ top_p=0.8,
279
+ extra_body={"penalty_score": 1},
280
+ timeout=30
281
+ )
282
+
283
+ # 5. 结果整理阶段
284
+ status = "正在整理分析结果..."
285
+ structured_result = "🔄 AI分析完成,正在整理结构化报告..."
286
+ yield structured_result, raw_json, status
287
+ time.sleep(0.1)
288
+
289
+ # 处理AI响应
290
+ raw_content = response.choices[0].message.content
291
+ cleaned_content = clean_json_response(raw_content)
292
+ raw_json = json.dumps(json.loads(cleaned_content), ensure_ascii=False, indent=2) # 格式化原始JSON
293
+
294
+ # 生成结构化展示结果
295
+ result_json = json.loads(cleaned_content)
296
+ structured_result = format_analysis_result(result_json)
297
+ status = "✅ 分析完成!"
298
+ yield structured_result, raw_json, status
299
+
300
+ # 异常处理(各阶段错误提示)
301
+ except APITimeoutError:
302
+ status = "❌ 分析超时"
303
+ structured_result = "⚠️ API调用超时(超过30秒)\n\n可能原因:\n1. 网络不稳定\n2. 模型负载较高\n建议:检查网络后重试"
304
+ yield structured_result, raw_json, status
305
+ except APIError as e:
306
+ status = "❌ API错误"
307
+ structured_result = f"⚠️ 百度千帆API错误\n\n错误详情:\n{str(e)}\n\n建议:检查API密钥是否正确,或前往百度智能云控制台确认服务状态"
308
+ yield structured_result, raw_json, status
309
+ except json.JSONDecodeError as e:
310
+ status = "❌ 格式错误"
311
+ structured_result = f"⚠️ AI返回结果非标准JSON\n\n原始内容:\n{cleaned_content[:500]}...\n\n错误详情:{str(e)}"
312
+ yield structured_result, cleaned_content, status # 返回原始错误内容
313
+ except Exception as e:
314
+ status = "❌ 分析失败"
315
+ structured_result = f"⚠️ 分析过程出错\n\n错误详情:\n{str(e)}\n\n建议:检查图片格式(仅支持JPG/PNG)或重试"
316
+ yield structured_result, raw_json, status
317
+
318
+ # ---------------------- 界面设计 ----------------------
319
+ with gr.Blocks(title="智能体截图分析工具", theme=gr.themes.Soft()) as demo:
320
+ # 存储原始JSON结果(用于下载)
321
+ raw_json_store = gr.State("")
322
+
323
+ # 预加载Base64图片(启动时读取一次)
324
+ logo_base64 = read_base64_from_file("logo.txt") # 从logo.txt读取Logo的Base64
325
+ demo_base64 = read_base64_from_file("demo.txt") # 从demo.txt读取示例图的Base64
326
+ logo_image = base64_to_pil_image(logo_base64) # 转换为PIL Image
327
+ demo_image = base64_to_pil_image(demo_base64) # 转换为PIL Image
328
+
329
+ # 顶部Logo/标题区(改用Base64图片)
330
+ with gr.Row(elem_id="logo_row", visible=logo_image is not None):
331
+ gr.Image(
332
+ value=logo_image, # 直接使用Base64转换后的PIL Image
333
+ height=50, width=165,
334
+ interactive=False,
335
+ show_label=False,
336
+ show_download_button=False
337
+ )
338
+ if logo_image is None:
339
+ gr.Markdown("# 智能体截图分析工具", elem_id="fallback_title")
340
+
341
+ # 功能说明(简洁明了)
342
+ gr.Markdown("### 功能说明", visible=logo_image is not None)
343
+ gr.Markdown("""
344
+ 上传智能体网页截图(JPG/PNG),系统将自动完成:
345
+ 1. 页面质量评分(整体评价、设计美感等5个维度)
346
+ 2. 智能体能力拆解(核心功能、优势、潜在用途等)
347
+ 3. 生成结构化报告,支持下载原始JSON结果
348
+ """, elem_id="description")
349
+
350
+ # 核心交互区域(左侧上传 + 右侧结果)
351
+ with gr.Row(elem_id="main_content", variant="panel"):
352
+ # 左侧:图片上传区(简洁布局)
353
+ with gr.Column(scale=1, elem_id="upload_col"):
354
+ input_image = gr.Image(
355
+ type="filepath",
356
+ label="上传截图",
357
+ height=300,
358
+ show_label=True,
359
+ elem_id="image_upload"
360
+ )
361
+
362
+ # 示例图片区域(改用Base64加载的示例图)
363
+ example_images = [demo_image] if demo_image is not None else []
364
+ if example_images:
365
+ gr.Examples(
366
+ examples=example_images,
367
+ inputs=input_image,
368
+ label="示例截图(点击可直接使用)",
369
+ elem_id="examples_box"
370
+ )
371
+
372
+ analyze_btn = gr.Button(
373
+ "开始分析",
374
+ variant="primary",
375
+ size="lg",
376
+ elem_id="analyze_btn",
377
+ interactive=PIL_AVAILABLE # 无pillow时禁用按钮
378
+ )
379
+ # 依赖提示(无pillow时显示)
380
+ if not PIL_AVAILABLE:
381
+ gr.Markdown("⚠️ 未安装pillow库,无法处理图片\n请执行 `pip install pillow` 后重启应用", elem_id="dependency_warning")
382
+
383
+ # 右侧:结果展示区
384
+ with gr.Column(scale=2, elem_id="result_col", variant="panel"):
385
+ # 状态显示(顶部固定)
386
+ status_display = gr.Textbox(
387
+ label="当前状态",
388
+ interactive=False,
389
+ value="就绪:请上传截图并点击「开始分析」",
390
+ elem_id="status_box",
391
+ max_lines=2
392
+ )
393
+
394
+ # 结果标签页(结构化报告 + 原始JSON)
395
+ with gr.Tabs(elem_id="result_tabs"):
396
+ # 标签1:结构化报告(默认显示)
397
+ with gr.Tab("结构化分析报告", elem_id="structured_tab"):
398
+ structured_result = gr.Markdown(
399
+ value="""
400
+ # 等待分析...
401
+
402
+ ## 操作指引
403
+ 1. 点击左侧「上传截图」区域,选择智能体网页的JPG/PNG图片
404
+ 2. 点击「开始分析」按钮,等待3-10秒
405
+ 3. 分析完成后,此处将显示结构化报告
406
+
407
+ ## 示例图说明
408
+ 若左侧有示例图片,可直接点击示例快速测试
409
+ """,
410
+ elem_id="structured_result"
411
+ )
412
+
413
+ # 标签2:原始JSON(供开发/调试使用)
414
+ with gr.Tab("原始JSON结果", elem_id="raw_json_tab"):
415
+ raw_json_result = gr.Textbox(
416
+ label=None,
417
+ lines=20,
418
+ placeholder="分析完成后,此处将显示格式化的原始JSON结果...",
419
+ elem_id="raw_json_box",
420
+ container=True
421
+ )
422
+
423
+ # 操作按钮区(下载 + 清除)
424
+ with gr.Row(elem_id="action_buttons"):
425
+ download_btn = gr.DownloadButton(
426
+ "下载JSON结果",
427
+ label=None,
428
+ elem_id="download_btn",
429
+ visible=False # 初始隐藏,分析成功后显示
430
+ )
431
+ clear_btn = gr.Button(
432
+ "清除结果",
433
+ variant="secondary",
434
+ size="sm",
435
+ elem_id="clear_btn"
436
+ )
437
+
438
+ # ---------------------- 交互逻辑 ----------------------
439
+ # 1. 分析按钮:触发分析流程
440
+ analyze_btn.click(
441
+ fn=analyze_image,
442
+ inputs=input_image,
443
+ outputs=[structured_result, raw_json_result, status_display]
444
+ ).then(
445
+ # 分析完成后:更新原始JSON存储 + 控制下载按钮显示
446
+ fn=lambda raw_json, status: (
447
+ raw_json, # 更新原始JSON存储
448
+ gr.update(visible=status.startswith("✅")) # 成功时显示下载按钮
449
+ ),
450
+ inputs=[raw_json_result, status_display],
451
+ outputs=[raw_json_store, download_btn]
452
+ )
453
+
454
+ # 2. 下载按钮:下载原始JSON结果
455
+ download_btn.click(
456
+ fn=lambda result: (result, "analysis_result.json"),
457
+ inputs=raw_json_store,
458
+ outputs=download_btn,
459
+ show_progress=False
460
+ )
461
+
462
+ # 3. 清除按钮:重置所有结果和状态
463
+ clear_btn.click(
464
+ fn=lambda: (
465
+ """
466
+ # 等待分析...
467
+
468
+ ## 操作指引
469
+ 1. 点击左侧「上传截图」区域,选择智能体网页的JPG/PNG��片
470
+ 2. 点击「开始分析」按钮,等待3-10秒
471
+ 3. 分析完成后,此处将显示结构化报告
472
+
473
+ ## 示例图说明
474
+ 若左侧有示例图片,可直接点击示例快速测试
475
+ """, # 重置结构化报告
476
+ "", # 清空原始JSON
477
+ "就绪:请上传截图并点击「开始分析」", # 重置状态
478
+ gr.update(visible=False), # 隐藏下载按钮
479
+ "" # 清空原始JSON存储
480
+ ),
481
+ outputs=[structured_result, raw_json_result, status_display, download_btn, raw_json_store]
482
+ )
483
+
484
+ # ---------------------- 样式优化 ----------------------
485
+ demo.load(
486
+ None,
487
+ None,
488
+ None,
489
+ js="""() => {
490
+ // 结果区整体样式
491
+ const resultCol = document.getElementById('result_col');
492
+ if (resultCol) {
493
+ resultCol.style.padding = '20px';
494
+ resultCol.style.borderRadius = '8px';
495
+ }
496
+
497
+ // 状态框样式和动态颜色
498
+ const statusBox = document.getElementById('status_box');
499
+ if (statusBox) {
500
+ statusBox.style.marginBottom = '15px';
501
+ statusBox.style.padding = '8px';
502
+
503
+ // 定时检查状态文本并更新颜色
504
+ setInterval(() => {
505
+ const statusText = statusBox.value || '';
506
+ if (typeof statusText === 'string') {
507
+ if (statusText.startsWith('✅')) statusBox.style.color = '#27ae60';
508
+ else if (statusText.startsWith('❌')) statusBox.style.color = '#e74c3c';
509
+ else if (statusText.includes('分析中') || statusText.includes('🔄')) statusBox.style.color = '#3498db';
510
+ else statusBox.style.color = '#34495e';
511
+ }
512
+ }, 100);
513
+ }
514
+
515
+ // 按钮区样式
516
+ const actionButtons = document.getElementById('action_buttons');
517
+ if (actionButtons) {
518
+ actionButtons.style.marginTop = '15px';
519
+ actionButtons.style.gap = '10px';
520
+ }
521
+
522
+ // 结构化报告样式优化
523
+ const structuredResult = document.getElementById('structured_result');
524
+ if (structuredResult) {
525
+ structuredResult.style.padding = '10px 0';
526
+ }
527
+ }"""
528
+ )
529
+
530
+ # ---------------------- 应用启动 ----------------------
531
+ if __name__ == "__main__":
532
+ try:
533
+ # 依赖检测
534
+ required_imports = {
535
+ "gradio": "gradio",
536
+ "openai": "openai",
537
+ "PIL": "pillow", # pillow实际导入的是PIL
538
+ "requests": "requests"
539
+ }
540
+
541
+ for import_name, package_name in required_imports.items():
542
+ try:
543
+ __import__(import_name)
544
+ except ImportError:
545
+ raise ImportError(package_name)
546
+
547
+ # 启动应用(适配旧版本Gradio)
548
+ demo.launch(
549
+ server_name="0.0.0.0",
550
+ server_port=1919,
551
+ share=False,
552
+ max_threads=4,
553
+ quiet=True
554
+ )
555
+ except ImportError as e:
556
+ logger.critical(f"缺少依赖包:请执行 `pip install {e.args[0]}` 安装")
557
+ except Exception as e:
558
+ logger.critical(f"应用启动失败:{str(e)}")
demo.txt ADDED
The diff for this file is too large to render. See raw diff
 
logo.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ 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