qinghuan
← 交付 Java 学习助手 / 全部笔记

单元 29 / 交付 Java 学习助手

P0 · 必会主线L2 · 进阶

把 Spring 服务接入模型工具

沿用熟悉的 Service、Repository 分层,把小而明确的 Java 业务方法开放为模型工具。

本篇目录 · 按需要跳转

读完这篇,你能做什么

能完成当前用户笔记查询工具,分别测试本人资料、他人资料、记录不存在和非法 ID 四种情况是否遵守权限规则。

学习理由、准备要求与知识点

为什么学它

把 Java 服务开放成模型工具后,原有的服务分层和权限检查仍要继续生效,不必为接模型另绕一条不受检查的路。

先读这些内容

知识点清单

  • ChatClient 接入
  • @Tool 与 @ToolParam
  • 工具注册
  • Service 与 Repository 分层
  • ToolContext 可信身份
  • 资源归属校验
  • DTO 返回与敏感字段
  • 实际工具链路验证

运行前先确认框架源码与工具片段基于Spring AI 2.0.1;完整算法仅使用Java17标准库。工具Bean片段需应用服务、DTO、注解导入和兼容依赖上下文。

01先弄懂它是什么

已有的 Java 业务服务,可以通过 Spring AI 包装成模型可请求的工具。比较稳妥的顺序是先把普通 Service 写好、测好,再只开放职责清楚的小方法。例如开放“查询当前用户的学习计划”,而不是直接给模型通用 SQL、任意 HTTP 请求或整个管理服务。工具方法依然属于后端接口,需要遵守原有业务和权限规则。

哪些参数可以由模型填写,哪些必须由后端确定,要分开处理。工具 Schema 说明参数叫什么、是什么类型、用来做什么;query 可以来自模型,但 userId、tenantId 应来自服务端认证链,并在查询时检查资源权限。即使工具名称看起来只读,也要检查返回的数据是否超出了当前用户的可读范围。

集成顺序是先验证工具业务,再验证参数适配,最后接入模型闭环。使用固定输入或桩响应覆盖缺参数、非法值、权限拒绝、空结果和异常映射;模型联网成功只能证明一次调用完成,不能替代这些确定性契约测试。

文中这几个词,先认一认

工具Bean
应用中包含可开放工具方法的对象。它仍然遵守依赖注入、业务校验和权限规则。
ToolContext
应用自己传给工具的附加信息,例如可信的身份上下文;认证信息不能让模型随意填写。
DTO
专门为接口或工具输出设计的一组有限字段,避免直接把数据库实体的全部内容暴露出去。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

检索工具可能从多份已授权索引取得按分数降序排列的结果,返回前需要合并、去重和限量。本题用多路归并维护每个列表的当前头部,每次取全局最优项,再推进它所在列表。若同一ID在多个源出现,保留最先弹出的最高分版本。

这个算法要求所有来源分数可比较,列表遵守相同排序规则,且输入已经经过权限过滤。不同检索器分数未经校准时,不能直接混排,应先采用RRF或重排等明确融合策略。算法不替代权限过滤,也不会判断同ID的证据内容是否一致。

完整检验每个列表是否有序需要扫描所有输入。维护小堆能够降低输出阶段成本,但若先做全量有序性校验,总时间至少线性;重复ID很多时也可能弹出全部候选才得到少量结果。复杂度应把这些成本算进去。

正在加载流程图…

图中状态只描述本篇可执行机制;业务调用仍需由外层应用负责。
查看流程图源码(Mermaid)
flowchart TD
  A["认证身份"] --> C["可信ToolContext"]
  M["模型工具请求"] --> S["参数Schema与业务校验"]
  C --> T["MethodToolCallback"]
  S --> T
  T --> R["授权检索与DTO合并"]
  R --> O["有限工具结果"]

