SystemPromptService.java
3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.xly.service;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* 构建 agent 的**唯一** system prompt:模板 {@code prompts/system.txt} + 业务域地图(DB,缓存一次)
* + 技能索引({@link SkillService},目录模式下热更)。
*
* <p>所有轮次共用同一份提示 + 同一组固定工具(useSkill/findForms/readFormData/lookupRecord/
* collectForm/proposeWrite/askUser):稳定前缀利于 KV-cache;流程知识在技能文本里,
* 由模型按需 useSkill 载入,不再压进 prompt。基准脚本(bench/bench_ext.py --arch new)
* 读取同一模板与技能文件,避免复刻漂移。
*/
@Service
public class SystemPromptService {
private final JdbcTemplate jdbc;
private final SkillService skills;
private volatile String template;
private volatile String domainMap;
public SystemPromptService(JdbcTemplate jdbc, SkillService skills) {
this.jdbc = jdbc;
this.skills = skills;
}
public String prompt() {
String t = template;
if (t == null) {
t = loadTemplate();
template = t;
}
String d = domainMap;
if (d == null) {
d = renderDomainMap();
domainMap = d;
}
return t.replace("{DOMAIN_MAP}", d).replace("{SKILL_INDEX}", skills.indexLines());
}
private static String loadTemplate() {
try {
return new String(new ClassPathResource("prompts/system.txt")
.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalStateException("system prompt 模板缺失(prompts/system.txt)", e);
}
}
private String renderDomainMap() {
List<Map<String, Object>> rows;
try {
rows = jdbc.queryForList(
"SELECT sDomain, sAiScene, iForms, sDownstreamDomains, sUpstreamDomains " +
"FROM viw_kg_domain ORDER BY iForms DESC");
} catch (Exception e) {
org.slf4j.LoggerFactory.getLogger(SystemPromptService.class)
.warn("加载 L1 业务域地图失败(viw_kg_domain 不可用),system prompt 将不含域图:{}", e.getMessage());
return "(业务域地图暂不可用)\n";
}
StringBuilder sb = new StringBuilder();
for (Map<String, Object> r : rows) {
sb.append("- ").append(r.get("sDomain"))
.append("(").append(r.get("sAiScene")).append(",")
.append(r.get("iForms")).append(" 张单据)");
String up = str(r.get("sUpstreamDomains"));
String down = str(r.get("sDownstreamDomains"));
if (notEmpty(up)) sb.append(" 上游←[").append(up).append("]");
if (notEmpty(down)) sb.append(" 下游→[").append(down).append("]");
sb.append('\n');
}
return sb.toString();
}
private static boolean notEmpty(String s) {
return s != null && !s.isBlank() && !"NULL".equalsIgnoreCase(s);
}
private static String str(Object o) {
return o == null ? "" : o.toString();
}
}