AI-Agent 智能体-问题梳理
库表设计
1. 核心智能体配置表
ai_agent - AI智能体配置表,用途 :存储智能体的基本配置信息
主要字段 :
- id : 主键ID(自增)
- agent_id : 智能体唯一标识(业务主键)
- agent_name : 智能体名称
- description : 智能体描述
- channel : 渠道类型(如agent、chat_stream)
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新) 示例数据 :包含自动发帖服务、智能对话体等多种类型的智能体配置
ai_agent_flow_config - 智能体-客户端关联表,用途 :定义智能体与客户端之间的关联关系及执行顺序
主要字段 :
- id : 主键ID(自增)
- agent_id : 智能体ID
- client_id : 客户端ID
- client_name : 客户端名称
- client_type : 客户端类型(如DEFAULT、RESPONSE_ASSISTANT等)
- sequence : 执行顺序号(决定客户端在工作流中的执行顺序)
- step_prompt : 步骤提示词(特定步骤的提示词模板)
- create_time : 创建时间 核心设计 :通过 agent_id 、 client_id 和 sequence 的唯一组合,控制智能体工作流的执行顺序
ai_agent_task_schedule - 智能体任务调度配置表,用途 :配置智能体的定时任务
主要字段 :
- id : 主键ID(自增)
- agent_id : 智能体ID
- task_name : 任务名称
- description : 任务描述
- cron_expression : Cron表达式(定时任务调度规则)
- task_param : 任务入参配置(JSON格式)
- status : 状态(0:无效,1:有效)
- create_time : 创建时间
- update_time : 更新时间(自动更新)
2. 客户端配置表
ai_client - AI客户端配置表,用途 :存储各类AI客户端的基本配置
主要字段 :
- id : 主键ID(自增)
- client_id : 客户端唯一标识
- client_name : 客户端名称
- description : 客户端描述
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新) 示例数据 :包含提示词优化、自动发帖、文件操作、流式对话等多种类型的客户端
ai_client_advisor - 顾问配置表,用途 :配置智能体的辅助功能组件(如记忆、RAG等)
主要字段 :
- id : 主键ID(自增)
- advisor_id : 顾问唯一标识
- advisor_name : 顾问名称
- advisor_type : 顾问类型(如ChatMemory、RagAnswer、SimpleLoggerAdvisor等)
- order_num : 顺序号
- ext_param : 扩展参数配置(JSON格式)
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新) 示例数据 :包含聊天记忆组件、知识库检索组件等
3. 模型与API配置表
ai_client_api - OpenAI API配置表,用途 :存储AI模型API的连接配置
主要字段 :
- id : 自增主键ID
- api_id : 全局唯一配置ID
- base_url : API基础URL
- api_key : API密钥
- completions_path : 补全API路径
- embeddings_path : 嵌入API路径
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新)
ai_client_model - 聊天模型配置表,用途 :配置具体的AI模型信息
主要字段 :
- id : 自增主键ID
- model_id : 全局唯一模型ID
- api_id : 关联的API配置ID
- model_name : 模型名称(如gpt-4.1-mini、gpt-5-mini等)
- model_type : 模型类型(如openai、deepseek、claude)
- status : 状态(0:禁用,1:启用)
- update_time : 更新时间(自动更新) 示例数据 :包含多种OpenAI模型的配置
4. 关联配置表
ai_client_config - AI客户端统一关联配置表,用途 :建立各类组件之间的关联关系(核心关联表)
主要字段 :
- id : 主键ID(自增)
- source_type : 源类型(如model、client)
- source_id : 源ID
- target_type : 目标类型(如tool_mcp、advisor、prompt、model)
- target_id : 目标ID
- ext_param : 扩展参数
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新) 核心设计 :通过灵活的源类型-目标类型映射,实现不同组件间的动态关联,支持复杂的智能体配置组合
示例关系 :
- Client → Model:客户端关联到特定模型
- Client → Prompt:客户端关联到系统提示词
- Client → Advisor:客户端关联到顾问组件
- Model → Tool_MCP:模型关联到工具组件
5. 工具与提示词配置表
ai_client_system_prompt - 系统提示词配置表,用途 :存储各类系统提示词模板
主要字段 :
- id : 主键ID(自增)
- prompt_id : 提示词唯一标识
- prompt_name : 提示词名称
- prompt_content : 提示词内容
- description : 描述
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新)
ai_client_tool_mcp - MCP客户端配置表,用途 :配置外部工具调用组件
主要字段 :
- id : 主键ID(自增)
- mcp_id : MCP唯一标识
- mcp_name : MCP名称
- transport_type : 传输类型(如sse/stdio)
- transport_config : 传输配置(JSON格式,包含命令、参数、环境变量等)
- request_timeout : 请求超时时间(分钟)
- status : 状态(0:禁用,1:启用)
- create_time : 创建时间
- update_time : 更新时间(自动更新) 示例数据 :包含elasticsearch-mcp-server、grafana-mcp-server等工具配置
设计特点
- 模块化设计 :将智能体、客户端、模型、工具等组件分离为独立表,便于维护和扩展
- 统一的状态管理 :大多数表都包含 status 字段控制启用/禁用状态
- 自动时间跟踪 :使用 create_time 和 update_time (带ON UPDATE CURRENT_TIMESTAMP)自动记录时间戳
- 业务主键与数据库主键分离 :除了自增ID外,提供业务主键(如agent_id、client_id)方便业务操作
- 灵活的关联机制 :通过 ai_client_config 表实现不同组件间的动态关联
项目架构
你们项目采用了DDD领域驱动设计,能简单介绍一下你们的四色模型是如何划分的?
项目严格按照DD四层建模进行架构设计。首先是实体层(Entity),包括AiClientVO、AiClientAdvisorVO等核心业务对象,承载着业务状态和标识; 值对象(Value)包含各种配置信息,如AiClientSystemPromptVO、AiClientModelVO等,保证数据的不可变性。领域服务(service)实现复杂的业务逻辑, 如AiClientNode、AiClientAdvisorNode等节点,负责Ai Agent的组装和编排;聚合根(aggregate)则通过ExecuteCommandEntity管理Ai Agent的生命周期, 这种设计让我们的业务逻辑更加清晰,各层职责分明,便于维护和扩展。
你们的微服务架构是如何设计的,各个模块之间是如何协作的?
项目采用标准的DDD分层架构,共分为6个核心模块:api负责对外接口契约,app层作为应用启动入口和配置中心,domain包含核心业务逻辑和领域模型 infrastructure层负责数据持久化和外部服务调用,trigger负责HTTP调用和事件触发,types层包含通用类型和枚举,各个模块之间通过依赖注入和事件驱动协作, domain作为核心不依赖其他模块,infrastructure实现domain层定义的接口,trigger调用domain层的服务,有利于业务逻辑的纯净下和系统的可测试性。
项目领域划分与限界上下文
主要划分为两个核心领域/限界上下文
- Agent:负责AI代理的核心功能,包括对话、RAG检索、预热机制和任务调度等
- Auth:围绕认证授权展开,与Agent上下文通过接口通信
Agent设计模式在项目中是如何体现的
Agent设计模式主要体现在多层次的稚嫩提架构设计上,首先是策略模式的应用,通过IExecuteStrategy接口定义相关执行策略, AutoAgentExecuteStrategy实现自动执行逻辑;其次是责任链模板的核心应用,设计了四步执行链:RootNode->Step1AnalyzerNode(任务分析) ->Step2PrecisionExecutorNode(精准执行)->Step3QualitySupervisorNode(质量监督)->Step4LogExecutionSummaryNode(执行总结), 每个节点继承AbstractExecuteAutoSupport并实现特定的业务逻辑;再次是工厂模式,通过DefaultAutoAgentExecuteStrategyFactory创建执行策略处理器, 管理DynamicCContext动态上下文,最后是模板方法模式,AbstractExecuteAutoSupport定义了执行模板,各个Step节点实现具体的doApply方法。 这种设计实现了“问题分析->自主规划->精准执行->质量监督"的完整AI Agent执行循环。每个步骤都可以独立配置不同的ChatClient、Prompt和Adviosr, 真正实现智能体的可编排和可扩展。
Spring AI中的Advisor顾问角色是如何实现的
Advisor顾问角色是AI Agent的核心能力之一,只要用于负责上下文记忆和访问向量库,实现了两个主要的Advisor:PromptChatAdvisor维护对话历史, 通过maxMessages参数来控制记忆长度;RagAnswerAdvisor访问向量库,通过topK(指定在执行向量相似度搜索时返回的最相关文档数量)和filterExpression参数(用于在检索时添加过滤条件,限制搜索范围)来控制检索精度。 项目中使用 AiClientAdvisorVO 值对象来表示顾问的配置信息,配置信息存储在ai_client_advisor表中包含顾问ID、类型、扩展参数等。 在实现上,我们继承了Spring AI的BaseAdvisor ,重写了aroundCall方法来拦截对话请求,在请求前注入相关上下文信息,在响应后更新记忆状态。 这种设计让AI Agent具备了持续学习和知识积累的能力,大大提升了对话的准确性和连贯性。
Advisor模块
- 核心接口 : org.springframework.ai.chat.client.advisor.api.Advisor (Spring AI提供)
- 主要实现类 : RagAnswerAdvisor (自定义实现,用于向量知识库检索)、 PromptChatMemoryAdvisor (Spring AI提供,用于对话记忆管理)
- 接口方法 :
- before() :在对话请求前执行,如检索知识库并注入上下文
- after() :在对话响应后执行,如添加元数据
- adviseCall() :处理同步调用
- adviseStream() :处理流式调用
Prompt模块
- 核心定义 : AiClientSystemPromptVO 类
- 主要字段 :
- promptId :提示词唯一标识
- promptName :提示词名称
- promptContent :提示词内容
- description :提示词描述
MCP模块
- 核心定义 : AiClientToolMcpVO 类
- 主要字段 :
- mcpId :MCP工具唯一标识
- mcpName :MCP工具名称
- transportType :传输类型(sse/stdio)
- transportConfig :传输配置
- requestTimeout :请求超时时间
- 嵌套类 TransportConfigSse 和 TransportConfigStdio 分别定义不同传输方式的配置
动态注入Spring容器的实现分析
项目采用了 BeanDefinitionRegistry 机制通过 DefaultListableBeanFactory 实现动态注入, 基础实现类 : AbstractArmorySupport
提供通用的 registerBean 方法实现Bean动态注册, 通过 applicationContext.getAutowireCapableBeanFactory() 获取 DefaultListableBeanFactory.
数据库和redis承担的角色
- MySQL数据库:存储应用程序的配置信息和元数据
- PostgreSQL (pgvector)数据库:向量数据库,用于存储和检索向量嵌入数据
- Redis:主要出现在Grafana等监控工具的配置中,作为监控数据的缓存。
- Guava本地缓存;使用Spring AI的 MessageWindowChatMemory 管理Agent对话上下文
RAG向量库
RAG知识库的分词和向量化是如何设计的
我们的知识库基于PGvector实现,使用OpenAI的Embedding模型进行向量化。 在分词策略上,我们使用TokenTextSplitter按照token数量进行智能分割,引入了 DOMPurify 库,主要用于网页内容的清洗和安全过滤。 (后续有添加了自定义文本分割器CustomTextSplitter按照段落换行分割),既保证了语义的完整性又控制了向量的维度。向量采用1536维的向量空间(OpenAI,Deepseek为768), 支持余弦相似度检索。在数据处理阶段,我们会对上传的文档进行清理和标准化,提取出关键信息并添加元数据标签,这些标签可以用于后续的过滤检索。 检索时通过searchRequest配置的TopK参数控制返回结果数量,通过filterExpression进行精准过滤,确保检索结果的准确性和相关性。
向量检索步骤
- 提取文本
- 构建搜索请求,应用过滤条件
- 执行向量相似度搜索
- 构建增强上下文
- 将检索到的文档作为上下文添加到用户提示中
- 返回增强后的请求
向量数据库检索性能如何优化的
我们从多个层面优化了向量检索性能。在索引层面,PGVector使用了HNSW(Hierarchical Navigable Small World)算法构建高效的向量索引,支持近似最近邻搜索; 在查询优化方面,我们实现了查询缓存机制,对于相似的查询直接返回缓存结果;在数据分片方面,我们按照业务领域对向量数据进行分片存储,减少检索范围; 在并发控制方面,我们使用了连接池和异步查询,提升并发处理能力。此外,我们还实现了智能的预加载机制,根据用户的历史查询模式预先加载可能需要的向量数据到内存中。 在 DataSourceConfig.java
中配置了专用的pgVectorDataSource连接池,使用性能优秀的HikariCP连接池管理器,并设置了合理的连接池参数: 最大连接数、最小空闲连接数、连接超时时间、空闲超时时间等。在 RagAnswerAdvisor 类中通过 vectorStore.similaritySearch(searchRequestToUse) 实现向量相似度搜索。 通过这些优化,我们的向量检索响应时间从原来的500ms优化到了50ms以内,同时支持千级并发查询。
HNSW(Hierarchical Navigable Small World,分层可导航小世界)是一种基于图结构的近似最近邻搜索(ANN)算法,专 为高维向量(如文本、图像、音频的嵌入向量)设计,旨在平衡搜索速度与召回率。其核心思想是通过分层图结构和小世界网络特性,实现高效的近似搜索。
为什么选择 PostgreSQL 向量库而不是 Milvus / Pinecone?
对比维度 | PostgreSQL+pgvector | 专用向量数据库(如 Milvus) |
---|---|---|
部署成本 | 复用现有数据库,零新增成本 | 需独立服务器部署 |
事务支持 | 支持 ACID,与业务数据强一致 | 多数仅最终一致性 |
混合查询 | 可同时执行向量检索 + 条件过滤(如 WHERE source='git' ) | 需额外处理业务过滤逻辑 |
适用场景 | 百万级向量 + 结构化数据混合场景 | 十亿级纯向量检索 |
多源解析模块处理不同格式的具体实现
在 AiAgentRagService.storeRagFile() 方法中,使用 TikaDocumentReader 读取上传的文件内容。项目引入了 ANTLR相关库 (antlr4-runtime:4.13.1、ST4:4.3.4、antlr-runtime:3.5.3),这些库通常用于构建语法解析器。 项目采用了 Apache Tika 作为主要的多格式文档解析引擎,其集成了完整的Tika解析模块套件(tika-parsers-standard-package),包含:
- tika-parser-code-module :用于代码解析
- tika-parser-html-module :用于网页解析
- tika-parser-pdf-module :用于PDF文档解析
- 以及其他Office文档、图像、视频等20多种格式的解析模块
整体处理流程如下:
文件上传 → 格式自动识别 (TikaDocumentReader) → 内容提取 → 文本分块 (TokenTextSplitter) → 向量存储 (PgVectorStore) → 检索时上下文构建 (RagAnswerAdvisor) → LLM回答生成
当Git代码变更时,如何触发知识库增量更新?
项目中的知识库配置主要通过 ai_client_rag_order 表进行管理,支持基本的CRUD操作(如 updateByRagId 方法),但更新过程完全依赖手动触发数据库操作。 可以考虑以下方案尝试实现:
- Webhook接收 :实现Git webhook接收端点,监听代码仓库变更事件
- 差异检测 :使用Git API获取变更文件列表和具体变更内容
- 增量处理 :针对变更文件重新进行文本分割、向量化并更新向量存储
- 版本管理 :设计版本快照表存储不同时期的知识库状态,支持版本回溯
- 定时补充 :结合定时任务定期进行全量检查,确保数据一致性
你如何做 知识更新和版本管理,避免知识库里出现陈旧数据?
- 没有实现知识内容的自动更新机制 :未发现定时刷新或自动同步数据源的功能;
- 缺乏版本管理系统 :没有为知识条目设计版本号或历史记录
- 缺少数据过期策略 :没有设置知识内容的有效期或过期检测机制
- 未实现增量更新 :没有针对知识库内容的增量更新方案
建议实现方案:
- 定时刷新机制 :
- 利用 ai_agent_task_schedule 表配置定时任务
- 实现基于cron表达式的知识库内容定期同步
- 版本管理系统 :
- 为知识条目添加版本号字段
- 记录每次更新的时间戳和更新人
- 支持回滚到历史版本
- 数据过期策略 :
- 为知识条目添加过期时间字段
- 实现过期数据的自动清理或标记
- 在查询时过滤已过期的内容
- 增量更新方案 :
- 记录上次同步时间点
- 实现基于时间戳的增量同步逻辑
- 支持事件驱动的实时更新
- 内容校验机制 :
- 定期验证知识库内容的准确性
- 标记可疑或需要更新的内容
在构建 RAG 系统时,最常遇到的问题是“检索不准”。你们遇到了哪些bad case?又是通过哪些策略(如:元数据过滤、重排序、HyDE、多向量检索等)来优化召回率和准确率的?
- 元数据过滤优化
- 系统已支持基础的filterExpression过滤,可进一步扩展多维度元数据标签系统
- 建议增加时间、作者、主题等多层次元数据,实现更精确的过滤
- 重排序机制
- 实现两段式检索:先向量检索获取候选文档,再通过BM25或交叉编码器进行精细排序
- 多向量检索实现:为每个文档的不同部分生成向量,存储多向量。
RAG知识库检索与问答流程
- 用户提问处理:用户提问首先通过应用层接收,然后传递给 RagAnswerAdvisor 类进行处理。该类实现了 BaseAdvisor 接口,是整个RAG流程的核心组件。
- 向量检索执行:在 RagAnswerAdvisor 的 before 方法中,系统执行以下关键步骤
- 从用户请求中提取查询文本:
String userText = chatClientRequest.prompt().getUserMessage().getText()
- 构建搜索请求:结合topK参数和过滤表达式创建 SearchRequest 对象
- 执行向量相似度搜索:调用
vectorStore.similaritySearch(searchRequestToUse)
执行检索
- 从用户请求中提取查询文本:
- 元数据过滤机制 系统通过 doGetFilterExpression 方法支持元数据过滤,优先从上下文获取过滤表达式,否则使用默认配置.
- 上下文构建:检索到相关文档后,系统将文档内容拼接为上下文
- 提示词构建:系统使用预定义模板构建包含上下文的提示词
- 大模型调用与回答生成:系统将构建好的提示词和上下文传递给大模型,生成基于检索知识的回答
- 结果处理:在 after 方法中,系统将检索到的文档信息添加到响应元数据中,以便进一步处理或记录。
MCP
MCP协议的设计理念和实现方式是什么?
MCP是实现工具调用的核心协议,它定义了AI模型和外部工具之间的标准化通信接口。使 AI Agent 能够以一致的方式调用不同类型的外部服务 我们支持两种通信方式:stdio模式适用于本地工具调用,通过标准输入输出进行通信;sse适用于远程服务调用,通过Server-Sent Event实现实时通信。满足不同场景下的需求 实现上,项目中通过AiClientToopMcp表和AiClientToolMcpVO值对象管理MCP客户端配置。AiClientToolMcpNode类负责根据配置创建MCP客户端实例并注册为Spring Bean, 通过 SyncMcpToolCallbackProvider 将MCP客户端与Spring AI的ChatClient集成,实现工具调用能力。我们为每个MCP服务定义了标准的Function接口, 使用@Tool注解标记为可调用的方法,使用MethodToolCallbackProvider将Java方法暴露为MCP工具。 比如微信通知MCP通过WeixinNoticeService提供消息推送能力,CSND自动发帖提供自动发帖能力,这种设计让AI Agent能够调用各种外部服务,大大扩展了智能体的能力边界。
MCP服务标准Function接口 MCP服务标准Function接口采用了一种轻量级的定义方式,主要通过工具名称和参数约定来实现,而非传统的Java接口定义。具体表现为:
- 工具命名规范 :采用服务名/功能名的格式,如 grafana/list_datasources 、 grafana/query_prometheus
- 参数约定:每个接口有明确的参数要求,如 query_prometheus 接口需要传入 query 、 start 、 end 、 step 等参数
- 调用模式:通过 callMcpTool() 方法统一调用,传入工具名称和参数Map
MCP服务如何保证安全性
我们设计了通用的MCP Nginx Token 校验机制来保证数据传输安全性。首先在Nginx层配置了统一的鉴权服务, 所有MCP请求必须携带有效的Token才能通过,token采用JWT格式,包含用户身份、权限范围、过期时间等信息。 在应用层,我们为每个MCP服务都配置了独立的访问密钥,支持定期轮换;对于敏感操作,如访问数据库、文件操作,还增加了二次验证机制,此外,我们还实现了请求频率限制、IP白名单、操作审计日志等安全措施, 在数据传输过程中,所有敏感信息都进行了加密处理,确保即使在网络传输过程中被拦截也无法直接使用。
在实现MCP服务的过程中,stdio和sse两种模式各有什么优缺点?
stdio模式的优点是实现简单,适合本地工具调用,通信开销小,调试方便;缺点是只能用于本地服务,不支持远程调用,扩展性有限。使用 StdioClientTransport 创建客户端连接。 sse模式的优点是支持远程调用,可以跨网络部署,支持实时双向通信,扩展性强;支持长连接和服务器推送。缺点是实现复杂度高,需要处理网络异常和重连机制,通信开销相对较大。使用 HttpClientSseClientTransport 创建 HTTP 长连接。 在实际应用中,我们根据具体场景选择合适的模式:对于文件系统操作、本地命令执行等场景使用stdio模式; 对于微信通知、CSDN发帖、远程API调用等场景使用sse模式。为了统一开发体验,我们封装了通用的MCP客户端,屏蔽了底层通信细节,开发者只需要关注业务逻辑即可。
选择stdio/sse协议而非gRPC的考量?如何处理高并发下的连接稳定性?
Stdio 和 SSE 协议实现相对简单,不需要像 gRPC 那样复杂的序列化/反序列化机制和服务定义; 系统主要需求是 AI Agent 调用外部服务的单向通信模式,Stdio/SSE 已能满足此需求
可配置的超时机制
- 支持为每个 MCP 客户端配置请求超时时间,防止长时间占用资源
- 从
AiClientToolMcp.java
可以看出,超时时间作为核心配置项存储
连接初始化验证
- 每次创建连接时都会调用 initialize() 方法验证连接是否成功建立
负载均衡支持
- 通过 Nginx 等反向代理实现服务端的负载均衡,提高系统整体吞吐量
连接认证与安全
- 支持通过 URL 参数传递认证信息(如 API Key),确保连接安全性
- Nginx 配置示例中展示了如何通过验证 API Key 增强安全性
错误处理与日志记录
- 系统对连接初始化和请求过程进行详细日志记录,便于问题排查
- 从
AiClientToolMcpNode.java
可以看到连接初始化结果的日志记录
灵活的配置管理
- 通过数据库表 ai_client_tool_mcp 集中管理 MCP 客户端配置,支持动态调整
AiAdminClientToolMcpController.java
提供了完整的配置管理 API
CSDN 发帖、微信公众号通知、ELK 日志分析、Prometheus 监控——这些场景是怎么通过 MCP 模块化抽象出来的?
- MCP模块化抽象基础架构
- AiClientToolMcpNode 负责创建和注册MCP客户端工具
- AiClientToolMcpVO 定义MCP工具配置结构
- 通过配置驱动方式创建MCP客户端;统一注册到Spring容器中,供上层应用调用;提供标准化接口,屏蔽底层实现细节
- Prometheus监控的MCP实现
- 提供 grafana/list_datasources 和 grafana/query_prometheus 等标准化工具接口
- CSDN发帖的MCP实现
- 通过 CSDNArticleService 类的 saveArticle 方法接收发帖请求,该方法被 @Tool 注解标记,可作为AI工具调用
- ArticleFunctionRequest :包含文章标题、Markdown内容、标签、简述等信息,并提供将Markdown转换为HTML的方法;ArticleFunctionResponse :包含返回码、消息和文章数据(URL、ID、二维码等);
- 通过 ICSDNPort 接口定义抽象,由 CSDNPort 实现具体逻辑;在适配器中完成领域模型到API请求DTO的转换;调用ICSDNService 接口发起实际的API请求。
- 使用 Retrofit 框架发送HTTP请求到CSDN的 /blog-console-api/v3/mdeditor/saveArticle 接口;请求头包含完整的浏览器模拟信息和CSDN认证Cookie; 发送的数据包含文章的各种属性(标题、内容、标签、分类等)。
- 微信公众号通知的MCP实现
- 接收请求 :客户端调用 WeiXinNoticeService.weixinNotice() 方法,传入包含平台、主题、描述和跳转地址的请求参数
- 获取AccessToken : WeiXiPort 实现类先检查缓存中是否存在有效的AccessToken: 如果存在直接使用; 如果不存在,调用微信API获取新的AccessToken并存入缓存
- 构建模板消息 :根据请求参数构建微信模板消息对象 WeixinTemplateMessageDTO ,包含: 接收者用户openid; 模板ID ;跳转URL 消息内容(平台、主题、描述)
- 发送消息 :调用微信API的 sendMessage 方法发送模板消息
- 返回结果 :返回通知发送结果
- ELK日志分析的MCP实现
MCP 执行过程中遇到失败(比如网络异常、外部 API 返回错误),平台是怎么处理重试、补偿或降级的?
MCP客户端配置中包含 requestTimeout 参数(分钟级),在创建MCP客户端时会设置请求超时时间 - SSE传输类型:超时时间为分钟级Duration.ofMinutes(aiClientToolMcpVO.getRequestTimeout())
- Stdio传输类型:超时时间为秒级Duration.ofSeconds(aiClientToolMcpVO.getRequestTimeout())
异常捕获:在AiAgentController中对请求处理异常进行了捕获和日志记录;主要是记录错误信息并向客户端返回异常消息,但没有实现重试逻辑。
质量监督:Step3QualitySupervisorNode负责质量监督,主要控制任务流程,但没有针对MCP失败的特定处理策略。
重试机制:为不同的步骤设置不同的重试次数。
/**
* 带重试机制的执行方法
*/
private <T> T executeWithRetry(Supplier<T> operation, String operationName, int maxRetries) {
Exception lastException = null;
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
log.info("执行操作: {} (第{}/{})次尝试", operationName, attempt, maxRetries);
T result = operation.get();
if (attempt > 1) {
log.info("操作 {} 在第{}次尝试后成功", operationName, attempt);
}
return result;
} catch (Exception e) {
lastException = e;
log.warn("操作 {} 第{}次尝试失败: {}", operationName, attempt, e.getMessage());
if (attempt < maxRetries) {
try {
long waitTime = (long) Math.pow(2, attempt - 1) * 1000;
log.info("等待 {}ms 后重试...", waitTime);
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试过程被中断", ie);
}
}
}
}
throw new RuntimeException(String.format("操作 %s 在 %d 次尝试后仍然失败", operationName, maxRetries), lastException);
}
- 支持配置最大重试次数
- 采用 指数退避 算法计算重试间隔时间
- 详细的日志记录,便于问题排查
- 重试失败后抛出包含原始异常的新异常
错误处理:
- 方法级重试机制 :通过 executeWithRetry 方法提供细粒度的操作重试
- 骤级错误处理 :通过 handleStepExecutionError 方法专门处理单个步骤执行失败
- 流程级异常捕获 :在主执行流程中捕获和处理未预期的异常
判定MCP调用失败规则
- 超时判定:AiClientToolMcp 表中配置了 request_timeout 字段,构建MCP客户端时设置超时时间。
- 在 handleStepExecutionError 方法中,专门检测网络相关错误
- 响应状态检查:通过状态标记(如 stepStatus )记录执行结果;可将失败状态设置为 FAILED_WITH_ERROR
以"CSDN自动发帖"为例,完整的MCP调用流程从用户配置到最终发帖成功的具体流程
- 用户通过ai-client-tool-mcp.html 页面进行配置管理,配置信息通过Ajax请求提交到后端API /ai/admin/client/tool/mcp/addMcp ,最终存储在 ai_client_tool_mcp 表中。
- 系统启动时,AiClientToolMcpNode类初始化MCP客户端(创建客户端、设置超时、初始化并验证连接),注册到Spring容器中备用
- 在 AiClientNode 类中,系统将初始化好的MCP客户端集成到AI Agent的工具链中:
- 创建 ChatClient 时,通过 SyncMcpToolCallbackProvider 注入MCP客户端
- 构建完整的工具调用链,使AI模型能够调用CSDN发帖功能
- 调用数据 :用户输入 → AI Agent → MCP客户端 → CSDN服务,触发任务时,根据AI Agent执行链生成文章。
主要依赖完整的用户Cookie进行反爬处理,
监控告警场景的幂等性设计?如何防止重复通知导致业务系统过载?
- 通过try-with-resources确保文件操作的原子性,只有在所有指标生成和写入成功后才会返回true,任何步骤失败都会返回false并记录错误日志。
- 指标生成采用覆盖写入方式
new FileWriter(METRICS_FILE)
,保证每次生成的指标文件只包含最新的指标数据,避免数据重复累积导致的监控系统误判。 - 通过注册JVM关闭钩子,确保程序退出时能够正确清理资源,防止因资源泄漏导致的系统负担加重。
AI Agent
如何实现AI Agent的动态编排和热部署
通过Spring的动态Bean注册机制实现了AI Agent的热部署能力。核心思路就是将AI Agent的各个组件(Model、Prompt、Advisor、MCP)抽象为可配置的Bean,存储在数据库中。 当配置发生变化时,通过ArmoryCommunityEntity触发重新装配流程,使用DefaultArmoryStrategyFactory的策略模式动态创建新的组件实例,然后通过registerBean方法将新组件注册到Spring容器中。 数据库表结构设计支持配置的动态更新。每次执行任务时都会从数据库加载最新配置,无需重启应用。 整个过程采用责任链模式,依次执行AiClientApiNode、AiClientModelNode、AiClientAdvisorNode、AiClientNode等节点,确保组件的正确装配顺序。 这种设计让我们能够在不重启服务的情况下,动态调正AI Agent的能力配置,大大提升了系统的灵活性和运维效率。
AI Agent的动态编排采用了基于树形策略路由的设计模式,DefaultArmoryStrategyFactory 和 DefaultAutoAgentExecuteStrategyFactory 负责创建策略处理器。 工厂类通过 armoryStrategyHandler() 方法提供统一的策略入口 ,实现了策略的集中管理和动态切换。以 RootNode 作为根节点,串联多个功能节点形成执行链, 任务分析->精准执行->质量监督->质量监督,每个节点通过重写 get() 方法指定下一个要跳转的节点,通过 router() 方法执行实际的路由跳转,实现动态路由。 DynamicContext 类作为执行过程中的数据容器,支持存储和获取各类数据,提供 setValue() 和 getValue() 方法实现上下文数据的动态存取。 实现了 multiThread() 方法支持并行加载配置数据,AiClientLoadDataStrategy 使用 CompletableFuture 并行查询各类配置信息
动态Bean注册
- 使用 registerBean() 方法动态注册组件,避免硬编码依赖
- 组件ID通过 AiAgentEnumVO 枚举统一管理,如 AI_CLIENT_ADVISOR.getBeanName(advisorId)
在动态构建 Agent 的过程中,如何保证 模块解耦 和 执行链路的可维护性?
- 责任链模型实现执行链路:项目中采用责任链模式构建Agent执行流程,主要通过StrategyHandler 接口和 AbstractMultiThreadStrategyRouter 类实现。 每个处理节点(如 AiClientApiNode 、 AiClientToolMcpNode 、 AiClientModelNode 等)都实现了 get() 方法,用于返回链中的下一个处理节点。 这种设计使得执行流程可以灵活配置和扩展,各节点之间通过接口解耦。
- 动态上下文管理机制:执行过程中使用 DefaultArmoryStrategyFactory.DynamicContext 和 DefaultAutoAgentExecuteStrategyFactory.DynamicContext 作为上下文容器, 在整个执行链路中传递数据。这些上下文类提供了 setValue() 和 getValue() 通用方法,支持存储和检索任意类型的对象,实现了数据在不同模块间的共享和流转。
- 工厂模式封装组件创建:项目使用工厂模式封装Agent组件的创建和组装逻辑。 DefaultArmoryStrategyFactory 和 DefaultAutoAgentExecuteStrategyFactory 作为工厂类,负责创建并返回执行链路的入口点(RootNode), 隐藏了复杂的组件创建和组装细节。这种方式使得客户端代码不需要了解具体实现,降低了模块间的耦合度。
- Spring 依赖注入实现模块依赖:模块间的依赖关系通过Spring的依赖注入机制实现解耦。各节点类之间通过接口进行依赖,具体实现由Spring容器负责注入。 AbstractArmorySupport 和 AbstractExecuteSupport 基类提供了 getBean() 方法,可以根据Bean名称动态获取Spring容器中的组件,进一步增强了解耦性。
- 数据加载策略分离:项目将数据加载逻辑抽象为 ILoadDataStrategy 接口,并提供了多种实现(如 AiClientLoadDataStrategy 、 AiClientModelLoadDataStrategy )。 RootNode 通过 loadDataStrategyMap 获取并执行相应的数据加载策略,实现了数据加载与业务逻辑的分离。
- 执行流程控制机制:执行流程中通过动态上下文的状态控制执行路径。例如, Step1AnalyzerNode 会根据 dynamicContext.isCompleted() 标志和步骤计数决定是继续执行下一步、提前结束还是进入总结阶段。 这种设计使得执行流程可以根据实际情况灵活调整,增强了系统的可维护性。
- 统一的异常处理和日志记录:各节点类中包含完善的异常处理和日志记录机制,确保执行过程中的问题能够被及时发现和处理。 同时,基类 AbstractExecuteSupport 提供了 sendSseResult() 等通用方法,封装了流式响应的处理逻辑,提高了代码的复用性和可维护性。
节点链式编排结构
- 采用 AbstractMultiThreadStrategyRouter 作为核心策略路由器框架
- 工作流以 RootNode 为入口节点,通过责任链模式依次连接后续处理节点
- 节点链结构: RootNode → AiClientToolMcpNode → AiClientAdvisorNode → AiClientModelNode → AiClientNode
- 每个节点专注于特定的业务逻辑处理,形成松耦合的处理链
上下文数据的加载与流转流程:整个上下文传递过程遵循 "数据加载 → 上下文存储 → 节点间传递 → 按需取用" 的流程。
- 多源数据并行加载:在 RootNode 中,系统通过 CompletableFuture 并行加载多源配置数据,提高预热效率。
- 节点间的链式传递:系统采用 策略链模式 ,通过 router 方法将上下文从一个节点传递到下一个节点。处理节点按以下顺序链式传递:
- RootNode → AiClientToolMcpNode → AiClientAdvisorNode → AiClientModelNode → AiClientNode
不同类型的 Advisor 负责不同维度的上下文管理: ChatMemory 管理对话历史, RagAnswer 管理知识库检索结果。
MCP 工具的上下文传递与应用
- 在 AiClientToolMcpNode 中,系统从上下文中获取 MCP 配置并创建 MCP 客户端实例
- 在 AiClientModelNode 中,系统根据模型配置从 Spring 容器获取对应的 MCP 客户端
- 在 AiClientNode 中,系统为 ChatClient 配置 MCP 工具回调
"分析-执行-监督-反馈"的上下文传递流程
- 初始化阶段 :在 RootNode 中初始化上下文,设置任务参数和执行环境
- 分析阶段 : Step1AnalyzerNode 分析任务,将分析结果存入上下文
- 执行阶段 : Step2PrecisionExecutorNode 从上下文获取分析结果,执行具体任务并保存执行结果
- 监督阶段 : Step3QualitySupervisorNode 从上下文获取执行结果,进行质量检查并更新任务状态
- 动态路由 :根据上下文状态决定继续执行、重新执行或结束任务
- 结果反馈 :通过SSE将执行过程和结果实时反馈给用户
对话历史记忆管理
对话上下文主要存储在内存中,使用 MessageWindowChatMemory 实现;从前端代码可以看出,对话历史在客户端也会存储在 localStorage 中,用于界面展示和状态恢复。
- 基于窗口的记忆管理 :通过 MessageWindowChatMemory 实现固定大小的对话历史窗口
- 可配置的记忆容量 :支持通过配置文件设置 maxMessages 参数,控制对话历史的保留数量
- 顾问注册机制 :通过 registerBean 方法将 Advisor 实例注册为Spring Bean,确保全局一致性
你提到的 “问题分析 → 自主规划 → 精准执行 → 结果判定” 是如何实现的?能举一个完整闭环的例子吗?
该流程基于 Planning模式 ,通过精心设计的系统提示词(System Prompt)引导大模型进行结构化思考,并结合工具调用机制完成实际任务执行。 (1) 问题分析阶段
- 通过ChatClient构建器中的 defaultSystem 方法定义Agent的角色与任务范围
- 系统提示词明确要求Agent首先分析用户输入内容,理解任务目标和约束条件
(2) 自主规划阶段
- 在系统提示词中预先定义规划模板,引导Agent按照特定步骤思考
- 例如在文章生成场景中,提示词明确要求规划包含4个步骤:生成文章→提取发布要素→获取URL→发送通知
- 规划过程在大模型内部完成,形成结构化的执行计划
(3) 精准执行阶段
- 借助 SyncMcpToolCallbackProvider 提供工具调用能力,连接外部系统
- 支持两种传输模式的MCP(Multi-Channel Protocol)客户端:
- SSE模式:通过HTTP SSE连接远程服务
- STDIO模式:通过命令行调用本地服务
- 执行过程中,Agent根据规划步骤自动调用相应工具完成任务
(4) 结果判定阶段
- 通过 chatClient.prompt().call().content() 获取完整执行结果
- 结果包含中间执行状态和最终输出,可通过Logger组件记录整个流程
- 支持对话记忆( PromptChatMemoryAdvisor )实现上下文延续和结果追溯
以CSDN发布文章为例
Agent接收到用户的文章生成请求,根据系统提示理解需要完成的核心任务
Agent内部规划出四步执行计划:生成文章内容→提取发布要素→发布到CSDN→发送通知
精准执行:
第一步:基于用户输入生成符合要求的技术文章
第二步:从文章中提取标题、标签、简述等要素
第三步:调用CSDN发布工具( sseMcpClient01 )完成文章发布并获取URL
第四步:调用微信通知工具( sseMcpClient02 )发送包含文章信息的通知
结果判定 :Agent整合各步骤结果,向用户返回完整执行报告
条件分支与并行节点支持
- 并行节点支持
- 引擎通过 CompletableFuture 和线程池实现并行处理能力
- 在 RootNode 中,多个数据查询任务通过 CompletableFuture.supplyAsync 并行执行
- 使用 CompletableFuture.allOf() 等待所有并行任务完成
- 线程池配置可通过 ThreadPoolConfigProperties 进行自定义(核心线程数、最大线程数等)
- 条件分支支持
- 每个节点通过重写 doApply() 方法实现具体业务逻辑,并返回处理结果
- 通过 get() 方法决定下一个处理节点,实现条件路由
- 节点可以根据上下文数据和业务规则决定流程走向
- 支持在节点内部根据条件执行不同的处理逻辑
线程安全保障
- 使用 synchronized 关键字保护共享资源(如 registerBean 方法)
- 采用线程安全的数据结构(如 ConcurrentHashMap )存储任务调度信息
Agent并发运行时Bean隔离机制分析
项目通过为每个Agent/Client分配唯一的Bean名称实现基本隔离,每个Agent使用独立的客户端ID执行,避免相互干扰.
- 客户端Bean命名格式: ChatClient_ + clientId
- 模型Bean命名格式: AiClientModel_ + modelId
- 顾问Bean命名格式: AiClientAdvisor_ + id
通过 synchronized 关键字确保Bean注册过程的线程安全
使用 ConcurrentHashMap 存储和管理任务调度,确保并发安全
项目使用 ThreadPoolTaskScheduler 管理任务执行线程,避免线程资源耗尽,通过 CompletableFuture 并行加载配置数据,提高多Agent初始化效率
生命周期管理:
- 所有动态注册的Bean都设置为单例模式,确保资源合理使用
- 注册前先检查并移除同名Bean,确保配置更新时的一致性
运营级配置服务如何实现 “实时调整、变更与上线”?配置变更时如何通知正在运行的 Agent 生效(比如推拉模式)?
系统配置主要存储在 数据库 中(MySQL),通过多张配置表管理不同类型的配置信息,
系统采用 拉取模式 实现配置的实时调整、变更与上线:
- 定时拉取:通过 AgentTaskJob 实现每分钟自动刷新任务调度配置;定时从数据库查询最新配置,并与内存中的配置进行对比更新;任务调度使用ThreadPoolTaskScheduler(10线程池)管理并发。
- 手动预热机制:提供 IAiAgentPreheatService 接口实现配置预热,应用启动时自动调用 preheat() 方法初始化所有智能体配置,管理后台提供预热按钮,可手动触发特定智能体配置更新;预热通过 DefaultArmoryStrategyFactory 和策略处理器应用最新配置。
- 配置清理机制:定时清理无效任务配置,确保资源合理利用;应用关闭时自动取消所有任务,释放资源
preheat(预热机制)实现流程
- 获取客户端ID列表 :通过 IAgentRepository 查询所有有效的客户端ID列表
- 构建预热实体 :创建 AiAgentEngineStarterEntity 并设置客户端ID列表
- 执行策略处理 :调用 DefaultArmoryStrategyFactory 的 strategyHandler().apply() 方法启动预热
- 我们先根据aiClientIds从RootNode节点出发, 异步从库表中查询组装出来业务中流转的VO对象, 并将它们添加到Map中
- 然后进入到AiClientToolMcpNode, 根据Map中的MCPVOList组转起来所有McpSyncClient然后将其注册为Bean
- 进入到AiClientAdvisorNode, 根据Advisor的类型, 组装rag或者chatmemory类型的bean
- 再到AiClientModelNode, 组装OpenAiChatModel并这个model绑定的MCP Tool添加进去
- 最后就是AiClientNode将chatModel, MCP, Advisor这三类Bean组装成一个对话客户端ChatClient, 然后将其注册为bean
如何通过数据库配置实现模块的动态组合?
- 关联表设计 :使用多对多关联表实现组件间的动态装配:
- ai_agent_client :智能体与客户端的组合
- ai_client_model_config :客户端与模型的组合
- ai_client_tool_config :客户端与工具的组合
- ai_client_advisor_config :客户端与顾问的组合
- ai_client_model_tool_config :模型与工具的组合
- 执行顺序控制 :在关联表中通过 sequence 字段控制组件执行顺序,如 ai_agent_client.sequence
- 条件过滤 :通过 status 字段和业务逻辑实现条件分支
- 参数动态注入 :通过JSON格式的扩展参数字段,实现配置的动态注入
领域层与数据层的映射方式
- 采用手动映射方式,通过 AgentRepository 作为领域层与数据层的桥梁
- Repository负责将数据库PO对象转换为领域层值对象(VO),例如:
- queryAiClientByClientIds 方法将多个表的PO对象组装成 AiClientVO 值对象
- queryAiClientModelVOListByClientIds 方法将 AiClientModel PO转换为 AiClientModelVO
- 映射过程包含JSON配置解析、关联数据组装等逻辑
动态构建Agent时从数据库配置映射到可执行运行时对象的流程
- 数据库配置到领域对象的映射过程:项目通过 AgentRepository 实现了从数据库配置到领域值对象(VO)的转换:
- 首先通过DAO层(如 aiClientModelDao 、 aiClientApiDao )查询数据库,获取原始配置数据(如 AiClientModel 、 AiClientApi 等实体)
- 然后将这些原始数据转换为领域值对象(VO),如 AiClientApiVO 、 AiClientModelVO 等
- 转换过程中会过滤掉无效配置(如状态不为1的记录),并避免重复添加相同的配置
- Spring容器动态注入的底层实现:项目中 AbstractArmorySupport 类提供了核心的动态Bean注册功能:
- 使用 applicationContext.getAutowireCapableBeanFactory() 获取Spring的 DefaultListableBeanFactory
- 通过 BeanDefinitionBuilder 构建Bean定义,并设置为单例模式( SCOPE_SINGLETON )
- 采用同步方法确保线程安全,在注册前检查并移除已存在的同名Bean
- 最终调用 beanFactory.registerBeanDefinition() 将Bean定义注册到Spring容器
- 运行时对象的构建与装配流程:整个Agent构建过程采用了策略工厂和责任链模式:
- DefaultArmoryStrategyFactory 创建并提供 StrategyHandler (实际是 RootNode )
- 以 RootNode 为起点,通过责任链模式依次经过不同节点(如 AiClientApiNode )
- 在各节点中,根据VO配置构建具体的运行时对象(如 OpenAiApi )
- 使用 registerBean 方法将构建好的对象注册到Spring容器中
- Bean命名规则与获取机制:项目使用 AiAgentEnumVO 枚举定义了统一的Bean命名规则:
- 为不同类型的组件(API、模型、提示词等)定义了唯一的前缀(如 ai_client_api_ )
- 通过 getBeanName 方法根据前缀和ID生成唯一的Bean名称
- 需要获取Bean时,使用相同的命名规则从Spring容器中获取
智能体任务调度服务是如何设计的,如何实现自动化巡检?
我们基于Spring TaskScheduler扩展实现了智能体任务调度服务,支持定时和事件驱动两种触发方式。通过 AiAgentTaskSchedule 实体类定义任务调度配置, 通过 IAiAgentTaskScheduleDao 接口提供任务调度配置的CRUD操作,支持按ID查询、按智能体ID查询及获取所有有效任务( queryEnabledTasks() )等功能。 AgentRepository 类注入 IAiAgentTaskScheduleDao 接口,作为领域层和数据层的桥梁 定时巡检通过Cron表达式配置执行周期,自动收集系统指标、日志信息、告警数据等,然后调用专门的巡检Agent进行智能分析,最终生成HTML格式的巡检报告。 事件驱动巡检则监听MQ消息,当收到告警信息时立即触发相应的巡检动作。在实现上,我们为每种巡检场景配置了专门的Agent, 比如性能巡检Agent配置了Prometheus MCP工具,日志巡检Agent配置了ELK MCP工具。巡检结果会通过微信公众号MCP自动推送给相关人员,实现了从发现问题到通知处理的全自动化流程。
前端的拖拽编排功能如何实现
我们基于React和flowgram.ai框架实现列AI Agent的可视化编排功能。前端采用节点式的流程图设计,每个节点代表一个AI Agent组件,用户可以通过拖拽的方式组合不同的Model、Prompt、Advisor、MCP组件。 在技术实现上,使用React Flow作为基础图形引擎,自定义了各种组件节点的渲染逻辑;通过Context API管理全局动态,实时同步编排配置;使用Websocket与后端保持连接,支持实时预览和调试。 当用户完成编排后,前端会将配置信息序列化为JSON格式发送给后端,后端解析配置并动态创建对应的AI Agent实例。这种设计让非技术人员也能够轻松配置和使用AI Agent,大大降低了使用门槛。
实现多模型适配时遇到了什么挑战,如何解决的
最大的挑战就是不同AI模型的API格式和调用方式差异很大,直接适配就会导致代码的耦合度过高。因此项目中引入one-api服务作为统一的模型网关, 把所有第三方模型同义转换成OpenAI格式。在系统内部,只需要适配OpenAi的接口规范,通过apiKey和baseUrl就能接入所有的模型,使用 AiClientApiVO 和 AiClientModelVO 等值对象统一管理不同模型的配置信息。 这种设计不仅简化了开发复杂度,还提升了系统稳定性和可扩展性,当介入新模型时,只需要在one-api层配置即可。
超时处理:
- MCP客户端配置超时 :在 AiClientToolMcpNode.java 中,通过 McpClient.sync(sseClientTransport).requestTimeout(Duration.ofMinutes(aiClientToolMcpVO.getRequestTimeout())).build() 配置客户端请求超时时间
- 数据库表结构支持 : ai_client_tool_mcp 数据库表中定义了 request_timeout 字段(默认180分钟),支持灵活配置不同MCP服务的超时时间
- 数据源连接超时 :在 DataSourceConfig.java 中配置了数据库连接超时和空闲超时:
大规模并发场景下,AI Agent性能是如何保证的
从多个维度进行性能优化。首先是连接池管理,为每个模型API配置了独立的连接池,避免了相互影响;其次是异步处理,所有AI调用都是异步方式,通过ResponseBodyEmitter实现流式响应,用户可以看到AI的思考过程。 其次是缓存策略,对于相同的问题我们会缓存Ai的回答,避免重复计算;在资源调度上,我们使用自定义的线程池进行任务分发,根据任务类型和优先级进行智能调度。 项目实现了灵活可配置的线程池管理机制,通过 ThreadPoolConfig.java
和 ThreadPoolConfigProperties.java
实现。配置CallerRunsPolicy拒绝策略 :当线程池和队列都饱和时,由调用线程执行任务,避免任务丢失 此外我们还实现了熔断降级机制,当某个模型不可用时会自动切换到备用模型,保证服务的高可用性。通过这些优化,可以支持数千并发用户同时使用。
如何保证AI Agent执行过程中的数据一致性?
采用事件驱动架构来保证数据一致性,采用基于策略模式和责任链模式的执行流程管理,每个AI Agent的执行过程拆分为多个步骤,每个步骤完成后都会发布相应的事件,下一个步骤监听事件后开始执行。 在数据库层面,通过分布式事务保证跨服务的一致性,关键操作都包装在事务中执行。 缓存机制 :配置了3秒过期的Guava本地缓存(GuavaConfig.java),有效减少数据库查询压力,同时通过短过期时间保证配置变更能及时生效 通过DynamicContext类集中存储和管理执行状态,通过 executionHistory (StringBuilder)记录执行过程,为后续步骤提供上下文参考。 异步处理 :通过AsyncConfiguration配置异步执行器处理非阻塞任务,提升系统并发处理能力。 对于长时间运行的AI任务,我们实现了断点续传机制(还未实现),将执行状态持久化到数据库,即使服务重启也能从断点处继续执行。同时我们还建立了完善的监控和告警机制,实时监控AI Agent的状态, 一旦发现异常就可以进行人工干预。
运行时频繁修改配置的一致性与性能保障策略
- 同步锁机制 :
AbstractArmorySupport.java
中的 registerBean() 方法使用 synchronized 关键字保证了在多线程环境下Bean注册的原子性,避免并发修改导致的不一致问题。 - Bean覆盖机制 :当配置变更时,系统会先移除旧的Bean定义( beanFactory.removeBeanDefinition(beanName) ),再注册新的Bean定义,确保使用最新配置。
- 数据库时间戳更新 :所有配置表(如 ai_client 、 ai_client_config )都设计了 update_time 字段,使用 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 自动记录最后修改时间,便于追踪配置变更和实现版本控制。
配置变更处理流程:
- 数据库配置更新(自动更新 update_time 字段)
- 应用重新查询最新配置(通过 repository 系列方法)
- 异步并行加载相关联的配置项
- 同步移除旧的Bean定义并注册新的Bean定义
- 通过 getBean() 方法从Spring容器获取最新配置的运行时对象
AI Agent可以为公司带来了实际价值
在开发效率方面,代码评审Agent将人工评审时间从平均2小时缩短到15分钟,准确率达到95%以上;在运维效率方面,智能巡检Agent实现了7×24小时自动监控, 故障发现时间从小时级缩短到分钟级,运维人员的重复性工作减少了80%;在内容创作方面,CSDN自动发帖可以自动产出高质量技术文章, 大大提升了公司的技术影响力。更重要的是,这套系统的可编排特性让我们能够快速响应新的业务需求,从需求提出到Agent上线的周期从原来的2周缩短到2天,大大提升了业务响应速度。
如何保证AI Agent在复杂业务场景下的稳定性的
我们建立了多层次的稳定性保障机制。在架构层面,采用微服务设计,单个服务的故障不会影响整个系统; 在服务层面,实现了熔断降级机制,当某个依赖服务不可用时自动切换到备用方案;在数据层面,建立了完善的备份和恢复机制,关键数据都有多副本保护; 在监控层面,实现了全链路监控,从请求接入到响应输出的每个环节都有详细的监控指标; 在异常处理方面,我们为每种可能的异常场景都设计了对应的处理策略,包括重试、降级、人工介入等。 系统通过 RateLimiterAOP 切面实现了限流功能,能够拦截超频次请求和黑名单用户。
完善的异常处理机制;
- 全局异常捕获 :在 AiAgentController 中实现了多层级的try-catch异常捕获,确保请求处理过程中的异常能够被妥善处理并返回友好提示
- 详细日志记录 :异常发生时会记录完整的错误信息,包括错误消息和堆栈跟踪,便于问题排查
- 流式响应保障 :即使在执行异常的情况下,也能确保 ResponseBodyEmitter 正确完成,避免连接泄漏
质量监督与自我纠正
- 三步执行流程 :实现了分析-执行-监督的三步执行流程,通过 Step3QualitySupervisorNode 对执行结果进行质量评估
- 自动重试机制 :当质量检查未通过时,系统会自动触发重新执行或优化执行结果
- 执行步骤限制 :通过 maxStep 参数限制最大执行步数,防止任务无限循环
自动恢复与容错机制
- 恢复提示词生成 :在 DynamicRateLimitQueryTest 中实现了 buildRecoveryPrompt 方法,用于在分析出错时生成恢复提示词
- 执行策略调整 :当执行遇到问题时,系统能够自动调整搜索策略,如修改关键词、扩大搜索范围等
- 任务执行历史 :维护完整的执行历史记录,便于问题追溯和执行过程分析
Agent 工作流的同步/异步特性
在 AiAgentController 中,系统使用了 ThreadPoolExecutor 来异步执行 Agent 任务,避免阻塞主线程。
在 RootNode 类中,使用 CompletableFuture.supplyAsync() 实现多线程并行加载配置数据,包括AI客户端模型、工具配置、顾问配置等信息,显著提高了数据加载效率。
IAiAgentChatService 接口提供了 aiAgentChatStream 方法,通过 Flux<ChatResponse>
返回异步流式响应。
总结和优化
未来你们计划如何进一步优化和扩展这个系统?
首先是增强AI Agent的自主学习能力,通过强化学习让Agent能够从历史执行结果中学习,不断优化自己的决策策略;其次是扩展更多的业务场景, 比如客服机器人、数据分析助手、项目管理助手等;在技术架构方面,我们计划引入更先进的向量数据库和图数据库,提升知识检索的准确性和效率; 在用户体验方面,我们正在开发移动端应用,让用户能够随时随地使用AI Agent服务。同时我们也在探索多模态AI的应用,让Agent能够处理图片、音频、视频等多种类型的数据, 进一步扩展应用场景。预计在未来一年内,我们的AI Agent系统将覆盖公司80%以上的业务流程。
在设计通用对话分析模型时,你们是如何处理问题分析、自主规划、精准执行这个循环的?
设计了一个四阶段的执行循环:问题分析、自主规划、精准执行、内容判罚。在问题分析阶段:AI Agent首先理解用户意图,提取关键信息和上下文;在自主规划阶段;根据提取的信息、可用的工具和知识库指定执行计划, 选择最优的执行路径;在精准执行阶段:根据制定的执行计划选择相应的MCP工具和Advisor,获取执行结果;内容判罚:AI Agent评估执行结果是否满足用户需求,如果不满足就重新进入规划阶段。 整个过程中,我们为每个阶段配置不同的Model、Prompt、CP组合,比如规划阶段使用逻辑推理能力强的模型,执行阶段使用工具调能力强的模型。
循环控制机制主要在 Step3QualitySupervisorNode 中实现,通过检查dynamicContext中的isCompleted标志和step/maxStep关系确定是否继续循环。
项目中的自主规划体现在两个方面:
- 策略生成规划 :在Step1AnalyzerNode中,AI模型通过分析当前状态和历史记录,自动生成下一步执行策略
- 执行调整规划 :在Step3QualitySupervisorNode中,根据执行结果的质量评估,动态调整后续执行方向
当同时有 1000 个用户构建定制化 AI Agent 时,系统的瓶颈可能在哪里?你做了哪些优化(比如缓存、异步处理)?
已实现的优化措施
- 可配置线程池
- 通过ThreadPoolConfigProperties配置类实现线程池参数可外部化配置
- 支持多种拒绝策略配置(AbortPolicy/DiscardPolicy/DiscardOldestPolicy/CallerRunsPolicy)
- 配置了较大的阻塞队列(5000)以应对突发流量
- 异步数据处理
- 在AiClientLoadDataStrategy中使用CompletableFuture实现多任务并行加载
- 通过supplyAsync方法异步执行数据库查询操作
- 使用allOf和join方法等待所有异步任务完成
- 异步日志处理
- logback配置了异步日志Appender(ASYNC_FILE_INFO/ASYNC_FILE_ERROR)
- 设置neverBlock=true避免日志记录阻塞业务线程
- 配置合适的队列大小(8192/1024)平衡性能和日志完整性
- 连接池优化配置
- 使用高性能的HikariCP作为数据库连接池
- 配置了合理的连接参数(minimum-idle=15, idle-timeout=180000)
- 设置了连接验证查询(SELECT 1)确保连接有效性
- 限流机制
- 实现了基于AOP的限流拦截器(RateLimiterAOP)
- 支持黑名单拦截(24h)和超频次拦截两种限流策略
- 通过ELK Stack记录和监控限流事件 系统架构优化建议
- 前端缓存机制:前端实现了 localStorage 本地存储工具函数,用于缓存部分数据
潜在系统瓶颈分析
- 数据库连接资源限制
- MySQL连接池最大连接数仅配置为25,在1000用户并发场景下会迅速耗尽连接资源,导致大量连接等待和超时
- 系统同时连接MySQL和PostgreSQL(pgvector)两个数据库,进一步分散了有限的连接资源
- 缺少连接池监控和动态调整机制
- 线程池容量限制
- 核心线程数20,最大线程数50,队列容量5000的配置在高并发场景下会导致任务积压
- 使用CallerRunsPolicy拒绝策略虽能避免任务丢失,但会导致调用线程阻塞
- 未针对不同类型任务(IO密集型/CPU密集型)进行线程池隔离
- AI模型调用瓶颈
- 系统依赖OpenAI等外部API,这些API通常有严格的速率限制(QPS/TPS)
- 缺乏API调用的统一网关和熔断限流机制
- 未实现模型调用结果缓存,导致重复请求
- 内存管理挑战
- MessageWindowChatMemory采用内存存储上下文,每个用户构建Agent时会创建独立的上下文对象
- 1000用户并发可能导致JVM内存占用过高,触发频繁GC或OOM风险
- 未实现上下文数据的持久化存储和共享机制
- 缓存机制不完善
- 未发现系统使用Redis等分布式缓存组件
- 缺乏热点数据缓存策略,所有配置数据都直接从数据库读取
- 未实现查询结果缓存,导致重复查询 已实施的优化措施
优化策略
- 增加数据库连接池大小,特别是MySQL和PGVector的最大连接数
- 实现Redis缓存机制,缓存频繁查询的配置数据和热点知识库内容
- 增加线程池容量,根据实际负载调整核心线程数和最大线程数
- 并发处理优化
- 实现线程池隔离,为不同类型任务(Agent构建/模型调用/数据加载)配置独立线程池
- 引入响应式编程模型(如Spring WebFlux)处理高并发请求
- 实现请求队列管理和优先级调度
- AI服务优化
- 实现AI模型调用结果缓存,避免短时间内重复请求
- 引入API网关统一管理外部API调用,实现熔断、限流和重试机制
- 考虑多模型提供商配置,实现故障转移
- 考虑引入消息队列(如RabbitMQ或Kafka)处理异步任务,提高系统吞吐量
- 缓存策略完善
- 引入Redis作为分布式缓存,缓存Agent配置、提示词模板等热点数据
- 实现两级缓存策略(本地缓存+分布式缓存)
- 配置合理的缓存过期时间和刷新策略
这个平台听起来模块很多,你在落地时遇到的最大难点是什么?是架构抽象、数据库驱动、还是外部 API 集成?
经过对项目代码和架构的深入分析,我发现这个AI Agent平台在落地时遇到的主要难点集中在以下几个方面:
- 架构抽象与复杂性挑战:系统采用DDD架构设计,核心基于责任链模式(AbstractMultiThreadStrategyRouter)实现, 通过DefaultArmoryStrategyFactory工厂类和一系列节点(RootNode、AiClientModelNode等)构建智能体。 项目采用了高度复杂的树状策略路由模式(StrategyHandler和AbstractMultiThreadStrategyRouter), 通过RootNode、AiClientToolMcpNode、AiClientModelNode等多层级节点构建执行流程。这种设计虽然提供了良好的扩展性,但也增加了系统的理解难度和调试复杂度。 特别是动态Bean注册机制(通过registerBean方法)和上下文传递(DynamicContext)需要精确的设计和管理,这是架构层面的主要挑战。
- 外部API集成与MCP调用的复杂性:从代码和优化文档可以看出,MCP(Model Context Protocol)工具集成是项目的核心难点。
- 多传输协议支持 :系统需要同时支持SSE(Server-Sent Events)和Stdio两种传输方式,增加了集成复杂度
- 超时处理与错误管理 :MCP调用的超时配置(默认180分钟)和错误处理机制需要精细控制
- 动态服务发现 :需要根据配置动态创建和管理外部服务连接,在AiClientToolMcpNode中需要根据配置动态创建McpSyncClient实例
- 系统配置与动态性管理:项目大量依赖数据库配置(ai_client_tool_mcp、ai_client_config等表)来动态构建AI Agent实例,这种设计带来了配置管理的复杂性:
- 配置项之间存在复杂的依赖关系
- 需要在运行时动态加载和应用配置
- 配置变更后的热更新机制
- 配置验证和错误处理
- Prompt工程与AI模型调用优化: 根据ai-agent-prompt-optimization.md文档,系统在Prompt设计和执行逻辑上存在明显脱节:
- 各执行节点(Step1-Step4)的Prompt配置没有与实际的MCP工具调用逻辑对应
- 缺乏对AI模型输出格式的严格约束和验证
- 需要平衡Prompt的指令明确性与模型的灵活性
解决方案与优化方向
- 在各执行节点中添加实际的MCP工具调用逻辑
- 优化Prompt配置,强制要求调用指定的MCP工具获取真实数据
- 增加质量监督机制,验证MCP调用的成功性和数据完整性
- 基于真实数据生成专业的分析报告
综合来看,这个项目在落地过程中面临的最大挑战是 外部API集成(特别是MCP工具调用)与系统架构抽象的复杂性 ,这两个方面相互交织, 需要精确的设计和严格的实现来确保系统的稳定运行和功能完整性。
是否考虑过向量数据库的冷热分层存储?如何优化历史数据查询性能?
项目中未实现冷热分层存储,可以考虑以下实方案
- 物理分层存储实现:创建热数据分区表;创建冷数据分区表:为热数据创建索引。
- 实现定时任务,将访问频率低的历史数据从热数据表迁移到冷数据表;基于 metadata 中的访问时间戳或访问计数进行判断;迁移过程中保持数据一致性
历史数据查询性能优化建议
- 向量索引优化:为热数据创建IVFFlat或HNSW索引以加速向量检索;根据数据量调整索引参数。
- 多级缓存策略:实现基于Redis的热点向量缓存;对高频访问的历史数据建立本地缓存;增加检索结果缓存层。
如果让你现在扩展一个新的场景(比如 Jenkins 流水线自动化运维),你会如何把它接入现有的 MCP + Agent 平台?
- 开发Jenkins MCP服务,选择SSE传输方式
- 向 ai_client_tool_mcp 表插入Jenkins MCP配置(也可直接在管理页面新增MCP模块)
- 通过 ai_client_config 表关联AI客户端与Jenkins MCP工具;确保配置状态为启用(也可直接在管理页面关联)
- 在Agent执行策略中添加工具调用:在步骤执行节点(如Step2PrecisionExecutorNode)中添加Jenkins工具调用逻辑
- 在 getMcpToolsCapabilities 方法中添加Jenkins工具的分析逻辑;确保系统能正确识别Jenkins工具的功能和适用场景
- 根据Jenkins操作特点调整执行步骤的粒度和依赖关系;对于长时间运行的流水线任务,添加状态轮询和超时处
日志链路追踪设计与ELK可视化分析方案
日志链路追踪设计方案基础架构设计 +------------------+ +------------------+ +------------------+ | 服务A | ---> | 服务B | ---> | 服务C | | TraceID生成/传递 | | TraceID传递 | | TraceID传递 | +------------------+ +------------------+ +------------------+ | | | v v v +-----------------------------------------------------------+ | ELK Stack | | Elasticsearch <--- Logstash <--- Filebeat/Logback Appender| +-----------------------------------------------------------+ 使用TraceID贯穿多服务调用
Agent执行结果问题定位指南
- 开启详细日志记录:首先,通过修改 logback-spring.xml 配置,开启DEBUG级别日志
- 使用调试Advisor组件:通过实现自定义的调试Advisor(参考测试代码中的 LoggingAdvisor ),拦截和记录执行过程:
- 验证Prompt设计问题的方法:要确认是否是Prompt设计问题,可以采用以下策略:
- 控制变量法 :固定知识库内容,只修改Prompt设计,观察结果变化
- 简化Prompt测试 :逐步简化Prompt到最基础形式,看是否能得到正确结果
- 直接测试底层模型 :绕过Agent框架,直接使用底层模型API测试相同Prompt
- 验证知识库缺陷的方法:知识库问题可通过以下步骤验证:
- 检查检索结果 :分析RagAnswerAdvisor检索到的文档是否相关和全面
- 独立验证检索功能 :直接调用VectorStore进行相似度搜索测试
- 检查文档质量 :验证知识库中文档的完整性、准确性和相关性
- 综合分析与定位:通过对比测试结果,结合日志信息,可以综合判断问题根源