上下文工程(Context Engineering)
概述
构建 Agent 的难点在于使其足够可靠、效果足够好。虽然我们可以很容易写一个 Agent 示例,但要做一个能在生产环境中稳定使用、能解决实际问题的 Agent 并不容易。
为什么 Agent 会失败?
当 Agent 失败时,通常是因为 Agent 内部的 LLM 调用采取了错误的操作或者没有按我们预期的执行。LLM 失败的原因有两个:
- 底层 LLM 能力不足
- 没有向 LLM 传递"正确"的上下文
大多数情况下 —— 实际上是第二个原因导致 Agent 不可靠。
上下文工程是以正确的格式提供正确的信息和工具,使 LLM 能够完成任务。这是 AI 工程师的首要工作。缺乏"正确"的上下文是更可靠 Agent 的头号障碍,Spring AI Alibaba 的 Agent 抽象专门设计用于优化上下文工程。
Agent 循环
典型的 Agent 循环由两个主要步骤组成:
- 模型调用 - 使用提示和可用工具调用 LLM,返回响应或执行工具的请求
- 工具执行 - 执行 LLM 请求的工具,返回工具结果

此循环持续进行,直到 LLM 决定任务完成并退出。
你可以控制什么
要构建可靠的 Agent,你需要控制 Agent 循环每个步骤发生的事情,以及步骤之间发生的事情。
| 上下文类型 | 你控制的内容 | 瞬态或持久 |
|---|---|---|
| 模型上下文 | 模型调用中包含什么(指令、消息历史、工具、响应格式) | 瞬态 |
| 工具上下文 | 工具可以访问和产生什么(对状态、存储、运行时上下文的读/写) | 持久 |
| 生命周期上下文 | 模型和工具调用之间发生什么(摘要、防护栏、日志等) | 持久 |
- 瞬态上下文。LLM 在单次调用中看到的内容。你可以修改消息、工具或提示,而不改变状态中保存的内容。
- 持久上下文。跨轮次保存在状态中的内容。生命周期钩子和工具写入会永久修改它。
数据源
在整个过程中,你的 Agent 访问(读/写)不同的数据源:
| 数据源 | 别名 | 范围 | 示例 |
|---|---|---|---|
| 运行时上下文 | 静态配置 | 会话范围 | 用户 ID、API 密钥、数据库连接、权限、环境设置 |
| 状态(State) | 短期记忆 | 会话范围 | 当前消息、上传的文件、认证状态、工具结果 |
| 存储(Store) | 长期记忆 | 跨会话 | 用户偏好、提取的见解、记忆、历史数据 |
工作原理
在 Spring AI Alibaba 中,Hook和Interceptor是实现上下文工程的机制。
它们允许你挂接到 Agent 生命周期的任何步骤并:
- 更新上下文
- 跳转到 Agent 生命周期的不同步骤
在本指南中,你将看到频繁使用 Hook 和 Interceptor API 作为上下文工程的手段。
模型上下文(Model Context)
控制每次模型调用中包含的内容——指令、可用工具、使用哪个模型以及输出格式。这些决策直接影响可靠性和成本。
所有这些类型的模型上下文都可以从状态(短期记忆)、存储(长期记忆)或运行时上下文(静态配置)中获取。
系统提示(System Prompt)
系统提示设置 LLM 的行为和能力。不同的用户、上下文或对话阶段需要不同的指令。成功的 Agent 利用记忆、偏好和配置为对话的当前状态提供正确的指令。
基于状态的动态提示
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelInterceptor;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelRequest;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelResponse;
import org.springframework.ai.chat.messages.Message;
// 创建一个模型拦截器,根据对话长度调整系统提示
class StateAwarePromptInterceptor extends ModelInterceptor {
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
List<Message> messages = request.getMessages();
int messageCount = messages.size();
// 基础提示
String basePrompt = "你是一个有用的助手。";
// 根据消息数量调整提示
if (messageCount > 10) {
basePrompt += "
这是一个长对话 - 请尽量保持精准简捷。";
}
// 更新系统消息(参考 TodoListInterceptor 的实现方式)
SystemMessage enhancedSystemMessage;
if (request.getSystemMessage() == null) {
enhancedSystemMessage = new SystemMessage(basePrompt);
} else {
enhancedSystemMessage = new SystemMessage(
request.getSystemMessage().getText() + "
" + basePrompt
);
}
// 创建增强的请求
ModelRequest enhancedRequest = ModelRequest.builder(request)
.systemMessage(enhancedSystemMessage)
.build();
// 调用处理器
return handler.call(enhancedRequest);
}
@Override
public String getName() {
return "StateAwarePromptInterceptor";
}
}
// 使用拦截器创建Agent
ReactAgent agent = ReactAgent.builder()
.name("context_aware_agent")
.model(chatModel)
.interceptors(new StateAwarePromptInterceptor())
.build();
基于存储的个性化提示
// 从长期记忆加载用户偏好
class PersonalizedPromptInterceptor extends ModelInterceptor {
private final UserPreferenceStore store;
public PersonalizedPromptInterceptor(UserPreferenceStore store) {
this.store = store;
}
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
// 从运行时上下文获取用户ID
String userId = getUserIdFromContext(request);
// 从存储加载用户偏好
UserPreferences prefs = store.getPreferences(userId);
// 构建个性化提示
String personalizedPrompt = buildPersonalizedPrompt(prefs);
// 更新系统消息(参考 TodoListInterceptor 的实现方式)
SystemMessage enhancedSystemMessage;
if (request.getSystemMessage() == null) {
enhancedSystemMessage = new SystemMessage(personalizedPrompt);
} else {
enhancedSystemMessage = new SystemMessage(
request.getSystemMessage().getText() + "
" + personalizedPrompt
);
}
// 创建增强的请求
ModelRequest enhancedRequest = ModelRequest.builder(request)
.systemMessage(enhancedSystemMessage)
.build();
// 调用处理器
return handler.call(enhancedRequest);
}
private String getUserIdFromContext(ModelRequest request) {
// 从请求上下文提取用户ID
return "user_001"; // 简化示例
}
private String buildPersonalizedPrompt(UserPreferences prefs) {
StringBuilder prompt = new StringBuilder("你是一个有用的助手。");
if (prefs.getCommunicationStyle() != null) {
prompt.append("
沟通风格:").append(prefs.getCommunicationStyle());
}
if (prefs.getLanguage() != null) {
prompt.append("
使用语言:").append(prefs.getLanguage());
}
if (!prefs.getInterests().isEmpty()) {
prompt.append("
用户兴趣:").append(String.join(", ", prefs.getInterests()));
}
return prompt.toString();
}
@Override
public String getName() {
return "PersonalizedPromptInterceptor";
}
}
消息历史(Messages)
控制发送给 LLM 的消息列表。你可以:
- 过滤或修改消息
- 添加上下文或摘要
- 压缩长对话
消息过滤
class MessageFilterInterceptor extends ModelInterceptor {
private final int maxMessages;
public MessageFilterInterceptor(int maxMessages) {
this.maxMessages = maxMessages;
}
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
List<Message> messages = request.getMessages();
// 只保留最近的N条消息
if (messages.size() > maxMessages) {
List<Message> filtered = new ArrayList<>();
// 添加系统消息
messages.stream()
.filter(m -> m instanceof SystemMessage)
.findFirst()
.ifPresent(filtered::add);
// 添加最近的消息
int startIndex = Math.max(0, messages.size() - maxMessages + 1);
filtered.addAll(messages.subList(startIndex, messages.size()));
messages = filtered;
}
ModelRequest enhancedRequest = ModelRequest.builder(request)
.messages(messages)
.build();
return handler.call(enhancedRequest);
}
@Override
public String getName() {
return "MessageFilterInterceptor";
}
}
瞬时消息更新 VS 持久消息更新
- 上述示例使用
ModelInterceptor来实现临时更新 —— 修改单次调用时发送给模型的消息内容,而不会改变状态中保存的数据。- 对于需要持久更新状态的情况(例如生命周期上下文中的摘要示例),请使用如 ModelHook 等生命周期钩子来永久更新对话历史。更多详情请参阅 Hook & Interceptor 文档。
工具(Tools)
动态控制 Agent 可以访问哪些工具。
基于上下文的工具选择
class ContextualToolInterceptor extends ModelInterceptor {
private final Map<String, List<ToolCallback>> roleBasedTools;
public ContextualToolInterceptor(Map<String, List<ToolCallback>> roleBasedTools) {
this.roleBasedTools = roleBasedTools;
}
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
// 从上下文获取用户角色
String userRole = getUserRole(request);
// 根据角色选择工具
List<ToolCallback> allowedTools = roleBasedTools.getOrDefault(
userRole,
Collections.emptyList()
);
// 更新工具选项(注:实际实现需要根据框架API调整)
// 这里展示概念性代码
System.out.println("为角色 " + userRole + " 选择了 " + allowedTools.size() + " 个工具");
return handler.call(request);
}
private String getUserRole(ModelRequest request) {
// 从请求上下文提取用户角色
return "user"; // 简化示例
}
@Override
public String getName() {
return "ContextualToolInterceptor";
}
}
// 配置基于角色的工具
Map<String, List<ToolCallback>> roleTools = Map.of(
"admin", List.of(readTool, writeTool, deleteTool),
"user", List.of(readTool),
"guest", List.of()
);
ReactAgent agent = ReactAgent.builder()
.name("role_based_agent")
.model(chatModel)
.interceptors(new ContextualToolInterceptor(roleTools))
.build();