news 2026/11/2 5:23:28

Spring 6.1 四大 HTTP 客户端选型指南:RestClient、WebClient、RestTemplate 与 HTTP Interface

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring 6.1 四大 HTTP 客户端选型指南:RestClient、WebClient、RestTemplate 与 HTTP Interface

Spring 6.1 正式版发布之后,我第一时间把手上几个还在用 RestTemplate 的老项目翻出来做了一轮改造试验。原因很简单:Spring 官方在这次版本里把 REST 客户端调用这件事重新梳理了一遍,明确给出了四条路线——RestClient、WebClient、RestTemplate、HTTP Interface。这四个东西不是简单的"新老交替"关系,而是各自面向不同场景的四种武器。很多同学一看到"新特性"三个字就急着把老代码全换成 RestClient,结果换完发现流式场景还是要 WebClient,声明式场景还是要 HTTP Interface,绕了一圈又回到原点。所以这篇内容我不打算写成 API 手册,而是按我实际踩坑的顺序,把这四种方式的定位、选型逻辑、核心代码、参数细节和避坑经验完整拆一遍,适合正在做 Spring 6.x 升级、或者准备重构服务间调用层的朋友参考,新手也能跟着代码直接跑起来。

1. 四种调用方式到底怎么选:先搞清楚定位再动手

1.1 一张表看懂四者的本质差异

在动手写代码之前,我建议先把这张对照表刻在脑子里。选型错了,后面写再多代码都是白费功夫。

调用方式编程模型是否阻塞所属模块典型场景
RestTemplate同步阻塞阻塞spring-web老项目维护、简单同步调用
RestClient同步阻塞阻塞spring-web新项目同步调用首选
WebClient响应式非阻塞非阻塞spring-webflux高并发、流式、异步编排
HTTP Interface声明式接口取决于底层spring-web像 Feign 一样的声明式调用

这张表里最关键的一列是"编程模型"。RestTemplate 和 RestClient 都是同步阻塞的,调用线程会一直挂在那里等结果;WebClient 基于 Reactor,返回的是 Mono/Flux,线程不会被占住;HTTP Interface 本身不实现通信,它是一层声明式代理,底层可以挂 RestClient、WebClient 或者 RestTemplate。

我见过太多人把 WebClient 当成"新版 RestTemplate"来用,然后在里面写.block(),这就等于把响应式的优势全部扔掉了,还额外背上了 Reactor 的学习成本。所以选型的第一原则是:先确定你的调用是同步还是异步,再决定用哪个客户端。

1.2 为什么 Spring 要同时保留四条路线

这个问题我在升级过程中反复问过自己。按常理,新版本应该淘汰旧的,为什么 RestTemplate 还在?后来想明白了,这是 Spring 团队对"存量生态"的妥协,也是对"场景差异"的尊重。

RestTemplate 从 Spring 3.0 就存在了,十几年下来,无数项目的代码里都是它。如果 Spring 6.1 直接把它标记为废弃,那升级成本会劝退一大半用户。所以官方的做法是:RestTemplate 进入维护模式,不再加新功能,但继续可用;同时推出 RestClient 作为同步调用的现代化替代,API 更流畅、更符合链式编程习惯;WebClient 继续负责响应式场景;HTTP Interface 则填补了"声明式调用"这块空白,让不喜欢手写 HTTP 细节的人可以像调本地方法一样调远程接口。

这个布局其实很清晰:同步用 RestClient,异步用 WebClient,声明式用 HTTP Interface,老代码继续 RestTemplate。四条路线各司其职,不存在谁完全替代谁的问题。

1.3 选型决策树:三个问题定方案

我总结了一个简单的决策流程,实际项目中按这个走基本不会错:

  1. 你的调用需要非阻塞或流式处理吗?需要 → WebClient,结束。不需要 → 进入第 2 步。
  2. 你希望像调本地方法一样调远程接口、减少样板代码吗?希望 → HTTP Interface(底层选 RestClient 或 WebClient)。不希望 → 进入第 3 步。
  3. 是新项目还是老项目?新项目 → RestClient。老项目且改动成本高 → 继续 RestTemplate,逐步迁移。

