实例化对话客户端
一、介绍
经过前期的一系列准备工作,包括:API、MCP 与模型的设置,本节我们将先实例化 Advisor 顾问角色,随后再完成 ChatClient 对话客户端的实例化。
二、功能流程
如图,整体 ChatClient 客户端实例化过程;

首先,我们的目标是构建 AiClientNode 对话客户端。前面已经完成了相关元素的实例化步骤。本节主要处理 顾问角色的创建 以及 AiClientNode 节点的构建。
在构建 AiClientNode 时,需要将其与其他元素关联。因此,需要在 AiClientNode 节点 中,通过 Spring 容器的 getBean 方法 获取相应的元素。
需要注意的是,ai_client_system_prompt 系统提示词应调整为 Map 结构数据。这样可以更方便地从数据中提取出 当前 AiClientNode 构建所需的元素。
三、工程实现
1. 工程结构

如图所示,在构建 AiClientNode 的过程中,需要额外增加 Advisor 顾问角色 的构建。
当你看到 ④ 这一部分时,就能体会到 规则树模式 的优势:这种设计能够清晰地区分各个节点,只要定位到对应的类,就能快速找到其实现。
2. 以下为本节涉及的主要修改点说明
增加 AiClientAdvisorNode 节点:
- 在此节点中,通过多线程加载
AiClientAdvisorVO数据。 - 根据顾问角色类型(如
ChatMemory、RagAnswer),分别构建不同的顾问实例。 - 顾问(Advisor) 的作用是作为一种设计模式,用于封装和管理 记忆上下文 与 知识库内容 的访问。
修改 AiClientLoadDataStrategy 数据加载逻辑:
- 调整
repository.queryAiClientSystemPromptMapByClientIds(clientIdList)的查询方式,使其直接返回 Map 结果。 - 其中 key 为关联 ID,对应构建客户端时所需的
system_prompt数据。 - 这种结构可以方便 AiClientNode 构建过程 中的数据获取与使用。
增加 AiClientNode 节点:
- 通过循环方式构建多个对话客户端。
- 每个客户端包含以下关联要素:
- 预设话术(SystemPrompt)
- 对话模型(ChatMode)
- MCP 服务(此处未配置数据库,而是绑定在
ChatMode上) - 顾问角色(Advisor)
- 构建完成后,实例化客户端并将其注册到 Spring 容器 中。
增加 AiClientAdvisorTypeEnumVO 枚举类:
- 作为 顾问角色类型的策略枚举类,用于管理不同顾问的构建逻辑。
- 通过在枚举类中定义抽象方法,不同顾问角色可在各自枚举实现中完成差异化处理。
3. 节点创建 - 顾问角色
3.1 策略枚举
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum AiClientAdvisorTypeEnumVO {
CHAT_MEMORY("ChatMemory", "上下文记忆(内存模式)") {
@Override
public Advisor createAdvisor(AiClientAdvisorVO aiClientAdvisorVO, VectorStore vectorStore) {
AiClientAdvisorVO.ChatMemory chatMemory = aiClientAdvisorVO.getChatMemory();
return PromptChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder()
.maxMessages(chatMemory.getMaxMessages())
.build()
).build();
}
},
RAG_ANSWER("RagAnswer", "知识库") {
@Override
public Advisor createAdvisor(AiClientAdvisorVO aiClientAdvisorVO, VectorStore vectorStore) {
AiClientAdvisorVO.RagAnswer ragAnswer = aiClientAdvisorVO.getRagAnswer();
return new RagAnswerAdvisor(vectorStore, SearchRequest.builder()
.topK(ragAnswer.getTopK())
.filterExpression(ragAnswer.getFilterExpression())
.build());
}
}
;
private String code;
private String info;
// 静态Map缓存,用于快速查找
private static final Map<String, AiClientAdvisorTypeEnumVO> CODE_MAP = new HashMap<>();
// 静态初始化块,在类加载时初始化Map
static {
for (AiClientAdvisorTypeEnumVO enumVO : values()) {
CODE_MAP.put(enumVO.getCode(), enumVO);
}
}
/**
* 策略方法:创建顾问对象
* @param aiClientAdvisorVO 顾问配置对象
* @param vectorStore 向量存储
* @return 顾问对象
*/
public abstract Advisor createAdvisor(AiClientAdvisorVO aiClientAdvisorVO, VectorStore vectorStore);
/**
* 根据code获取枚举
* @param code 编码
* @return 枚举对象
*/
public static AiClientAdvisorTypeEnumVO getByCode(String code) {
AiClientAdvisorTypeEnumVO enumVO = CODE_MAP.get(code);
if (enumVO == null) {
throw new RuntimeException("err! advisorType " + code + " not exist!");
}
return enumVO;
}
}3.2 顾问构建
@Slf4j
@Service
public class AiClientAdvisorNode extends AbstractArmorySupport {
@Resource
private VectorStore vectorStore;
@Resource
private AiClientNode aiClientNode;
@Override
protected String doApply(ArmoryCommandEntity requestParameter, DefaultArmoryStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("Ai Agent 构建节点,Advisor 顾问角色{}", JSON.toJSONString(requestParameter));
List<AiClientAdvisorVO> aiClientAdvisorList = dynamicContext.getValue(dataName());
if (aiClientAdvisorList == null || aiClientAdvisorList.isEmpty()) {
log.warn("没有需要被初始化的 ai client advisor");
return router(requestParameter, dynamicContext);
}
for (AiClientAdvisorVO aiClientAdvisorVO : aiClientAdvisorList) {
// 构建顾问访问对象
Advisor advisor = createAdvisor(aiClientAdvisorVO);
// 注册Bean对象
registerBean(beanName(aiClientAdvisorVO.getAdvisorId()), Advisor.class, advisor);
}
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ArmoryCommandEntity, DefaultArmoryStrategyFactory.DynamicContext, String> get(ArmoryCommandEntity requestParameter, DefaultArmoryStrategyFactory.DynamicContext dynamicContext) throws Exception {
return aiClientNode;
}
protected String beanName(String beanId) {
return AiAgentEnumVO.AI_CLIENT_ADVISOR.getBeanName(beanId);
}
@Override
protected String dataName() {
return AiAgentEnumVO.AI_CLIENT_ADVISOR.getDataName();
}
private Advisor createAdvisor(AiClientAdvisorVO aiClientAdvisorVO) {
String advisorType = aiClientAdvisorVO.getAdvisorType();
AiClientAdvisorTypeEnumVO advisorTypeEnum = AiClientAdvisorTypeEnumVO.getByCode(advisorType);
return advisorTypeEnum.createAdvisor(aiClientAdvisorVO, vectorStore);
}
}根据配置数据
List<AiClientAdvisorVO> aiClientAdvisorList,通过循环方式逐一构建顾问角色。构建逻辑由
createAdvisor方法 承担,该方法基于 枚举策略(AiClientAdvisorTypeEnumVO) 来创建对应的顾问实例。若未来需要新增其他顾问角色,只需在
AiClientAdvisorTypeEnumVO中扩展相应的实现方法,即可完成扩展,无需改动核心构建流程。
4. 节点创建 - 客户端
@Slf4j
@Service
public class AiClientNode extends AbstractArmorySupport {
@Override
protected String doApply(ArmoryCommandEntity requestParameter, DefaultArmoryStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("Ai Agent 构建节点,客户端{}", JSON.toJSONString(requestParameter));
List<AiClientVO> aiClientList = dynamicContext.getValue(dataName());
if (null == aiClientList || aiClientList.isEmpty()) {
return router(requestParameter, dynamicContext);
}
Map<String, AiClientSystemPromptVO> systemPromptMap = dynamicContext.getValue(AiAgentEnumVO.AI_CLIENT_SYSTEM_PROMPT.getDataName());
for (AiClientVO aiClientVO : aiClientList) {
// 1. 预设话术
StringBuilder defaultSystem = new StringBuilder("Ai 智能体 \r\n");
List<String> promptIdList = aiClientVO.getPromptIdList();
for (String promptId : promptIdList) {
AiClientSystemPromptVO aiClientSystemPromptVO = systemPromptMap.get(promptId);
defaultSystem.append(aiClientSystemPromptVO.getPromptContent());
}
// 2. 对话模型
OpenAiChatModel chatModel = getBean(aiClientVO.getModelBeanName());
// 3. MCP 服务
List<McpSyncClient> mcpSyncClients = new ArrayList<>();
List<String> mcpBeanNameList = aiClientVO.getMcpBeanNameList();
for (String mcpBeanName : mcpBeanNameList) {
mcpSyncClients.add(getBean(mcpBeanName));
}
// 4. advisor 顾问角色
List<Advisor> advisors = new ArrayList<>();
List<String> advisorBeanNameList = aiClientVO.getAdvisorBeanNameList();
for (String advisorBeanName : advisorBeanNameList) {
advisors.add(getBean(advisorBeanName));
}
Advisor[] advisorArray = advisors.toArray(new Advisor[]{});
// 5. 构建对话客户端
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem(defaultSystem.toString())
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients.toArray(new McpSyncClient[]{})))
.defaultAdvisors(advisorArray)
.build();
registerBean(beanName(aiClientVO.getClientId()), ChatClient.class, chatClient);
}
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ArmoryCommandEntity, DefaultArmoryStrategyFactory.DynamicContext, String> get(ArmoryCommandEntity requestParameter, DefaultArmoryStrategyFactory.DynamicContext dynamicContext) throws Exception {
return defaultStrategyHandler;
}
@Override
protected String beanName(String id) {
return AiAgentEnumVO.AI_CLIENT.getBeanName(id);
}
@Override
protected String dataName() {
return AiAgentEnumVO.AI_CLIENT.getDataName();
}
}AiClientNode 节点构建逻辑:
- 在构建
AiClientNode客户端节点 时,需要依次将其关联的各个元素,按照 Bean 的名称 从 Spring 容器 中获取并注入。 - 其中,
AiClientSystemPromptVO无需复杂构建,直接从数据库查询并使用即可。
5. 链接处理
AiClientModelNode -> AiClientAdvisorNode -> AiClientNode
举例说明;
@Override
public StrategyHandler<ArmoryCommandEntity, DefaultArmoryStrategyFactory.DynamicContext, String> get(ArmoryCommandEntity requestParameter, DefaultArmoryStrategyFactory.DynamicContext dynamicContext) throws Exception {
return aiClientAdvisorNode;
}- 每个节点构建完成后,就开始关联到下一个节点。
四、测试验证
@Test
public void test_aiClient() throws Exception {
var armoryStrategyHandler = defaultArmoryStrategyFactory.armoryStrategyHandler();
String apply = armoryStrategyHandler.apply(
ArmoryCommandEntity.builder()
.commandType(AiAgentEnumVO.AI_CLIENT.getCode())
.commandIdList(Arrays.asList("3001"))
.build(),
new DefaultArmoryStrategyFactory.DynamicContext());
ChatClient chatClient = (ChatClient) applicationContext.getBean(AiAgentEnumVO.AI_CLIENT.getBeanName("3001"));
log.info("客户端构建:{}", chatClient);
var content = chatClient.prompt(Prompt.builder()
.messages(new UserMessage(
"""
写出一个关于AI智能体的提示词,并优化。
"""))
.build()).call().content();
log.info("测试结果(call):{}", content);
}25-09-11.18:01:10.518 [main ] INFO AgentTest - 测试结果(call):# Role: AI Intelligence Agent
## Profile
- language: 多语言支持
- description: AI智能体擅长分析和处理大量数据,帮助用户简化工作流程,提高效率。
- background: 拥有先进的机器学习和自然语言处理能力,通过不断更新的数据提供准确的分析和预测。
- personality: 理性、精确、富有适应性,能根据用户需求调整沟通风格。
- expertise: 数据分析、自动化流程、问题解决、语言翻译
- target_audience: 商业分析师、数据科学家、项目管理者、技术开发人员
## Skills
1. 数据分析
- 数据整理: 能快速收集和整理各种数据源的信息。
- 模式识别: 通过数据识别和预测趋势、异常。
- 偏好建模: 分析用户行为,为个性化建议提供支持。
- 报告生成: 自动创建详细的数据分析报告。
2. 自动化流程
- 任务调度: 高效规划和管理项目流程。
- 脚本编写: 为日常任务创建自动脚本。
- 系统集成: 整合多个软件工具实现无缝协作。
- 监控与反馈: 持续监控系统性能并提供优化建议。
## Rules
1. 基本原则:
- 保密性: 确保用户信息及数据的隐私和安全。
- 准确性: 提供精确的数据和分析结果。
- 可靠性: 在任务执行中保持稳定和高效。
- 可扩展性: 能够适应不断变化和增长的用户需求。
2. 行为准则:
- 及时反馈: 快速响应用户的请求和疑问。
- 用户优化: 优先考虑提高用户便利性和工作效率。
- 透明沟通: 在行动和决策过程中保持清晰透明。
- 知识更新: 持续学习和适应最新技术发展。
3. 限制条件:
- 数据依赖: 必须在有限的数据范围内进行操作。
- 操作权限: 只能在授权范围内进行文件和数据的操作。
- 资源限制: 必须在规定的硬件和软件资源条件下工作。
- 网络依赖: 依赖稳定的网络连接进行数据传输和操作。
## Workflows
- 目标: 提供快速的数据分析和自动化解决方案
- 步骤 1: 收集并整理用户提供的数据。
- 步骤 2: 分析数据并识别关键模式和异常。
- 步骤 3: 输出个性化建议和自动化流程策略。
- 预期结果: 提高用户工作效率,优化决策质量
## Initialization
作为AI Intelligence Agent,你必须遵守上述Rules,按照Workflows执行任务。- 客户端测试,构建完成后,获取客户端节点。
- 之后可以验证提问
有哪些工具可以使用或者其他的也可以。这部分提问的操作,会结合 prompt 一起提问给 ai。