001/*
002 *  Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.agentsflex.core.agent.react;
017
018import com.agentsflex.core.model.chat.tool.Parameter;
019import com.agentsflex.core.model.chat.tool.Tool;
020
021import java.util.List;
022
023/**
024 * ReAct Agent 工具函数辅助类
025 */
026public class Util {
027
028    /**
029     * 生成带缩进的空格字符串
030     */
031    public static String indent(int depth) {
032        StringBuilder sb = new StringBuilder();
033        for (int i = 0; i < depth; i++) {
034            sb.append("  "); // 2 spaces per level
035        }
036        return sb.toString();
037    }
038
039    /**
040     * 基于工具列表生成结构化、LLM 友好的工具描述文本
041     */
042    public static String buildToolsDescription(List<Tool> tools) {
043        StringBuilder sb = new StringBuilder();
044        for (Tool tool : tools) {
045            sb.append("### 工具名称: ").append(tool.getName()).append("\n");
046            sb.append("**描述**: ").append(tool.getDescription()).append("\n");
047            sb.append("**调用参数格式 (JSON 对象)**:\n");
048            sb.append("```json\n");
049
050            sb.append("{\n");
051            Parameter[] rootParams = tool.getParameters();
052            for (int i = 0; i < rootParams.length; i++) {
053                appendParameter(sb, rootParams[i], 1);
054                if (i < rootParams.length - 1) {
055                    sb.append(",");
056                }
057                sb.append("\n");
058            }
059            sb.append("}\n");
060            sb.append("```\n\n");
061        }
062        return sb.toString();
063    }
064
065    /**
066     * 递归追加参数描述(支持 object 和 array)
067     */
068    private static void appendParameter(StringBuilder sb, Parameter param, int depth) {
069        String currentIndent = indent(depth);
070        String typeLabel = getTypeLabel(param);
071
072        // 构建注释信息
073        StringBuilder comment = new StringBuilder();
074        if (param.isRequired()) {
075            comment.append(" (必填)");
076        } else {
077            comment.append(" (可选)");
078        }
079
080        if (param.getDescription() != null && !param.getDescription().trim().isEmpty()) {
081            comment.append(" - ").append(param.getDescription().trim());
082        }
083
084        if (param.getEnums() != null && param.getEnums().length > 0) {
085            comment.append(" [可选值: ").append(String.join(", ", param.getEnums())).append("]");
086        }
087
088        String paramName = param.getName() != null ? param.getName() : "item";
089
090        // 判断是否为数组类型
091        boolean isArray = isArrayType(param.getType());
092
093        if (isArray && param.getChildren() != null && !param.getChildren().isEmpty()) {
094            // 数组元素为对象:描述其结构
095            sb.append(currentIndent).append("\"").append(paramName).append("\": [");
096            sb.append(comment).append("\n");
097
098            String innerIndent = indent(depth + 1);
099            sb.append(innerIndent).append("{\n");
100
101            List<Parameter> elementFields = param.getChildren();
102            for (int i = 0; i < elementFields.size(); i++) {
103                appendParameter(sb, elementFields.get(i), depth + 2);
104                if (i < elementFields.size() - 1) {
105                    sb.append(",");
106                }
107                sb.append("\n");
108            }
109            sb.append(innerIndent).append("}\n");
110            sb.append(currentIndent).append("]");
111
112        } else if (isArray) {
113            // 简单类型数组
114            sb.append(currentIndent).append("\"").append(paramName).append("\": [ \"<")
115                .append(typeLabel).append(">")
116                .append(comment)
117                .append("\" ]");
118
119        } else if (param.getChildren() != null && !param.getChildren().isEmpty()) {
120            // 嵌套对象
121            sb.append(currentIndent).append("\"").append(paramName).append("\": {");
122            sb.append(comment).append("\n");
123
124            List<Parameter> children = param.getChildren();
125            for (int i = 0; i < children.size(); i++) {
126                appendParameter(sb, children.get(i), depth + 1);
127                if (i < children.size() - 1) {
128                    sb.append(",");
129                }
130                sb.append("\n");
131            }
132            sb.append(currentIndent).append("}");
133
134        } else {
135            // 叶子字段(简单类型)
136            sb.append(currentIndent).append("\"").append(paramName).append("\": \"<")
137                .append(typeLabel).append(">")
138                .append(comment)
139                .append("\"");
140        }
141    }
142
143    /**
144     * 判断类型是否为数组
145     */
146    private static boolean isArrayType(String type) {
147        if (type == null) return false;
148        String lower = type.toLowerCase();
149        return "array".equals(lower) || "list".equals(lower);
150    }
151
152    /**
153     * 获取标准化的类型标签
154     */
155    private static String getTypeLabel(Parameter param) {
156        String type = param.getType();
157        if (type == null) return "string";
158
159        // 若有子字段,则视为 object
160        if (param.getChildren() != null && !param.getChildren().isEmpty()) {
161            return "object";
162        }
163
164        String lower = type.toLowerCase();
165        if ("string".equals(lower) || "str".equals(lower)) {
166            return "string";
167        } else if ("integer".equals(lower) || "int".equals(lower)) {
168            return "integer";
169        } else if ("number".equals(lower) || "float".equals(lower) || "double".equals(lower)) {
170            return "number";
171        } else if ("boolean".equals(lower) || "bool".equals(lower)) {
172            return "boolean";
173        } else if ("array".equals(lower) || "list".equals(lower)) {
174            return "array";
175        } else {
176            return type; // 保留自定义类型名,如 date, uri 等
177        }
178    }
179}