这个决策树背后的逻辑是:非阻塞是硬需求,优先级最高;声明式是开发体验优化,属于锦上添花;同步调用的新老之争则取决于迁移成本。把这三点想清楚,选型就不会纠结。

提示:HTTP Interface 不是独立的通信实现,它必须绑定一个底层客户端。所以严格来说,实际参与网络通信的只有 RestTemplate、RestClient、WebClient 三个,HTTP Interface 是"第四种写法"而非"第四个引擎"。

2. RestClient:同步调用的新门面,我为什么优先推荐它

2.1 RestClient 的设计思路与核心优势

RestClient 是 Spring 6.1 引入的同步 HTTP 客户端,官方定位是"RestTemplate 的现代化替代"。它的核心设计思路是** fluent API + 不可变构建**,也就是链式调用风格,每一步返回新的构建器对象,最终retrieve()或exchange()才真正发起请求。

我为什么优先推荐它?三个理由。第一,API 流畅,restClient.get().uri("/users/{id}", id).retrieve().body(User.class)一行搞定,比 RestTemplate 的getForObject(url, User.class, id)可读性强太多,尤其是带请求头、带查询参数的复杂场景。第二,它天然支持RestClient.Builder注入,和 Spring Boot 的自动配置无缝衔接,想加拦截器、改超时、配连接池都很自然。第三,它是同步的,没有 Reactor 的心智负担,团队里刚入行的同学也能快速上手。

对比 RestTemplate,RestClient 最大的改进在于错误处理。RestTemplate 默认对 4xx/5xx 抛异常,想拿到响应体做业务判断很别扭;RestClient 提供onStatus()钩子,可以精细控制哪些状态码抛异常、哪些交给业务处理,这一点在实际对接第三方接口时特别有用。

2.2 最小可用示例:从创建到发起请求

先看最基础的用法。创建 RestClient 有两种方式,一种是直接RestClient.create(),适合临时脚本;另一种是注入RestClient.Builder,适合正式项目。

// 方式一:直接创建(简单场景) RestClient client = RestClient.create(); // 方式二:注入 Builder(推荐,可统一配置) @Service public class UserService { private final RestClient restClient; public UserService(RestClient.Builder builder) { this.restClient = builder .baseUrl("https://api.example.com") .defaultHeader("X-App-Key", "demo-key") .build(); } public User getUser(Long id) { return restClient.get() .uri("/users/{id}", id) .retrieve() .body(User.class); } }

这里有个细节值得说:uri("/users/{id}", id)用的是 URI 模板变量,RestClient 会自动做 URL 编码。我早期图省事直接字符串拼接"/users/" + id,结果遇到 id 里带特殊字符时请求直接 400,排查了半天。养成用模板变量的习惯,能省掉一大类编码问题。

2.3 请求头、查询参数与请求体的完整写法

实际项目里,一个请求往往要带认证头、追踪 ID、查询参数、JSON 请求体。RestClient 把这些都做成了链式方法,写起来很顺:

public Order createOrder(OrderRequest req, String token) { return restClient.post() .uri(uriBuilder -> uriBuilder .path("/orders") .queryParam("source", "web") .build()) .header("Authorization", "Bearer " + token) .header("X-Trace-Id", UUID.randomUUID().toString()) .contentType(MediaType.APPLICATION_JSON) .body(req) .retrieve() .body(Order.class); }

注意uriBuilder那个 lambda,它适合参数较多或需要动态拼接的场景。如果参数固定,直接用uri("/orders?source=web")也行,但涉及用户输入的值一定要走queryParam,让框架帮你编码。

