基于知识库的知策智能体
一、核心设计理念
知策智能体采用经典四阶段Agent架构,实现「用户问题→智能决策→工具调用→答案生成」的完整链路,各组件职责单一、解耦设计,可独立扩展/替换:
- 思考器(Thinker):大脑核心,基于用户问题决策「是否调用工具」「调用哪个工具」
- 执行器(Executor):工具调度核心,根据思考结果执行对应工具、返回执行数据
- 总结器(Summarizer) :答案生成核心,将工具返回的原始数据加工为精准、规范、带溯源的最终答案
- 工具注册表(Registry):工具管理中心,统一注册/发现所有Agent工具,支持动态扩展
- 工具接口(Tool):标准化工具规范,所有自定义工具只需实现接口即可无缝接入
二、完整代码实现(全量可运行)
【基础层】工具标准化接口 + 通用实体类(核心依赖)
1. 工具顶层接口 AgentTool
java
/**
* Agent工具标准化接口
* 所有智能体工具(检索、总结、翻译等)必须实现此接口
*/
public interface AgentTool {
// 工具唯一名称(给大模型决策使用,不可重复)
String toolName();
// 工具详细描述(给大模型看,精准描述工具能力与适用场景)
String toolDesc();
// 工具执行方法(统一入参、统一出参,保证调用一致性)
ToolResult execute(ToolParam param);
}
2. 工具入参实体 ToolParam
java
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* 工具执行统一入参
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ToolParam {
/** 用户原始问题 */
private String query;
/** 会话ID(用于上下文/历史记录关联) */
private String sessionId;
/** 扩展参数(适配多工具差异化入参) */
private Map<String, Object> params;
}
3. 工具出参实体 ToolResult
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* 工具执行统一出参
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ToolResult {
/** 执行是否成功 */
private boolean success;
/** 工具返回核心内容 */
private String content;
/** 错误信息(失败时必填) */
private String errorMsg;
/** 扩展溯源信息(如文档来源、行号、得分等) */
private Map<String, Object> extra;
// ========== 快捷构建方法(简化工具开发) ==========
public static ToolResult success(String content) {
ToolResult result = new ToolResult();
result.setSuccess(true);
result.setContent(content);
return result;
}
public static ToolResult success(String content, Map<String, Object> extra) {
ToolResult result = new ToolResult();
result.setSuccess(true);
result.setContent(content);
result.setExtra(extra);
return result;
}
public static ToolResult fail(String errorMsg) {
ToolResult result = new ToolResult();
result.setSuccess(false);
result.setErrorMsg(errorMsg);
return result;
}
}
4. 思考器决策结果 ThinkResult
java
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.Map;
/**
* 思考器决策结果实体
* 承载大模型的决策指令:直接回答 / 调用指定工具
*/
@Data
public class ThinkResult {
/**
* 决策动作类型
* DIRECT_ANSWER - 直接回答,无需调用工具
* TOOL_CALL - 调用工具,需执行对应工具逻辑
*/
@JSONField(name = "action")
private String action;
/**
* 直接回答的内容(action=DIRECT_ANSWER时必填)
*/
@JSONField(name = "content")
private String content;
/**
* 目标工具名称(action=TOOL_CALL时必填,需与AgentTool.toolName()一致)
*/
@JSONField(name = "tool_name")
private String toolName;
/**
* 工具调用参数(action=TOOL_CALL时必填,核心为query)
*/
@JSONField(name = "tool_params")
private Map<String, Object> toolParams;
// ========== 快捷判断方法 ==========
public boolean isToolCall() {
return "TOOL_CALL".equals(this.action);
}
public boolean isDirectAnswer() {
return "DIRECT_ANSWER".equals(this.action);
}
}
5. 前端调用入参 AgentChatParam(完善校验)
java
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 智能体对话接口入参
*/
@Data
public class AgentChatParam {
/** 用户问题(必填) */
@NotBlank(message = "用户问题不能为空")
private String query;
/** 会话ID(必填,用于会话隔离与上下文关联) */
@NotBlank(message = "会话ID不能为空")
private String sessionId;
}
6. 通用结果封装 Result
java
import lombok.Data;
/**
* 全局统一接口返回结果
*/
@Data
public class Result<T> {
private int code;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> fail(String msg) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMsg(msg);
return result;
}
}
7. 结果清洗工具类 AgentResultCleanUtils
java
import org.springframework.stereotype.Component;
import java.util.regex.Pattern;
/**
* 智能体结果清洗工具
* 解决大模型返回内容格式混乱、含多余字符的问题
*/
@Component
public class AgentResultCleanUtils {
// 匹配首尾无关字符、markdown标记
private static final Pattern INVALID_CHAR_PATTERN = Pattern.compile("^[`\"\\s]+|[`\"\\s]+$|^```json|```$");
/**
* 清洗并标准化大模型返回内容
*/
public String cleanAndStandardize(String content) {
if (content == null || content.isBlank()) {
return "";
}
// 1. 去除首尾空格、引号、markdown的json标记
String cleanContent = INVALID_CHAR_PATTERN.matcher(content).replaceAll("");
// 2. 替换换行/制表符为空格,保证JSON格式完整
return cleanContent.replaceAll("\\n|\\t", " ").trim();
}
}
【工具层】知识库检索工具
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.stream.Collectors;
/**
* 核心工具:知识库检索工具
* 实现AgentTool接口,适配智能体调度规范
*/
@Component
@Slf4j
public class KnowledgeSearchTool implements AgentTool {
@Autowired
private HybridSearchService hybridSearch;
// ES向量索引名称(建议配置到yml,此处硬编码仅示例)
private static final String VECTOR_INDEX_NAME = "ai_vector_index";
@Override
public String toolName() {
return "知识库检索工具";
}
@Override
public String toolDesc() {
return "【核心工具】用于检索知识库中的文档/Excel/文本内容,可精准回答用户关于产品介绍、技术文档、业务规则、数据报表的所有问题;输入为用户原始问题,输出为高匹配度的知识库文本片段+完整溯源信息(文档名、行号、工作表名)";
}
@Override
public ToolResult execute(ToolParam param) {
try {
String query = param.getQuery();
if (query == null || query.isBlank()) {
return ToolResult.fail("检索失败:用户问题不能为空");
}
log.info("【知识库检索工具】执行检索,会话ID:{},检索词:{}", param.getSessionId(), query);
// 1. 调用混合检索服务,获取匹配的文档片段
var docFragments = hybridSearch.hybridSearch(VECTOR_INDEX_NAME, query);
if (docFragments == null || docFragments.isEmpty()) {
return ToolResult.success("暂无匹配的知识库内容");
}
// 2. 拼接检索结果(文本内容+溯源信息,提升总结准确性)
String context = docFragments.stream()
.map(frag -> {
// 拼接内容+溯源,格式:内容\n【来源:XXX 行X 表X】
String source = frag.getMetaData().getOrDefault("source_detail", "未知来源").toString();
return frag.getContent() + "\n【来源:" + source + "】";
})
.collect(Collectors.joining("\n\n"));
// 3. 封装结果(含溯源扩展信息,供总结器使用)
return ToolResult.success(context);
} catch (Exception e) {
log.error("【知识库检索工具】执行失败,会话ID:{}", param.getSessionId(), e);
return ToolResult.fail("知识库检索失败:" + e.getMessage());
}
}
}
【核心层】智能体核心组件(思考/执行/总结)
1. 工具注册表 AgentToolRegistry
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
/**
* Agent工具注册表(单例)
* 自动扫描Spring容器中所有AgentTool实现类,统一管理、按需获取
*/
@Component
@Slf4j
public class AgentToolRegistry implements ApplicationContextAware {
// 核心映射:工具名称 → 工具实例(全局唯一)
private final Map<String, AgentTool> toolName2InstanceMap = new HashMap<>(8);
// Spring上下文,用于扫描工具实现类
private ApplicationContext applicationContext;
/**
* 根据工具名称获取工具实例(核心对外方法)
*/
public AgentTool getToolByToolName(String toolName) {
if (toolName == null || toolName.isBlank()) {
log.warn("工具名称为空,无法获取工具实例");
return null;
}
return toolName2InstanceMap.get(toolName);
}
/**
* 获取所有工具的「名称-描述」映射(给思考器Prompt使用)
*/
public Map<String, String> getAllToolNameAndDesc() {
Map<String, String> toolName2DescMap = new HashMap<>(toolName2InstanceMap.size());
toolName2InstanceMap.forEach((toolName, toolInstance) ->
toolName2DescMap.put(toolName, toolInstance.toolDesc()));
return toolName2DescMap;
}
// ======================== Spring生命周期方法 ========================
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 项目启动时自动执行:扫描+注册所有AgentTool工具
*/
@PostConstruct
public void initToolRegistry() {
// 1. 扫描容器中所有AgentTool实现类
Map<String, AgentTool> springBeanToolMap = applicationContext.getBeansOfType(AgentTool.class);
if (springBeanToolMap.isEmpty()) {
log.warn("【Agent工具注册表】未扫描到任何AgentTool接口实现类,智能体功能将不可用");
return;
}
// 2. 注册工具,校验工具名称唯一性
springBeanToolMap.forEach((beanName, toolInstance) -> {
String businessToolName = toolInstance.toolName();
if (businessToolName == null || businessToolName.isBlank()) {
log.error("【Agent工具注册表】工具Bean[{}]的toolName()返回空,拒绝注册", beanName);
return;
}
if (toolName2InstanceMap.containsKey(businessToolName)) {
log.error("【Agent工具注册表】工具名称[{}]重复,已存在同名工具,拒绝注册Bean[{}]",
businessToolName, beanName);
return;
}
toolName2InstanceMap.put(businessToolName, toolInstance);
log.info("【Agent工具注册表】工具注册成功 → 工具名:{},SpringBean名:{}", businessToolName, beanName);
});
log.info("【Agent工具注册表】初始化完成 ✅,共注册{}个可用工具", toolName2InstanceMap.size());
}
}
2. 思考器 AgentThinker
java
import com.alibaba.fastjson2.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 智能体思考器(核心大脑)
* 职责:接收用户问题 → 分析意图 → 决策「直接回答/调用工具」→ 返回标准化决策结果
*/
@Component
@Slf4j
public class AgentThinker {
@Autowired
private ChatModel ollamaChatModel; // 统一使用Spring AI标准ChatModel,适配任意大模型
@Autowired
private AgentToolRegistry agentToolRegistry;
@Autowired
private AgentResultCleanUtils agentResultCleanUtils;
/**
* 核心思考方法
* @param query 用户问题
* @param sessionId 会话ID
* @return 标准化决策结果列表
*/
public List<ThinkResult> think(String query, String sessionId) {
try {
log.info("【智能体思考器】开始思考,会话ID:{},用户问题:{}", sessionId, query);
// 1. 拼接所有工具的名称+描述,传入Prompt
String toolDescs = agentToolRegistry.getAllToolNameAndDesc().entrySet().stream()
.map(entry -> "🔧" + entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining("\n"));
// 2. 优化版核心Prompt(关键升级:格式强制约束+决策逻辑引导+容错性提升)
String prompt = """
你是知策智能体的专业思考器,拥有精准的工具调用决策能力,严格遵守以下规则完成任务:
===================== 可用工具列表 =====================
%s
===================== 决策规则 =====================
1. 分析用户问题,仅两种决策结果:能通过常识直接回答的用DIRECT_ANSWER,需要知识库内容的必须用TOOL_CALL;
2. 技术问题、产品问题、业务数据问题,**必须调用【知识库检索工具】**,禁止直接回答;
3. 天气、日期、通用常识等无知识库依赖的问题,直接返回答案,无需调用工具;
===================== 输出格式(强制遵守,否则失效) =====================
✅ 格式要求1:仅返回JSON数组,前后无任何多余字符、无```json/```标记;
✅ 格式要求2:调用工具固定格式:[{"action":"TOOL_CALL","tool_name":"工具名称","tool_params":{"query":"用户原始问题","sessionId":"%s"}}]
✅ 格式要求3:直接回答固定格式:[{"action":"DIRECT_ANSWER","content":"你的精准答案内容"}]
✅ 格式要求4:JSON字段名严格大小写一致,无拼写错误;
===================== 用户问题 =====================
%s
""".formatted(toolDescs, sessionId, query);
// 3. 调用大模型获取决策结果,并清洗格式
String rawThinkContent = ollamaChatModel.call(new Prompt(prompt))
.getResult().getOutput().getText();
String cleanContent = agentResultCleanUtils.cleanAndStandardize(rawThinkContent);
log.info("【智能体思考器】决策结果已清洗,会话ID:{},清洗后内容:{}", sessionId, cleanContent);
// 4. 解析JSON为标准化ThinkResult
return parseThinkResult(cleanContent);
} catch (Exception e) {
log.error("【智能体思考器】思考失败,会话ID:{}", sessionId, e);
throw new RuntimeException("智能体决策失败,请稍后重试", e);
}
}
/**
* 解析大模型决策结果,适配异常场景
*/
private List<ThinkResult> parseThinkResult(String content) {
try {
if (content.isBlank()) {
throw new IllegalArgumentException("大模型返回决策内容为空");
}
JSONArray jsonArray = JSONArray.parseArray(content);
return jsonArray.toJavaList(ThinkResult.class);
} catch (Exception e) {
log.error("【智能体思考器】决策结果解析失败,内容:{}", content, e);
throw new RuntimeException("决策结果格式异常,无法解析", e);
}
}
}
3. 执行器 AgentExecutor
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 智能体执行器
* 职责:接收思考器决策结果 → 调度对应工具执行 → 返回工具原始结果
*/
@Component
@Slf4j
public class AgentExecutor {
@Autowired
private AgentToolRegistry agentToolRegistry;
/**
* 执行工具调用
*/
public ToolResult execute(ThinkResult thinkResult) {
// 非工具调用,直接返回失败
if (!thinkResult.isToolCall()) {
String errorMsg = "执行器拒绝执行:当前决策为直接回答,无需调用工具";
log.warn(errorMsg);
return ToolResult.fail(errorMsg);
}
// 1. 获取目标工具实例
String targetToolName = thinkResult.getToolName();
AgentTool targetTool = agentToolRegistry.getToolByToolName(targetToolName);
if (targetTool == null) {
String errorMsg = "执行失败:未找到指定工具【" + targetToolName + "】,请检查工具注册状态";
log.error(errorMsg);
return ToolResult.fail(errorMsg);
}
// 2. 构建标准化工具入参
ToolParam toolParam = ToolParam.builder()
.query(thinkResult.getToolParams().getOrDefault("query", "").toString())
.sessionId(thinkResult.getToolParams().getOrDefault("sessionId", "").toString())
.params(thinkResult.getToolParams())
.build();
log.info("【智能体执行器】开始执行工具,工具名称:{},会话ID:{}", targetToolName, toolParam.getSessionId());
// 3. 执行工具并返回结果
return targetTool.execute(toolParam);
}
}
4. 总结器 AgentSummarizer
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Component;
/**
* 智能体总结器(答案生成核心)
* 职责:接收工具原始结果 → 结合用户问题/上下文 → 生成「精准、规范、带溯源」的最终答案
*/
@Component
@Slf4j
public class AgentSummarizer {
@Autowired
private ChatModel ollamaChatModel;
// ========== 可注入你现有上下文管理组件,实现多轮对话 ==========
// @Autowired
// private ContextManager contextManager;
/**
* 核心总结方法
* @param query 用户问题
* @param toolResult 工具执行原始结果
* @param sessionId 会话ID
* @return 标准化最终答案
*/
public String summarize(String query, String toolResult, String sessionId) {
try {
log.info("【智能体总结器】开始生成答案,会话ID:{}", sessionId);
// 1. 获取对话上下文(如需多轮对话,打开注释即可)
String historyContext = "";
// String historyContext = contextManager.getHistory(sessionId, 5); // 获取最近5轮上下文
// 2. 行业级优化Prompt(核心升级:答案质量+格式+溯源+容错性)
String prompt = """
你是知策智能体的专业总结师,具备极强的信息整合与答案生成能力,严格按要求完成回答:
===================== 基础信息 =====================
【工具检索结果】:%s
【历史对话上下文】:%s
【用户当前问题】:%s
===================== 回答强制要求(必须全部遵守) =====================
1. 数据来源:仅基于【工具检索结果】和【历史上下文】作答,严禁编造任何信息;无相关内容时,仅返回「暂无相关答案」;
2. 内容规范:语言简洁、逻辑清晰、重点突出;技术问题分点作答(用1/2/3),产品/业务问题段落式作答;
3. 溯源必现:所有答案末尾必须附带【溯源信息】,格式统一为「👉 来源:XXX文档 | 工作表X 行X」;
4. 格式要求:无多余换行、无markdown标记,纯文本输出,适配前端直接展示;
5. 容错处理:工具结果为「暂无匹配内容」时,直接返回「暂无相关答案」,无需额外补充。
===================== 输出要求 =====================
直接返回最终答案,无任何前置/后置多余字符!
""".formatted(toolResult, historyContext, query);
// 3. 调用大模型生成答案并返回
return ollamaChatModel.call(new Prompt(prompt))
.getResult().getOutput().getText().trim();
} catch (Exception e) {
log.error("【智能体总结器】生成答案失败,会话ID:{}", sessionId, e);
return "很抱歉,生成答案失败,请稍后重试!";
}
}
}
【接入层】智能体对话接口
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 知策智能体对外统一接口
* 提供一站式对话能力,封装「思考→执行→总结」完整链路
*/
@RestController
@RequestMapping("/agent")
@Slf4j
public class AgentController {
@Autowired
private AgentThinker agentThinker;
@Autowired
private AgentExecutor agentExecutor;
@Autowired
private AgentSummarizer agentSummarizer;
/**
* 智能体核心对话接口
*/
@PostMapping("/chat")
public Result<String> chat(@Validated @RequestBody AgentChatParam param) {
try {
String query = param.getQuery();
String sessionId = param.getSessionId();
log.info("【智能体对话接口】接收请求,会话ID:{},用户问题:{}", sessionId, query);
// ========== 步骤1:思考器决策 ==========
List<ThinkResult> thinkResults = agentThinker.think(query, sessionId);
StringBuilder toolResultSb = new StringBuilder();
// ========== 步骤2:执行器调度工具 / 直接获取答案 ==========
for (ThinkResult thinkResult : thinkResults) {
if (thinkResult.isDirectAnswer()) {
// 直接回答,无需调用工具
toolResultSb.append(thinkResult.getContent());
} else {
// 调用工具,获取原始结果
ToolResult toolResult = agentExecutor.execute(thinkResult);
if (toolResult.isSuccess()) {
toolResultSb.append(toolResult.getContent());
} else {
return Result.fail("工具执行失败:" + toolResult.getErrorMsg());
}
}
}
// ========== 步骤3:总结器生成最终答案 ==========
String finalAnswer = agentSummarizer.summarize(query, toolResultSb.toString(), sessionId);
log.info("【智能体对话接口】处理完成,会话ID:{},最终答案:{}", sessionId, finalAnswer);
return Result.success(finalAnswer);
} catch (Exception e) {
log.error("【智能体对话接口】处理失败,参数:{}", param, e);
return Result.fail("智能体对话失败:" + e.getMessage());
}
}
}
三、扩展能力说明(按需启用)
🚀 扩展1:多工具支持(如文本总结、翻译工具)
只需新建类实现AgentTool接口,Spring启动时会自动注册到注册表,思考器可自动识别并决策调用,示例:
java
@Component
public class TextSummaryTool implements AgentTool {
@Override
public String toolName() { return "文本总结工具"; }
@Override
public String toolDesc() { return "用于长文本总结,输入长文本,输出核心摘要"; }
@Override
public ToolResult execute(ToolParam param) { /* 实现逻辑 */ }
}
🚀 扩展2:多轮对话能力
启用上下文管理,只需注入你的ContextManager,并在总结器中打开historyContext注释即可,无需修改其他代码。
🚀 扩展3:大模型无缝替换
项目中统一使用Spring AI的ChatModel接口,替换模型时只需修改配置文件,无需改动业务代码,支持:
- Ollama本地模型(nomic-embed-text、llama3)
- 远程大模型(DeepSeek、OpenAI、文心一言)
🚀 扩展4:工具调用权限控制
可在注册表中新增工具权限标识,在执行器中校验用户权限,实现精细化工具调度控制。
四、部署与使用说明
- 依赖确认 :确保项目中已引入
easyexcel、spring-ai-ollama、elasticsearch、fastjson2核心依赖 - 配置确认:配置Ollama地址、ES地址、向量索引名称,与现有知识库链路保持一致
- 启动验证 :项目启动后,控制台会打印
Agent工具注册表初始化完成,共注册X个工具,说明工具注册成功 - 接口调用 :POST请求
/agent/chat,入参示例:
json
{
"query": "产品的核心功能有哪些?",
"sessionId": "user_123456_20260105"
}
五、核心亮点总结
✅ 架构解耦:思考/执行/总结/工具四层架构,职责清晰、可独立扩展 ✅ 标准规范:工具标准化接口,新增工具零侵入 ✅ 生产可用:全链路异常处理、日志规范、参数校验,满足生产级要求 ✅ 效果保障:优化版Prompt工程,提升决策准确性与答案质量 ✅ 无缝适配:完美衔接现有知识库、混合检索、向量化链路