AI项目博客
记录 AI 工程实践与大模型应用

Spring AI Alibaba 32|上下文工程 · 模型上下文(Model Context):系统提示(System Prompt)

构建 Agent 的难点在于使其足够可靠、效果足够好。虽然我们可以很容易写一个 Agent 示例,但要做一个能在生产环境中稳定使用、能解决实际问题的 Agent 并不容易。

为什么 Agent 会失败?

当 Agent 失败时,通常是因为 Agent 内部的 LLM 调用采取了错误的操作或者没有按我们预期的执行。LLM 失败的原因有两个:

  1. 底层 LLM 能力不足
  2. 没有向 LLM 传递”正确”的上下文

大多数情况下 —— 实际上是第二个原因导致 Agent 不可靠。

上下文工程是以正确的格式提供正确的信息和工具,使 LLM 能够完成任务。这是 AI 工程师的首要工作。缺乏”正确”的上下文是更可靠 Agent 的头号障碍,Spring AI Alibaba 的 Agent 抽象专门设计用于优化上下文工程。

Agent 循环

典型的 Agent 循环由两个主要步骤组成:

  1. 模型调用 – 使用提示和可用工具调用 LLM,返回响应或执行工具的请求
  2. 工具执行 – 执行 LLM 请求的工具,返回工具结果

reactagent

此循环持续进行,直到 LLM 决定任务完成并退出。

你可以控制什么

要构建可靠的 Agent,你需要控制 Agent 循环每个步骤发生的事情,以及步骤之间发生的事情。

上下文类型 你控制的内容 瞬态或持久
模型上下文 模型调用中包含什么(指令、消息历史、工具、响应格式) 瞬态
工具上下文 工具可以访问和产生什么(对状态、存储、运行时上下文的读/写) 持久
生命周期上下文 模型和工具调用之间发生什么(摘要、防护栏、日志等) 持久

瞬态上下文。LLM 在单次调用中看到的内容。你可以修改消息、工具或提示,而不改变状态中保存的内容。
持久上下文。跨轮次保存在状态中的内容。生命周期钩子和工具写入会永久修改它。

数据源

在整个过程中,你的 Agent 访问(读/写)不同的数据源:

数据源 别名 范围 示例
运行时上下文 静态配置 会话范围 用户 ID、API 密钥、数据库连接、权限、环境设置
状态(State) 短期记忆 会话范围 当前消息、上传的文件、认证状态、工具结果
存储(Store) 长期记忆 跨会话 用户偏好、提取的见解、记忆、历史数据

工作原理

在 Spring AI Alibaba 中,Hook和Interceptor是实现上下文工程的机制。

它们允许你挂接到 Agent 生命周期的任何步骤并:

  • 更新上下文
  • 跳转到 Agent 生命周期的不同步骤

在本指南中,你将看到频繁使用 Hook 和 Interceptor API 作为上下文工程的手段。

控制每次模型调用中包含的内容——指令、可用工具、使用哪个模型以及输出格式。这些决策直接影响可靠性和成本。

所有这些类型的模型上下文都可以从状态(短期记忆)、存储(长期记忆)或运行时上下文(静态配置)中获取。

系统提示设置 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) {
      // 相当于是从 RunnableConfig 中读取提取用户ID,所以agent调用时要设置 user-id
      return request.getContext().get("user-id"); // 简化示例
  }

  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";
  }
}

控制发送给 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 文档。

动态控制 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();

根据任务复杂度或用户偏好动态选择模型。

class DynamicModelInterceptor extends ModelInterceptor {
  private final ChatModel simpleModel;
  private final ChatModel complexModel;

  public DynamicModelInterceptor(ChatModel simpleModel, ChatModel complexModel) {
      this.simpleModel = simpleModel;
      this.complexModel = complexModel;
  }

  @Override
  public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
      // 分析任务复杂度
      boolean isComplexTask = analyzeComplexity(request.getMessages());

      // 选择合适的模型
      ChatModel selectedModel = isComplexTask ? complexModel : simpleModel;

      // 注意:在实际实现中,你可能需要在Agent级别切换模型
      // 这里展示的是概念性示例

      return handler.call(request);
  }

  private boolean analyzeComplexity(List<Message> messages) {
      // 实现复杂度分析逻辑
      // 例如:检查消息长度、关键词等
      return messages.size() > 5;
  }

  @Override
  public String getName() {
      return "DynamicModelInterceptor";
  }
}

本文整理自 Spring AI Alibaba 官方文档《上下文工程》:https://java2ai.com/docs/frameworks/agent-framework/advanced/context-engineering

赞(0)
未经允许不得转载:AI项目博客 » Spring AI Alibaba 32|上下文工程 · 模型上下文(Model Context):系统提示(System Prompt)

评论 抢沙发

AI项目博客

专注 AI 工程化落地 —— 覆盖智能体、大模型、Java AI 与 Python 的学习笔记,以及 AI 平台的项目实战记录。

关于本站文章归档

登录

找回密码

注册