feat: 初始提交

This commit is contained in:
2025-06-09 19:18:36 +08:00
commit 35bd13af3e
7 changed files with 486 additions and 0 deletions

40
.gitignore vendored Normal file
View File

@@ -0,0 +1,40 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
.idea

148
pom.xml Normal file
View File

@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>precariat</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>3.4.3</spring-boot.version>
<spring-cloud.version>2024.0.1</spring-cloud.version>
<spring-ai.version>1.0.0</spring-ai.version>
<solon-ai.version>3.3.1</solon-ai.version>
<eclipse-collections.version>11.1.0</eclipse-collections.version>
<hutool.version>5.8.27</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring boot 相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>dev.failsafe</groupId>
<artifactId>failsafe</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>${eclipse-collections.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections-api</artifactId>
<version>${eclipse-collections.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>2.13.2</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-ai</artifactId>
<version>${solon-ai.version}</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-ai-dialect-openai</artifactId>
<version>${solon-ai.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>${releases.id}</id>
<name>${releases.name}</name>
<url>${releases.url}</url>
</repository>
<snapshotRepository>
<id>${snapshots.id}</id>
<name>${snapshots.name}</name>
<url>${snapshots.url}</url>
</snapshotRepository>
</distributionManagement>
</project>

View File

@@ -0,0 +1,66 @@
package com.lanyuanxiaoyao;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lanyuanxiaoyao
* @version 20250609
*/
@Configuration
public class ChatClientFactory {
private static final String SILICONFLOW_URL = "https://api.siliconflow.cn";
private static final String SILICONFLOW_API_KEY = "sk-xrguybusoqndpqvgzgvllddzgjamksuecyqdaygdwnrnqfwo";
private static final String ZHIPU_URL = "https://open.bigmodel.cn/api/paas";
private static final String ZHIPU_API_KEY = "d1e97306540d12bb2f834be961fcacb1.SNBShlCxWYJCx0qZ";
@Bean
public ChatClient chatClient() {
return ChatClient.builder(
OpenAiChatModel.builder()
.openAiApi(
OpenAiApi.builder()
.baseUrl(SILICONFLOW_URL)
.apiKey(SILICONFLOW_API_KEY)
.build()
)
.defaultOptions(
OpenAiChatOptions.builder()
// .model("deepseek-ai/DeepSeek-R1")
.model("Qwen/Qwen3-8B")
// .model("Qwen/Qwen3-32B")
.build()
)
.build()
)
.build();
}
@Bean
public ChatClient visualChatClient() {
return ChatClient.builder(
OpenAiChatModel.builder()
.openAiApi(
OpenAiApi.builder()
.baseUrl(ZHIPU_URL)
.apiKey(ZHIPU_API_KEY)
.completionsPath("/v4/chat/completions")
// .baseUrl(SILICONFLOW_URL)
// .apiKey(SILICONFLOW_API_KEY)
.build()
)
.defaultOptions(
OpenAiChatOptions.builder()
.model("glm-4v-flash")
// .model("Qwen/Qwen2.5-VL-72B-Instruct")
.build()
)
.build()
)
.build();
}
}

View File

@@ -0,0 +1,15 @@
package com.lanyuanxiaoyao;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author lanyuanxiaoyao
* @version 20250609
*/
@Data
@AllArgsConstructor
public class FunctionInformation {
private String name;
private String imagesPath;
}

View File

@@ -0,0 +1,17 @@
package com.lanyuanxiaoyao;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author lanyuanxiaoyao
* @version 20250609
*/
@Data
@AllArgsConstructor
public class OutputInformation {
private String group;
private String name;
private String subName;
private String description;
}

View File

@@ -0,0 +1,188 @@
package com.lanyuanxiaoyao;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.content.Media;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.MimeTypeUtils;
/**
* @author lanyuanxiaoyao
* @version 20250609
*/
@Slf4j
@SpringBootApplication
public class PrecariatApplication implements ApplicationRunner {
private final List<FunctionInformation> informationList = List.of(
// new FunctionInformation("数据中心-数据集管理", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/数据中心/数据集管理"),
// new FunctionInformation("数据中心-数据网盘", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/数据中心/数据网盘"),
// new FunctionInformation("数据中心-数据审批", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/数据中心/数据审批")
new FunctionInformation("标注中心-标签模板", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/标注中心/标签模板"),
new FunctionInformation("标注中心-我的任务", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/标注中心/我的任务"),
new FunctionInformation("标注中心-标注项目", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/标注中心/标注项目"),
new FunctionInformation("标注中心-标注场景", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/标注中心/标注场景"),
new FunctionInformation("标注中心-标注总览", "/Users/lanyuanxiaoyao/Pictures/星海AI中台/标注中心/标注总览")
);
@Resource
private ChatClient chatClient;
@Resource
private ChatClient visualChatClient;
public static void main(String[] args) {
SpringApplication.run(PrecariatApplication.class, args);
}
private String describeScreenshoot(String imagePath) {
return describeScreenshoot(imagePath, null);
}
private String describeScreenshoot(String imagePath, String extraPrompt) {
// language=TEXT
String prompt = """
任务:你是一个视觉大模型,需要精确分析并描述提供的网页截图内容。
要求:严格遵循以下指令进行客观描述:
1.核心要求:
客观详尽: 只描述图像中实际可见的文字、布局、视觉元素及其关系。务必详尽、细致,避免任何遗漏。
零猜测/推断: 绝对禁止猜测功能、意图或页面未显示的信息。禁止任何推断性描述。
零额外内容: 仅输出对图像内容的描述,禁止任何解释、评论、总结、建议或与图像无关的信息。
2.描述重点 (按优先级和结构)
整体布局: 描述页面主要区域划分(如顶部栏、侧边栏、主内容区、底部栏)及其相对位置和大小比例(例如:左侧窄导航栏,右侧宽主内容区)。
文字内容:
清晰、完整地转录所有可见文字(标题、标签、按钮文字、提示文本、列表内容文字等)。
保持文字出现的原始位置和视觉分组关系。
层级关系 (关键)
菜单/导航栏: 精确描述菜单项及其层级。明确标注:
哪些项是顶级菜单?
哪些项是子菜单?隶属于哪个父项?
菜单的当前展开/折叠状态(根据图像显示)。
选中的菜单项(如有高亮显示)。
页面结构: 描述区域间的包含关系(如:主内容区包含一个标题和一个数据列表)。
列表/表格 (关键)
清晰列出所有表头/列标题的名称。
描述每列内容的形式(纯文本、数字、日期、带图标的标签、按钮等)。
避免列举具体数据行内容。
明确指出行末尾或行内是否存在可见的操作元素(如图标按钮、文字链接按钮),并描述其视觉特征(如:每行末尾有“编辑”图标按钮和“删除”文字链接)。
交互元素: 描述所有可见按钮、输入框、下拉选择框、单选框、复选框、选项卡等的文字标签和视觉状态(如:激活/未激活、选中/未选中)。
视觉元素: 描述图标(注明其旁边的文字或位置)、分割线、背景色块、图片占位符等及其位置。
3.禁止事项 (强化)
禁止遗漏: 确保扫描并描述了页面的所有可见区域和元素。
禁止功能猜测: 仅描述元素是什么(如:“删除”图标按钮),禁止描述其可能的功能(如:“用于删除数据的按钮”)。
禁止推断内容: 只描述图像中实际渲染出的文字和元素。例如,如果下拉菜单未展开,禁止描述其隐藏的选项。
禁止联想: 不基于页面内容联想其所属系统或业务场景。
禁止缩写: 转录文字时使用完整原文,除非原文本身就是缩写。
禁止输出无关信息: 严格只输出对图像内容的客观描述。
输出: 你的输出应该是纯粹的、结构化的、详尽的图像内容描述文本,完全基于视觉输入。
""";
if (StrUtil.isNotBlank(extraPrompt)) {
prompt += """
以下是关于该图片内容的补充描述:
""" + extraPrompt;
}
return visualChatClient.prompt()
.messages(
UserMessage.builder()
.text(prompt)
.media(
Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new FileSystemResource(imagePath))
.build()
)
.build()
)
.call()
.content();
}
@Override
public void run(ApplicationArguments args) {
List<OutputInformation> lines = new ArrayList<>();
for (FunctionInformation information : informationList) {
List<String> descriptions = FileUtil.listFileNames(information.getImagesPath())
.parallelStream()
.filter(name -> StrUtil.equals(FileUtil.extName(name), "png"))
.map(name -> FileUtil.file(information.getImagesPath(), name))
.map(FileUtil::getAbsolutePath)
.map(imagePath -> {
String description = describeScreenshoot(imagePath);
log.info("{}\n{}", imagePath, description);
return description;
})
.toList();
String response = chatClient.prompt(StrUtil.format(
// language=TEXT
"""
你将被提供某个管理系统关于“{}”功能的所有相关网页内容描述。
这些描述包含页面元素、功能名称、以及同一页面在不同操作(如点击按钮、选择下拉框)后的状态变化。请仔细分析,合并描述中相同的页面内容和功能。
1.功能列表分析要求
核心目标:从提供的网页描述中,详尽识别并抽象出所有业务逻辑功能。
分析原则:
聚焦逻辑: 避免直接罗列按钮、下拉框等操作元素。必须将用户操作点击、选择或基础的UI功能列表分页表单项校验如字数、格式等抽象总结为它们所实现的核心业务逻辑功能例如“点击保存按钮”应抽象为“保存XX信息”功能
识别功能层级:区分主功能(父功能)和其下的子功能(操作、步骤、分支逻辑)。
合并相同功能:同一逻辑功能在不同页面或状态下被多次描述时,应合并为一项。
排除视觉/样式:忽略对颜色、字体、简单布局等纯粹视觉或样式元素的描述,它们不构成独立功能。
严格基于描述:所有输出的功能必须明确来源于提供的页面描述内容,禁止臆测或添加描述中不存在的信息。
详尽无遗漏:务必仔细梳理所有描述,确保识别出每一个描述中体现的逻辑功能点,避免遗漏。
输出内容:详细的功能列表及其描述。
2.输出格式要求
使用严格的表格形式输出功能列表。
表格包含三列:
1.功能名称: 主功能的名称,表述其核心业务目的。
2.子功能名称: 隶属于主功能的子操作、步骤或分支功能的名称。如果主功能没有子功能或本身是原子操作,此栏可为空。
3.功能描述: 对该功能(主功能或子功能)做什么进行清晰、详细的描述,保证在没有上下文的前提下也能够将功能、逻辑、业务等信息描述完整。
输出符合如下json结构的结果
[
{"name": "功能名称", "sub_name": "子功能名称", "description": "功能描述"}
]
3.输入数据
以下内容是严格基于视觉大模型读取的网页信息生成的详细描述。不同的页面(或同一页面的不同状态)使用“------”分隔:
{}""",
StrUtil.join("\n------\n", descriptions)
))
.call()
.content();
log.info(response);
JSONArray array = JSONUtil.parseArray(StrUtil.removeAny(StrUtil.trim(response), "```json", "```"));
for (Object o : array) {
JSONObject obj = (JSONObject) o;
lines.add(new OutputInformation(
information.getName(),
obj.getStr("name"),
obj.getStr("sub_name"),
obj.getStr("description")
));
}
}
try (ExcelWriter writer = ExcelUtil.getWriter(StrUtil.format("/Users/lanyuanxiaoyao/Project/Precariat/{}.xlsx", DateUtil.current()))) {
writer.addHeaderAlias("group", "一级功能");
writer.addHeaderAlias("name", "二级功能");
writer.addHeaderAlias("subName", "三级功能");
writer.addHeaderAlias("description", "功能描述");
writer.write(lines);
}
}
}

View File

@@ -0,0 +1,12 @@
spring:
main:
web-application-type: none
autoconfigure:
exclude: |
org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration,
org.springframework.ai.model.openai.autoconfigure.OpenAiModerationAutoConfiguration