请求体这块,.body(req)会自动根据contentType选择消息转换器。Spring 默认注册了 Jackson,所以传对象就是 JSON。如果对方接口要求表单格式,把contentType改成MediaType.APPLICATION_FORM_URLENCODED,然后传MultiValueMap即可。

2.4 错误处理:onStatus 是我最喜欢的一个钩子

RestTemplate 时代,想对 404 做特殊处理,得写ResponseErrorHandler,代码又长又绕。RestClient 的onStatus()直接把这个痛点解决了:

public User getUserSafely(Long id) { return restClient.get() .uri("/users/{id}", id) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { if (response.getStatusCode().value() == 404) { throw new UserNotFoundException(id); } throw new BizException("客户端错误: " + response.getStatusCode()); }) .onStatus(HttpStatusCode::is5xxServerError, (request, response) -> { throw new RemoteServiceException("下游服务异常"); }) .body(User.class); }

这段代码的意图很明确:404 转成业务异常UserNotFoundException,其他 4xx 转成通用业务异常,5xx 转成远程服务异常。这样上层调用方就能按异常类型做不同处理,而不是拿到一个笼统的HttpClientErrorException再去解析状态码。

注意:onStatus里抛出的异常会原样向上传播,不会被包装。所以自定义异常最好继承RuntimeException,避免被 Spring 的异常转换机制二次处理。

2.5 超时与连接池:别用默认值上生产

RestClient 本身不管理连接池,它依赖底层的ClientHttpRequestFactory。默认情况下用的是 JDK 的HttpURLConnection,没有连接池,超时也是无限等待。这在生产环境是灾难。

我的做法是显式配置JdkClientHttpRequestFactory(基于 Java 11+ 的 HttpClient)或者 Apache HttpClient 5:

@Bean public RestClient restClient() { var requestFactory = new JdkClientHttpRequestFactory(); requestFactory.setReadTimeout(Duration.ofSeconds(5)); return RestClient.builder() .requestFactory(requestFactory) .baseUrl("https://api.example.com") .build(); }

读超时设 5 秒是我根据下游接口的 P99 延迟定的,一般建议是 P99 的 1.5 到 2 倍。连接超时则通过底层 HttpClient 配置,通常 2 到 3 秒足够。这两个值不设,等于把服务稳定性交给运气,我见过因为下游卡住导致整个线程池被拖垮的案例,教训很深刻。

3. WebClient:响应式场景的唯一正解

3.1 WebClient 的适用边界:什么时候必须用它

WebClient 属于 spring-webflux 模块,返回Mono或Flux,天生非阻塞。它的适用边界很清晰:高并发、流式传输、需要并行编排多个远程调用。

举个我实际遇到的场景:一个聚合接口要同时调用户服务、订单服务、库存服务,三个调用互不依赖。用 RestClient 串行调,总耗时是三者之和;用 WebClient 的Mono.zip并行调,总耗时是三者最大值。当每个调用 200ms 时,串行 600ms,并行 200ms,差距三倍。这种场景下 WebClient 不是"更好",而是"必须"。

另一个场景是流式响应,比如下游返回 SSE 或者大文件流。RestClient 会把整个响应读进内存,WebClient 可以用Flux<DataBuffer>边收边处理,内存占用完全不是一个量级。

3.2 基础用法与线程模型说明

@Service public class ReactiveUserService { private final WebClient webClient; public ReactiveUserService(WebClient.Builder builder) { this.webClient = builder .baseUrl("https://api.example.com") .defaultHeader("X-App-Key", "demo-key") .build(); } public Mono<User> getUser(Long id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, resp -> Mono.error(new UserNotFoundException(id))) .bodyToMono(User.class); } }

注意这里返回的是Mono<User>,方法调用时并不会真正发请求,只有订阅(subscribe)时才触发。这是响应式编程最容易被误解的地方。很多同学写完发现"请求没发出去",就是因为忘了订阅,或者在上层把 Mono 丢掉了。

