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},目录模式下热更)。 * *

所有轮次共用同一份提示 + 同一组固定工具(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> 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 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(); } }