按顺序看,每一步做了什么

  1. 锁定依赖版本并检查对应参考文档。
  2. 构建最小业务服务和有限工具DTO。
  3. 由认证链建立可信上下文,参数只保留任务需要的信息。
  4. 测试适配、授权、错误和结果合并。
  5. 接入受限模型循环,记录工具轨迹与预算。

03对照实现代码

Spring AI 2.0.1 MethodToolCallback 源码 (新标签页)↗

Spring AI 2.0.1,固定v2.0.1源码;完整算法Java17 · MethodToolCallback.call / buildMethodArguments / callMethod

下方为原创工具Bean方法片段,使用Spring AI 2.0.1的@Tool与ToolContext,假定存在应用自己的检索服务和结果DTO;不是框架原样源码,也不是完整可运行Spring工程。

核心机制代码 · java
@Tool(description = "检索当前用户有权限阅读的学习资料")
public List<StudyHit> search(String query, ToolContext context) {
    String userId = (String) context.getContext().get("userId"); // 身份由服务端认证后传入。
    if (userId == null || userId.isBlank()) throw new SecurityException("missing identity");
    List<StudyHit> hits = service.searchAuthorized(userId, query); // 数据层继续校验资源授权。
    return hits.stream().limit(5).toList(); // 返回有限DTO,避免暴露内部实体与敏感字段。
}

该版本MethodToolCallback.call依次检查上下文要求、提取JSON参数、构造方法参数、调用反射方法并转换结果。模型提交的是工具调用请求,真正执行业务代码的是应用进程。

buildMethodArguments对ToolContext参数走独立分支,从调用上下文注入,而普通参数从JSON映射。服务端应使用认证得到的用户标识构造上下文,不能接受模型声明自己是谁。

ToolContext的getContext返回不可修改视图,但构造函数并不深拷贝原Map或内部对象。应用宜传入Map.copyOf及不可变值,避免外部别名修改;不可修改视图本身也不会验证身份。

反射调用和JSON转换不等于业务校验。查询长度、结果数量、租户过滤、写入确认及超时仍由应用设计;异常映射需避免把内部堆栈和密钥返回模型。

代码先调用谁,再调用谁

  1. 认证请求并构造可信上下文
  2. 注册有限工具方法与Schema
  3. 模型提出工具名和JSON参数
  4. MethodToolCallback转换参数并注入ToolContext
  5. 业务服务鉴权检索并返回有限DTO

04动手写一个例子

已授权有序检索结果的多路归并

输入多个按分数降序、同分按ID升序的列表,返回去重后前limit项。分数有限且各源已校准、已授权。Java17程序不加载Spring框架。

输入 1
源一A9,C4;源二B8,A7;limit3
输出 1
A9,B8,C4
输入 2
limit0
输出 2
空结果,但仍校验输入

每个源只将首个尚未处理元素放入优先队列,其余元素不会比当前头更优,因此堆顶就是所有剩余元素的全局最优。

弹出后立刻补入同源下一项,保持每个非空源一个头部。HashSet记录已返回ID,重复候选跳过但仍推进该源,避免在重复头部停住。

所有输入先校验排序、分数和ID。相同ID不同分数选择最高分是本题政策,真实资料需要额外用版本和摘要检查冲突,不能只凭ID去重隐藏不一致。

为什么这样写是对的堆中始终包含每个未耗尽来源的首个未处理元素;输出ID唯一且按统一比较器非递减优先级排列。

时间 O(N + P log k),最坏O(N log k)额外空间 O(k + limit)

N为总输入,k为非空来源数,P为弹出项数且P不超过N。全量校验O(N),堆存k个头,输出与去重集合最多limit项。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