线程模型上,WebClient 基于 Netty(默认)或 Reactor Netty,事件循环线程数默认是 CPU 核数。这意味着少量线程就能支撑大量并发连接,但也意味着你不能在响应式链路里做阻塞操作,否则事件循环线程被卡住,整个客户端的吞吐会断崖式下跌。

3.3 并行编排:Mono.zip 的实战用法

public Mono<AggregatedResult> aggregate(Long userId) { Mono<User> userMono = userService.getUser(userId); Mono<List<Order>> ordersMono = orderService.getOrders(userId).collectList(); Mono<Integer> stockMono = stockService.getStock(userId); return Mono.zip(userMono, ordersMono, stockMono) .map(tuple -> new AggregatedResult( tuple.getT1(), tuple.getT2(), tuple.getT3())); }

Mono.zip会并行订阅三个 Mono,全部完成后合并结果。这里有个坑:如果其中一个 Mono 报错,整个 zip 会立即失败,其他两个即使成功也拿不到结果。如果业务上允许部分失败,要用onErrorResume给每个 Mono 兜底,返回一个默认值,保证 zip 能正常完成。

3.4 阻塞调用的正确姿势:什么时候可以 block

我必须强调:在响应式链路里调用.block()是反模式。但现实是,很多老项目是同步架构,只在某个局部想用 WebClient 的并行能力,这时候在边界处 block 一次是可以接受的。

正确姿势是把 block 限制在最外层,且设置超时:

public AggregatedResult aggregateBlocking(Long userId) { return aggregate(userId) .block(Duration.ofSeconds(3)); }

block(Duration)带超时参数,避免无限等待。如果不带参数,一旦下游不响应,线程会永久挂起。这个细节我在代码评审里见过太多次遗漏,一定要养成带超时的习惯。

注意:在 WebFlux 的 Controller 里绝对不能 block,会直接抛异常。只有在同步的 Service 边界、且确认当前线程不是事件循环线程时,才可以谨慎使用。

4. RestTemplate:老将不死,但别再往它身上加新功能

4.1 RestTemplate 的现状与维护策略

RestTemplate 在 Spring 6.1 里依然可用,但官方已经明确它进入维护模式——不再新增功能,只做必要的 bug 修复。这不代表它不能用,而是说新项目没必要选它。

我手上有个跑了六年的老系统,里面几十个 Service 全是 RestTemplate。这次升级我的策略是:不动存量,新代码用 RestClient,等某个模块大改时顺手迁移。这种渐进式策略比一次性全量替换稳妥得多,风险可控,测试成本也低。

4.2 典型用法与它绕不开的痛点

@Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .rootUri("https://api.example.com") .setConnectTimeout(Duration.ofSeconds(3)) .setReadTimeout(Duration.ofSeconds(5)) .additionalInterceptors(new TraceInterceptor()) .build(); }

RestTemplate 的痛点主要集中在三处。第一,API 设计偏"方法重载爆炸",getForObject、getForEntity、exchange各种组合,参数顺序容易记混。第二,错误处理要靠自定义ResponseErrorHandler,代码量大。第三,请求头的设置方式不统一,有时候用HttpHeaders,有时候用RequestEntity,风格割裂。

这些痛点在 RestClient 里都被解决了,所以我的建议很直接:新代码不要再用 RestTemplate,老代码按需迁移。

4.3 从 RestTemplate 迁移到 RestClient 的对照表

迁移时最实用的是一张方法对照表,我整理了自己迁移过程中用到的映射关系:

RestTemplate 写法RestClient 等价写法
getForObject(url, T.class)get().uri(url).retrieve().body(T.class)
getForEntity(url, T.class)get().uri(url).retrieve().toEntity(T.class)
postForObject(url, req, T.class)post().uri(url).body(req).retrieve().body(T.class)
exchange(url, POST, entity, T.class)method(POST).uri(url).body(...).retrieve().toEntity(T.class)
ResponseErrorHandleronStatus(...)

