Add File
This commit is contained in:
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com).
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.agentsflex.core.prompt.template;
|
||||||
|
|
||||||
|
import com.agentsflex.core.prompt.TextPrompt;
|
||||||
|
import com.agentsflex.core.util.MapUtil;
|
||||||
|
import com.alibaba.fastjson.JSONPath;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class TextPromptTemplate implements PromptTemplate<TextPrompt> {
|
||||||
|
|
||||||
|
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{\\{\\s*(.+?)\\s*}}");
|
||||||
|
private static final Map<String, TextPromptTemplate> CACHES = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final String originalTemplate;
|
||||||
|
private final List<TemplateToken> tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:初始化时解析模板内容
|
||||||
|
*
|
||||||
|
* @param template 模板字符串
|
||||||
|
*/
|
||||||
|
public TextPromptTemplate(String template) {
|
||||||
|
this.originalTemplate = template != null ? template : "";
|
||||||
|
this.tokens = parseTemplate(this.originalTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 TextPromptTemplate 实例
|
||||||
|
*/
|
||||||
|
public static TextPromptTemplate of(String template) {
|
||||||
|
String finalTemplate = template != null ? template : "";
|
||||||
|
return MapUtil.computeIfAbsent(CACHES, finalTemplate, k -> new TextPromptTemplate(finalTemplate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化模板
|
||||||
|
*/
|
||||||
|
public TextPrompt format(Map<String, Object> rootMap) {
|
||||||
|
return new TextPrompt(formatToString(rootMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化模板
|
||||||
|
*/
|
||||||
|
public String formatToString(Map<String, Object> rootMap) {
|
||||||
|
if (tokens.isEmpty()) return "";
|
||||||
|
|
||||||
|
if (rootMap == null) {
|
||||||
|
rootMap = Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(originalTemplate.length() + 256);
|
||||||
|
|
||||||
|
for (TemplateToken token : tokens) {
|
||||||
|
if (token.isStatic()) {
|
||||||
|
sb.append(token.content);
|
||||||
|
} else {
|
||||||
|
Object value = getValueByJsonPath(rootMap, token.expression);
|
||||||
|
String replacement = value != null ? value.toString() : token.defaultValue;
|
||||||
|
sb.append(replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析模板为 Token 列表
|
||||||
|
*/
|
||||||
|
private List<TemplateToken> parseTemplate(String template) {
|
||||||
|
List<TemplateToken> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (template == null || template.isEmpty()) return result;
|
||||||
|
|
||||||
|
Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
|
||||||
|
int lastEnd = 0;
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
int start = matcher.start();
|
||||||
|
int end = matcher.end();
|
||||||
|
|
||||||
|
// 添加前面的静态文本
|
||||||
|
if (start > lastEnd) {
|
||||||
|
result.add(TemplateToken.staticText(template.substring(lastEnd, start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析占位符
|
||||||
|
String content = matcher.group(1);
|
||||||
|
ParseResult parseResult = parseExpressionWithDefault(content);
|
||||||
|
result.add(TemplateToken.dynamic(parseResult.expression, parseResult.defaultValue));
|
||||||
|
|
||||||
|
lastEnd = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加结尾的静态文本
|
||||||
|
if (lastEnd < template.length()) {
|
||||||
|
result.add(TemplateToken.staticText(template.substring(lastEnd)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析带默认值的表达式,如 "user.name ?? '匿名'"
|
||||||
|
*/
|
||||||
|
private ParseResult parseExpressionWithDefault(String content) {
|
||||||
|
String[] parts = content.split("\\s*\\?\\?\\s*", 2);
|
||||||
|
if (parts.length == 2) {
|
||||||
|
String expr = parts[0].trim();
|
||||||
|
String defaultValue = unquote(parts[1].trim());
|
||||||
|
return new ParseResult(expr, defaultValue);
|
||||||
|
} else {
|
||||||
|
return new ParseResult(content.trim(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去掉字符串两边的引号(单引号或双引号)
|
||||||
|
*/
|
||||||
|
private String unquote(String str) {
|
||||||
|
if ((str.startsWith("'") && str.endsWith("'")) ||
|
||||||
|
(str.startsWith("\"") && str.endsWith("\""))) {
|
||||||
|
return str.substring(1, str.length() - 1);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 FastJSON JSONPath 获取嵌套值
|
||||||
|
*/
|
||||||
|
private Object getValueByJsonPath(Map<String, Object> root, String path) {
|
||||||
|
try {
|
||||||
|
return JSONPath.eval(root, "$." + path);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token 类型定义
|
||||||
|
*/
|
||||||
|
private static class TemplateToken {
|
||||||
|
boolean isStatic;
|
||||||
|
String content;
|
||||||
|
String expression;
|
||||||
|
String defaultValue;
|
||||||
|
|
||||||
|
private TemplateToken(boolean isStatic, String content, String expression, String defaultValue) {
|
||||||
|
this.isStatic = isStatic;
|
||||||
|
this.content = content;
|
||||||
|
this.expression = expression;
|
||||||
|
this.defaultValue = defaultValue == null ? "" : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TemplateToken staticText(String text) {
|
||||||
|
return new TemplateToken(true, text, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TemplateToken dynamic(String expr, String defaultValue) {
|
||||||
|
return new TemplateToken(false, null, expr, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isStatic() {
|
||||||
|
return isStatic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部类:用于保存解析后的表达式和默认值
|
||||||
|
*/
|
||||||
|
private static class ParseResult {
|
||||||
|
String expression;
|
||||||
|
String defaultValue;
|
||||||
|
|
||||||
|
ParseResult(String expression, String defaultValue) {
|
||||||
|
this.expression = expression;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user