展开收起代码 · Main.java · 完整可运行程序Java · 33 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    record Hit(String id,double score){}
    record Head(int source,int index,Hit hit){}
    static final Comparator<Hit> ORDER=Comparator.comparingDouble(Hit::score).reversed().thenComparing(Hit::id);
    static List<Hit> merge(List<List<Hit>> sources,int limit){
        if(limit<0)throw new IllegalArgumentException("limit");
        PriorityQueue<Head> q=new PriorityQueue<>((a,b)->{int c=ORDER.compare(a.hit(),b.hit());return c!=0?c:Integer.compare(a.source(),b.source());});
        for(int s=0;s<sources.size();s++){
            List<Hit> list=sources.get(s);
            for(int i=0;i<list.size();i++){Hit h=list.get(i);if(h.id()==null||h.id().isBlank()||!Double.isFinite(h.score())||i>0&&ORDER.compare(list.get(i-1),h)>0)throw new IllegalArgumentException("order");} // 验证统一排序前提。
            if(!list.isEmpty())q.add(new Head(s,0,list.get(0)));
        }
        List<Hit> out=new ArrayList<>();Set<String> seen=new HashSet<>();
        while(!q.isEmpty()&&out.size()<limit){
            Head h=q.remove();if(seen.add(h.hit().id()))out.add(h.hit()); // 全局最优先出,同ID保留最高分。
            List<Hit> list=sources.get(h.source());int next=h.index()+1;
            if(next<list.size())q.add(new Head(h.source(),next,list.get(next))); // 重复项也要推进来源。
        }
        return List.copyOf(out);
    }
    static void eq(List<Hit> e,List<Hit> a){if(!e.equals(a))throw new AssertionError(a);}
    public static void main(String[] args){
        List<List<Hit>> lists=List.of(List.of(new Hit("A",9),new Hit("C",4)),List.of(new Hit("B",8),new Hit("A",7)));
        eq(List.of(new Hit("A",9),new Hit("B",8),new Hit("C",4)),merge(lists,3));
        eq(List.of(new Hit("A",9)),merge(lists,1));eq(List.of(),merge(lists,0));
        eq(List.of(),merge(List.of(List.of()),3));
        eq(List.of(new Hit("A",5),new Hit("B",5)),merge(List.of(List.of(new Hit("B",5)),List.of(new Hit("A",5))),2));
        try{merge(List.of(List.of(new Hit("A",1),new Hit("B",2))),2);throw new AssertionError();}catch(IllegalArgumentException expected){}
        try{merge(List.of(List.of(new Hit("A",Double.NaN))),2);throw new AssertionError();}catch(IllegalArgumentException expected){}
        System.out.println("PASS 7 tests");
    }
}

这些特殊情况也要试一试

  • 输入有序和分数可比是明确前提。
  • 同ID内容冲突需应用层处理。
  • 先授权后合并,不能先向模型暴露再过滤。
进阶选读 · 后续再读:远程分页工具合并

场景各源只能分页返回,且来源延迟不同。

做法为每源维护游标、版本与超时,只有掌握其剩余分数上界才能安全提前返回精确全局前若干项;否则承认近似或部分结果。

复杂度本地堆每次弹出O(log k),网络请求数取决于分页和上界;不能仅用堆复杂度代表端到端性能。

好处与代价等待所有来源保证更完整但延迟高,部分结果更快却需明确缺失来源和重新合并策略。

05项目里怎么用

工具返回面向任务的DTO,限制数量和字段,数据库查询同时带用户或租户条件。ToolContext只是一条传递通道,服务方法仍需验证身份存在、资源可读和操作允许;不要在模型可填参数中再次接收可覆盖身份的字段。

版本升级前阅读对应源码和迁移说明,特别注意JSON库、参数转换和工具循环策略。本文源码固定Spring AI 2.0.1,不能把示例机械混入其他大版本;完整项目应使用兼容的依赖管理并运行实际框架契约测试。

写工具要增加业务幂等键、状态检查与必要确认,读工具也要设置超时和结果上限。将模型响应、工具参数、业务结果和错误映射分别测试,保持日志脱敏,避免把反射异常细节作为可见答案返回。

