共计 3098 个字符,预计需要花费 8 分钟才能阅读完成。
背景与需求
在构建智能代理服务时,开发者常面临三个核心挑战:

- 技能动态发现:传统微服务架构中,服务间调用需要预先知道端点信息,而 AI Agent 需要运行时动态发现可用技能
- 会话状态维护:多轮对话中需要保持上下文一致性,包括用户意图、实体参数和历史交互记录
- 异常处理复杂度:既要处理业务逻辑异常,又要维护友好的对话交互体验
核心架构
Spring AI Agent 采用三层架构设计:
@startuml
component "Skill Registry" as registry
component "Message Broker" as broker
component "Context Manager" as context
registry -[hidden]-> broker
broker -[hidden]-> context
agent "User Agent" as user
user -> broker : 发送消息
broker -> registry : 查询可用技能
registry --> broker : 返回技能列表
broker -> context : 获取对话上下文
context --> broker : 返回上下文
broker -> skill : 路由消息
skill --> broker : 返回响应
broker -> user : 返回结果
@enduml
- Skill Registry:通过
@Skill注解自动注册服务,维护技能元数据(名称、描述、参数规格) - Message Broker:基于内容类型和上下文进行智能路由,支持同步 / 异步消息模式
- Context Manager:采用分层存储设计,包含会话级、用户级和全局级上下文
实战:天气查询 Skill
@Skill(
name = "weatherQuery",
description = "提供城市天气查询功能",
parameters = {@Param(name = "city", type = String.class, required = true),
@Param(name = "date", type = LocalDate.class, defaultValue = "today")
}
)
public class WeatherSkill {
private final WeatherClient client;
private final ContextManager context;
// 自动注入上下文管理器
public WeatherSkill(WeatherClient client, ContextManager context) {
this.client = client;
this.context = context;
}
@MessageHandler
public Message handleRequest(Message request) {
// 从上下文中获取用户偏好(如温度单位)UserPreference pref = context.get("userPref", UserPreference.class)
.orElse(UserPreference.DEFAULT);
// 参数校验与净化
String city = sanitize(request.getParameter("city"));
LocalDate date = parseDate(request.getParameter("date"));
// 调用外部 API
WeatherData data = client.query(city, date);
// 构建自然语言响应
return Message.builder()
.content(formatResponse(data, pref))
.contextKey("lastQuery") // 设置上下文键
.contextValue(data) // 存储查询结果
.build();}
private String sanitize(String input) {return input.replaceAll("[^\\p{L}\\p{N}\\s-]", "");
}
}
关键实现要点:
- @Skill 注解:声明技能元数据,支持 OpenAPI 规范描述
- 上下文存取 :通过
ContextManager实现跨对话的状态保持 - 输入净化:防止 XSS 攻击的基本处理
生产环境建议
性能优化
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager skillMetadataCache() {return new CaffeineCacheManager("skillMetadata") {
@Override
protected Cache<Object, Object> createNativeCache(String name) {return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();}
};
}
}
安全增强
@Aspect
@Component
public class SecurityAspect {@Around("@annotation(org.springframework.ai.agent.skill.MessageHandler)")
public Object validateInput(ProceedingJoinPoint joinPoint) throws Throwable {Message message = (Message) joinPoint.getArgs()[0];
// 参数白名单校验
if (containsMaliciousPattern(message.getContent())) {throw new InvalidInputException("检测到可疑输入");
}
return joinPoint.proceed();}
}
常见陷阱
- 状态管理误区:
- 错误做法:在 Skill 类中使用成员变量保存状态
-
正确方案:所有会话状态应通过 ContextManager 存储
-
异步处理:
- 必须维护
correlationId保证请求 - 响应匹配 - 使用 Spring 的
@Async时要配置异常处理器
@Async
@MessageHandler
public CompletableFuture<Message> asyncHandler(Message msg) {
return CompletableFuture
.supplyAsync(() -> process(msg))
.exceptionally(ex -> fallbackMessage(ex));
}
进阶思考
跨技能上下文共享
考虑实现方案:
1. 定义共享上下文命名空间(如shared:userPrefs)
2. 使用发布 - 订阅模式广播关键事件
3. 采用 CRDT 数据结构解决冲突
扩展练习
尝试实现计时器 Skill:
1. 支持设置倒计时(如 ” 设置 5 分钟计时 ”)
2. 到期后发送通知消息
3. 使用 Spring 的ScheduledAnnotationBeanPostProcessor
@Skill(name = "timer")
public class TimerSkill {
@MessageHandler
public Message startTimer(Message request) {
// 解析时间参数
// 创建定时任务
// 返回确认消息
}
}
总结
通过本文的实践,我们实现了:
1. 符合 Spring 风格的 Skill 开发模式
2. 生产可用的安全防护措施
3. 可扩展的上下文管理方案
建议在实际项目中:
– 使用 Jaeger 等工具进行调用链跟踪
– 对高频技能实施限流保护
– 定期审计上下文存储的内容
正文完
