资讯动态

大模型不懂业务?用业务上下文工程与Spring Boot构建智能工单助手

发布时间:2026/8/28 1:27:16 来源:尧图企业网站定制
先问大家一个很实际的问题你的项目接入大模型之后是不是经常出现“回答很流畅但完全不懂业务”的情况模型可能知道苏轼是北宋诗人却不知道你公司里“VIP客户超时赔付”的具体规则它能把代码写得像模像样却说不出当前工单对应的客户等级和历史订单。问题往往不在模型能力而在于我们根本没有把业务上下文交给它。这也是我最近在关注 Odyssey Framework 的原因。它的核心理念用一句话概括就是给 AI 它真正需要的业务上下文。这篇文章我想完整梳理一下这套框架解决什么问题、底层原理是什么并用一个 Spring Boot 的典型示例带你走一遍“从业务数据到模型输入”的最小实现思路。不管你是正在做 AI 应用开发的工程师还是想把大模型真正落地到内部系统里的后端开发只要是遇到“模型不懂业务、回答不专业、不敢放上线”这类问题这篇文章都值得花几分钟读完。1. 为什么AI需要业务上下文1.1 通用大模型的两个盲区大模型的训练语料来自公开互联网、书籍、开源代码等它能回答大量通用问题但天然缺少两部分信息。第一是私有业务数据。你的客户表、订单表、工单记录、售后政策、内部知识库这些数据根本不会出现在模型训练集里。模型不知道某个指定客户是谁不知道他买了什么也不知道他现在的订单是什么状态。第二是实时状态。模型训练完成后是静态的它不知道今天的库存、当前的活动规则、这个时刻的并发量。即使模型了解某个行业它也无法感知你系统的当下情况。举个例子。你让通用模型写一段“客户催单”的回复它写出来的文字可能很得体但因为没有看到客户等级、订单延迟原因、赔付政策所以要么含糊其辞要么直接给出不准确的承诺。这类问题不是提示词“写长一点”能解决的。1.2 从“塞提示词”到“上下文工程”很多团队的第一反应是把业务信息写进 prompt 里。这确实是有效手段但它停留在“手工拼字符串”阶段。真实业务里一个 AI 功能往往要对接订单系统、CRM、工单系统、知识库、权限系统不同场景要带不同范围的数据。如果每次都在 Controller 里一个一个地查询再拼接成超长字符串代码很快就会变成没人敢动的巨型方法也会带来几个直接问题数据该查哪些不清晰、字段多了模型容易分心、敏感信息容易越权带出去、换了场景就要重写一套。于是“上下文工程”开始被单独拿出来讲。它强调的是不是把尽可能多的信息塞给模型而是按业务意图把最相关、最新的数据组织成模型容易理解的输入。RAG 解决的是“从知识库里检索片段”而上下文工程的范围更广不仅包括知识库还包括业务对象、实时数据、策略规则、用户画像等。1.3 框架要解决的三件事当我看到 Odyssey Framework 时我觉得它正是在回答这三个问题上下文该从哪里来哪些业务系统、哪些接口、哪些数据表参与上下文构建。上下文该怎么组装如何把结构化业务对象、策略文本、知识片段组织成模型友好的格式。上下文如何治理如何做权限控制、数据脱敏、缓存更新、日志审计。它不是简单地帮你“调大模型”而是在模型和业务系统之间增加了一个语义层。这个语义层把散落在各个系统里的数据翻译成模型能理解的业务上下文。没有这一层模型只看见孤立的参数有了这一层模型才“看懂”当前到底在发生什么。2. Odyssey Framework核心概念与定位2.1 一句话理解 Odyssey Framework你可以把 Odyssey Framework 理解为一个面向 AI 应用的业务上下文框架。它专注于解决“模型缺少业务上下文”的问题帮助开发者在调用大模型之前自动化地完成业务数据的收集、过滤、组装和注入。用类比的思路来理解传统开发里Controller 层负责把 HTTP 请求转换成业务参数Service 层负责业务逻辑而 Odyssey 这类框架负责把复杂的业务状态转换成模型可以消费的 prompt 上下文。它最大的特点不是“再包一层接口”而是把上下文本身当成可设计、可复用、可治理的一等公民。一个 AI 应用可以针对不同场景定义不同的上下文模板比如“售后咨询上下文”“数据分析上下文”“工单总结上下文”每个模板都知道自己要去找哪些数据、按什么格式组织、哪些字段不能暴露。2.2 它和 LangChain、Spring AI 有什么区别这是容易混淆的地方。LangChain、Spring AI 这类工具解决的是模型调用、Agent 编排、工具调用、向量检索等通用能力它们是“AI 应用的骨架”。而 Odyssey 这类上下文框架更聚焦在业务语义层它解决的是“把正确业务数据装进正确位置”。两者并不是二选一的关系。更常见的架构是Spring AI 负责与模型通信、管理对话轮次及工具调用Odyssey Framework 负责在请求进入模型前去订单中心、用户中心、知识库拉取上下文并完成组装。你甚至可以把 Odyssey 的上下文构建能力封装成一个 PromptTemplate 增强组件嵌到 LangChain 的 Chain 里。我个人的理解是如果说 LangChain 是在帮你想“Agent 怎么规划”那 Odyssey 是在帮你想“AI 面对具体业务时手里该拿到什么牌”。2.3 适合哪些业务场景下面几类业务场景会比较受益智能客服需要结合客户等级、订单状态、售后政策、历史沟通记录生成回复。工单助手需要根据工单内容、处理人技能、SLA 规则给出处理建议。数据分析助手需要把数据库表结构、字段中文含义、业务指标口径注入给模型。内部知识问答需要结合部门权限只让模型看到当前用户有权访问的知识文档。智能营销需要结合用户画像、消费行为、当前活动规则生成个性化文案。这些场景有一个共同特征模型输出质量高度依赖输入上下文而且上下文与用户的身份、实时业务状态绑定。没有业务上下文的模型调用在这些场景里几乎是不可用的。3. 核心原理拆解业务上下文是怎么流动的3.1 一次请求的完整流程为了让概念更具体我们先把一次带业务上下文的 AI 请求拆成几个阶段用户请求 ↓ 上下文发现判断当前场景需要哪些业务数据 ↓ 上下文收集调用内部接口 / 查询数据库 / 检索知识库 ↓ 上下文组装按模板生成结构化的系统提示词和用户消息 ↓ 模型调用把组装结果发送给大模型 ↓ 输出校验检查生成结果是否合规做后处理这里面最容易被忽视的是第一步和最后一步。大部分失败案例不是模型调用出错而是“该带的数据没带全”或“不该带的数据被带出去了”。以智能工单助手为例。用户在页面上问“这个客户退款能不能通过”系统需要先知道提问人是谁、当前工单对应哪个客户、客户等级是什么、退款政策文本在哪里、历史工单里是否已有人处理过同类请求。这些信息如果不在请求时收集模型就只能瞎猜。3.2 上下文的组成结构一份完整的业务上下文通常由这样几个部分组成组成部分示例作用用户身份当前操作人、部门、角色用于权限判断和人称对齐业务对象订单、工单、客户信息提供问题涉及的核心数据策略规则售后政策、赔付标准、SLA约束模型回答边界知识片段产品文档、FAQ、历史案例补充模型不知道的专业知识实时状态库存、物流轨迹、系统状态保证回答与当下一致对话历史用户最近几轮的问题与回答保持多轮对话的连贯性组装并不是简单拼接。尽量把业务对象整理成紧凑的 JSON 或条目式文本把策略规则单独列出来将用户问题放到最后。模型对“先看系统背景再看见当前问题”的输入结构理解更稳定。3.3 权限与数据边界这是业务上下文框架里最关键、也最容易出事的一环。大模型本身没有权限概念它只是忠实处理输入。如果系统把全部订单信息都塞进 prompt那一个普通客服 AI 就能“知道”所有客户的订单——这显然是不允许的。因此上下文框架必须和权限系统打通。在收集阶段就做一次过滤当前用户能看哪些客户、哪些单据、哪些字段。我们通常遵循最小权限原则——模型只需要拿到完成回答所需的最少字段而不是整个表的全部字段。敏感字段比如手机号、身份证号在进入 prompt 前应该做脱敏或直接剔除。4. 环境准备与项目结构4.1 技术选型与版本说明下面我们用一段完整的可运行代码演示“业务上下文收集 组装 模型调用”这个链路。这里要说明一下Odyssey Framework 作为一个独立的框架不同版本下 API 可能会有差异所以本文重点展示的是实现思路而不是某个特定版本的接口。示例采用 Spring Boot 3.2.x Java 17 这是当前后端项目里比较常见的组合。如果你在实际项目中使用的是 Spring AI、LangChain4j 或其他 AI 框架可以把本文的AIClient替换为框架自带的ChatClient核心的“上下文构建”代码完全不需要大改。依赖方面本次演示只需要一个 Web 依赖和一个 HTTP 客户端相关依赖。具体版本根据项目实际情况调整!-- 文件路径pom.xml -- ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent groupIdcom.example/groupId artifactIdodyssey-demo/artifactId version1.0.0-SNAPSHOT/version nameodyssey-demo/name descriptionOdyssey Framework context demo/description properties java.version17/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project4.2 示例项目结构为了让代码清晰我们按下面的目录组织项目odyssey-demo/ ├── pom.xml └── src/main/ ├── java/com/example/odyssey/ │ ├── OdysseyDemoApplication.java │ ├── model/ │ │ ├── WorkOrder.java │ │ └── Customer.java │ ├── context/ │ │ ├── BusinessContext.java │ │ ├── WorkOrderContextProvider.java │ │ └── ContextAssemblyService.java │ ├── ai/ │ │ ├── AIClient.java │ │ └── OpenAiCompatibleClient.java │ └── web/ │ └── AIChatController.java └── resources/ ├── application.yml └── prompts/ ├── system-prompt.txt └── user-prompt.txt5. 实战案例构建一个带业务上下文的智能工单助手5.1 场景与需求假设我们有一个售后工单系统客服在处理工单时想借助 AI 生成回复。AI 需要知道以下信息当前工单的基本信息工单号、产品、状态、问题描述。客户的基本信息客户名称、会员等级、累计消费。当前会员等级对应的售后政策。用户想解决的问题。我们需要把前三部分作为业务上下文与第四部分一起交给模型。数据来源可以是数据库也可以是内部 API。为了演示下面用模拟数据来替代真实查询。5.2 定义业务数据模型先创建两个简单的数据模型。这里使用 Java 17 的 record 语法简洁且不容易出错。// 文件路径src/main/java/com/example/odyssey/model/WorkOrder.java package com.example.odyssey.model; import java.time.LocalDateTime; public record WorkOrder( String orderId, String customerId, String productName, String status, String description, LocalDateTime createdAt ) { }// 文件路径src/main/java/com/example/odyssey/model/Customer.java package com.example.odyssey.model; import java.math.BigDecimal; public record Customer( String customerId, String name, String level, Integer totalOrders, BigDecimal totalAmount ) { }这里WorkOrder描述一张工单Customer描述一个客户。在实际项目里这两个类通常对应数据库表或上游接口返回的 DTO。5.3 实现业务上下文收集器接下来定义一个BusinessContext类它用来承载组装好之后的上下文数据。这里不仅包含业务对象还包含策略规则和知识片段后续的提示词模板会从这里取值。// 文件路径src/main/java/com/example/odyssey/context/BusinessContext.java package com.example.odyssey.context; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class BusinessContext { private final MapString, Object data new LinkedHashMap(); private final ListString policies new ArrayList(); private final ListString knowledge new ArrayList(); public void put(String key, Object value) { data.put(key, value); } public void addPolicy(String policy) { policies.add(policy); } public void addKnowledge(String knowledgeItem) { knowledge.add(knowledgeItem); } public MapString, Object getData() { return data; } public ListString getPolicies() { return policies; } public ListString getKnowledge() { return knowledge; } }然后实现收集器。这个类的职责是根据当前请求参数从各个数据源取出必要数据组装成BusinessContext。这里我刻意把“模拟查询”封装成私有方法后续接真实接口时只需要替换这些方法内部逻辑。// 文件路径src/main/java/com/example/odyssey/context/WorkOrderContextProvider.java package com.example.odyssey.context; import com.example.odyssey.model.Customer; import com.example.odyssey.model.WorkOrder; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Component public class WorkOrderContextProvider { // 模拟本地缓存实际项目中应通过数据库或内部 API 查询 private static final MapString, WorkOrder ORDER_STORE new ConcurrentHashMap(); private static final MapString, Customer CUSTOMER_STORE new ConcurrentHashMap(); static { ORDER_STORE.put(WO001, new WorkOrder(WO001, C001, 智能扫地机器人 X1, 待审核, 客户反馈扫地机无法开机要求退货退款, LocalDateTime.now().minusDays(1))); CUSTOMER_STORE.put(C001, new Customer(C001, 张先生, VIP, 12, new BigDecimal(15800.00))); } public BusinessContext buildContext(String orderId, String customerId) { WorkOrder workOrder queryWorkOrder(orderId); Customer customer queryCustomer(customerId); BusinessContext context new BusinessContext(); context.put(order, workOrder); context.put(customer, customer); // 根据客户等级加载不同的售后策略 if (VIP.equals(customer.level())) { context.addPolicy(VIP 客户享受优先处理支持 30 天无理由退货。); context.addPolicy(退款金额原路返回预计 24 小时内到账。); } else { context.addPolicy(普通客户支持 7 天无理由退货。); context.addPolicy(退款金额原路返回预计 3 个工作日内到账。); } context.addKnowledge(X1 扫地机常见故障包含无法开机、无法联网、清扫中断。); context.addKnowledge(无法开机时建议优先排查电源适配器与电池状态。); return context; } private WorkOrder queryWorkOrder(String orderId) { WorkOrder order ORDER_STORE.get(orderId); if (order null) { throw new IllegalArgumentException(工单不存在 orderId); } return order; } private Customer queryCustomer(String customerId) { Customer customer CUSTOMER_STORE.get(customerId); if (customer null) { throw new IllegalArgumentException(客户不存在 customerId); } return customer; } }这里要注意策略规则的加载通常不应该是硬编码而是来自配置中心、规则引擎或数据库。本例只是为了展示上下文组织方式。5.4 组装提示词上下文有了BusinessContext下一步就是把它的内容渲染成模型能理解的文本。我倾向于把系统提示词和用户消息分开系统提示词放业务上下文和规则用户消息里放用户的问题与当前目标。先写一个组装服务// 文件路径src/main/java/com/example/odyssey/context/ContextAssemblyService.java package com.example.odyssey.context; import com.example.odyssey.model.Customer; import com.example.odyssey.model.WorkOrder; import org.springframework.stereotype.Service; Service public class ContextAssemblyService { public String buildSystemPrompt(BusinessContext context) { StringBuilder sb new StringBuilder(); sb.append(你是某电商公司的售后客服助手。请根据以下业务上下文回答用户问题。\n); sb.append(如果信息不足请直接说明不要编造。\n\n); WorkOrder order (WorkOrder) context.getData().get(order); Customer customer (Customer) context.getData().get(customer); sb.append(【工单信息】\n); sb.append(工单号).append(order.orderId()).append(\n); sb.append(产品).append(order.productName()).append(\n); sb.append(状态).append(order.status()).append(\n); sb.append(问题描述).append(order.description()).append(\n\n); sb.append(【客户信息】\n); sb.append(客户姓名).append(customer.name()).append(\n); sb.append(客户等级).append(customer.level()).append(\n); sb.append(历史订单数).append(customer.totalOrders()).append(\n); sb.append(累计消费金额).append(customer.totalAmount()).append( 元\n\n); sb.append(【售后政策】\n); for (String policy : context.getPolicies()) { sb.append(- ).append(policy).append(\n); } sb.append(\n); sb.append(【产品知识】\n); for (String knowledge : context.getKnowledge()) { sb.append(- ).append(knowledge).append(\n); } sb.append(\n); sb.append(请基于以上信息生成回复注意语气专业、简洁。); return sb.toString(); } public String buildUserMessage(String userQuestion, String orderId) { return 工单号 orderId \n用户问题 userQuestion; } }这段代码的核心思想是把BusinessContext中的结构化对象转换成模型友好的文本块。顺序上先告诉模型它的角色然后给出业务对象信息再给出规则和知识最后是用户问题。这样模型能够一步步理解背景而不是在混乱的信息中猜测。5.5 调用大模型定义一个统一的模型调用接口方便替换为不同的大模型服务// 文件路径src/main/java/com/example/odyssey/ai/AIClient.java package com.example.odyssey.ai; public interface AIClient { String chat(String systemPrompt, String userMessage); }然后实现一个兼容 OpenAI Chat Completions 接口的客户端。这里用RestTemplate发送 HTTP 请求避免引入过多依赖。如果你的项目已经接了 Spring AI可以直接在 Controller 里用 Spring AI 的 ChatClient不需要这个类。// 文件路径src/main/java/com/example/odyssey/ai/OpenAiCompatibleClient.java package com.example.odyssey.ai; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; Component public class OpenAiCompatibleClient implements AIClient { private final RestTemplate restTemplate new RestTemplate(); Value(${ai.endpoint}) private String endpoint; Value(${ai.api-key}) private String apiKey; Value(${ai.model}) private String model; Override SuppressWarnings(unchecked) public String chat(String systemPrompt, String userMessage) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); MapString, Object systemMessage Map.of( role, system, content, systemPrompt ); MapString, Object userMessageBody Map.of( role, user, content, userMessage ); MapString, Object requestBody Map.of( model, model, messages, List.of(systemMessage, userMessageBody), temperature, 0.3 ); HttpEntityMapString, Object request new HttpEntity(requestBody, headers); ResponseEntityMap response restTemplate.postForEntity(endpoint, request, Map.class); if (response.getStatusCode().is2xxSuccessful() response.getBody() ! null) { ListMapString, Object choices (ListMapString, Object) response.getBody().get(choices); if (choices ! null !choices.isEmpty()) { MapString, Object message (MapString, Object) choices.get(0).get(message); return (String) message.get(content); } } throw new RuntimeException(AI 调用失败 response.getStatusCode()); } }代码里温度设置为 0.3是为了让客服回复更稳定、更贴近规则而不是自由发挥。如果场景是文案创作可以适当调高温度。5.6 控制器与外部接口最后提供一个 REST 接口把整条链路串起来。这里故意设计了两个入参用户在页面上输入的工单号、用户提问。实际项目中customerId应该从登录态或 Token 中获取而不是由客户端随意传参这一点很关键。// 文件路径src/main/java/com/example/odyssey/web/AIChatController.java package com.example.odyssey.web; import com.example.odyssey.ai.AIClient; import com.example.odyssey.context.BusinessContext; import com.example.odyssey.context.ContextAssemblyService; import com.example.odyssey.context.WorkOrderContextProvider; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/ai) public class AIChatController { private final WorkOrderContextProvider contextProvider; private final ContextAssemblyService assemblyService; private final AIClient aiClient; public AIChatController(WorkOrderContextProvider contextProvider, ContextAssemblyService assemblyService, AIClient aiClient) { this.contextProvider contextProvider; this.assemblyService assemblyService; this.aiClient aiClient; } PostMapping(/workorder) public String workOrderChat(RequestParam String orderId, RequestParam String question) { // 实际项目中 customerId 应从登录态获取这里为了演示简化为固定值 String customerId C001; // 1. 收集业务上下文 BusinessContext context contextProvider.buildContext(orderId, customerId); // 2. 组装提示词 String systemPrompt assemblyService.buildSystemPrompt(context); String userMessage assemblyService.buildUserMessage(question, orderId); // 3. 调用大模型 return aiClient.chat(systemPrompt, userMessage); } }再补充启动类和配置文件// 文件路径src/main/java/com/example/odyssey/OdysseyDemoApplication.java package com.example.odyssey; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class OdysseyDemoApplication { public static void main(String[] args) { SpringApplication.run(OdysseyDemoApplication.class, args); } }# 文件路径src/main/resources/application.yml server: port: 8080 ai: endpoint: https://your-llm-service.example.com/v1/chat/completions api-key: your-api-key model: your-model-name如果你的模型服务是 OpenAI 兼容接口直接把endpoint、api-key、model替换成你实际使用的值即可如果是私有化部署的模型网关通常只需要改endpoint。5.7 运行与验证启动 Spring Boot 应用后用 curl 进行验证curl -X POST http://localhost:8080/api/ai/workorder?orderIdWO001question客户要求退款能否直接同意如果模型服务配置正确你会得到一段根据工单信息、客户等级和政策生成的回复。例如根据您的工单信息客户张先生为 VIP 等级产品为智能扫地机器人 X1订单状态为待审核。按照 VIP 客户售后政策支持 30 天无理由退货退款原路返回预计 24 小时内到账。建议先与客户确认是否符合退货条件再发起退款审批。这里的重点是模型输出的依据全部来自我们拼接的BusinessContext而不是它自己编造的通用知识。你可以试一下把Customer等级改成普通客户模型就会自动切换成“7 天无理由退货”的表述。这就是业务上下文带来的效果差异。6. 常见问题与排查思路6.1 高频问题速查表问题现象常见原因解决思路模型回答不专业像在泛泛而谈业务上下文没有注入或注入不全检查BusinessContext是否包含关键业务对象回答中出现了其他客户信息上下文收集阶段未做权限过滤在收集前增加数据范围校验请求报错 400提示 token 超限注入的上下文过大精简字段只保留必要信息回答与最新政策不一致上下文来源是旧缓存为策略规则单独设置短缓存或实时读取模型经常编造不存在的政策提示词没有强调“信息不足时不能编造”在系统提示词中增加约束说明同样的上下文不同时间结果差异大temperature 过高面向业务规则场景调低 temperature6.2 上下文丢失或错拿这类问题最隐蔽。比如接口返回了字段但字段名对不上模型读取时拿到的就是空值。排查时建议在组装阶段打印完整系统提示词人工确认每个字段是否都正常渲染。我习惯在ContextAssemblyService里临时增加一段日志输出把组装后的systemPrompt打到日志里。这样能快速定位是“数据没查到”还是“模板拼接出错”。6.3 上下文过大导致请求超时上下文工程里最常踩的坑是“什么都想塞”。一个工单关联了好几十个字段加上知识库里检索出来的十几段文档一次请求可能就超出模型的输入长度限制或者导致首字延迟很高。解决思路有两个方向一是精简字段只保留模型回答问题真正需要的字段二是做知识片段裁剪比如先按相关性打分只把分数最高的几段拼进去。还要注意BusinessContext里不要塞大字段比如工单的完整日志文本、图片 base64 内容应该先做摘要再拼入。6.4 权限与数据泄露这是安全底线问题。即使你的系统里没有 Odysssey 这样的框架只要你把业务数据拼进 prompt就必须考虑权限。最典型的事故是一个普通客服的 AI 助手因为上下文里带了“全部订单”导致模型能回答出其他用户的订单详情。要避免这个问题需要在收集数据之前就确定当前用户的权限边界。比如先调用一个dataScopeService拿到当前用户可见的客户范围再根据范围查询订单。敏感字段在进入BusinessContext前统一脱敏。7. 最佳实践与工程建议7.1 上下文最小化原则每次都想清楚一个问题模型要完成这一次回答最少需要哪些信息多余的字段不仅浪费 token还会干扰模型的注意力。比如工单详情里有一长串内部备注这些信息不应该进入面向客户的回复上下文而专门做内部工单总结的场景则可以把内部备注包含进来。建议在构建BusinessContext时就定义出“对外上下文”和“对内上下文”两组字段集合。面向 C 端用户的 AI 回复字段要少而精面向内部运营的 AI 分析字段可以多一些但仍要聚焦需求。7.2 提示词模板与数据分离不要把提示词写成一大串字符串拼在代码里。更推荐的做法是把系统提示词模板放到独立的资源文件用占位符代替动态数据再用模板引擎渲染。这样做的好处是产品和运营同学可以单独调整话术不需要改代码发布。对于 Java 项目可以用简单的字符串模板实现也可以引入 FreeMarker 或 Pebble。核心原则是模板负责“怎么说”BusinessContext负责“说什么”两者解耦。7.3 缓存与实时性权衡业务数据并不都是实时敏感的。客户画像、会员等级这些数据可以设置分钟级缓存工单状态、库存数量则需要实时读取。建议在上下文收集器里给每个数据源单独配置缓存策略而不是一刀切全部实时查询或全部缓存。策略规则这类频繁改动的内容建议走配置中心或独立的规则表并记录规则版本号。把版本号也拼进系统提示词当回答出现争议时可以追溯是哪一版规则触发的。7.4 可观测性与效果评估AI 应用的排查比传统应用难因为问题往往不在报错里而在输出质量上。建议为每次模型调用记录完整的输入和输出至少包括业务上下文摘要、系统提示词、用户问题、模型回复、消耗 token 数、响应耗时。这样出了问题才能复盘。有条件的话建立一个小规模回归集。把几十个常见问题连同期望答案保存下来每次修改提示词模板或上下文结构后跑一遍回归集对比输出是否符合预期。这是防止“改一个模板坏了另一个场景”的最有效手段。8. 总结与下一步学习路线这篇文章的核心是介绍 Odyssey Framework 所代表的“业务上下文”理念以及它在一个真实项目中如何落地。我们从“为什么 AI 需要业务上下文”出发拆解了上下文收集、组装、调用的完整流程并用 Spring Boot 实现了一个最小可运行的智能工单助手。通过这个例子你应该能理解模型本身的能力当然重要但决定 AI 能否在业务中真正可用的往往是它进入对话前拿到的那份上下文。如果你接下来想深入这个方向可以从这些路径继续学习 Spring AI 的 ChatClient 和 PromptTemplate理解它如何与业务上下文组合。研究 RAG 的切分与检索策略把知识库接入上下文体系。调研 Agent 模式下的工具调用看上下文如何跨越多个工具传递。关注权限模型设计把组织架构和数据权限真正融入上下文收集流程。在实际项目中优先关注三点上下文最小化、权限边界、可观测性。先把这三件事做好AI 应用即使模型换成别的整体效果也不会差太多。希望这篇文章能帮你减少一些在“AI 不懂业务”这件事上的试错成本。如果你有更好的上下文组织思路也欢迎在评论区交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价