容易踩的坑

  • 让模型提供身份并直接信任。
  • 误把ToolContext不可修改视图当作深拷贝与鉴权。
  • 分数未校准就合并不同检索器结果。
  • 工具注册成功便认为业务契约已验证。

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
注解方法工具与Java服务集成直接需约束可暴露方法和参数范围清晰的业务能力
显式ToolCallback控制Schema与执行更细编写适配代码更多复杂转换和统一拦截
窄DTO与有限结果减少信息暴露和上下文负担可能需要后续分页工具生产检索与查询

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01ToolContext提供什么?

展开参考答案与推导

提供应用注入的上下文通道;身份可信性取决于认证来源,资源授权仍在业务层。

02多路归并前提是什么?

展开参考答案与推导

各源使用相同可比较排序规则且已授权有序,堆只负责合并顺序。

03框架反射调用替代校验吗?

展开参考答案与推导

不替代,参数业务范围、权限、超时和结果限制仍需显式实现。

换个情况,还会处理吗

01模型填入另一个userId,如何处理?

展开参考答案与推导

工具Schema不暴露可覆盖身份字段,使用服务端认证上下文,并让查询层校验资源归属。

02升级后参数映射失败,先查什么?

展开参考答案与推导

锁定实际依赖版本,对照MethodToolCallback参数构造、JSON库与编译参数名称保留设置。

03某源分数都是一千,另一个都是零点几,能直接合并吗?

展开参考答案与推导

不能默认可比,应先校准或使用排名融合,再评估检索质量。

写代码试一试

01重复头部是否阻塞?

输入 / 条件
源一A9,A8,C7
输出 / 预期
输出A9,C7

提示 跳过重复但继续推进。

展开参考答案与推导

每次弹出无论是否输出都补下一项,否则会漏掉C。

02同分如何稳定?

输入 / 条件
B5与A5
输出 / 预期
A5,B5

提示 比较器次关键字ID。

展开参考答案与推导

先比较分数降序,再按ID升序,使输出可预测并便于测试。

03校验会影响复杂度吗?

输入 / 条件
一百万候选只取5条
输出 / 预期
至少O(N)校验

提示 不能忽略输入检查。

展开参考答案与推导

本题逐条检查有序和有限分数,因此整体不只是五次堆操作。

商户项目 · 本篇加练

让商户工具经过真实的授权服务

核查日期:2026-10-11。状态:新增教学练习未运行。

目标与前置知识

证明工具只能按服务器确认的身份读取被授权商户。

前置:完成上一节的只读服务和明确结果类型;理解认证身份与 merchantId 的区别。

按顺序完成

  1. 先查看自己的 Maven/Gradle 依赖树,记录 JDK、Boot、Spring AI 和 Security 的实际版本。本文核查的页面显示 Spring AI 2.0.1、Security 7.1.1,它们是来源版本,不代表已验证组合;不要为复制示例盲目升级。

  2. 请求认证成功后,由服务器取得 tenantId 和 subjectId;通过当前框架支持的上下文传递方式交给工具。ToolContext 可承载非模型数据,但并不替你认证或检查权限;不要接受用户文本、模型参数或 FAQ 中自报的租户。

  3. 方法授权需要显式启用。把授权放在实际调用的受控 Service 上,检查工具注册的是哪个对象、回调如何到达该 Service、是否经过预期代理与授权拦截器、身份能否到达执行线程。不能用注解截图或直接 new 出来的测试对象证明真实回调安全。

  4. 通过与上线相同的工具入口测试 A 查 1001、A 查 1002、B 查 1001、无身份及伪造 tenantId。方法层检查 merchant:read;查询条件始终使用服务端 tenantId 与 merchantId。A 查 1002 与 9999 对外统一为 404“商户不存在或当前身份无权访问”。工具可以执行受租户约束的查询并返回空结果;用仓库断言证明没有取得其他租户的受保护记录,不能把进入工具入口本身当成越权读取。

