为什么要写这个系列
做Java后端十年,我接触过不少企业的核心系统。
金融、电商、政务------行业不同,但底层的现状惊人地相似:生产系统还在Java 8,框架停在Spring Boot 2.x甚至更早,代码跑了很多年,没人敢轻易动。
去年开始,几乎每个项目都在谈接AI。
但真正动手的时候,团队就卡住了。
不是因为不懂大模型,而是老系统本身接不住。JDK版本不够,Spring AI引不进来;依赖树牵一发动全身,升级一个包怕带崩一片;生产流量压着,不敢拿主流程赌一个AI试点。
更危险的是硬塞。我见过团队在老系统的Service层直接new一个HttpClient调模型API,Prompt拼在业务代码里,超时没配、降级没有。模型响应慢的时候,老系统的订单查询线程池被占满,主流程跟着卡住。还有团队把用户手机号、身份证原样送进Prompt,过了两个月才被安全审计发现。
这种事见多了,我就开始想一个问题:老系统不具备直接接入AI的条件,这是不是大多数企业的常态?
答案是肯定的。而且这不应该成为接不了AI的理由。
核心思路其实就三条:
text
老系统少改 ------ 不升级 JDK,不引入 Spring AI 依赖
AI 能力旁路 ------ 独立部署,老系统通过 HTTP 或 MQ 调用
企业边界先行 ------ 脱敏、审计、降级、幂等比模型调用更重要
这个系列就是把这三条线展开成10讲。
从AI Gateway到MCP工具中心,从SQL Agent到RAG知识库,从工单Agent到多Agent研发团队------每一讲都围绕同一个前提:
你的老系统还在跑Java 8,你不能为了AI去赌它的稳定性。
每讲配套一个可运行的Maven Lab,不讲空架构,不写Hello World。你跑得通Demo,看得到边界,拿得到代码。
这就是我做这个系列的原因。
这个系列帮你建立企业 AI 接入的架构意识。每个 Demo 都可独立运行,每讲都配有边界设计和企业避坑。
但意识不等于系统。当你真正要在自己的项目里落地时,会发现:
- 10 个独立 Lab 的组件需要整合成一个完整的 AI 能力中心
- Stub 模型需要替换成真实模型调用
- 权限、审计、脱敏需要从 Demo 级别升级到生产级别
先把架构意识打好,落地时才能走得更稳。
Java 8老系统AI工具接入:API包装成受控工具,只读优先+权限拦截
第1讲我们先解决了一个基础问题:
text
Java 8 老系统不升级
↓
通过旁路 AI Gateway 接入 AI 能力
↓
AI 只生成建议,不直接改业务状态
这个结论很重要。
因为很多团队一接入AI,就忍不住让AI"直接干活":
text
直接查订单
直接查客户
直接查库存
直接改状态
直接发审批
如果没有边界,这些动作很快就会从"智能助手"变成"系统权限入口"。
所以第2讲要把第1讲的结论往前推进一步:
AI可以使用老系统能力,但不能直接拿到老系统API。
更准确地说:
老系统API要先包装成可授权、可审计、可脱敏的工具,再交给AI使用。
这一讲标题里用了MCP Tool Center,但我们先不讲协议细节。先讲企业真正关心的问题:一个API到底能不能变成AI工具?如果能,怎么控制它的输入、执行和输出?
AI调工具前,先问三个问题
当你准备把一个老系统API暴露给AI时,先不要急着写Tool。先问三个问题:
text
这个能力是读还是写?
这个能力的参数边界是什么?
这个能力的返回值能不能原样给模型?
这三个问题分别对应三条边界:
text
读写边界:AI 能不能执行这个动作
参数边界:AI 能带着什么条件执行
输出边界:执行结果能不能原样返回
第一个问题:这个能力是读还是写
比如订单系统里有两个能力:
text
queryOrder(orderId) 查询订单
updateOrderStatus(...) 修改订单状态
它们看起来都只是一个API,但风险完全不同。
queryOrder是只读能力,适合第一阶段包装成Tool。AI可以用它查询订单状态、总结异常原因、给出处理建议。
updateOrderStatus是写能力,涉及真实业务状态,不能默认交给AI自动执行。
这正好呼应第1讲的结论:
text
AI 只给建议
老系统状态不由 AI 直接修改
所以工具中心第一条规则应该是:
text
默认只读
写操作拒绝或转人工确认
第二个问题:参数边界是什么
只读也不代表没有风险。
比如queryOrder(orderId)看起来只是查订单,但企业里至少要继续问:
text
orderId 格式是否合法?
这个用户能不能查这个租户的订单?
一次能不能查多个订单?
能不能模糊查询?
能不能查历史归档订单?
如果参数边界不清楚,AI工具就可能从"查一个订单"滑向"批量探测数据"。
所以工具不是简单包一层函数,而是要把可接受的参数收窄:
text
只允许明确订单号
只允许当前租户
只允许单笔查询
不允许让模型拼任意查询条件
本讲Demo为了保持简单,只演示从用户问题中提取一个订单号。但你在真实项目里要把这个思路补全成参数校验、租户校验和权限校验。
第三个问题:返回值能不能原样给模型
很多人做工具调用,只盯着"模型能不能调到API"。
但真正容易泄露数据的地方,往往在返回值。
订单对象里可能有:
text
客户手机号
收货地址
客户姓名
支付信息
内部备注
即便工具本身是只读的,也不代表返回值可以原样给模型或前端。
所以工具中心还要管输出:
text
敏感字段脱敏
内部字段裁剪
只返回模型完成任务所需的信息
记录哪些字段被处理过
到这里你会发现,AI工具中心不是为了"让AI多调几个接口"。它真正解决的是:
text
AI 能调用什么
AI 能带什么参数调用
AI 调完以后能看到什么
本讲能学到什么
配套代码目录:
text
code/spring-ai-enterprise-lab/labs/chapter02-mcp-tool-center
运行:
powershell
.\compile-and-run.ps1
运行后可以看到两条路径:
- 用户问"订单有什么异常",Agent调用
queryOrder只读工具。 - 用户要求"把订单状态改成已发货",工具中心拒绝,老系统API不会被调用。
这不是为了演示一个复杂MCP Server。它想让你先建立一个边界意识:
text
AI 不是直接访问老系统
AI 只能通过工具中心访问被授权的能力
工具中心同时管输入、执行和输出
端到端走一遍
用户输入:
text
帮我查询 O202606050001 这个订单现在有什么异常
第一步 ,ToolCallingAgent收到问题。
它不是立刻去调老系统API,而是先问权限策略:
java
if (permissionPolicy.isWriteIntent(request.getQuestion())) {
return rejected(...);
}
第二步 ,ToolPermissionPolicy判断这是不是写操作。
当前Demo中,下面这些词会被视为写意图:
text
改成
修改
删除
更新
发货
第三步,如果不是写操作,Agent从问题里提取订单号:
text
O202606050001
第四步 ,Agent选择并调用OrderQueryTool。
这里要特别注意:Agent调用的是Tool,不是直接调用LegacyOrderApi。
代码里的调用关系是:
text
ToolCallingAgent
↓
选择 queryOrder 这个只读工具
↓
OrderQueryTool.query(tenantId, operatorId, orderId)
↓
ToolAuditService.record(...)
↓
LegacyOrderApi.queryOrder(orderId)
↓
ToolResultMasker.mask(order)
这一层包装至少做了三件事:
text
记录是谁在什么租户下调用工具
把工具调用限定为 queryOrder 只读能力
把老系统返回值转成脱敏后的结果
这就是Tool和API的差别。API只关心"能不能调通"。Tool要关心"能不能被AI安全地使用"。
第五步 ,OrderQueryTool记录审计:
text
[TOOL_AUDIT] tenant=demo, operator=u1001, tool=queryOrder, detail=orderId=O202606050001, mode=READ_ONLY
第六步 ,老系统返回订单数据后,ToolResultMasker对返回值脱敏。
最终Agent拿到的是处理后的结果,而不是老系统原始对象。
为什么Agent不能直接调老系统API
很多Demo会把Tool写成一个简单函数:
java
Order order = legacyOrderApi.queryOrder(orderId);
这在教学里很快,但在企业里很危险。
因为一旦Agent可以直接访问老系统API,你就很难回答:
text
谁调用的?
调用了哪个 API?
传了什么参数?
有没有越权?
返回结果有没有脱敏?
如果是写操作,谁确认的?
所以中间必须有一层工具中心。工具中心不是为了增加复杂度,而是为了让AI使用系统能力时有边界。
这一层至少要做4件事:
text
工具目录:哪些能力能被 AI 使用
权限策略:哪些请求允许,哪些请求拒绝
审计记录:谁在什么时候调用了什么
结果处理:返回值脱敏、裁剪、结构化
只读工具是第一阶段最稳的入口
本讲只实现了queryOrder。它是一个只读工具:
text
输入:tenantId、operatorId、orderId
输出:订单状态、异常原因、脱敏后的客户信息
运行结果类似:
json
{
"answer": "订单 O202606050001 当前状态为 DELAYED,异常原因:仓库尚未确认出库时间。客户手机号和地址已脱敏。",
"rejected": false,
"rejectReason": "ALLOW_READ_ONLY_TOOL",
"toolCalls": ["queryOrder"],
"maskedFields": ["customerMobile", "address"]
}
观察点:
rejected=false:请求允许执行。toolCalls=["queryOrder"]:Agent只调用了只读工具。maskedFields:返回值做过脱敏。
适合第一阶段上线的能力:
text
AI 可以查
AI 可以总结
AI 可以建议
AI 不能直接改
写操作为什么要挡住
另一个输入:
text
把 O202606050001 订单状态改成已发货
系统返回:
json
{
"answer": "该问题包含写操作意图,AI 不能直接修改老系统订单状态,请转人工审批。",
"rejected": true,
"rejectReason": "WRITE_INTENT_REJECTED",
"toolCalls": [],
"maskedFields": []
}
关键是toolCalls=[]------请求在权限策略层就被挡住了,没有进入老系统API。这比"调用后发现不该改"安全得多。
写操作应该走另一条流程:
text
AI 生成建议 → 展示影响范围 → 人工确认 → 系统执行 → 审计记录
返回值也要过边界
OrderQueryTool不直接返回LegacyOrder,而是先经过ToolResultMasker。
老系统返回的:
text
customerMobile=13812345678
address=上海市浦东新区某某路 88 号
给模型和前端的结果:
text
customerMobile=138****5678
address=上海市浦东新区***
工具调用的安全边界,不只在调用前,也在调用后。
MCP应该怎么理解
这篇文章不展开MCP协议细节。你可以先把MCP理解成一种企业工具接入思维:
text
把外部能力描述成工具
让 Agent 通过统一方式调用工具
工具提供者负责边界控制
更准确的理解:
text
企业把一部分能力包装成可授权工具
AI 只能在授权范围内使用
调用过程可审计
返回结果可控制
高风险动作转人工
代码应该先看哪些类
第2讲建议先看6个类:
text
agent/ToolCallingAgent.java --- Agent 如何选择工具
mcp/ToolPermissionPolicy.java --- 权限策略如何拒绝写操作
mcp/OrderQueryTool.java --- Tool 如何包装老系统 API
mcp/ToolAuditService.java --- Tool 调用如何记录审计
mcp/ToolResultMasker.java --- Tool 返回值如何脱敏
legacy/LegacyOrderApi.java --- 老系统原始 API 长什么样
核心链路:
text
用户问题 → 权限判断 → 只读 Tool → 审计记录 → 老系统 API → 返回值脱敏 → 回答用户
企业避坑
- 不要让Agent直接访问老系统API --- 中间必须有工具中心做工具目录、权限策略、审计记录和返回值处理。
- 不要一开始就开放写操作 --- 查询、总结、解释适合第一阶段;修改、删除、审批、退款应转人工确认。
- 不要只校验入参,不处理返回值 --- 入参决定AI能查什么,返回值决定AI能看什么。两边都要收口。
- 不要把"工具调用成功"当成"企业可用" --- 企业可用还要看授权、审计、脱敏、拒绝策略和责任边界。
从 Demo 到落地,还差什么
本讲帮你建立了"API 不能直接交给 AI"的边界意识,但企业真实场景还差几步:
动态权限模型:当前 Demo 用关键词匹配判断写意图。真实项目需要基于 RBAC 的动态权限------不同角色、不同租户、不同场景,能用的 Tool 不同。关键词匹配在生产环境远远不够。
工具注册中心:企业不会只有一两个 Tool。需要一个支持动态注册、版本管理、灰度发布的工具中心,而不是硬编码在代码里。
写操作的完整人工确认流程:本讲只实现了"拒绝写操作"。真实流程是:AI 生成建议 → 展示影响范围 → 人工确认 → 系统执行 → 审计记录。这条链路会在后续工作流章节展开。
工具调用链路追踪:一次用户问题可能触发多个 Tool,企业需要完整的调用链追踪------哪个 Tool 先调、哪个后调、哪些被拒绝、总耗时多少。
如果你正在推进企业 AI 工具接入落地,后续会有完整版把这些能力整合成企业级工具中心。
小结,而是一种授权思维:
text
老系统能力
↓
包装成工具
↓
默认只读
↓
调用前做权限判断
↓
调用中做审计
↓
调用后做脱敏
↓
写操作转人工确认
AI要接入企业系统,不是拿到更多权限,而是被放进更清楚的权限边界里。