package com.vincent.rsf.server.ai.tool; import com.vincent.rsf.framework.exception.CoolException; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; public final class BuiltinToolGovernanceSupport { private BuiltinToolGovernanceSupport() { } /** * 把工具入参里的 limit 统一收敛到安全范围内。 * 所有内置只读工具都通过该方法限制返回规模,避免模型一次查询过多数据。 */ public static int normalizeLimit(Integer limit, int defaultValue, int maxValue) { if (limit == null) { return defaultValue; } if (limit < 1 || limit > maxValue) { throw new CoolException("limit 必须在 1 到 " + maxValue + " 之间"); } return limit; } /** * 要求多个过滤条件里至少有一个有效值。 * 这是防止 AI 工具被模型当成“全表扫描接口”使用的第一道保护。 */ public static void requireAnyFilter(String message, String... values) { if (values == null || values.length == 0) { throw new CoolException(message); } for (String value : values) { if (StringUtils.hasText(value)) { return; } } throw new CoolException(message); } /** * 清洗单个文本型查询参数,并限制最大长度。 * 这里只做轻量治理,不做模糊兜底或自动纠错,非法输入直接拒绝。 */ public static String sanitizeQueryText(String value, String fieldLabel, int maxLength) { if (!StringUtils.hasText(value)) { return null; } String normalized = value.trim(); if (normalized.length() > maxLength) { throw new CoolException(fieldLabel + "长度不能超过 " + maxLength); } return normalized; } /** * 清洗字符串数组型参数,常用于站点类型、状态列表等批量过滤条件。 * 返回结果会自动剔除空值,但如果最终为空仍然视为非法请求。 */ public static List sanitizeStringList(List values, String fieldLabel, int maxSize, int maxItemLength) { if (values == null || values.isEmpty()) { throw new CoolException(fieldLabel + "不能为空"); } if (values.size() > maxSize) { throw new CoolException(fieldLabel + "数量不能超过 " + maxSize); } List result = new ArrayList<>(); for (String value : values) { String normalized = sanitizeQueryText(value, fieldLabel + "项", maxItemLength); if (!StringUtils.hasText(normalized)) { continue; } result.add(normalized); } if (result.isEmpty()) { throw new CoolException(fieldLabel + "不能为空"); } return result; } }