验收与交付

  • 交付依赖树、实际 bean/回调路径说明、可信身份来源和逐分支断言
  • 越权没有进入受保护数据读取,也没有把结果送进模型、回答或日志
  • 身份丢失、授权未启用或路径绕过时测试应失败,不能降级成默认租户

未验证任一 Spring 组合或方法授权集成;以真实调用路径测试为准。

原始来源

商户项目 · 延伸练习 · 未运行

异步任务的身份与检索范围

核查日期:2026-10-12 12:09 JST;2026-10-16复核相关文档。来源版本为 Spring Security 7.1.1、Spring AI 2.0.1,发布日期未标明;它们不是已验证的项目依赖组合。以下执行器、授权与检索练习均未运行。

先完成真实工具授权入口和FAQ 检索范围检查。沿用虚构身份 A、B 和各自商户,另外准备能暂停、放行任务并确认工作线程身份的测试执行器。

Concurrency Support区分两种执行器用法:指定固定上下文时,各任务共用该身份;省略该固定上下文时,可在提交任务时取得当前身份。任务包装器还负责结束后的清理。固定身份适合相应后台任务,不能未经检查就套到多用户查询。RAG 的请求级过滤可以优先于检索器默认过滤,因此“默认配置里写了租户”仍需通过请求入口验证。

  1. 交错提交 A 查 1001、B 查 1002;分别记录提交身份、执行身份、最终查询范围和进入模型的片段。再加入 A 请求 1002 的负例,同时保留合法查询对照。缺少身份时按契约拒绝,不退回公共或管理员身份。
  2. 让 A 的任务抛出受控异常,再让同一工作线程处理 B 和无身份任务。通过线程标识及放行顺序证明确实发生复用,检查后续任务没有继承 A;只比较线程池名称不够。
  3. 由可信服务端身份确定 A 的授权范围,再提交试图改成 B 或取消范围的请求过滤。核对最终检索条件仍受 A 约束,并检查检索结果与模型上下文两处。用户文本和模型参数只能提出范围内的业务筛选。
  4. 若该路径使用结果缓存,补一例缓存命中,核对当前授权仍生效。公开资料是否共享缓存应按其语义决定,不机械套用此处的私有 A/B 场景。

提交用例、请求关联标识、提交与执行身份、线程复用证据、最终查询条件、返回资源归属和异常清理记录。只使用假身份和必要标识,不保存令牌或完整私有片段。验收看身份与数据范围是否一致,不能只看最终回答是否拒绝。

方法级返回过滤不能替代仓库读取前的范围约束。本节不证明 Reactor 或其他异步框架自动传播身份;提交后发生撤权时怎样生效,也要另行规定并验证。

↑ 返回延伸练习目录

商户项目 · 延伸练习 · 未运行

撤权后,旧记忆和缓存还能不能使用

核查日期:2026-10-12 18:05 JST;2026-10-16复核相关文档。来源为 Spring AI 2.0.1 与 Spring Security 7.1.1,发布日期未标明。撤权、会话和并发练习均未运行。

前置是本页授权入口、记忆生命周期及能暂停任务的假权限适配器。先让 A 合法读取虚构私有商户 1001 和 FAQ,产生会话记忆与缓存,再撤销其访问。运行前写出撤权生效点,并选择本练习政策:生效后,受保护内容不再加入新的模型请求或新的交付。

Chat Memory按会话 ID 读取历史;ID 应由服务端结合用户与会话分配。消息窗口管保留数量,Redis 记忆的默认 TTL 为无过期。标准退出清理 HTTP 会话和安全上下文;浏览器数据清理也不等于服务器记忆、向量历史和结果缓存已同步失效。因此,退出登录、撤权、停止重用上下文和删除存储需要分开约定。

  1. 撤权后沿用同一会话追问旧资料,检查旧助手消息、检索片段、摘要和向量记忆是否仍进入新提示词。按自己的设计排除失去授权的内容或建立不含它们的新上下文,同时保留仍有权进行的正常对话。
  2. 保留旧缓存并重复查询,验证命中路径使用当前授权。分别检查缓存归属和授权有效性;键里含用户 ID、TTL 尚未到期,都不能直接证明仍可交付。
  3. 在撤权前提交任务,在准备交付时暂停,撤权后再放行。检查交付前判断及后续记忆写入,避免旧结果重新进入当前会话。权限版本或会话代次若被用来协调并发,要记录真实检查与写入的边界,不能只凭字段存在就认定没有竞态。

