尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

服务框架源码分析,上下文和工具如何分工

服务框架源码分析,上下文和工具如何分工 服务框架源码分析上下文和工具如何分工RAG 服务把检索结果、上下文和工具调用放进一次请求前先要厘清数据由谁负责、在哪一层限额、失败时怎样表达。本文以 Spring Boot 的接口边界为线索不把演练现象当作生产结论。拆开日志记录的 JSON 请求体一看里面包含了整套系统 32 个 Tool Calling 的 JSON Schema 定义加上前 15 轮没有过期的完整对话历史。大模型光是首字 Prefill 阶段就耗费了 4.8 秒。在 Spring Boot 中集成大模型能力时很多开发团队习惯把上下文Context和工具Tool/Function混在一起一股脑往 Prompt 里塞。上下文是给模型提供“状态与事实”的而工具是给模型提供“动作与能力”的。一旦两者的分工界限模糊不仅会让 Token 开销呈指数级暴涨更会导致模型因工具过多产生 Hallucination幻觉调用。# 过滤日志中单次 Tool Calling 相关的 Token 消耗量与耗时数据 grep -E TOKEN_USAGE|TOOL_INVOKE_TIME /var/log/spring-boot-ai.log | tail -n 20 # 使用 curl 测试 Agent 路由接口打印出响应头部与 HTTP 状态码 curl -i -X POST http://localhost:8080/v1/ai/context-orchestrate \ -H Content-Type: application/json \ -d {session_id:sess_99381,user_input:查询用户 10086 的未支付订单并发送催缴短信}动态上下文编排与工具分发流图要在 Spring Boot 内部优雅实现上下文与工具的分工必须在 Controller / Service 层之前增加一道上下文与工具编排器Orchestration Layer。整体流转的核心逻辑上下文Context做减法通过滑动窗口与滚动摘要机制将膨胀的对话历史严格压制在 Token 预算限制线以内。工具Tools做按需加载绝不注册全局全量 Tools而是通过意图路由Intent Matcher扫描当前 Request 依赖的最小工具子集实现毫秒级的工具注入。生产级上下文编排与工具分工器源码实现下面基于 Spring Boot 实现一套带 Token 预算控制与 Tool 按需过滤的编排器。package com.company.ai.boot.orchestrator; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Service public class AgentContextOrchestrator { private static final Logger log LoggerFactory.getLogger(AgentContextOrchestrator.class); private static final int MAX_CONTEXT_TOKENS 3000; private final MapString, ToolDefinition globalToolRegistry new ConcurrentHashMap(); private final ObjectMapper objectMapper; public AgentContextOrchestrator(ObjectMapper objectMapper) { this.objectMapper objectMapper; registerDefaultTools(); } private void registerDefaultTools() { globalToolRegistry.put(queryOrder, new ToolDefinition(queryOrder, 查询用户订单, {\type\:\object\,\properties\:{\user_id\:{\type\:\string\}}})); globalToolRegistry.put(sendSms, new ToolDefinition(sendSms, 发送短信通知, {\type\:\object\,\properties\:{\phone\:{\type\:\string\},\msg\:{\type\:\string\}}})); globalToolRegistry.put(queryStock, new ToolDefinition(queryStock, 查询商品库存, {\type\:\object\,\properties\:{\sku_id\:{\type\:\string\}}})); } public ExecutionPayload orchestrate(String userInput, ListChatMessage historyMessages) { log.info(开始编排上下文与工具当前历史消息条数: {}, historyMessages.size()); // 1. 上下文按 Token 预算从后往前截断Pruning ListChatMessage prunedHistory pruneContext(historyMessages, MAX_CONTEXT_TOKENS); // 2. 根据用户输入做工具意图路由Scoped Tools Loading ListToolDefinition matchedTools resolveRelevantTools(userInput); log.info(编排完成: 保留历史消息 {} 条, 注入工具 {} 个, prunedHistory.size(), matchedTools.size()); return new ExecutionPayload(userInput, prunedHistory, matchedTools); } private ListChatMessage pruneContext(ListChatMessage history, int maxTokenBudget) { ListChatMessage result new ArrayList(); int accumulatedTokens 0; // 从最近的消息开始倒序累加 for (int i history.size() - 1; i 0; i--) { ChatMessage msg history.get(i); int estimatedToken estimateToken(msg.content()); if (accumulatedTokens estimatedToken maxTokenBudget) { log.warn(触发 Token 预算墙截断第 0 到第 {} 条早期历史, i); break; } accumulatedTokens estimatedToken; result.add(0, msg); // 保持时间正序 } return result; } private ListToolDefinition resolveRelevantTools(String userInput) { ListToolDefinition selected new ArrayList(); // 简单意图路由规则生产环境可换为 Fast Embeddings 相似度匹配 if (userInput.contains(订单) || userInput.contains(买)) { selected.add(globalToolRegistry.get(queryOrder)); } if (userInput.contains(短信) || userInput.contains(通知)) { selected.add(globalToolRegistry.get(sendSms)); } if (userInput.contains(库存) || userInput.contains(货)) { selected.add(globalToolRegistry.get(queryStock)); } // 如果均未匹配只给默认查询类工具决不全量注入 if (selected.isEmpty()) { selected.add(globalToolRegistry.get(queryOrder)); } return selected; } private int estimateToken(String text) { if (text null || text.isBlank()) return 0; // 中文按 1 字符 ≈ 0.6 Token 粗略换算 return (int) (text.length() * 0.6); } public record ChatMessage(String role, String content) {} public record ToolDefinition(String name, String description, String jsonSchema) {} public record ExecutionPayload(String prompt, ListChatMessage history, ListToolDefinition tools) {} }上下文与工具接口契约设计规范在 Spring Boot Controller 提供给前端或下游微服务时错误语义与契约必须极其清晰。1. 明确区别“业务逻辑失败”与“Tool Calling 失败”模型调用工具失败时如queryOrder返回用户不存在这属于Tool Business Failure应当将错误信息原样作为 Tool Message 返回给 LLM 重新生成回答而不是在 HTTP 层直接抛出500 Internal Server Error。2. 状态隔离与错误语义定义在 RESTful API 返回时建议采用三级状态码区分问题属性{ code: TOOL_EXECUTION_TIMEOUT, message: 下游订单微服务响应超时1500ms, error_type: ENGINEERING_RETRYABLE, detail: { target_tool: queryOrder, elapsed_ms: 1502 } }MODEL_SCHEMA_INVALID模型生成的工具参数无法通过 JSON Validation网关可自动重试一次。ENGINEERING_RETRYABLE工具执行抛出网络超时重试闸门接管。CONTEXT_WINDOW_EXCEEDED对话历史超长强行触发压缩。分清了上下文的“记忆”角色与工具的“手脚”角色Spring Boot 应用在大模型交互中才能保持高吞吐和低耗时。
返回列表