迁移时有个容易忽略的点:RestTemplate 的getForEntity返回的ResponseEntity包含状态码和响应头,RestClient 的toEntity同样保留这些信息,但方法名从body变成toEntity,别写错了。

5. HTTP Interface:声明式调用,像 Feign 一样的体验

5.1 HTTP Interface 的核心机制

HTTP Interface 是 Spring 6 引入的声明式 HTTP 客户端。你定义一个 Java 接口,加上@HttpExchange注解,Spring 会自动生成代理实现,把方法调用翻译成 HTTP 请求。这个思路和 OpenFeign 几乎一样,但它是 Spring 原生的,不需要额外引入 Feign 依赖。

它的核心机制是动态代理 + 注解解析。启动时,HttpServiceProxyFactory扫描接口上的注解,为每个方法生成请求模板;调用时,代理拦截方法调用,根据模板组装请求,交给底层客户端(RestClient 或 WebClient)执行,再把响应反序列化成方法返回值。

这个设计的价值在于:把 HTTP 细节从业务代码里彻底剥离。业务层只看到接口方法,不关心 URL、请求头、序列化,测试时也容易 mock。

5.2 定义接口与绑定底层客户端

@HttpExchange("/users") public interface UserApi { @GetExchange("/{id}") User getUser(@PathVariable Long id); @PostExchange User createUser(@RequestBody User user); @GetExchange List<User> listUsers(@RequestParam("page") int page, @RequestParam("size") int size); }

接口定义好之后,需要创建代理实例并绑定底层客户端:

@Configuration public class HttpInterfaceConfig { @Bean public UserApi userApi(RestClient.Builder builder) { RestClient restClient = builder .baseUrl("https://api.example.com") .build(); HttpServiceProxyFactory factory = HttpServiceProxyFactory .builderFor(RestClientAdapter.create(restClient)) .build(); return factory.createClient(UserApi.class); } }

这里的关键是RestClientAdapter.create(restClient),它把 RestClient 适配成 HTTP Interface 的底层引擎。如果底层想用 WebClient,换成WebClientAdapter.create(webClient)即可,接口定义完全不用改。这种"接口与实现分离"的设计,让切换底层客户端变得非常轻量。

5.3 注解体系与参数绑定规则

HTTP Interface 的注解分两层:接口级用@HttpExchange定义公共路径前缀,方法级用@GetExchange、@PostExchange、@PutExchange、@DeleteExchange、@PatchExchange定义具体操作。

参数绑定规则需要记牢:

  • @PathVariable:绑定路径变量,对应 URI 模板里的{id}
  • @RequestParam:绑定查询参数
  • @RequestBody:绑定请求体
  • @RequestHeader:绑定请求头
  • 无注解的简单类型参数:默认按查询参数处理(这点和 Spring MVC 不同,容易踩坑)

最后一条我要特别提醒:在 Spring MVC 的 Controller 里,无注解参数默认是查询参数,但在 HTTP Interface 里行为一致,不过如果参数是复杂对象且无注解,会被当成请求体。这个规则我一开始没搞清楚,写了个getUser(UserQuery query)结果请求体被塞了对象,GET 请求带 body 直接报错。GET 请求的参数一定要显式加@RequestParam或@PathVariable。

5.4 与 OpenFeign 的取舍

很多团队已经在用 OpenFeign,会问要不要换成 HTTP Interface。我的看法是:如果现有 Feign 用得好,没必要为了"原生"而迁移。Feign 生态成熟,和 Spring Cloud 集成度高,服务发现、负载均衡、熔断降级都有现成方案。

HTTP Interface 的优势在于轻量、无额外依赖、和 Spring 6 的客户端体系一致。适合的场景是:项目没有引入 Spring Cloud、只是简单调几个外部接口、或者想减少依赖数量。如果项目已经在 Spring Cloud 体系里,Feign 依然是更省心的选择。