提交请求 ID、会话所属用户、撤权事件序号、任务提交/执行/交付顺序、缓存命中、实际模型输入的假数据标记和最后判定。分别验收后续读取、旧上下文重用和迟到结果写入;授权查询失败应标记未知并进入事先定义的保守路径。

本练习不执行物理删除,也不处理备份和审计保留政策;更不能撤回用户已经看到的资料。尚未测得权限刷新延迟、全局撤权时效或并发保证,实际存储适配器和会话路由仍需按项目核对。

↑ 返回延伸练习目录

读完之后,独立完成

课后作业:自己做一遍

本篇共 12 道作业,先完成 9 道基础与综合任务,再按前置知识挑战进阶题。其中包含 2 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

能完成当前用户笔记查询工具,分别测试本人资料、他人资料、记录不存在和非法 ID 四种情况是否遵守权限规则。

本篇重点加练

可信身份传播与资源归属检查需要贯穿完整工具链路,不能只核对入口参数。

  • ToolContext 可信身份
  • 资源归属校验

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    画出一次ChatClient请求如何到达带注解的Java工具方法,列出方法名、参数描述和注册配置需要核查的项目。

    提交内容
    一份独立书面回答或标注图,包含自造例子、概念边界及判断依据。
    怎样算完成
    • 逐项回应题目要求,区分事实、假设与个人判断。
    • 能用自己的例子解释适用条件,记录尚未弄懂的点。

    练到的知识点ChatClient 接入 · @Tool 与 @ToolParam

  2. 作业 02代码推演

    将查笔记功能分成工具适配层、Service和Repository,推演工具未注册、Service抛异常与数据库无记录三种执行路径。

    提交内容
    手工推演记录,写明输入约定、中间状态、最终判断;可运行时附程序对照记录。
    怎样算完成
    • 推演包含中间过程,数据形状、单位、顺序或状态前后一致。
    • 主动检查至少一个边界或异常条件,能解释手算与运行差异。

    练到的知识点工具注册 · Service 与 Repository 分层

  3. 作业 03原理问答

    为同步工具调用和异步任务各画一条身份传播路径,逐段标注可信来源、生命周期和禁止从模型输出覆盖的字段。

    提交内容
    概念或合同说明、关系图与自造案例,明确事实、假设和适用条件。
    怎样算完成
    • 关键概念有明确边界,能用案例检验声明。
    • 未获证据支持的假设单独标注。

    练到的知识点ToolContext 可信身份 · 资源归属校验

