一、前言
在上一篇文章 通义千问3-VL-Plus - 界面交互(本地图片)-CSDN博客 中,我搞定了对桌面软件的定位信息(交互的核心),但是发现输出的内容有残缺,如下:

我分析了一下原因可能如下:
从返回结果能看出 JSON 格式不完整(x 值数组截断、缺少 y 值、大括号未闭合),核心原因主要有 3 类:
- 模型输出截断 :GUI-Plus 默认输出长度有限,未配置
max_tokens参数,导致长 JSON 被截断; - 提示词约束不足:原提示词对「JSON 完整性」「参数必填性(如 CLICK 必须有 x/y 整数)」的约束不够明确,模型生成时遗漏字段;
- 提示词格式问题:原提示词中 JSON 模板的转义 / 排版混乱,模型理解规则时出错,生成不完整 JSON。
根据以上原因我们开始来修改代码内容
二、代码修改
1. 紧急修复:配置模型输出长度,避免截断
在构建MultiModalConversationParam时,新增maxTokens参数(设置足够大的值,如 2048),确保 JSON 完整输出:
java
// 非流式调用的参数构建处(streamCall同理)
MultiModalConversationParam param = MultiModalConversationParam.builder()
.apiKey(apiKey)
.model(modelName)
.messages(Arrays.asList(systemMsg, userMessage))
.maxTokens(2048) // 新增:设置最大输出长度,避免截断
.build();
2. 核心优化:强化提示词,强制 JSON 完整 + 参数必填
修改buildSystemPrompt()方法,重点补充「JSON 完整性约束」「参数必填规则」:
java
private String buildSystemPrompt() {
return """
## 1. 核心角色 (Core Role)
你是一个顶级的AI视觉操作代理。你的任务是分析电脑屏幕截图,理解用户的指令,然后将任务分解为单一、精确的GUI原子操作。
## 2. [CRITICAL] JSON Schema & 绝对规则(必须严格遵守)
你的输出必须是一个**完整、合法、可直接解析**的JSON对象,任何情况下都不能截断、遗漏字段、缺少闭合符号。
### 强制规则
- [R1] 纯JSON输出:回复只能是JSON对象,无任何前缀、后缀、注释、解释性文字。
- [R2] 字段必填性:
- 所有Action的parameters字段必须包含模板中**所有必填键**(如CLICK必须有x、y整数,缺一不可);
- x/y必须是**单个整数**(禁止数组/空值),代表屏幕坐标(像素);
- thought字段必须是一句话,描述思考过程,不能为空。
- [R3] Action值规范:只能是 CLICK/TYPE/SCROLL/KEY_PRESS/FINISH/FAIL(大写、无空格)。
- [R4] JSON格式校验:生成后必须自检------确保大括号闭合、逗号正确、字符串用双引号、数值无引号。
## 3. 工具集 (Available Actions)
### CLICK(必填x、y,可选description)
- 功能: 单击屏幕。
- 必须返回如下完整JSON结构(x/y为非空整数):
{
"thought": "一句话描述思考过程",
"action": "CLICK",
"parameters": {
"x": 1205,
"y": 800,
"description": "描述点击的对象(可选)"
}
}
### TYPE(必填text、needs_enter)
- 功能: 输入文本。
- 必须返回如下完整JSON结构:
{
"thought": "一句话描述思考过程",
"action": "TYPE",
"parameters": {
"text": "要输入的文本",
"needs_enter": true/false
}
}
### SCROLL(必填direction、amount)
- 功能: 滚动窗口。
- 必须返回如下完整JSON结构:
{
"thought": "一句话描述思考过程",
"action": "SCROLL",
"parameters": {
"direction": "up/down",
"amount": "small/medium/large"
}
}
### KEY_PRESS(必填key)
- 功能: 按下功能键。
- 必须返回如下完整JSON结构:
{
"thought": "一句话描述思考过程",
"action": "KEY_PRESS",
"parameters": {
"key": "enter/esc/alt+f4等"
}
}
### FINISH(必填message)
- 功能: 任务成功完成。
- 必须返回如下完整JSON结构:
{
"thought": "一句话描述思考过程",
"action": "FINISH",
"parameters": {
"message": "总结任务完成情况"
}
}
### FAIL(必填reason)
- 功能: 任务无法完成。
- 必须返回如下完整JSON结构:
{
"thought": "一句话描述思考过程",
"action": "FAIL",
"parameters": {
"reason": "清晰解释失败原因"
}
}
## 4. 思维与决策框架
1. 目标分析: 用户的最终目标是什么?
2. 屏幕观察: 仅基于截图中的视觉证据决策,看不见的元素不交互。
3. 行动决策: 选择最合适的Action,确保parameters字段完整。
4. 最终校验: 检查JSON是否完整闭合、字段是否必填、格式是否合法,再输出。
""";
}
3. 兜底处理:代码层修复不完整 JSON(应急方案)
如果模型仍偶尔返回不完整 JSON,可在结果解析处增加「JSON 修复逻辑」,示例:
java
// 非流式调用的结果解析处
String resText = content != null && !content.isEmpty()
? content.get(0).get("text").toString()
: "{}";
// 新增:修复不完整JSON(应急兜底)
resText = fixIncompleteJson(resText);
log.info("GUI-Plus非流式调用完成,修复后结果:{}", resText);
return resText;
// 新增JSON修复工具方法
private String fixIncompleteJson(String json) {
try {
// 1. 移除多余换行/空格
json = json.trim().replaceAll("\\n", "").replaceAll("\\r", "");
// 2. 检查大括号闭合
int openBrace = countOccurrences(json, "{");
int closeBrace = countOccurrences(json, "}");
if (openBrace > closeBrace) {
json += "}".repeat(openBrace - closeBrace);
}
// 3. 检查CLICK的x/y是否为数组,转为单个整数(针对你遇到的x是数组的问题)
if (json.contains("\"action\":\"CLICK\"") && json.contains("\"x\":[")) {
// 提取数组第一个值,替换数组为单个整数
Pattern pattern = Pattern.compile("\"x\":\\[(\\d+),?.*?]");
Matcher matcher = pattern.matcher(json);
if (matcher.find()) {
json = matcher.replaceFirst("\"x\":" + matcher.group(1));
}
// 补充缺失的y值(默认0,或提示模型重新生成)
if (!json.contains("\"y\":")) {
json = json.replace("\"parameters\": {", "\"parameters\": {\"y\":0,");
}
}
// 4. 校验JSON合法性(不合法则返回空JSON)
new com.alibaba.fastjson.JSONObject().parse(json);
return json;
} catch (Exception e) {
log.error("修复JSON失败,返回空JSON", e);
return "{}";
}
}
// 辅助方法:统计字符出现次数
private int countOccurrences(String str, String target) {
return (str.length() - str.replace(target, "").length()) / target.length();
}
三、额外优化建议
- 模型版本确认 :确保使用的
gui-plus是最新版本(可在阿里云百炼控制台确认),旧版本可能存在输出不完整的 bug(目前来说版本并没有变动,可能后续会发生变动,其实目前这个模型对定位还是有所欠缺的); - 测试提示词:先在百炼控制台「在线调试」功能中测试提示词,确认模型能生成完整 JSON 后,再接入代码;
- 参数校验强化:在代码中对返回的 JSON 做结构化校验(如用 FastJSON 解析为实体类),不合法则重试调用:
java
// 定义GUI操作实体类
@Data
public class GuiOperation {
private String thought;
private String action;
private Map<String, Object> parameters;
}
// 解析时校验
try {
GuiOperation operation = new com.alibaba.fastjson.JSONObject().parseObject(resText, GuiOperation.class);
// 校验CLICK必须有x/y
if ("CLICK".equals(operation.getAction())) {
if (operation.getParameters().get("x") == null || operation.getParameters().get("y") == null) {
throw new IllegalArgumentException("CLICK操作缺少x/y参数");
}
}
} catch (Exception e) {
log.error("JSON解析/校验失败,重试调用", e);
// 可选:重试调用模型(避免单次截断)
return conv.call(param).getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
}
四、问题复现验证

问题解决,然而,我发现我的豆包的位置其实并不是模型返回的结果,我的豆包app的位置为:

所以我怀疑是不是因为需要其他参数的原因(比如缩放百分比,屏幕分辨率等)
由于篇幅的关系,我在下一篇文章改进看看。
如果觉得这份修改实用、总结清晰,别忘了动动小手点个赞👍,再关注一下呀~ 后续还会分享更多 AI 接口封装、代码优化的干货技巧,一起解锁更多好用的功能,少踩坑多提效!🥰 你的支持就是我更新的最大动力,咱们下次分享再见呀~🌟