6. 实操踩坑实录:这些问题我替你踩过了

6.1 常见问题速查表

问题现象根因解决方案
RestClient 请求 400,URL 里有特殊字符字符串拼接未编码改用uri(template, vars)模板变量
WebClient 请求不发出忘记订阅 Mono确保返回值被订阅或 block
HTTP Interface 启动报找不到代理未配置HttpServiceProxyFactory补上工厂 Bean
超时后线程不释放未配置读超时显式设置setReadTimeout
响应中文乱码未指定字符集设置Accept头和转换器字符集
大文件 OOM同步客户端全量读入内存改用 WebClient 流式处理

6.2 三个我印象最深的坑

第一个坑:RestClient 的默认超时是无限。我在测试环境跑得好好的,上了预发环境,下游某个接口偶发卡顿,结果调用方线程全部挂起,线程池十分钟就满了。排查时才发现 RestClient 默认没有读超时。这个坑的教训是:任何 HTTP 客户端上生产前,必须显式配置连接超时和读超时,没有例外。

第二个坑:WebClient 在同步方法里 block 导致死锁。有个同学在@Async方法里用 WebClient 然后 block,而@Async的线程池和 Reactor 的事件循环线程有交叉,结果互相等待。后来改成在响应式链路里全程不 block,用then、flatMap编排,问题消失。响应式的链路要么全响应式,要么在边界处一次性 block,中间不能混。

第三个坑:HTTP Interface 的路径拼接。接口级@HttpExchange("/users")加方法级@GetExchange("/{id}"),最终路径是/users/{id},这个没问题。但如果接口级写成@HttpExchange("/users/")带尾斜杠,方法级又带前斜杠,会拼出/users//{id}双斜杠,某些网关会直接 404。路径拼接时统一不带尾斜杠,方法级带前斜杠,这个约定要团队统一。

6.3 性能与稳定性上的几条经验

第一,连接池一定要配。RestClient 和 RestTemplate 底层如果用 JDK HttpClient,默认没有连接池,每次请求新建连接,高并发下 TIME_WAIT 堆积严重。换成 Apache HttpClient 5 并配置PoolingHttpClientConnectionManager,最大连接数和每路由连接数按下游 QPS 估算,一般最大连接数 = 下游峰值 QPS × 平均响应时间(秒) × 1.5。

第二,重试要谨慎。GET 请求可以重试,POST 请求重试可能导致重复下单。如果要用重试,确保接口幂等,或者用幂等键。Spring 6 的RestClient本身不带重试,需要自己包一层或者用 Resilience4j。

第三,日志要能定位问题。请求日志至少记录:目标 URL、方法、状态码、耗时、追踪 ID。追踪 ID 通过拦截器透传,这样一次调用链路在日志里能串起来。我见过太多团队只记 URL 不记耗时,出问题时根本不知道慢在哪。

7. 我的最终选型建议与迁移路线

7.1 新项目的默认组合

如果让我从零搭一个新项目,我的默认组合是:同步调用用 RestClient,声明式接口用 HTTP Interface 绑定 RestClient,需要并行的局部用 WebClient。这个组合覆盖了 95% 的场景,依赖干净,学习曲线平缓。

具体落地时,我会在配置类里统一构建一个RestClientBean,配好 baseUrl、超时、拦截器、连接池,然后所有 Service 注入它。声明式接口则单独建一个api包,每个下游服务一个接口,通过HttpServiceProxyFactory统一创建代理。

7.2 老项目的渐进迁移路线

老项目不要一刀切。我的路线是三步走:第一步,先把 RestTemplate 的超时和连接池补齐,这是止血,成本最低收益最高;第二步,新写的 Service 一律用 RestClient,形成示范;第三步,等某个模块因为业务需求要大改时,顺手把该模块的 RestTemplate 迁到 RestClient,用前面那张对照表逐方法替换,配合集成测试验证。