综合应用

  1. 作业 04面试场景

    模型参数带userId=B,可信上下文身份却是A,目标笔记属于B。设计授权判定及错误响应,并说明每个身份值来自哪里。

    提交内容
    场景分析与决策记录,包含信息缺口、约束、备选方案和验证计划。
    怎样算完成
    • 方案回应给定约束,列出至少一种替代方案及代价。
    • 每项关键结论说明所需证据,不能核实的部分明确标注。

    练到的知识点ToolContext 可信身份 · 资源归属校验

  2. 作业 05编程实践

    为只读笔记工具实现DTO映射和最小集成测试,源对象含内部备注与密钥占位字段;分别保存直接调用和模型触发调用的证据。

    提交内容
    可复现的最小程序或实验材料、自写测试、环境说明与运行记录;题目允许纸面实验时提交完整案例表。
    怎样算完成
    • 提交物按说明可以重现;测试覆盖正常、边界与失败场景。
    • 运行证据与断言能核验声明的行为,注明模拟与真实执行范围。

    练到的知识点DTO 返回与敏感字段 · 实际工具链路验证

  3. 作业 06排错实验

    构造“普通接口可查、工具链路不可查”的故障,检查依赖注入、工具注册与模型选择三个环节,提交定位顺序及排除证据。

    提交内容
    最小故障输入、复现步骤、观测记录、定位依据及回归用例。
    怎样算完成
    • 其他人可按记录复现故障,并区分现象与已验证原因。
    • 定位过程有观测依据,回归用例可以再次暴露原问题。

    练到的知识点ChatClient 接入 · 工具注册 · 实际工具链路验证

  4. 作业 07编程实践

    在本地用两个用户轮流调用同一工具实例,加入一次异常退出;用假Repository记录每次查询身份与目标归属,检验上下文是否残留。

    提交内容
    可复现的小程序或题目允许的离线实验、自写测试、输入和运行记录。
    怎样算完成
    • 输入、环境和运行过程可重现,测试覆盖声明的重点条件。
    • 验证目标由合同确定,模拟结果不冒充真实服务证据。

    练到的知识点ToolContext 可信身份 · 资源归属校验

  5. 作业 08排错实验

    工具发起异步操作后原HTTP请求结束。设计可控执行顺序,观察延迟执行时还能取得哪些身份信息,并为丢失或错配建立回归测试。

    提交内容
    最小故障序列、观测记录、原因核对与可再次触发原问题的测试。
    怎样算完成
    • 故障可以稳定重现,现象与已验证原因分开。
    • 观测记录足以判断声明的合同是否被违反。

    练到的知识点ToolContext 可信身份 · 资源归属校验

  6. 作业 09面试场景

    一个管理员代用户查询笔记。定义操作者、资源所有者与被代理身份的区别,给出工具输入、可信上下文和审计记录的合同。

    提交内容
    场景评审记录,包含约束、备选方案、未决信息和验证计划。
    怎样算完成
    • 方案回应题目约束,取舍能对应到可核验的事实。
    • 给出失败或条件变化时的检查方式。

    练到的知识点ToolContext 可信身份 · 资源归属校验

进阶挑战 · 掌握前置后选做

  1. 作业 10设计评审

    给同一Service增加第二种模型适配方式,比较身份传递、DTO兼容、错误分类与测试复用,不在工具方法中复制业务逻辑。

    提交内容
    设计评审文档或对照实验报告,包含前置条件、方案取舍、验收证据与尚未验证的范围。
    怎样算完成
    • 比较使用一致条件与事先声明的标准,记录失败与未知结果。
    • 说明方案的适用前提、未覆盖风险及后续验证方法。

    练到的知识点Service 与 Repository 分层 · ToolContext 可信身份 · DTO 返回与敏感字段

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 11思维拓展 · 反例构造

    构造“Service已校验资源归属,但工具链路仍可能使用错误用户身份”的最小场景,指出复现需要控制哪些调用条件。

    提交内容
    具体反例、原主张的适用前提、复现或逻辑证明及结论边界。
    怎样算完成
    • 反例满足原主张所声称的前提,确实针对其结论。
    • 说明一个反例能推翻什么、不能进一步证明什么。

    练到的知识点ToolContext 可信身份 · 资源归属校验

  2. 作业 12思维拓展 · 条件变化

    当同一工具被HTTP、定时任务和模型三种入口复用时,重新评审身份合同与测试边界;说明哪些检查应在每个入口独立验证。

    提交内容
    变更前后条件表、重新评审的方案、检验用例与新增代价。
    怎样算完成
    • 只改变明确列出的条件,比较口径保持一致。
    • 修改方案说明收益、代价与仍未验证的范围。

    练到的知识点ToolContext 可信身份 · 资源归属校验

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