这个路线的好处是风险分散,每一步都可回滚。我实际执行下来,一个中等规模的项目,三个月内迁移了 60% 的调用点,没有出过一次线上事故。

7.3 团队协作上的两个约定

最后分享两个团队层面的约定,比技术选型更重要。第一,统一封装。不要让每个开发自己 new RestClient,统一在一个配置类里构建,超时、拦截器、错误处理全部标准化,避免各写各的。第二,统一异常体系。远程调用的异常要转换成团队统一的业务异常,带上下游服务名和错误码,这样上层处理逻辑一致,排查时也能快速定位是哪个下游出的问题。

这两个约定看起来是"管理问题",但实际决定了这套客户端体系能不能长期稳定运行。技术选型只是起点,工程规范才是终点。我在多个项目里验证过,凡是这两点做到位的团队,远程调用的故障率明显更低,排查效率也高得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 5:22:33

图论概念映射训练:从简单图、二部图到补图的精准理解

简介&#xff1a;本资源是哈尔滨工业大学《集合论与图论&#xff08;下&#xff09;》慕课MOOC的配套习题答案整理&#xff0c;面向计算机、数学及相关专业本科生及自学者&#xff0c;助力系统掌握图论核心理论与解题思路。内容覆盖图的基本类型&#xff08;简单图、有向/无向/…

作者头像 李华
网站建设 2026/11/2 5:22:05

ITIL 4 Foundation中文版资料整理:术语对照与知识仓库搭建

做运维和IT服务管理这行的朋友&#xff0c;硬盘里大概率都躺着一个叫"ITIL资料"的文件夹&#xff1a;里面有中英文混杂的PDF、几张来源不明的思维导图、一个从某个群里转存过来的压缩包&#xff0c;还有几份不知道对应哪一版的术语表。我自己就经历过这个阶段——第一…

作者头像 李华
网站建设 2026/11/2 5:21:30

给Agent加权限门禁:dsh-permission-rules踩坑与落地实践

1. 为什么我要给 Agent 加一道权限门禁给 Agent 加权限门禁这件事&#xff0c;最初并不是我主动想做的。起因很简单&#xff1a;我手上跑着一个基于 DeepSeek Harness 的自动化工作流 Agent&#xff0c;日常负责帮我处理一批本地文件的整理、归档和部分脚本调用。某天它在执行一…

作者头像 李华
网站建设 2026/11/2 5:20:52

基于STM32的可解释AI健康监测系统设计与实现

简介&#xff1a;本资源是一份面向嵌入式与人工智能方向本科生的毕业设计文档&#xff0c;聚焦智能健康监测与建议系统的整体设计与实现&#xff0c;解决传统健康管理缺乏实时性、个性化与主动干预能力的问题。文档以传感器数据采集、AI算法建模&#xff08;含决策树、随机森林…

作者头像 李华
网站建设 2026/11/2 5:20:26

Electron 封装 DeepSeek Harness:桌面端 profile 管理与打包实践

1. 为什么要把 DeepSeek Harness 塞进 Electron 壳里第一次听到"用 Electron 封装 DeepSeek Harness"这个需求时&#xff0c;我脑子里冒出来的第一个念头是&#xff1a;这不是多此一举吗&#xff1f;Harness 本身已经能在命令行里跑得好好的&#xff0c;套一层桌面壳…

作者头像 李华
网站建设 2026/11/2 5:17:49

Univer SDK:可编程文档引擎与单元格级权限控制

1. 项目概述&#xff1a;Univer 是什么&#xff0c;它解决的不是“又一个表格库”问题Univer 不是 Excel 的简化版&#xff0c;也不是某个大厂内部工具的开源马甲。我第一次在客户现场看到它跑起来时&#xff0c;第一反应是&#xff1a;“这玩意儿居然真能把协作逻辑塞进前端 S…

作者头像 李华