<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/feeds/style.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>jetbrains</title>
    <description>rssume processed feed for jetbrains</description>
    <link>/feeds/jetbrains</link>
    <atom:link href="/feeds/jetbrains" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Thu, 4 Jun 2026 09:56:03 +0000</lastBuildDate>
    <generator>rssume</generator>
    <item>
      <title>告别粘贴令牌：JetBrains IDE 插件的 OAuth2 登录</title>
      <dc:creator>Jakub Chrzanowski</dc:creator>
      <category>intellij-platform</category>
      <category>plugins</category>
      <category>authentication</category>
      <category>oauth2</category>
      <category>plugin-development</category>
      <description>[AI 摘要] 本文介绍了如何在 JetBrains IDE 插件中安全实现 OAuth2 登录，避免用户手动粘贴令牌，并展示了完整的 PKCE 流程。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文介绍了如何在 JetBrains IDE 插件中安全实现 OAuth2 登录，避免用户手动粘贴令牌，并展示了完整的 PKCE 流程。</div>当插件需要账户数据时，一个简单的 API 调用就会变成一个身份验证问题。这种不好的捷径大家都不陌生：要求用户创建个人访问令牌（PAT），让他们将其粘贴到设置中，并祈祷它永远不会泄露。

<p>对于一个 JetBrains IDE 插件，请改用此流程：用户点击 <strong>登录</strong> 按钮，浏览器打开，提供商处理登录，IDE 接收回调，插件存储令牌。</p>

<p>在高层面上，插件将：</p>

<ol>
<li>在浏览器中打开提供商的授权页面。</li>
<li>在 IDE 内部接收 OAuth2 回调。</li>
<li>验证返回的 <code>state</code>。</li>
<li>使用 PKCE 交换授权码。</li>
<li>将访问令牌存储在 <code>PasswordSafe</code> 中。</li>
</ol>

<p>本文使用 GitHub 作为 OAuth2 提供商，但同样的模式也适用于其他地方。作用域、URL、令牌响应和刷新规则会有所不同。</p>

<p><strong>示例代码：</strong><a href="https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/oauth2" rel="noopener noreferrer">https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/oauth2</a></p>

<figure><div>

</div></figure>

<blockquote></blockquote>

<h2>思维模型</h2>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/image-3.png" alt=""></figure>

<p>将 OAuth2 理解为酒店房卡会更容易。
办理入住时，你拿到的不是万能钥匙。你拿到的是一张只针对你房间的卡，也许还能用电梯或健身房。当你退房后，这张卡就失效了。</p>

<p>这才是关键点：允许访问，但访问是受限且临时的。OAuth2 访问令牌的工作原理相同。用户通过提供商登录，插件获得一个用于用户批准的 API 访问的令牌。插件永远不需要用户的密码。</p>

<p>这种方法优于要求用户将长期有效的密钥粘贴到设置中。用户留在他们已经信任的浏览器登录流程中，而提供商则控制着作用域、过期时间和撤销。</p>

<p>因此，目标很简单：为插件获取一个有限的令牌，而无需让用户手动粘贴一个。问题在于，桌面插件无法保护传统的客户端密钥。</p>

<h2>为什么 PKCE 是故事的一部分</h2>

<p>在 Web 应用中，服务器可以在后端保存客户端密钥。桌面插件无法做到这一点。任何捆绑在插件中的东西都可以被检查。</p>

<p>这就是 PKCE 的用武之地。PKCE 代表 Proof Key for Code Exchange，它将返回的授权码与创建它的登录请求绑定在一起。</p>

<p>在打开浏览器之前，插件会创建一个随机的 <code>code_verifier</code> 并向 GitHub 发送一个派生的 <code>code_challenge</code>。稍后，当 GitHub 通过临时代码重定向回来时，插件会将原始的验证器发送到令牌端点。</p>

<p>GitHub 会比较验证器与早期的质询。如果它们不匹配，则不会发放令牌。这意味着返回的代码本身不足以获取令牌，这正是我们桌面插件所需要的。</p>

<h2>流程</h2>

<p>以下是完整流程：</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/image-1.png" alt=""></figure>

<ol>
<li>用户点击 <strong>使用 GitHub 登录</strong>。</li>
<li>插件创建 <code>state</code>、<code>code_verifier</code> 和 <code>code_challenge</code>。</li>
<li>插件在浏览器中打开 GitHub 的授权 URL。</li>
<li>GitHub 将带有 <code>state</code> 和临时 <code>code</code> 的信息重定向回 IDE。</li>
<li>插件验证 <code>state</code>。</li>
<li>插件使用代码和验证器交换访问令牌。</li>
<li>插件将令牌存储在 <code>PasswordSafe</code> 中并调用 GitHub API。</li>
</ol>

<h2>代码中的流程位置</h2>

<p>示例代码位于 <a href="https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/oauth2" rel="noopener noreferrer"><code>code_samples/oauth2</code></a>。上面的流程被分割成四个小部分：</p>

<ul>
<li><code>plugin.xml</code> 注册设置界面和本地回调处理程序。</li>
<li><code>AuthConfigurable</code> 为用户提供登录和注销按钮。</li>
<li><code>AuthRestService</code> 处理 GitHub 发送到 IDE 内置 HTTP 服务器的请求。</li>
<li><code>AuthService</code> 创建 OAuth2 请求、交换代码、存储令牌并调用 API。</li>
</ul>

<p>这种分割是需要注意的主要事情。当 OAuth2 被描述为一个庞大的机制时，会让人觉得混乱。在代码中，当每个类负责流程的一部分时，就更容易跟踪了。</p>

<h3>注册界面和回调</h3>

<p>插件描述符注册了两样东西：</p>

<ul>
<li>设置页面</li>
<li>本地 HTTP 回调处理程序</li>
</ul>

<pre>&lt;extensions defaultExtensionNs="com.intellij"&gt;
  &lt;applicationConfigurable
      instance="org.intellij.sdk.oauth2.AuthConfigurable"
      id="org.intellij.sdk.oauth2.AuthConfigurable"
      displayName="My Plugin Auth"/&gt;

  &lt;httpRequestHandler implementation="org.intellij.sdk.oauth2.AuthRestService"/&gt;
&lt;/extensions&gt;
</pre>

<p><code>applicationConfigurable</code> 添加了设置页面。<code>httpRequestHandler</code> 向 IDE 的内置 HTTP 服务器注册了一个处理程序，因此对 <code>/api/myplugin</code> 的请求可以被路由到 <code>AuthRestService</code>。这为 GitHub 在浏览器授权后提供了一个本地重定向目标。</p>

<h3>让设置界面保持简洁</h3>

<p><code>AuthConfigurable</code> 是设置界面。在示例中，它继承自 <code>BoundConfigurable</code>，使用 Kotlin UI DSL，其职责很小：</p>

<ul>
<li>如果未连接，则显示 <strong>使用 GitHub 登录</strong></li>
<li>如果已连接，则显示用户名和 <strong>注销</strong></li>
</ul>

<p>面板观察 <code>AuthService.state</code>，视图是一个小的状态切换：</p>

<pre>private fun createView(state: AuthState) = panel {
  when (state) {
    is AuthState.Connected -&gt; row("Username") {
      label(state.username ?: "Unknown")
      button("Logout") { authService.logout() }
    }

    is AuthState.Disconnected -&gt; row {
      button("Login with GitHub") { authService.login() }
    }
  }
}
</pre>

<h3>接收浏览器重定向</h3>

<p>批准后，GitHub 会重定向回 IDE 的内置 HTTP 服务器。回调使用 IntelliJ 平台 <a href="https://github.com/JetBrains/intellij-community/blob/master/platform/built-in-server/src/org/jetbrains/ide/RestService.kt" rel="noopener noreferrer"><code>RestService</code></a> 处理：</p>

<pre>http://localhost:&lt;built-in-server-port&gt;/api/myplugin
</pre>

<p><code>AuthRestService</code> 读取 <code>state</code> 和 <code>code</code>，找到待处理的登录请求，完成它，并返回一个小的 HTML 响应：</p>

<pre>val parameters = urlDecoder.parameters()
val state = parameters["state"]?.firstOrNull()
    ?: return "No authorization state found"
val code = parameters["code"]?.firstOrNull()
    ?: return "No authorization code found"
val callback = service&lt;AuthService&gt;().callbacks.remove(state)
    ?: return "No active OAuth request found"

callback.complete(code)
sendResponse(
  request,
  context,
  response("text/html", Unpooled.wrappedBuffer(HTML_RESPONSE.toByteArray()))
)
return null
</pre>

<p>之后，<code>AuthService</code> 通过交换代码获取令牌来继续流程。</p>

<h3>运行流程</h3>

<p><code>AuthService</code> 创建登录请求，等待回调，并交换返回的代码：</p>

<pre>private suspend fun requestToken(): String {
  val state = UUID.randomUUID().toString()
  val codeVerifier = UUID.randomUUID().toString().padStart(43, '0')
  val callback = CompletableDeferred&lt;String&gt;().also { callbacks[state] = it }

  try {
    BrowserUtil.browse(authorizationUrl(state, codeVerifier))
    return exchangeCodeForToken(callback.await(), codeVerifier)
  } finally {
    callbacks.remove(state)?.cancel()
  }
}
</pre>

<p><code>CompletableDeferred</code> 是 HTTP 回调与 <code>requestToken()</code> 中等待的协程之间的桥梁。<code>requestToken()</code> 等待 <code>callback.await()</code>，而 <code>AuthRestService</code> 在 GitHub 通过重定向返回代码时完成同一个对象。</p>

<p><code>padStart(43, '0')</code> 之所以存在，是因为 GitHub 要求 PKCE 验证器满足最小验证器长度。有些提供商不那么严格，可能直接接受 UUID，但 GitHub 要求验证器至少 43 个字符长。</p>

<p>授权 URL 携带了两个安全检查：<code>state</code> 和 PKCE 质询。</p>

<pre>private fun authorizationUrl(state: String, codeVerifier: String) = url(
  AUTHORIZATION_URL,
  "client_id" to CLIENT_ID,
  "scope" to SCOPES,
  "state" to state,
  "redirect_uri" to redirectUri,
  "code_challenge" to codeChallenge(codeVerifier),
  "code_challenge_method" to "S256",
)
</pre>

<p>质询派生自代码验证器：</p>

<pre>private fun codeChallenge(codeVerifier: String) =
  DigestUtil.sha256().digest(codeVerifier.toByteArray())
    .let { Base64.getUrlEncoder().withoutPadding().encodeToString(it) }
</pre>

<p>实际的令牌交换是对 GitHub 令牌端点的 POST 请求：</p>

<pre>private suspend fun exchangeCodeForToken(code: String, codeVerifier: String) =
  withContext(Dispatchers.IO) {
    parseAccessToken(post(tokenUrl(code, codeVerifier), null).readString())
  }
</pre>

<p>令牌请求发回临时代码和原始验证器：</p>

<pre>private fun tokenUrl(code: String, codeVerifier: String) = url(
  ACCESS_TOKEN_URL,
  "client_id" to CLIENT_ID,
  "client_secret" to CLIENT_SECRET,
  "code" to code,
  "redirect_uri" to redirectUri,
  "code_verifier" to codeVerifier,
)
</pre>

<p>示例中包含一个 GitHub 客户端密钥，因为 GitHub 的 OAuth 应用程序流程需要它。对于桌面插件，不要将该值视为机密。PKCE 才是这里的有用检查：没有原始验证器，返回的代码就毫无用处。</p>

<h2>将令牌存储在 <code>PasswordSafe</code> 中</h2>

<p>一旦提供商返回访问令牌，就将其存储在 <code>PasswordSafe</code> 中。常规的持久设置对于首选项来说没问题，但不适合用于访问令牌。</p>

<p>示例使用一个凭据键：</p>

<pre>private val credentials =
  CredentialAttributes(generateServiceName("MyPluginAuth", "OAuthToken"))
</pre>

<p>在启动时，服务会恢复之前保存的现有令牌：</p>

<pre>init {
  coroutineScope.launch {
    val token = PasswordSafe.instance.getPassword(credentials) ?: return@launch
    _state.value = AuthState.Connected(fetchUserProfile(token))
  }
}
</pre>

<p>存储和清除通过同一个辅助函数进行：</p>

<pre>private fun storeToken(token: String?) =
  PasswordSafe.instance.setPassword(credentials, token)
</pre>

<p>对于真正的插件，请使用稳定的服务名称。如果您支持多个帐户，请为每个提供商帐户存储一个凭据。</p>

<p>平台源码：<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/credential-store/src/ide/passwordSafe/PasswordSafe.kt" rel="noopener noreferrer"><code>PasswordSafe</code></a>、<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/credential-store/src/credentialStore/CredentialStore.kt" rel="noopener noreferrer"><code>CredentialStore</code></a> 和 <a href="https://github.com/JetBrains/intellij-community/blob/master/platform/credential-store/src/credentialStore/CredentialAttributes.kt" rel="noopener noreferrer"><code>CredentialAttributes</code></a>。</p>

<h2>调用 API</h2>

<p>登录后，插件的其余部分不应关心 OAuth2 是如何工作的。示例使用外部 <code>org.kohsuke:github-api</code> 库，并将令牌传入 <code>GitHubBuilder</code> 以获取当前的 GitHub 用户名：</p>

<pre>private suspend fun fetchUserProfile(token: String): String? =
  withContext(Dispatchers.IO) {
    runCatching { GitHubBuilder().withOAuthToken(token).build().myself.login }
      .onFailure { thisLogger().warn("Failed to fetch user profile", it) }
      .getOrNull()
  }
</pre>

<p>在更大的插件中也要保持这种边界。API 代码不应知道浏览器登录是如何工作的。</p>

<h2>总结</h2>

<p>插件中的 OAuth2 主要是将职责放在正确的位置。</p>

<p>让提供商处理登录。让浏览器处理面向用户的登录。让 IDE 接收回调。让 <code>AuthService</code> 处理令牌。一旦令牌存储在 <code>PasswordSafe</code> 中，插件的其余部分就可以不再关心用户是如何认证的了。</p>

<p>如果你正在构建类似的东西，或者遇到了提供商的边缘情况，请到 <a href="https://platform.jetbrains.com/c/intellij-platform/5" rel="noopener noreferrer">JetBrains 平台论坛</a>提出。
祝你好运！</p><p><em>由 mimo-v2.5 模型翻译，花费 8845 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/platform/2026/06/stop-pasting-tokens-oauth2-login-for-jetbrains-ide-plugins/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=platform&amp;p=710790</guid>
      <pubDate>Mon, 1 Jun 2026 13:46:25 +0000</pubDate>
    </item>
    <item>
      <title>Mellum2 开源发布：专为AI工作流设计的快速模型</title>
      <dc:creator>Anton Semenkin</dc:creator>
      <category>news</category>
      <category>releases</category>
      <category>ai</category>
      <category>mellum</category>
      <category>open-source</category>
      <description>[AI 摘要] JetBrains 开源了专为软件工程任务优化的快速AI模型 Mellum2，旨在高效解决生产环境中的延迟和成本问题。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> JetBrains 开源了专为软件工程任务优化的快速AI模型 Mellum2，旨在高效解决生产环境中的延迟和成本问题。</div><p><strong>Mellum2 从头开始训练，专为实际部署而设计，适用于路由、问答、子智能体以及软件工程系统中的私有AI应用。</strong></p>

<p>今天，我们开源了 Mellum2，这是一个为了解决生产级AI最棘手问题——延迟、吞吐量和成本——而设计的120亿参数模型。它基于 Apache 2.0 许可证从头构建并发布，<a href="https://www.jetbrains.com/mellum/" rel="noopener noreferrer">Mellum2</a> 为您的基础设施提供了一种高性能、高性价比的选择。</p>

<p>Mellum 起初用于<a href="https://blog.jetbrains.com/ai/2025/04/mellum-goes-open-source-a-purpose-built-llm-for-developers-now-on-hugging-face/" rel="noopener noreferrer">代码补全</a>；现在我们已将其演进到能够同时处理自然语言和代码。它现在是一个多功能的工具，可以驱动现代AI工作流中的路由、摘要和中间推理步骤。</p>

<p>无论您是想实验、微调还是大规模部署，Mellum2 都已准备好在您自己的系统中运行。</p>

<p><a href="https://huggingface.co/collections/JetBrains/mellum-2" rel="noopener noreferrer"><i></i>试用 Mellum</a></p>

<h2>架构与性能</h2>

<p>Mellum2 通过其架构和专注的、效率驱动的设计，致力于解决生产级系统的瓶颈问题。</p>

<ul>
<li><strong>混合专家（MoE）设计：</strong>该模型总参数量为120亿，但由于采用了混合专家设计，每个令牌仅激活25亿参数。这降低了计算成本，同时支持高吞吐量、低延迟的实时工作负载推理。</li>
<li><strong>专一化焦点：</strong>与许多现代模型不同，Mellum2 并非多模态模型。它专门在自然语言和代码数据上进行训练。这种专一性确保模型在软件工程环境中表现出色，同时保持精简和快速。</li>
</ul>

<div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-1-LiveCodeBench-v6.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-1-LiveCodeBench-v6.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-2-BFCL-V4-1.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-2-BFCL-V4-1.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-3-AIME-2526-1.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-3-AIME-2526-1.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-4-GSMPlus.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-4-GSMPlus.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-5-GPQA-Diamond.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-5-GPQA-Diamond.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-6-MMLU-Redux.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-6-MMLU-Redux.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-7-JetBrains-Internal-Pairwise.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-7-JetBrains-Internal-Pairwise.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-8-MixEval-1.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-8-MixEval-1.png" alt="">
        </a>
    </div>
    <div>
        <a href="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-9-IFEval.png" rel="noopener noreferrer">
            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/DSGN-26150-9-IFEval.png" alt="">
        </a>
    </div>
</div>

<p>在我们的<a href="https://arxiv.org/abs/2605.31268" rel="noopener noreferrer">技术报告</a>中，我们详细说明了模型在代码生成、科学、数学和推理基准测试中的表现。Mellum2 在与其他类似规模模型的比较中具有竞争力，同时将推理时间缩短了一半以上——这对生产级部署来说是一个决定性的优势。</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Blog-1280x720-1-1.png" alt=""></figure>

<h2>Mellum2 的关键用例</h2>

<ul>
<li><strong>路由和编排AI工作负载：</strong>使用 Mellum2 分析传入的提示词，并帮助为每个任务选择合适的模型或工具。<br></li>
<li><strong>构建低延迟RAG管道：</strong>检索相关上下文，使用 Mellum2 进行摘要，并即时生成响应。<br></li>
<li><strong>为复杂工作流中的快速子智能体提供动力：</strong>将智能体管道分解为上下文收集、规划和验证等步骤。使用 Mellum2 执行快速、专门的任务，而不是依赖单一的大型模型。<br></li>
<li><strong>支持私有化、本地化AI部署：</strong>在本地运行 Mellum2 或自托管它，以完全控制您的代码和数据。</li>
</ul>

<h2>“焦点模型”理念：为什么专注的模型能更好地扩展</h2>

<p>随着AI系统变得越来越复杂，性能瓶颈从原始能力转向了大规模下的延迟、吞吐量和成本。并非每个任务都需要最大的模型。现代AI系统中的许多步骤是重复的、对延迟敏感且高频的。这些步骤受益于一个快速、可靠的模型，该模型可以被高效地路由、托管和控制。</p>

<p>在JetBrains，我们相信未来属于协调的系统，而不是单一模型。前沿模型将继续推动极限，但实用的AI产品也需要焦点模型：能够高效处理高频任务的快速、专门化组件。</p>

<p>这就是我们为 Mellum2 在下一代AI软件工具中预见的角色。</p>

<h2>开始使用 Mellum2</h2>

<p>如果您正在为软件工程构建AI系统——无论是在IDE内、在RAG管道中、作为智能体工作流的一部分，还是完全在您自己的基础设施上——我们很希望您能试用 Mellum2。</p>

<p>开源是打造更好工具的方式。</p>

<p><a href="https://huggingface.co/collections/JetBrains/mellum-2" rel="noopener noreferrer"><i></i>试用 Mellum</a></p><p><em>由 mimo-v2.5 模型翻译，花费 5115 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/ai/2026/06/mellum2-goes-open-source-a-fast-model-for-ai-workflows/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=ai&amp;p=708231</guid>
      <pubDate>Mon, 1 Jun 2026 12:57:50 +0000</pubDate>
    </item>
    <item>
      <title>如何使用 Qodana 修复常见的 TypeScript 问题</title>
      <dc:creator>Efim Samoylov</dc:creator>
      <category>qodana</category>
      <category>typescript</category>
      <description>[AI 摘要] 本文介绍了如何利用 Qodana 检测和修复 ESLint 无法覆盖的五种常见 TypeScript 问题，如隐式 any、未使用导出和重复逻辑。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文介绍了如何利用 Qodana 检测和修复 ESLint 无法覆盖的五种常见 TypeScript 问题，如隐式 any、未使用导出和重复逻辑。</div><figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/ChatGPT-Image-Jun-1-2026-10_27_53-AM.png" alt="How to fix common TypeScript issues with Qodana "></figure>



<p>大多数 TypeScript 项目已经运行 <a href="https://eslint.org/" rel="noopener noreferrer">ESLint</a> 并配合 <code><kbd>@typescript-eslint</kbd></code>。这覆盖了很多方面：显式的 <code><kbd>any</kbd></code>、悬空 Promise、非空断言等等。如果你的代码检查设置很稳固，你能在代码审查之前就在编辑器中捕获明显的问题。</p>



<p>ESLint 规则无法生成跨文件的结果。每个规则都在单个文件的范围内运行，这意味着 ESLint 无法告诉你某个导出在代码库的其他地方从未被使用，一个文件中的 <kbd>any</kbd> 类型值是否导致了五个文件之外的不安全假设，或者两个组件是否独立实现了相同的逻辑。这正是 Qodana 填补的空白。</p>



<p>以下是五个值得解决的 TypeScript 问题，按照 ESLint 能处理的范围和它无能为力的范围来组织。</p>



<h2 id="implicit-any-spreading-through-your-codebase">隐式 <kbd>any</kbd> 在代码库中传播</h2>



<p>ESLint 的 <kbd>no-explicit-any</kbd> 会捕获你编写 <kbd>any</kbd> 的地方。但它不会追踪当 <kbd>any</kbd> 从外部来源（例如 <kbd>response.json()</kbd>、没有类型的第三方库或无类型的导入）进入代码库后发生了什么。一旦一个外部类型的 any 值进入你的代码，它就会通过属性访问和函数调用悄无声息地传播。ESLint 的 <kbd>no-unsafe-*</kbd> 规则可以捕获这一点，但前提是你使用 <kbd>@typescript-eslint/recommended-type-checked</kbd>，这需要类型感知的代码检查，并且比标准的推荐配置使用得少得多。</p>



<pre>async function getUser(id: string) {
  const res = await fetch(`/api/users/${id}`);
  const data = await res.json(); // 类型: any

  return data.profile.name; // 没有错误——如果 profile 是 undefined 则会崩溃
}
</pre>



<p><kbd>response.json()</kbd> 在标准库中返回 <kbd>any</kbd>。下游的一切都是无类型的。编译器接受任何属性名和方法调用。这个 bug 在运行时才会暴露。Qodana 能跨文件追踪 <kbd>any</kbd> 如何在程序中流动。当一个 <kbd>any</kbd> 类型的值到达一个假定有特定形状的代码路径时，即使它距离 <kbd>any</kbd> 进入代码库的地方有几个函数调用之远，Qodana 也会标记出这种差异。</p>



<p>添加 <kbd>UserResponse</kbd> 并不能解决这个问题。它只是把谎言挪得离崩溃更近了一点。应该对边界进行类型定义：</p>



<pre>interface UserResponse {
  profile: { name: string };
}

const data: UserResponse = await res.json();
</pre>



<p>如果 API 响应的形状改变，类型错误就会在编译时暴露。</p>



<h2 id="non-null-assertions-used-as-shortcuts">被当作捷径使用的非空断言</h2>



<p>ESLint 的 <kbd>no-non-null-assertion</kbd> 统一地对每个 <kbd>!</kbd> 操作符发出警告。这很有效，但许多团队会禁用该规则或添加宽泛的例外，因为合法的用法（比如在运行时检查之后）也会和危险的用法一起被标记。信号变得嘈杂，规则被关闭，问题就从视野中消失了。</p>



<pre>function renderUser(user: User | null) {
  return `Hello, ${user!.name}`; // 如果 user 为 null 则在运行时崩溃
}

const button = document.querySelector(".submit-btn");
button!.addEventListener("click", handleSubmit); // 如果元素不存在则崩溃</pre>



<p>这两个例子都能在没有错误的情况下编译。但在可预见的条件下都会崩溃。添加 ! 通常是为了在不修复根本问题的情况下消除类型错误。</p>



<p>正确的做法是处理 null 的情况：</p>



<pre>function renderUser(user: User | null) {
  if (!user) return "Guest";
  return `Hello, ${user.name}`;
}

const button = document.querySelector(".submit-btn");
if (button) {
  button.addEventListener("click", handleSubmit);
}</pre>



<p>Qodana 将非空断言作为报告中的一个单独类别显示。并非每个 ! 都是错误的，但将它们集中在一处查看，使得更容易区分合法用法和捷径，而无需在嘈杂的规则和完全没有规则之间做出选择。</p>



<h2 id="floating-promises">悬空 Promise</h2>



<p>ESLint 的 <kbd>@typescript-eslint/no-floating-promises</kbd> 是有效的，但它是一个类型感知规则。它需要在你的 ESLint 配置中通过 <kbd>parserOptions.project</kbd> 启用 TypeScript 类型检查。在未配置或仅对代码库的一部分进行配置的项目中，该规则对未覆盖的文件会静默失效。</p>



<pre>async function onSubmit(data: FormData) {
  saveToDatabase(data); // Promise&lt;void&gt;, 没有被 await
  router.push("/success"); // 在保存完成之前运行
}</pre>



<p>TypeScript 接受这个而不报错。调用一个异步函数而不使用 await 被视为有效语法，返回值会被丢弃。然而，这种行为是不正确的：用户在保存完成之前就会看到成功页面，并且任何数据库错误都会被静默吞掉。</p>



<pre>async function onSubmit(data: FormData) {
  await saveToDatabase(data);
  router.push("/success");
}
</pre>



<p>Qodana 的分析默认是类型感知的，并且覆盖整个项目，而无需单独配置 ESLint 的 TypeScript 集成。无论项目的 ESLint 设置结构如何，悬空 Promise 都会被一致地标记出来。</p>



<h2 id="unused-exports">未使用的导出</h2>



<p><kbd>tsconfig</kbd> 中的 <kbd>noUnusedLocals</kbd> 会捕获文件内未使用的变量。导出的符号根据设计被排除在外。从编译器的角度看，当前文件之外的某个地方可能导入了它们。ESLint 的 <kbd>eslint-plugin-import</kbd> 提供了一个 <kbd>import/no-unused-modules</kbd> 规则可以检测这一点，但它需要在每次检查运行时扫描整个依赖图，并且在大型代码库上有显著的性能开销。对大多数项目来说，保持启用是不切实际的。</p>



<pre>// utils/format.ts
export function formatCurrency(n: number): string { ... }
export function formatPercent(n: number): string { ... } // 移除了功能，但代码还在
export function formatBytes(n: number): string { ... }    // 从未在任何地方导入</pre>



<p>所有这些都能通过而没有任何警告。但 <kbd>formatPercent</kbd> 和 <kbd>formatBytes</kbd> 是死代码。它们增加了维护面，拖慢了重构速度，并误导了那些假设导出符号正在被使用的开发者。</p>



<p>检测这一点需要全项目分析。Qodana 在整个代码库中构建一个引用图，并追踪每一个导入和再导出。仅作为源出现、从未作为导入目标出现的符号会被标记出来。<kbd>tsc</kbd> 和 ESLint 都无法做到这一点。</p>



<h2 id="duplicated-logic-across-files">跨文件的重复逻辑</h2>



<p>ESLint 没有原生的重复检测功能。像 jscpd 这样的独立工具可以做到这一点，但它们不是你的代码检查管道的一部分。这意味着需要单独的设置、单独的维护，还有另一件事需要记住。结果就是：在组件或工具文件之间复制的逻辑不断积累，却没有人标记出来。</p>



<pre>// components/UserCard.tsx
function formatUserName(user: User): string {
  if (!user.firstName &amp;&amp; !user.lastName) return "Anonymous";
  return [user.firstName, user.lastName].filter(Boolean).join(" ");
}
// components/UserBadge.tsx
function getDisplayName(user: User): string {
  if (!user.firstName &amp;&amp; !user.lastName) return "Anonymous";
  return [user.firstName, user.lastName].filter(Boolean).join(" ");
}</pre>



<p>这不是一个风格问题。这意味着 bug 修复需要在多个地方应用，而当修复不完整时，两份副本之间的行为就会悄无声息地产生分歧。<br></p>



<p>Qodana 在相同的分析过程中检测跨文件的重复代码，该过程也揭示了类型问题和未使用的导出。当它出现在报告中，与其他所有问题并列时，它比一个没人记得运行的独立工具更难被降低优先级。</p>



<h2 id="setting-up-qodana-for-your-type-script-project">为你的 TypeScript 项目设置 Qodana</h2>



<p>以上所有五个问题在 Qodana 针对 JavaScript 和 TypeScript 项目的默认配置中都是可见的。以下是一个开始的最小 <kbd>qodana.yaml</kbd> 示例：</p>



<pre>  version: "1.0"
  linter: jetbrains/qodana-js:2026.1                                                                                                                                                                                                             
  bootstrap: npm ci                                                                                                                                                                                                                              
  profile:                                                                                                                                                                                                                                       
    name: qodana.recommended                                                                                                                                                                                                                     
  failThreshold: 0                                                                                                                                                                                                                               
  exclude:        
    - name: All                                                                                                                                                                                                                                  
      paths:
        - dist                                                                                                                                                                                                                                   
        - node_modules
</pre>



<p>如果第一次运行显示出数百个现有问题，不要让它阻碍 CI 采用。Qodana 的基线功能会在一个 <kbd>qodana.sarif.json</kbd> 文件中捕获项目的当前状态。提交它，从那时起，CI 只会因为新引入的问题而失败。现有的积压问题在报告中仍然可见，但它们不会在你处理它们时阻塞每一个 PR。</p>



<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Screenshot-2026-05-28-at-16.07.34.png" alt="Fix common TypeScript issues"></figure>



<p><strong>准备好使用 Qodana 修复常见的 TypeScript 问题了吗？</strong></p>



<p>试试 Qodana 并<a href="https://blog.jetbrains.com/qodana/2026/03/qodana-survey/" rel="noopener noreferrer">告诉我们</a>你的想法。</p>



<p><a title="Try Qodana Ultimate Plus" href="https://www.jetbrains.com/qodana/buy/?billing=yearly" rel="noopener noreferrer"><i></i>试用 Qodana Ultimate Plus</a></p>



<p>我们要特别感谢 Qodana 开发者 Lev Liadov 对本指南的贡献。</p><p><em>由 mimo-v2.5 模型翻译，花费 6771 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/qodana/2026/06/fix-common-typescript-issues/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=qodana&amp;p=710096</guid>
      <pubDate>Mon, 1 Jun 2026 08:40:44 +0000</pubDate>
    </item>
    <item>
      <title>Rust IDE 实际需要什么才能真正理解 Rust 代码？</title>
      <dc:creator>Irina Mihajlovic</dc:creator>
      <category>engineering</category>
      <category>industry-trends</category>
      <category>livestream</category>
      <category>rustrover</category>
      <description>[AI 摘要] 本文总结了构建 Rust IDE 所面临的核心技术挑战，如重实现编译器前端、处理循环模块图和过程宏隔离。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文总结了构建 Rust IDE 所面临的核心技术挑战，如重实现编译器前端、处理循环模块图和过程宏隔离。</div><p><em>免责声明：本文由基于人工智能的写作与沟通助手创作完成。借助它们的帮助，这场内容丰富且细致入微的直播核心主题被提炼成了紧凑的博客文章形式。</em></p>

<p>Rust IDE 如何理解代码？这是一场最近的 RustRover 直播的核心探讨问题，参与者包括 Zed 的 Rust 工程师、<a href="https://rust-analyzer.github.io/" rel="noopener noreferrer">rust-analyzer</a> 团队负责人 Lukas Wirth，以及 JetBrains <a href="https://www.jetbrains.com/rust/" rel="noopener noreferrer">RustRover</a> 的工程师 Vlad Beskrovny。讨论并非比较编辑器或争论偏好，而是专注于当 IDE 分析 Rust 代码时，底层实际发生了什么。</p>

<p>如果您错过了直播，可以在 JetBrains TV 观看完整录音。以下是本次会议中关键问题和见解的结构化回顾。</p>

<figure><div>

</div></figure>

<h2><strong>Q1：Lukas 和 Vlad 是如何开始使用 Rust 的？</strong></h2>
<p>在深入探讨编译器前端和 IDE 架构之前，直播以一个更个人化的问题开始：他们最初是如何接触编程的？有趣的是，Lukas 和 Vlad 都提到用 Java 编写 Minecraft 模组是他们最早的编程经历之一。Lukas 在校期间就开始为 Minecraft 编写 Java 模组，后来在进入大学时自学了 Rust。</p>

<div>
<blockquote><p>“我在进入大学时自学了 Rust，从那以后基本上就不再使用其他任何语言了。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Lukas.jpeg" alt="Lukas Wirth, Rust IDEs creator">
<div>
<strong>Lukas Wirth</strong>
<span>rust-analyzer</span>
</div>
</div>
</div>

<p>Vlad 大约在 2014 年发现了 Rust，但直到加入 JetBrains 并开始开发 IntelliJ Rust 插件（RustRover 的前身）后，才真正开始认真使用它。</p>

<h2><strong>Q2：为什么 Rust IDE 要重新实现编译器的一部分？</strong></h2>
<p>为了提供诸如代码补全、跳转到定义、语义高亮和重构等功能，Rust IDE 需要几乎与编译器本身一样深入地理解语言。</p>

<div>
<blockquote><p>“为了提供诸如补全和跳转到定义等智能功能，我们基本上必须重新实现半个编译器，即整个编译器前端。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
<strong>Vlad Beskrovny</strong>
<span>RustRover</span>
</div>
</div>
</div>

<p>那么为什么不直接复用编译器呢？编译器优化的是吞吐量：它们将源代码转换为二进制文件的效率。</p>

<p>IDE 优化的是延迟：它们在开发者输入时回答小型交互式问题的速度。</p>

<div>
<blockquote><p>“我输入一个点，我需要多快才能看到补全选项？此时，我不关心其他函数体、文件的其余部分或项目中的任何其他文件。我只想让我的补全立即出现。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<p>这种差异从根本上改变了架构。编译器倾向于急切且顺序地处理代码：解析所有内容、解析所有内容、展开所有内容、推断所有内容。而 IDE 则试图只计算当前交互所需的最小信息。</p>

<h2><strong>Q3：Rust 工具链是如何从 RLS 演变到 rust-analyzer 和 RustRover 的？</strong></h2>
<p>直播还回顾了 Rust 工具链的历史。在 rust-analyzer 之前，Rust 的主要语言服务器是 RLS（Rust 语言服务器）。</p>

<p>RLS 试图使用“保存分析”直接在编译器之上构建 IDE 功能。编译器产生包含语义信息的大型 JSON 输出，然后语言服务器查询这些信息。在实践中，这种方法在延迟和处理不完整代码方面遇到了困难。</p>

<div>
<blockquote><p>“用这种方法实现补全几乎是不可能的，因为 rustc 几乎无法处理不完整的代码，而当用户需要补全时，代码几乎总是不完整的。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<p>RLS 最终被 rust-analyzer 取代，后者采用了专注于 IDE 响应性的更增量的架构。</p>

<p>讨论还涉及了 IntelliJ Rust 的起源，该项目最终发展为 RustRover。有趣的是，rust-analyzer 和 IntelliJ Rust 都起源于 Alex Kladov 开始的工作，尽管这两个项目后来在架构方向上发展得非常不同。</p>

<h2><strong>Q4：为什么 Rust 中的名称解析如此困难？</strong></h2>
<p>Rust 的模块图是循环的，这意味着 IDE 无法像许多其他语言那样简单地以增量方式解析名称。</p>

<p>Lukas 使用一个嵌套重导出的链条来演示这一点，其中解析单个符号需要跟踪多个模块、别名和 glob 导入，然后才能找到原始声明。为了支持这种工作流程，IDE 需要反复：收集模块、解析导入、展开宏、收集新生成的项，然后重复此过程直到没有未解析的符号。</p>

<p>这个反复的过程通常被称为“不动点迭代”。不幸的是，对于工具作者来说，宏使这个过程更加复杂。</p>

<h2><strong>Q5：为什么过程宏对 IDE 来说是一个挑战？</strong></h2>
<p>理论上，过程宏只是一个将 token 转换为其他 token 的函数。实践中，过程宏是动态加载的库，可以访问文件系统、读取环境变量、执行任意代码、使进程崩溃或完全终止执行。</p>

<div>
<blockquote><p>“过程宏不仅仅如此。它们是动态链接库。它们可以在宿主系统上为所欲为。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Lukas.jpeg" alt="Lukas Wirth, Rust IDEs creator">
<div>
</div>
</div>
</div>

<p>这给 IDE 带来了重大挑战。如果过程宏在 IDE 进程本身内部崩溃，可能会终止整个 IDE 会话。为避免这种情况，rust-analyzer 和 RustRover 都将过程宏的执行隔离到单独的进程中，并通过自定义协议进行通信。</p>

<div>
<blockquote><p>“如果过程宏确实严重崩溃或退出进程，最坏的情况是我们只是丢失一个可以重新启动的过程宏服务器。但至少 IDE 会继续运行。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Lukas.jpeg" alt="Lukas Wirth, Rust IDEs creator">
<div>
</div>
</div>
</div>

<h2><strong>Q6：为什么 Rust 的类型推断难以复制？</strong></h2>
<p>Rust 的类型系统引入了另一层复杂性。对工具作者来说，好消息是 Rust 类型推断大部分局限于函数体内部，这使得增量分析成为可能。坏消息是，Rust 包含无数特殊的推断规则和边缘情况，IDE 必须精确复制。</p>

<div>
<blockquote><p>“Rust 类型推断的问题在于它有太多任意的规则。确实有数千条任意的规则，我们必须一丝不苟地复制它们。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<p>在直播中，他演示了几个微小的结构变化完全改变代码是否成功编译的例子。</p>

<p>其中一些行为甚至取决于推断过程中表达式处理的内部顺序。这些细节直接影响编辑器功能，如：补全、诊断、导航、检查以及内联提示。</p>

<p>而且与编译器不同，IDE 必须在代码不完整时就提供有用的语义结果。</p>

<h2><strong>Q7：RustRover 如何分析大型 Rust 项目？</strong></h2>
<p>RustRover 首先从 Cargo 元数据和 crate 依赖项构建项目模型。然后使用 PSI（程序结构接口，JetBrains IDE 中使用的抽象层）对项目文件进行索引。Vlad 说，PSI 可以由完整语法树或仅包含声明和签名的轻量级“存根”来支持。</p>

<p>这使得 RustRover 无需急切地完全解析每个文件，从而显著减少内存使用并提高响应性。索引系统本身使用 MapReduce 风格的架构，其中文件被独立且增量地处理。</p>

<p>一个特别有趣的细节是，在索引期间，RustRover 在某些阶段可以跳过解析函数体，因为存根只需要声明和签名。</p>

<div>
<blockquote><p>“在索引过程中，我们完全不解析函数体。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<p>相反，RustRover 可以通过词法分析和计数大括号来高效地浏览文件结构，从而显著加快索引速度。更广泛的意义是，现代 IDE 不可能是纯粹惰性的。在某些时候，它们仍然需要急切的分析。</p>

<div>
<blockquote><p>“IDE 设计的真正艺术在于正确地划定这条界线。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<h2><strong>Q8：rust-analyzer 如何以不同方式解决同样的问题？</strong></h2>
<p>虽然 RustRover 严重依赖索引基础设施，但 rust-analyzer 使用受 Rust 编译器本身启发的查询驱动架构。语义操作使用 Salsa 框架建模为带记忆化、依赖跟踪的查询。</p>

<div>
<blockquote><p>“rust-analyzer 中所有语义上有趣的部分都置于所谓的查询之后。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Lukas.jpeg" alt="Lukas Wirth, Rust IDEs creator">
<div>
</div>
</div>
</div>

<p>这使得 rust-analyzer 能够仅使受编辑影响的精确语义信息无效并重新计算。不必要的依赖可能会意外地使每次按键都触发计算失效，使得性能优化出奇地微妙。</p>

<p>Lukas 解释了 rust-analyzer 内部使用的几层垃圾收集和内存优化，包括：LRU 查询缓存、符号驻留以及用于类型内部结构的自定义标记-清除跟踪收集器。</p>

<h2><strong>Q9：IDE 分析如何与调试连接？</strong></h2>
<p>Vlad 展示了 RustRover 如何将语义分析直接集成到调试工作流中。RustRover 的调试器使用定制的 LLDB 集成以及 IDE 为求值表达式生成的 MIR 表示。</p>

<p>当开发者在调试会话中求值表达式时，RustRover 为相关的表达式图生成 MIR，将其序列化，并通过调试器后端进行解释。</p>

<p>这是一个强有力的例子，展示了现代 IDE 越来越不像文本编辑器，而更像围绕语言本身构建的完整语义环境。直播以一个观众问题结束，关于调试异步 Rust 工作流以及 RustRover 是否最终能像 Rider 一样可视化 Tokio 异步任务。</p>

<h2><strong>Q10：Rust 工具作者是否暗地里讨厌 Rust？</strong></h2>

<div>
<blockquote><p>“通常，使语言更易用的特性往往在实现方面引入更多复杂性。”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Lukas.jpeg" alt="Lukas Wirth, Rust IDEs creator">
<div>
</div>
</div>
</div>

<p>Vlad 补充道：</p>

<div>
<blockquote><p>“我热爱 Rust。否则你如何解释我为什么花了大约九年时间去经历所有这些复杂性？”</p></blockquote>
<div>
<img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-1-e1780055673849.jpg" alt="">
<div>
</div>
</div>
</div>

<p>同时，两人也承认，Rust 的一些特性是在语言历史早期出现的，当时生态系统尚未完全理解它们对长期工具的影响，尤其是在过程宏方面。</p>

<p>如果您对 Rust 工具、编译器内部、IDE 架构或语言设计权衡感兴趣，Lukas Wirth 和 Vlad Beskrovny 之间的完整讨论值得一看。</p>

<div>
<div>
<a href="https://www.youtube.com/live/VbdD1c3owKc" rel="noopener noreferrer">观看完整视频</a>
</div>
</div><p><em>由 mimo-v2.5 模型翻译，花费 8469 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/rust/2026/05/29/how-rust-ides-understand-code/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=rust&amp;p=710674</guid>
      <pubDate>Fri, 29 May 2026 15:30:07 +0000</pubDate>
    </item>
    <item>
      <title>Hibernate 7.4 新功能</title>
      <dc:creator>Siva Katamreddy</dc:creator>
      <category>java</category>
      <category>hibernate</category>
      <category>jpa</category>
      <description>[AI 摘要] Hibernate 7.4 改进了分页查询处理，并内置了历史记录与审计表支持，无需额外库。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> Hibernate 7.4 改进了分页查询处理，并内置了历史记录与审计表支持，无需额外库。</div><p>Hibernate 7.4 <a href="https://docs.hibernate.org/orm/7.4/whats-new/" rel="noopener noreferrer">引入了几项改进</a>，简化了加载一页数据及其关联的子集合、访问历史数据和审计日志记录。</p>



<p>本文将重点介绍以下功能：</p>



<ul>
<li><strong>限制与抓取连接</strong>：Hibernate 7.4 如何改进使用抓取关联的分页查询。</li>



<li><strong>历史记录与审计表</strong>：新功能如何支持跨时间查询实体状态和处理历史数据。</li>
</ul>



<p><br><strong>你可以在</strong><a href="https://github.com/sivaprasadreddy/hibernate-7.4-demo" rel="noopener noreferrer"><strong>GitHub 仓库</strong></a><strong>中查看本文的示例代码。</strong></p>



<h2>限制与抓取连接</h2>



<p>数据驱动应用程序中的一个常见需求是加载一页父实体及其关联的子实体集合。例如，假设一个应用程序有一个 <code>Order</code> 实体，包含一个 <code>Set&lt;OrderItem&gt;</code> 集合，我们想加载前几个订单及其订单项。</p>



<pre>List&lt;Order&gt; orders = session
        .createSelectionQuery(
            "select o from Order o join fetch o.items order by o.id",
            Order.class
        )
        .setMaxResults(10)
        .getResultList();</pre>



<p>在 Hibernate 7.4 之前的版本中，对使用集合抓取连接的查询应用限制无法安全地向下推送到数据库层。因为每个 <code>Order</code> 可能有多个 <code>OrderItem</code> 行，直接限制 SQL 结果可能会截断订单的部分项集合。为避免返回不完整的集合，Hibernate 会从数据库加载所有匹配的行，并在应用程序层中在内存中应用分页。</p>



<p>该行为是正确的，但可能代价高昂。一个旨在仅加载 10 个订单的查询，如果表中包含大量订单和订单项，可能仍会读取更多行。</p>



<p>在 Hibernate 7.4 之前，生成的 SQL 如下所示：</p>



<pre>select
    o1_0.id, i1_0.order_id, i1_0.id, i1_0.product_code,
    i1_0.quantity, o1_0.order_number, o1_0.status
from
    orders o1_0
        join
    order_items i1_0
    on o1_0.id=i1_0.order_id</pre>



<p>如你所见，限制（分页）未在 SQL 查询级别应用。因此，它会加载所有 <code>orders</code> 及其关联的 <code>order_items</code>，这可能是一个非常耗时的操作，并可能导致 <em>OutOfMemoryException</em>。</p>



<p>你可以看到 Hibernate 记录的如下警告：</p>



<pre>[WARN] HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory</pre>



<p>防止 Hibernate 在内存中执行分页的一个选项是设置以下属性：</p>



<pre>hibernate.query.fail_on_pagination_over_collection_fetch=true</pre>



<p>配置此属性后，Hibernate 会抛出异常而不是在内存中执行分页。</p>



<p>Hibernate 7.4 通过使用嵌套查询解决了这个问题。Hibernate 不是直接对连接的结果集应用限制，而是首先确定有限的父实体标识符集，然后仅为这些父行获取关联的集合。</p>&nbsp;<p></p>



<p>这允许在数据库中进行分页，同时仍为每个选定的 Order 返回完整的项集合。</p>



<p>使用 Hibernate 7.4，SQL 将生成如下：</p>



<pre>select
        o1_0.id, i1_0.order_id, i1_0.id, i1_0.product_code,
        i1_0.quantity, o1_0.order_number,o1_0.status 
    from
        (select
            o1_0.id, o1_0.order_number, o1_0.status 
        from
            orders o1_0 
        where
            exists(select
                1 from order_items i1_0 
            where
                o1_0.id=i1_0.order_id) 
        offset
            ? rows 
        fetch
            first ? rows only) o1_0(id, order_number, status) 
    join
        order_items i1_0 
            on o1_0.id=i1_0.order_id</pre>



<p>这一改进使得抓取连接对于分页屏幕更加实用，例如显示每个订单及其行项目的订单列表页面，而无需强制应用程序首先加载完整的结果集。</p>



<h2>历史记录与审计表</h2>



<p>Hibernate 7.4 增加了对时间历史表和审计表的内置支持。这两个功能都有助于跟踪实体数据的变化，但它们服务于略有不同的用例：历史表让我们可以查询某个时间点的实体状态，而审计表则记录实体发生的变化序列。</p>



<p>考虑以下 <code>Product</code> 实体：</p>



<pre>@Entity
@Table(name = "products")
class Product {
    //fields id, code, name, price
}</pre>



<h3>历史表</h3>



<p>要为 <code>Product</code> 启用时间历史记录，请使用 <code>@Temporal</code> 注解实体，并可选择使用 <code>@Temporal.HistoryTable</code> 指定历史表名称。</p>



<pre>@Entity
@Table(name = "products")
@Temporal
@Temporal.HistoryTable(name="products_history")
class Product {
    //fields id, code, name, price
}</pre>



<p>通过此映射，Hibernate 将产品行的先前版本存储在 <code>products_history</code> 表中。该表包含实体列加上两个时间列：<strong><em>effective</em></strong>，标记某个版本何时开始生效，以及 <strong><em>superseded</em></strong>，标记该版本何时被替换。</p>



<p><strong>products_history</strong> 表：</p>



<figure><table><thead><tr><th><strong>id</strong></th><th><strong>code</strong></th><th><strong>name</strong></th><th><strong>price</strong></th><th><strong>effective</strong></th><th><strong>superseded</strong></th></tr></thead><tbody><tr><td>2251</td><td>P1000</td><td>Product-1000</td><td>40.00</td><td>2026-05-15 08:21:39.949001 +00:00</td><td>null</td></tr><tr><td>2301</td><td>P1001</td><td>Product-1001</td><td>90.00</td><td>2026-05-15 08:22:24.765883 +00:00</td><td>2026-05-15 08:22:24.778067 +00:00</td></tr><tr><td>2301</td><td>P1001</td><td>Product-1001</td><td>100.00</td><td>2026-05-15 08:22:24.778067 +00:00</td><td>null</td></tr></tbody></table></figure>



<p>我们可以按如下方式获取给定时间点的 Product 实体数据：</p>



<pre>Instant someTime = ...
try (var session = sessionFactory.withOptions().asOf(someTime).open()) {
    var product = session.find(Product.class, productId);
    
}</pre>



<p>这使得时间查询感觉像普通的实体查找，而 Hibernate 在幕后解析正确的历史行。</p>



<p>Hibernate 提供了几种不同的策略（NATIVE、SINGLE_TABLE、HISTORY_TABLE）来映射时间实体。更多信息请查看 <a href="https://docs.hibernate.org/orm/7.4/introduction/html_single/#temporal-data" rel="noopener noreferrer">时间数据</a> 部分。</p>



<h3>审计表</h3>



<p>以前，基于 Hibernate 的应用程序通常使用单独的 <a href="https://hibernate.org/orm/envers/" rel="noopener noreferrer">Hibernate Envers</a> 库进行实体变更审计。Hibernate 7.4 将审计表支持引入 Hibernate ORM 本身，因此应用程序可以原生使用审计功能，而无需为此用例添加 <strong>Envers</strong>。</p>



<p>通过添加 <code>@Audited</code> 可启用审计支持，并可以使用 <code>@Audited.Table</code> 映射到自定义表。</p>



<pre>@Entity
@Table(name = "products")
@Audited
@Audited.Table(name="products_aud_log")
class Product {
    //fields id, code, name, price
}</pre>



<p>启用审计后，Hibernate 将每次变更写入审计表一行。与历史表不同，审计表侧重于记录发生了什么操作以及何时发生。</p>



<figure><table><thead><tr><th><strong>id</strong></th><th><strong>code</strong></th><th><strong>name</strong></th><th><strong>price</strong></th><th><strong>rev</strong></th><th><strong>revtype</strong></th></tr></thead><tbody><tr><td>2001</td><td>P1002</td><td>Product-1002</td><td>90.00</td><td>2026-05-13 14:58:17.505775 +00:00</td><td>0</td></tr><tr><td>2001</td><td>P1002</td><td>Product-1002</td><td>100.00</td><td>2026-05-13 14:58:17.518194 +00:00</td><td>1</td></tr></tbody></table></figure>



<p><code>rev</code> 值是变更发生的时间戳。<code>revtype</code> 值使用 <code>ModificationType</code> 枚举表示如下：</p>



<pre>public enum ModificationType {
    /**
    * Creation, encoded as 0
    */
    ADD,
    /**
    * Modification, encoded as 1
    */
    MOD,
    /**
    * Deletion, encoded as 2
    */
    DEL
}</pre>



<p>更多信息请查看 <a href="https://docs.hibernate.org/orm/7.4/introduction/html_single/#audit-logs" rel="noopener noreferrer">审计日志</a> 部分。</p>



<h2>总结</h2>



<p>大多数应用程序使用分页来显示资源列表，我们过去常常编写自定义逻辑来加载分页数据及其关联的子集合。现在这在框架层面本身就得到了处理。此外，我们过去常常依赖外部库如 <strong>Envers</strong> 来实现审计，现在 Hibernate 本身就提供了。</p>



<p>Hibernate 7.4 带来了实用的改进，解决了基于 JPA/Hibernate 应用程序中的实际问题。无论是优化分页查询行为还是跟踪历史数据，Hibernate 7.4 减少了所需的自定义基础设施数量，并提供了更好的开箱即用支持，无需额外的库。</p>



<p>请使用此 <a href="https://github.com/sivaprasadreddy/hibernate-7.4-demo" rel="noopener noreferrer"><strong>GitHub 仓库</strong></a><strong>探索这些新功能。</strong></p><p><em>由 mimo-v2.5 模型翻译，花费 6990 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/idea/2026/05/hibernate-7-4-new-features/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&amp;p=709833</guid>
      <pubDate>Fri, 29 May 2026 14:29:53 +0000</pubDate>
    </item>
    <item>
      <title>我们如何使用 AlphaEvolve 让复杂的 IDE 算法更快</title>
      <dc:creator>Denis Shiryaev</dc:creator>
      <category>ai</category>
      <category>ai-in-ides</category>
      <description>[AI 摘要] 该文章介绍了 JetBrains 如何使用谷歌 DeepMind 的 AlphaEvolve 系统，通过自动化搜索和优化，成功加速了基于 IntelliJ 的 IDE 中复杂的索引算法。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 该文章介绍了 JetBrains 如何使用谷歌 DeepMind 的 AlphaEvolve 系统，通过自动化搜索和优化，成功加速了基于 IntelliJ 的 IDE 中复杂的索引算法。</div><p><a href="https://deepmind.google/blog/alphaevolve-impact/" rel="noopener noreferrer">AlphaEvolve</a> 是谷歌 DeepMind 的一个算法发现系统，它使用 Gemini 模型来生成、测试和优化可能的算法改进。它的任务不是回答问题；而是搜索解决复杂算法问题的更快方法。我们在基于 IntelliJ 的 IDE 的一个狭窄但重要的部分上对其进行了尝试：索引，这是项目打开后使导航、搜索、补全、重构、检查和其他代码洞察功能可用的后台工作。</p>

<p>这使得索引速度成为一个简单但难以提升的衡量指标。它取决于语言、框架、项目结构、后台 IDE 工作以及底层的存储层。微小的变化可能会淹没在噪声中。有些优化在微基准测试中是有效的，但在完整的 IDE 运行中则不可见。</p>

<p>我们已经在此投入了大量的工程时间，这种手动性能优化工作仍在继续。本文所述的实验并非要取代工程判断、性能分析、代码审查或产品验证。它只是一种额外搜索方法的测试：谷歌 DeepMind 的 AlphaEvolve 能否帮助我们在已经研究了多年的代码中找到有用的优化候选方案？</p>

<div>
  
    <div>
      <h2>结果快照</h2>
      <p>我们首先在合成基准测试上测试了生成的候选方案，然后在完整的 IDE 环境中验证了其中最有前景的几个。</p>
      <div>
        
      </div>
      <p>集成测试，以秒为单位，数值越低越好：在修改后的 IntelliJ IDEA 2026.2 每夜构建版本上运行的 Kotlin Spring Petclinic 项目。基线为 17.4 ± 0.5s。在我们的运行表中，解决方案 1 的测量结果为 16.6 ± 0.2s。</p>
    </div>
  

  
    <div>
      <strong>15-20%</strong>
      <span>在大多数进行超过 50 次迭代的 AlphaEvolve 会话中观察到的合成性能分数提升。</span>
    </div>
    <div>
      <strong>17.4s</strong>
      <span>Kotlin Spring Petclinic 的完整 IDE 基线，存在 ±0.5s 的波动。</span>
    </div>
    <div>
      <strong>16.6s</strong>
      <span>测量到的最佳候选方案，报告波动为 ±0.2s。</span>
    </div>
    <div>
      <strong>2 / 5</strong>
      <span>在集成测试中显示出统计学显著改进的生成候选方案数量。</span>
    </div>
  

  
    <h2>交互式测量仪表板</h2>
    <p>使用标签页切换查看端到端结果、单次运行和实验漏斗。对于时间和分数图表，数值越低越好。</p>
    <div>
      端到端索引
      运行分布
      合成与漏斗
    </div>
     显示报告的波动范围
    <div>
      <div>
        
      </div>
      <aside></aside>
    </div>
  
</div>





<p>谷歌 DeepMind 在其 <a href="https://cloud.google.com/blog/products/ai-machine-learning/alphaevolve-on-google-cloud?e=48754805" rel="noopener noreferrer">AlphaEvolve 预览博客</a> 中将 AlphaEvolve 描述为一个由 Gemini 驱动的编程代理，旨在通过结合 LLM 生成的代码和自动化评估器来设计算法。在本实验中，该评估器是我们的性能和正确性验证设置。</p>

<h2 id="the-target-a-b-tree-in-the-indexing-stack">目标：索引栈中的 B-tree</h2>

<p>我们选择了索引实现基础的 <a href="https://en.wikipedia.org/wiki/B-tree" rel="noopener noreferrer">B-tree</a>。起点不是一个简单的原型。它是一个经过深度优化的基础设施部分，手动探索的成本已经变得很高。即使是看似合理的改动也需要时间来编写、审查和验证，而一个错误的改动可能因为错误的原因而显得很快。</p>

<p>工程描述被刻意写得很平实：原始算法本质上是一个经典的 B-tree，而提出的候选方案大多是针对边缘情况进行了优化的改进 B-tree 变体。这正是 AlphaEvolve 擅长的那类问题：有代码可以修改，有明确的评分标准，有测试可以拒绝错误的想法。</p>

<h2 id="the-loop-generate-score-validate">循环：生成、评分、验证</h2>

<figure><figcaption>AlphaEvolve 正在 <a href="https://alphaevolve-examples.web.app/ae/experiment/f5ff0dbd_0bb3_4c6b_9bf7_6a98363b935e" rel="noopener noreferrer">优化</a> 一个 “<a href="https://en.wikipedia.org/wiki/Tammes_problem" rel="noopener noreferrer">Tammes 问题</a>” 的实例。</figcaption></figure>

<p>我们为 AlphaEvolve 提供了存储层的内部性能测试套件。该套件是合成的。它不使用真实的客户项目。它写入和读取合成数据，以便可以快速且反复地测试候选方案的更改。</p>

<p>评分基于我们中等规模基准测试的中位数结果之和。单元测试充当正确性检查。有了这个设置，大多数进行超过 50 次迭代的 AlphaEvolve 会话都在合成性能分数上产生了 15-20% 的提升。</p>

<p>这令人鼓舞，但这还不够。合成基准测试之所以有用，是因为它们是受控的。用户不会运行受控的基准测试。他们运行的是完整的 IDE，同时运行后台进程、语言服务和项目特定的行为。因此，我们将最好的生成候选方案带入了集成测试。</p>

<p>对于完整的 IDE 测试步骤，团队使用了 Kotlin Spring Petclinic 项目和修改后的 IntelliJ IDEA 2026.2 每夜构建版本。报告的完整端到端索引时间基线为 17.4 ± 0.5 秒。在五个生成的候选方案中，两个显示出统计学上的显著改进，其可复现的结果低于 16.8 秒。</p>

<div>
  <h2>主张边界</h2>
  <div>
    <strong>合成基准主张</strong><span>AlphaEvolve 直接优化的内容。</span>
    <strong>集成测试主张</strong><span>通过完整 IDE 运行的内容。</span>
    <strong>下一步验证</strong><span>仍需要产品级别证明的内容。</span>
  </div>
  <p>大多数进行超过 50 次迭代的会话将合成性能分数提高了 15-20%。这是关于自主优化循环最强有力的主张，因为基准测试本身就是优化目标。</p>
</div>





<h2 id="what-changed-in-the-numbers">数据发生了什么变化</h2>

<p>我们的端到端运行表包含两个测量到的候选方案。解决方案 1 产生了 16.6 秒的平均结果，报告波动为 ±0.2 秒。与 17.4 秒的基线相比，在本次集成场景中大约快了 0.8 秒，即减少了约 4.6%。</p>

<p>解决方案 2 对于这个故事也很有用，尽管不是因为它赢得了完整的 IDE 测试。它测量结果为 17.5 ± 0.4 秒，在此场景中实际上与基线相同。两个候选方案都在快速的合成基准测试中有所改进，但在这两个方案中，只有一个在集成测量中显示出用户可感知的端到端改进。</p>

<p>这种区别很重要。一个只庆祝合成基准测试胜利的性能工作流程最终会发布具有误导性的声明。一个将自主搜索与完整 IDE 验证相结合的工作流程，更有可能找到用户能感受到的更改。</p>

<blockquote>
<p>AlphaEvolve 可以改变我们处理复杂性能工作的方式。它将曾经因过于耗时而无法探索的优化转化为我们可以常规测试的候选方案。工程师仍然拥有基准测试、审查和发布决策权。只是搜索空间变小了。</p>
<cite>Dmitrii Batkovich，IntelliJ 平台工程总监</cite></blockquote>

<h2 id="what-we-measure-next">我们接下来测量什么</h2>

<p>下一步是产品验证。团队计划检查改进是否在 Mega Index 指标中体现，这是一个用于跟踪索引性能和用户体验的内部 KPI，特别是用户是否对索引过程更加满意。这是正确的衡量标准。更快的内部基准测试是有用的。更快的完整 IDE 测试是更好的。更好的用户体验才是最终重要的成果。</p>

<p>对我们而言，重要的教训不是 AlphaEvolve 神奇地让索引变快了。它做了一件更实际的事情。它帮助在手工探索缓慢的领域中，生成和排序底层优化想法。JetBrains 工程师提供了问题、测试、测量规范和判断力。AlphaEvolve 扩展了搜索。</p>

<h2 id="acknowledgements">致谢</h2>

<p>这个项目是 JetBrains 团队（包括 Denis Shiryaev 和 Dmitrii Batkovich）与谷歌云的 AI for Science 及客户团队（包括 Anant Nawalgaria、Skander Hannachi、Kartik San、Laurynas Tamulevičius、Nicolas Stroppa 和 Artemiy Yashin）之间合作的成果。</p><p><em>由 mimo-v2.5 模型翻译，花费 25105 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/ai/2026/05/how-we-use-alphaevolve-to-make-complex-ide-algorithms-faster/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=ai&amp;p=710184</guid>
      <pubDate>Fri, 29 May 2026 13:46:30 +0000</pubDate>
    </item>
    <item>
      <title>JetBrains Academy：五月精选资讯</title>
      <dc:creator>Margarita Aleeva</dc:creator>
      <category>digest</category>
      <category>jetbrains-academy</category>
      <category>newsletter</category>
      <description>[AI 摘要] 该文介绍了JetBrains Academy五月提供的多项学习资源，包括奖学金、新AI课程、IDE集成项目以及一篇关于编程学习的反思文章。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 该文介绍了JetBrains Academy五月提供的多项学习资源，包括奖学金、新AI课程、IDE集成项目以及一篇关于编程学习的反思文章。</div><p>嗨！</p>

<p>本月的资讯列表虽短，但每一项都值得你花时间了解。</p>

<p>请在6月9日之前申请最多40个JetBrains基金会提供的CSAI学士学位课程奖学金，尝试一门面向开发者的全新AI工具课程，了解一个将动手编程实践引入JetBrains IDE的项目，并阅读一篇关于在学习编程中“有价值的挣扎”的文章。</p>

            <div>
                            <h2>学习亮点</h2>
                                                            <article>
                                                                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/1779443084114.jpeg" alt="">
                                                                            <div>
                                                            <h3>JetBrains基金会奖学金</h3>
                                                        <p>JetBrains基金会为帕福斯内亚波利斯大学为期四年的CSAI学士学位课程提供多达40个全额奖学金，涵盖学费、住宿和其他费用，外加每月300欧元的津贴。第二轮申请即将截止。</p>
                                                            <a href="https://lp.jetbrains.com/academy/csai-program/?utm_campaign=jba&amp;utm_content=csai&amp;utm_medium=social&amp;utm_source=linkedin&amp;utm_term=2026" rel="noopener noreferrer">立即申请</a>
                                                    </div>
                    </article>
                                    <article>
                                                                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Newsletter-Banner_1280x720-2.png" alt="">
                                                                            <div>
                                                            <h3>面向开发者的AI工具</h3>
                                                        <p>JetBrains Academy推出这门全新的自定进度课程，帮助开发者使用生成式AI进行复杂的代码生成、自动化调试、测试、重构和性能优化。你将超越单次提示，学习如何将AI工具整合到日常的软件开发工作流程中。</p>
                                                            <a href="https://hyperskill.org/courses/135" rel="noopener noreferrer">开始学习</a>
                                                    </div>
                    </article>
                                    </div>
    

            <div>
                            <h2>致课程创作者</h2>
                                                            <article>
                                                                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/1779197454160.jpeg" alt="">
                                                                            <div>
                                                            <h3>JetBrains课程创作者计划</h3>
                                                        <p>你是否已经在Udemy、Coursera、LinkedIn Learning或自己的平台上教授编程？将你的课程集成到JetBrains IDE中，让你的学生在与开发者工作中使用的相同工具上进行实践。你将获得产品访问权限、技术指导、推广支持以及对集成方案的帮助。大多数创作者在两到四周内完成设置流程。</p>
                                                            <a href="https://www.jetbrains.com/academy/course-creators/" rel="noopener noreferrer">加入计划</a>
                                                    </div>
                    </article>
                                    </div>
    

            <div>
                            <h2>阅读与思考</h2>
                                                            <article>
                                                                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Newsletter-Banner_1280x720-1.png" alt="">
                                                                            <div>
                                                            <h3>“最大化摩擦”、失败与学习编程</h3>
                                                        <p>一点点摩擦真的能改善学习吗？克拉拉·缅因探讨了为什么挣扎、犯错和调试能帮助构建更深刻的理解——以及为什么AI导师有时会让你产生自己学到的东西比实际更多的错觉。</p>
                                                            <a href="https://blog.jetbrains.com/education/2026/05/13/friction-maxxing-failure-and-learning-to-code/" rel="noopener noreferrer">阅读全文</a>
                                                    </div>
                    </article>
                                    </div>
    

            <div>
                            <h2>观看与学习</h2>
                                                            <article>
                                                                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/Newsletter-Banner_1280x720.png" alt="">
                                                                            <div>
                                                            <h3>当软件工程变成上下文工程时该怎么办</h3>
                                                        <p>在这场网络研讨会中，微软的马克西姆·萨尔尼科夫和JetBrains的奇亚拉·伯恩讨论了软件开发中的上下文工程和智能体AI。他们探讨了开发者角色如何演变、为什么审查周期越来越长，以及团队应该警惕哪些AI采用反模式。</p>
                                                            <a href="https://events.jetbrains.net/on-demand/5d7bc373-ce62-496d-b437-12a4d567bba2" rel="noopener noreferrer">观看回顾</a>
                                                    </div>
                    </article>
                                    </div><p><em>由 mimo-v2.5 模型翻译，花费 3195 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/education/2026/05/29/jetbrains-academy-may-2026/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=education&amp;p=710354</guid>
      <pubDate>Fri, 29 May 2026 12:53:54 +0000</pubDate>
    </item>
    <item>
      <title>TeamCity 2026.1.1 现已发布</title>
      <dc:creator>Dmitrii Korovin</dc:creator>
      <category>bug-fix</category>
      <description>[AI 摘要] TeamCity 2026.1.1 是一个错误修复更新，解决了超过 20 个问题和性能问题。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> TeamCity 2026.1.1 是一个错误修复更新，解决了超过 20 个问题和性能问题。</div><p>今天我们推出了 TeamCity 本地部署版 2026.1 服务器的首个错误修复。此次更新解决了超过 20 个问题和性能问题，包括：</p>

<ul>
<li>TeamCity 忽略构建代理的备用 IP 地址；</li>
<li>Rake 插件损坏；</li>
<li>上传到 S3 存储桶失败；</li>
<li>带有 "Exists" 代理要求的 .NET 构建无法找到兼容的构建代理来运行。</li>
</ul>

<p>完整的已解决问题列表请参阅 <a href="https://www.jetbrains.com/help/teamcity/teamcity-2026-1-1-release-notes.html" rel="noopener noreferrer">TeamCity 2026.1.1 发行说明</a>。</p>

<h3><strong>为何要更新？</strong></h3>

<p>跟进次要版本更新可确保您的 TeamCity 实例获得以下好处：</p>

<ul>
<li>性能改进。</li>
<li>与集成工具的兼容性更好。</li>
<li>构建更快、更稳定。</li>
<li>工作流的安全性得到增强。</li>
</ul>

<h3><strong>兼容性</strong></h3>

<p>TeamCity 2026.1.1 与所有 2026.1.x 版本共享相同的数据格式。您可以在该系列版本内升级或降级，无需备份和恢复。</p>

<h3><strong>如何升级</strong></h3>

<ol>
<li>使用您当前 TeamCity 版本中的<strong>自动更新</strong>功能。</li>
<li>直接从 <a href="https://www.jetbrains.com/teamcity/download/" rel="noopener noreferrer">JetBrains 网站</a>下载最新版本。</li>
<li>拉取更新的 TeamCity Docker 镜像。</li>
</ol>

<h3><strong>需要帮助？</strong></h3>

<p>感谢您报告问题和提供反馈！如果您有任何疑问或遇到任何问题，请通过 <a href="https://teamcity-support.jetbrains.com/hc/en-us" rel="noopener noreferrer">TeamCity 论坛</a>或 <a href="https://youtrack.jetbrains.com/issues/TW" rel="noopener noreferrer">问题跟踪器</a>告知我们。</p>

<p>祝您构建愉快！</p><p><em>由 mimo-v2.5 模型翻译，花费 1481 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/teamcity/2026/05/teamcity-2026-1-1-bug-fix/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=teamcity&amp;p=710649</guid>
      <pubDate>Fri, 29 May 2026 11:11:53 +0000</pubDate>
    </item>
    <item>
      <title>DataSpell的落幕将至</title>
      <dc:creator>Stanislav Garkusha</dc:creator>
      <description>[AI 摘要] JetBrains将停止销售DataSpell作为独立产品，并将其功能整合到PyCharm中，同时为现有用户提供迁移路径。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> JetBrains将停止销售DataSpell作为独立产品，并将其功能整合到PyCharm中，同时为现有用户提供迁移路径。</div><p>经过慎重考虑，我们做出了一个艰难的决定：将DataSpell作为独立产品停止运营。</p>

<p>DataSpell的诞生旨在JetBrains生态系统中，专注于满足数据科学与分析专业人士的需求。它使我们能够构建并优化用于Jupyter笔记本、数据探索和分析工作流的专属体验。这些改进已成功集成到PyCharm中，从而能够惠及更广泛的用户群体。</p>

<p>如今，我们已认识到，维护一个独立产品已不再是前进的最佳路径。这一选择是JetBrains更广泛战略的一部分，旨在围绕用户最依赖的工作流构建一个更加统一的生态系统。通过将这些功能整合到PyCharm中，我们可以发展得更快、简化整体体验，并在一个地方提供更大价值。</p>

<h2>DataSpell用户的下一步？</h2>

<p>自2026年9月1日起，我们将协助现有的DataSpell用户过渡到PyCharm，在那里，数据科学与分析工作流将继续被积极开发和改进。</p>

<p>为使过渡尽可能顺利，符合条件的DataSpell客户将能够以零额外费用迁移到PyCharm。当前的DataSpell用户还将通过备用许可证保持访问权限。</p>

<p>过渡将分阶段进行：</p>

<ul>
<li><strong>2026年5月28日：</strong> DataSpell将被弃用为独立产品，这意味着将无法再购买新的DataSpell订阅。现有许可证将继续有效。</li>

<li><strong>2026年9月1日：</strong> 符合条件的DataSpell订阅将转换为PyCharm Pro订阅。符合条件的客户将获得与其剩余DataSpell订阅期价值相匹配的JetBrains AI积分。
<ul>
<li>如果个人客户在9月1日转换时已同时拥有PyCharm Pro和DataSpell许可证，则不会发生任何变化。其DataSpell许可证将保持有效，直至原始到期日。</li>
</ul>
</li>
</ul>

<p></p>

<p>如需为9月1日的过渡做准备并寻求帮助，请联系我们。</p>

<h3>针对组织商业客户</h3>

<p>我们计划在<strong>2026年9月1日</strong>将组织的DataSpell许可证转换为PyCharm许可证。</p>

<p>由于DataSpell订阅包含捆绑的AI积分，除了许可证转换外，符合条件的组织客户还将获得与其剩余DataSpell订阅期价值相匹配的JetBrains AI积分。</p>

<p>组织客户还将通过备用许可证保留对DataSpell的访问权限，为其团队提供更多时间来完成向PyCharm的过渡。</p>

<h3>针对个人商业客户</h3>

<p>2026年9月1日，您的DataSpell许可证将自动转换为PyCharm Pro许可证，有效期覆盖剩余订阅期，符合条件的客户将获得与其剩余DataSpell订阅期价值相匹配的JetBrains AI积分。</p>

<p>您还将通过备用许可证继续访问DataSpell，以便有时间完成向PyCharm的过渡。</p>

<p>如果您已拥有PyCharm Pro和DataSpell订阅，您的DataSpell许可证将保持不变，不会进行转换。</p>

<p>所有产品包持有者将保留其DataSpell许可证的访问权限。</p>

<h3>针对免费许可证持有者</h3>

<p>如果您正使用免费许可证使用DataSpell，您可以根据当前的许可证条款继续使用。但我们建议您转向PyCharm进行数据科学工作流。符合条件的用户可以通过现有的JetBrains针对学生、教师、开源贡献者等支持群体的计划申请免费的PyCharm Pro许可证。</p>

<h2>在JetBrains IDE中继续您的数据工作流</h2>

<p>尽管DataSpell将作为独立产品退役，但其关键工作流仍将在其他JetBrains工具中保持可用。</p>

<p>PyCharm Pro内置支持：</p>

<ul>
<li>Jupyter笔记本</li>

<li>SQL和数据库</li>

<li>Python数据分析工作流</li>
</ul>

<p>您可以在JetBrains生态系统中继续处理笔记本、数据可视化和基于Python的数据处理，而不会丢失您今天所依赖的功能。您可以在此处了解更多关于PyCharm对数据科学和数据分析的支持<a href="https://www.jetbrains.com/pycharm/data-science/" rel="noopener noreferrer">。</a>&nbsp;</p>

<h2><strong>致谢</strong></h2>

<p>我们想向DataSpell社区表达最诚挚的谢意——你们的反馈、支持和参与无比宝贵。</p>

<p>虽然这标志着DataSpell作为独立产品的终结，但它也代表着一个新篇章：数据工作流将在更广泛的JetBrains生态系统中持续发展。</p>

<p><strong>JetBrains团队</strong></p>

<p></p>

<h2><strong>常见问题解答</strong></h2>

<p><strong>DataSpell的最后一个版本是什么？</strong><strong><br></strong>DataSpell的最终版本将是2026.1。除了潜在的安全漏洞修复外，将不再有后续的常规更新，支持将逐步终止。</p>

<p><strong>DataSpell用户有哪些替代方案？</strong><br>大多数DataSpell工作流都已在PyCharm Pro中可用，该产品支持Jupyter笔记本、Python数据分析和交互式开发工作流。如果您主要使用数据库，您可能需要考虑使用DataGrip。</p>

<p><strong>如果我对自动许可证迁移有疑问或保留意见该怎么办？</strong><br>如果您对自动转换有疑问或保留意见，请务必在<strong>2026年8月31日</strong>之前联系JetBrains代表。</p>

<p><strong>如果我最近购买了DataSpell许可证该怎么办？</strong><br>您也将自动转换为PyCharm Pro许可证。此外，我们的标准<a href="https://sales.jetbrains.com/hc/en-gb/articles/115000913704-How-can-I-get-a-refund" rel="noopener noreferrer">退款政策适用</a>。</p>

<p><strong>如果需要更多信息，我可以做什么？</strong><br>请联系<a href="https://www.jetbrains.com/support/sales/?fromMenu=&amp;_cl=MTsxOzE7c1lBdTZnSTdlbHVPNVFMTnd3cjZrM0JuYVNOWDl6YXJGd0FJODR0QXhMQUtQbEgzUFlwTE0xcG80YUhnRTdkejs%3D&amp;responseType=email-sales" rel="noopener noreferrer">JetBrains销售支持</a>或您现有的JetBrains销售代表以获得额外帮助。您还可以在JetBrains<a href="https://www.jetbrains.com/legal/#terms-and-agreements" rel="noopener noreferrer">法律信息门户</a>了解更多关于许可证和条款的信息。&nbsp;</p><p><em>由 mimo-v2.5 模型翻译，花费 3561 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/dataspell/2026/05/the-upcoming-sunset-of-dataspell/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=dataspell&amp;p=708334</guid>
      <pubDate>Thu, 28 May 2026 12:29:12 +0000</pubDate>
    </item>
    <item>
      <title>弃用 dotMemory Unit</title>
      <dc:creator>Alexander Kurakin</dc:creator>
      <category>net-tools</category>
      <category>dotmemory</category>
      <category>deprecated</category>
      <category>dotmemory-unit</category>
      <description>[AI 摘要] 由于缺乏维护、兼容性及安全问题，JetBrains 宣布将弃用 dotMemory Unit 内存分析测试工具。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 由于缺乏维护、兼容性及安全问题，JetBrains 宣布将弃用 dotMemory Unit 内存分析测试工具。</div><p>dotMemory Unit 长期以来一直作为检测 .NET 代码内存问题的单元测试框架。我们感谢所有在其开发和测试工作流中使用过它的用户。</p>

<p>经过仔细考虑，我们决定停止使用 dotMemory Unit。该项目将不再进行积极维护、兼容性更新或安全修复，我们建议用户计划停止使用它。</p>

<h2>我们做出此变更的原因</h2>

<p>此决定基于若干技术、安全和产品方面的考量。</p>

<p>dotMemory Unit 已有一段时间未进行积极开发，且不支持最新的 .NET 版本。要使其达到现代兼容性、可靠性和安全标准，需要进行重大的架构重新设计。</p>

<p>此外，dotMemory Unit 生成的工作空间采用旧版格式，与近期版本的 dotMemory 不兼容。这给用户带来了不便，并阻碍了与最新 JetBrains 性能分析工具的无缝集成。</p>

<p>最后，该项目的部分依赖项已过时，且包含已知的安全漏洞。由于 dotMemory Unit 不再积极维护，我们无法在不引发兼容性问题或进行完全重建的情况下可靠地更新这些依赖项。继续分发或支持存在未修补漏洞的工具将无法满足我们的安全标准。</p>

<h2>这对您意味着什么</h2>

<p>如果您当前正在使用 dotMemory Unit，我们强烈建议您停止使用，尤其是在安全敏感的环境中。</p>

<p>我们理解 dotMemory Unit 对于测试内内存分析很有价值，也认识到其弃用可能会在某些工作流中造成缺口。目前，我们没有直接的替代方案。</p>

<h2>时间表</h2>

<p>[2026年5月20日]：dotMemory Unit 在 <a href="https://www.nuget.org/packages/JetBrains.DotMemoryUnit/" rel="noopener noreferrer">NuGet.org</a> 上被标记为已弃用。将不再发布任何更新、补丁或安全修复。</p>

<p>[2026年5月28日]：在不同渠道发布正式弃用通知。<a href="https://www.jetbrains.com/help/dotmemory-unit/DMU_Introduction.html" rel="noopener noreferrer">文档</a> 将在线保留一段时间，但会更新以反映项目的当前状态和潜在风险。</p>

<h2>致谢</h2>

<p>我们衷心感谢多年来信任 dotMemory Unit 的所有人。</p><p><em>由 mimo-v2.5 模型翻译，花费 1561 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/dotnet/2026/05/28/deprecating-dotmemory-unit/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=dotnet&amp;p=710060</guid>
      <pubDate>Thu, 28 May 2026 10:55:03 +0000</pubDate>
    </item>
    <item>
      <title>Cloud9 JetStream 主题现已登陆 JetBrains IDE</title>
      <dc:creator>Elena Kerpeleva</dc:creator>
      <category>marketplace</category>
      <category>new-products</category>
      <category>news</category>
      <category>gamedev</category>
      <description>[AI 摘要] 本文介绍了 Cloud9 与 JetBrains 合作推出的专为 JetBrains IDE 打造的 Cloud9 JetStream 深色主题。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文介绍了 Cloud9 与 JetBrains 合作推出的专为 JetBrains IDE 打造的 Cloud9 JetStream 深色主题。</div><p>Cloud9 与 JetBrains 一直在合作开展连接软件开发与电子竞技的项目，从 <a href="https://blog.jetbrains.com/blog/2026/04/16/sky-s-the-limit-hackathon-180-projects-connecting-developers-and-esports/" rel="noopener noreferrer">Sky’s The Limit 黑客松</a>，到为现场活动、播客和战队内容构建的定制工具。</p>

<p>这一合作的最新成果之一便是 Cloud9 JetStream，一款专为 <a href="https://plugins.jetbrains.com/plugin/31876-cloud9-jetstream" rel="noopener noreferrer">JetBrains IDE 定制的主题</a>。</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/default-vs-jetstream.gif" alt=""></figure>

<p>该主题将 Cloud9 的视觉标识融入开发环境，采用深色界面、Cloud9 蓝色调以及精心调校的语法高亮。其设计初衷是让 Cloud9 粉丝感到熟悉，同时保持日常编码的实用性。</p>

<h2>专为 JetBrains IDE 打造的主题</h2>

<p><a href="https://plugins.jetbrains.com/plugin/31876-cloud9-jetstream" rel="noopener noreferrer">Cloud9 JetStream</a> 专为那些希望拥有整洁、具有品牌标识的编辑器设置，同时又不改变自身工作方式的开发者打造。</p>

<p>该主题以深色为基底，搭配高对比度的界面元素，确保在长时间编码会话中编辑器依然清晰易读。Cloud9 蓝色调贯穿整个界面，用于活动状态、选中区域和关键点缀，而语法颜色则保持清晰且功能性强。</p>

<p>目标很简单：创建一个外观像 Cloud9、在 JetBrains IDE 内运行良好，并且作为日常开发环境感到舒适的主题。</p>

<h2>更广泛技术合作的一部分</h2>

<p>该主题是 Cloud9 x JetBrains 广泛合作的一部分。</p>

<p>过去几个月里，Cloud9 使用 JetBrains IDE 和 AI Coding Agent Junie 创建了一系列定制工具和体验，包括：</p>

<ul>
<li>在 AWS re:Invent 活动中使用的瞄准训练器。</li>
<li>用于 Cloud9 播客环节的 VCT 和 LCS 选手选择工具。</li>
<li>英雄联盟皮肤排名工具。</li>
<li>英雄联盟 3D 数据可视化工具。</li>
<li>通过 Sky’s The Limit Cloud9 x JetBrains 黑客松开发的项目。</li>
</ul>

<p>这些项目展示了开发工具如何支持电竞内容、粉丝互动和内部实验。Cloud9 JetStream 将这种协作带回到了 IDE 本身。</p>

<h2>为开发者与 Cloud9 粉丝设计</h2>

<p>Cloud9 JetStream 面向任何想要一个以醒目的 Cloud9 外观为特色的专注深色主题的用户。</p>

<p>对于写代码的 Cloud9 粉丝来说，这是将战队带入工作区的一种小方式。对于通过 JetBrains Marketplace 发现该主题的开发者来说，这是一个具有独特视觉标识的整洁深色主题。</p>

<div>
    <div>
        <a href="https://plugins.jetbrains.com/plugin/31876-cloud9-jetstream" rel="noopener noreferrer">尝试 Cloud9 JetStream</a>
    </div>
</div>

<p>Cloud9 JetStream 可通过 JetBrains Marketplace 获取。</p>

<p>安装该主题，在您的 JetBrains IDE 中应用，并在下一个项目中试用它。</p><p><em>由 mimo-v2.5 模型翻译，花费 2315 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/blog/2026/05/27/introducing-the-cloud9-jetstream-theme-for-jetbrains-ides/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=blog&amp;p=709709</guid>
      <pubDate>Wed, 27 May 2026 15:19:19 +0000</pubDate>
    </item>
    <item>
      <title>使用TensorFlow和PyCharm为Reachy Mini构建实时物体检测应用</title>
      <dc:creator>Evgenia Verbina</dc:creator>
      <category>data-science</category>
      <category>tutorials</category>
      <category>computer-vision</category>
      <category>object-detection</category>
      <category>python</category>
      <category>tensorflow</category>
      <description>[AI 摘要] 本文教程讲解如何使用 TensorFlow、PyCharm 和 OpenCV 构建实时物体检测应用，并将其部署到 Reachy Mini 开源机器人上，实现物体跟踪与交互。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文教程讲解如何使用 TensorFlow、PyCharm 和 OpenCV 构建实时物体检测应用，并将其部署到 Reachy Mini 开源机器人上，实现物体跟踪与交互。</div><p><em>这是来自 <a href="https://blog.jetbrains.com/pycharm/2026/05/build-a-live-object-detection-app-for-reachy-mini-with-tensorflow-and-pycharm/#author" rel="noopener noreferrer">Iulia Feroli</a> 的客座文章，她是 Back To Engineering YouTube 社区的创始人。</em></p>



<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/PC-social-BlogFeatured-1280x720-1-5.png" alt="构建实时物体检测应用"></figure>



<p>在本教程中，我们将使用 TensorFlow 和 <a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer">PyCharm</a> 构建一个实时物体检测应用，然后将其部署到 Reachy Mini 开源机器人上，以实现实时物体跟踪。</p>



<p><a href="https://github.com/pollen-robotics/reachy_mini" rel="noopener noreferrer">Reachy Mini</a> 是一款紧凑型开源机器人，由 Pollen Robotics、Hugging Face 和 Seeed Studio 合作打造。它最近非常火爆，出现在 NVIDIA 的视频中，甚至在他们一些会议的主题演讲中被提及。它特别有趣的地方在于，不仅所有代码是开源的，其机身结构也是开源的，这意味着你可以打印自己的部件并为其开发应用程序。</p>



<p>有一个由社区构建的项目应用商店，你可以探索、尝试并轻松参与贡献。任何涉及对话或摄像头的功能都特别有趣，这得益于它配备的硬件：一个扬声器、一个麦克风、一个摄像头，以及用于表达情绪的可活动天线。</p>



<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/image-26.png" alt="Reachy Mini 教程"></figure>



<p>这真正凸显了 Reachy Mini 所体现的新型机器人的独特之处：它几乎感觉像是一个大语言模型或人工智能代理的物理体现，而不是一个被添加了人工智能的机器人。它没有可以移动的身体或可以抓取东西的手，因此它真正的主要卖点在于其“大脑”。这种设计选择决定了用它来构建什么最有趣。</p>



<p>让我们学习如何构建一个 TensorFlow 物体检测应用并将其部署在 Reachy Mini 上，这将使我们能够进行实时物体跟踪。你可以前往 PyCharm 频道查看完整的代码详解并尝试。所有代码都在 <a href="https://github.com/iuliaferoli/Reachy-mini-object-detection" rel="noopener noreferrer">Reachy-mini-object-detection GitHub 仓库</a> 中。</p>



<p>要了解这款机器人的介绍，你可以先观看 Iulia 的视频：</p>



<figure><div>

</div></figure>



<h2><strong>你将学到什么</strong></h2>



<ul>
<li>如何构建实时 TensorFlow 物体检测流水线。</li>



<li>如何使用来自 TensorFlow Hub 的 SSD MobileNet V2。</li>



<li>如何使用 OpenCV 创建 TensorFlow 物体检测示例。</li>



<li>如何在 PyCharm 笔记本中运行实时摄像头推理。</li>



<li>如何在 Reachy Mini 机器人上部署物体检测。</li>



<li>如何使用头部移动逻辑跟踪检测到的物体。</li>



<li>如何将带注释的检测结果流式传输到实时仪表板。</li>
</ul>



<h2><strong>我们将构建什么</strong></h2>



<p>项目分为两个阶段。</p>



<p><strong>阶段 1</strong> 是一个独立的笔记本，完全在您的笔记本电脑上使用摄像头运行。不需要机器人。这是我们在接触任何硬件之前，确保检测流水线正确工作的阶段。</p>



<p><strong>阶段 2</strong> 是一个 Reachy Mini 应用程序，它将相同的模型与机器人集成：她的头部会移动以跟踪检测到的物体，当她发现新物体时天线会摇摆，并且一个在 http://0.0.0.0:8042 的实时网络仪表板会显示带注释的摄像头画面和检测结果。</p>



<p>你可以按照分步视频教程进行操作：</p>



<figure><div>

</div></figure>



<h2><strong>TensorFlow 物体检测的工作原理：分步详解</strong></h2>



<p>1. 从摄像头捕获图像帧。</p>



<p>2. 将帧转换为 TensorFlow 张量。</p>



<p>3. 通过预训练模型运行推理。</p>



<p>4. 接收边界框、标签和置信度分数。</p>



<p>5. 过滤低置信度的检测结果。</p>



<p>6. 将带注释的结果绘制到帧上。</p>



<p>7. 实时显示处理后的图像。</p>



<p><strong>前提条件</strong></p>



<ul>
<li>Python 3.12+。</li>



<li>带有其 <a href="https://www.jetbrains.com/help/pycharm/jupyter-notebook-support.html" rel="noopener noreferrer">Jupyter Notebook 集成</a> 的 PyCharm。</li>



<li>阶段 2 需要一台 Reachy Mini（阶段 1 完全在您的笔记本电脑上运行）。</li>



<li>对 TensorFlow 基础知识有一定了解——如果您是新手，<a href="https://blog.jetbrains.com/pycharm/2026/04/how-to-train-your-first-tensorflow-model/" rel="noopener noreferrer">本系列的上一篇文章</a> 是一个很好的起点。</li>
</ul>



<h2><strong>阶段 1：在 PyCharm 中构建 TensorFlow 物体检测流水线</strong></h2>



<p>在连接机器人之前，我们希望确保 TensorFlow 部分能够独立工作。我们将创建一个笔记本，仅通过物体检测模型执行并使其运行流畅。PyCharm 的原生笔记本集成非常适合这里：你可以检查流水线的每一步并内联可视化结果。</p>



<h3><strong>物体检测模型</strong></h3>



<p>我们使用来自 TensorFlow Hub 的 <a href="https://tfhub.dev/google/openimages_v4/ssd/mobilenet_v2/1" rel="noopener noreferrer">SSD MobileNet V2</a>，该模型在 Open Images V4 上训练。这个来自 Google 的流行模型提供了基于 SSD 的物体检测，并且已经在大量开放图像上进行了训练。通过一些微调，你可以将其部署到你自己的用例中，不过对于本教程，通用模型无需任何微调就能很好地工作。</p>



<p>它在 CPU 上以大约 10 FPS 的速度运行，对于机器人上的实时响应行为来说已经足够快了。</p>



<h3><strong>安装依赖项</strong></h3>



<pre>!pip install tensorflow tensorflow-hub opencv-python numpy Pillow</pre>



<h3><strong>加载模型</strong></h3>



<pre>import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
import cv2
import time
from IPython.display import display, clear_output
from PIL import Image

MODEL_HANDLE = "https://tfhub.dev/google/openimages_v4/ssd/mobilenet_v2/1"

print(f"TensorFlow version: {tf.__version__}")
print("Loading model (first time downloads ~30MB)...")

detector = hub.load(MODEL_HANDLE)
print("Model loaded!")</pre>



<p>该模型大约 30 兆字节，首次下载后会缓存在本地。因为它非常通用，所以无需额外的训练数据就能在许多不同的场景中工作，这使得入门变得容易得多。</p>



<h3><strong>检测和绘制辅助函数</strong></h3>



<p>我们需要两个辅助函数：一个运行推理并返回检测结果列表，另一个在帧上绘制边界框。这些是我们在 Reachy 应用中稍后使用的相同函数。</p>



<pre>def detect_objects(frame_bgr, min_score=0.5, max_detections=10):
    rgb = frame_bgr[:, :, ::-1]
    img_tensor = tf.image.convert_image_dtype(rgb, tf.float32)[tf.newaxis, ...]

    results = detector.signatures['default'](img_tensor)

    boxes = np.array(results["detection_boxes"])
    scores = np.array(results["detection_scores"])
    class_labels = np.array(results["detection_class_entities"])

    if boxes.ndim &gt; 2:
        boxes = boxes[0]
    if scores.ndim &gt; 1:
        scores = scores[0]
    if class_labels.ndim &gt; 1:
        class_labels = class_labels[0]

    scores = np.atleast_1d(scores)
    indices = [i for i, score in enumerate(scores) if score &gt;= min_score][:max_detections]

    detections = []
    for idx in indices:
        ymin, xmin, ymax, xmax = boxes[idx]
        label = class_labels[idx].decode('utf-8') if isinstance(class_labels[idx], bytes) else str(class_labels[idx])
        detections.append({
            "box": [ymin, xmin, ymax, xmax],
            "score": float(scores[idx]),
            "label": label
        })

    return detections


def draw_detections(frame_bgr, detections):
    h, w = frame_bgr.shape[:2]
    annotated = frame_bgr.copy()

    for det in detections:
        ymin, xmin, ymax, xmax = det["box"]
        x1, y1 = int(xmin * w), int(ymin * h)
        x2, y2 = int(xmax * w), int(ymax * h)

        color = (0, 255, 0)
        cv2.rectangle(annotated, (x1, y1), (x2, y2), color, 2)

        label = f"{det['label']} {det['score']:.0%}"
        font_scale, thickness = 0.6, 2
        (tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)
        cv2.rectangle(annotated, (x1, y1 - th - 8), (x1 + tw + 4, y1), color, -1)
        cv2.putText(annotated, label, (x1 + 2, y1 - 4),
                    cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0), thickness)

    return annotated</pre>



<p><code>detect_objects</code> 函数使用模型的 <code>detect_objects</code> 入口点运行推理，并处理输出张量中批次维度的展平。标签从模型返回的是字节，因此我们在返回前将其解码为字符串。</p>



<h3><strong>在单帧上测试</strong></h3>



<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/obj.png" alt=""></figure>



<pre>cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cap.release()

if not ret:
    print("ERROR: Could not access webcam. Make sure no other app is using it.")
else:
    print(f"Frame captured: {frame.shape}")

    t0 = time.time()
    detections = detect_objects(frame)
    elapsed = time.time() - t0

    print(f"Inference time: {elapsed:.2f}s ({1/elapsed:.1f} FPS)")
    print(f"Found {len(detections)} objects:")
    for d in detections:
        print(f"  - {d['label']}: {d['score']:.0%}")

    annotated = draw_detections(frame, detections)
    display(Image.fromarray(annotated[:, :, ::-1]))</pre>



<p>这是你检查模型是否检测正确以及边界框是否绘制在正确位置的阶段。PyCharm 笔记本视图中的内联图像显示使得可以轻松地在那里查看结果。</p>



<p><strong>使用 OpenCV 运行实时 TensorFlow 物体检测</strong></p>



<p>一旦单帧测试看起来不错，你就可以连续运行它：</p>



<pre>cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("ERROR: Could not open webcam.")
else:
    print("Running live detection... (interrupt kernel to stop)")
    try:
        while True:
            ret, frame = cap.read()
            if not ret:
                break

            t0 = time.time()
            detections = detect_objects(frame)
            fps = 1.0 / max(time.time() - t0, 0.001)

            annotated = draw_detections(frame, detections)
            cv2.putText(annotated, f"{fps:.1f} FPS", (10, 30),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)

            clear_output(wait=True)
            display(Image.fromarray(annotated[:, :, ::-1]))

            labels = ", ".join(f"{d['label']} ({d['score']:.0%})" for d in detections)
            print(f"{fps:.1f} FPS | {len(detections)} objects: {labels or 'none'}")

    except KeyboardInterrupt:
        print("Stopped.")
    finally:
        cap.release()
        print("Camera released.")</pre>



<p>至此，我们已经构建了一个仅具有物体检测功能的笔记本，我们可以将其与手边的任何类型的简单摄像头一起使用。现在，我们可以将其封装成一个应用程序，部署到 Reachy 上。</p>



<hr>



<h2><strong>阶段 2：在 Reachy Mini 上部署 TensorFlow 物体检测应用</strong></h2>



<p>Reachy Mini 应用位于 <code>reachy_mini_object_detector/</code> 文件夹中，它扩展了检测逻辑，增加了头部跟踪、天线反应和网络仪表板。我们遵循了 <a href="https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps" rel="noopener noreferrer">这篇博文</a> 中关于构建 Reachy 应用的指导。特别是，我们可以通过提供 <a href="https://github.com/pollen-robotics/reachy_mini/blob/main/AGENTS.md" rel="noopener noreferrer">预定义的 Agent Helper 文档</a> 来利用像 Claude 这样的辅助大语言模型系统。</p>



<h3><strong>项目结构</strong></h3>



<pre>reachy_mini_object_detector/
├── pyproject.toml
└── reachy_mini_object_detector/
    ├── detector.py       # TF Hub 模型封装器
    ├── main.py           # 应用：头部跟踪 + 网络仪表板
    └── static/           # Web UI 资源 (在 :8042 提供服务)</pre>



<p><code>detector.py</code> 文件封装了模型和 <code>detect_objects</code> 逻辑。<code>main.py</code> 从中导入，并添加了所有特定于机器人的功能。</p>



<h3><strong>安装应用程序</strong></h3>



<p>从 Reachy Mini 仪表板的 <em>Apps</em> 下，或手动添加：</p>



<pre>pip install git+https://huggingface.co/spaces/backtoengineering/reachy_mini_object_detector</pre>



<h3><strong>头部跟踪的工作原理</strong></h3>



<p>该应用程序并行运行两个循环：一个推理线程从机器人的摄像头捕获帧并运行检测，以及一个大约 50Hz 的主控循环，用于处理头部移动和天线控制。</p>



<p>头部跟踪功能将检测到的物体在画面中的位置映射为头部的偏航和俯仰偏移。摄像头的水平视场角为 60 度，垂直视场角为 45 度。当物体在画面中心时，其 <code>center_x</code> 为 0.5，因此减去 0.5 并乘以视场角即可得到跟踪它所需的角度偏移：</p>



<pre>target_yaw = -(largest.center_x - 0.5) * CAMERA_FOV_H_DEG
target_pitch = (largest.center_y - 0.5) * CAMERA_FOV_V_DEG</pre>



<p>该应用程序没有让头部瞬间转向目标，而是使用了一个平滑因子（<code>TRACKING_ALPHA = 0.15</code>），使移动看起来更自然：</p>



<pre>self._current_yaw += TRACKING_ALPHA * (target_yaw - self._current_yaw)
self._current_pitch += TRACKING_ALPHA * (target_pitch - self._current_pitch)</pre>



<p>当没有检测到任何物体时，头部会缓慢回漂至中心，而不是停留在原地。</p>



<h3><strong>天线摇摆</strong></h3>



<p>天线在首次检测到新物体类别时摇摆，而不是在每一帧都摇摆。该应用程序在 <code>_seen_classes</code> 中跟踪已看到的类别，当新物体出现时，它会设置一个持续 1.5 秒的摇摆计时器。在那个窗口期内，控制循环以如下方式驱动天线进行正弦运动：</p>



<pre>phase = (t - t0) * 8.0  # 快速摇摆
antenna_val = np.deg2rad(20.0 * np.sin(phase))
antennas = np.array([antenna_val, -antenna_val]</pre>



<p>这使得交互感觉是有意为之的：Reachy 在看到新事物时做出反应，而不是在跟踪时不断摇摆。</p>



<h3><strong>网络仪表板</strong></h3>



<p>该应用程序提供一个实时仪表板（在 http://0.0.0.0:8042 可用），其中包含带注释的摄像头画面（作为 MJPEG 流）、当前检测列表、FPS 计数器以及启用或禁用头部跟踪的开关。这在开发过程中很有用，因为你可以实时看到模型从机器人视角检测到的内容。</p>



<hr>



<h2><strong>接下来的方向</strong></h2>



<p>这是一个很好的起点，有很多方向可以发展：</p>



<ul>
<li><strong>带着特定用例运行应用程序。</strong> 模型是通用的，但如果你想让 Reachy 识别特定物体，你可以使用 TensorFlow 的 Object Detection API 在你自己的数据集上进行微调。</li>



<li><strong>添加更多应用程序。</strong> 用户已经在 Reachy Mini 商店中创建了许多应用程序，构建一个同时使用摄像头和对话功能的应用程序会开启许多可能性。</li>



<li><strong>连接到机械臂。</strong> 我接下来真正想探索的是将 Reachy 连接到 SO-101 机械臂，这样她不仅能看到物理世界，还能实际伸出并在物理世界中做事。</li>
</ul>



<p>你可以在 <a href="https://github.com/iuliaferoli/Reachy-mini-object-detection" rel="noopener noreferrer">Reachy-mini-object-detection 仓库</a> 中找到所有代码。一切都是开源的，所以请随意基于它构建、修改或部署你自己的版本。</p>



<h2><strong>常见问题解答</strong></h2>



<h3><strong>什么是 TensorFlow 物体检测？</strong></h3>



<p>TensorFlow 物体检测是一种计算机视觉技术，它使用机器学习模型来识别和定位图像或视频流中的物体。</p>



<h3><strong>对于实时应用，最好的 TensorFlow 物体检测模型是什么？</strong></h3>



<p>SSD MobileNet V2 常用于实时 TensorFlow 物体检测，因为它在推理速度和准确性之间取得了良好的平衡。</p>



<h3><strong>TensorFlow 物体检测能在 CPU 上运行吗？</strong></h3>



<p>可以。像 SSD MobileNet V2 这样的模型可以完全在 CPU 上运行，使其适用于笔记本电脑、边缘设备和机器人项目。</p>



<h3><strong>TensorFlow Object Detection API 和 TensorFlow Hub 有什么区别？</strong></h3>



<p>TensorFlow Hub 提供可重用的预训练模型，而 TensorFlow Object Detection API 提供了一个更大的框架，用于训练、评估和部署工作流。</p>



<h3><strong>我能用自定义数据训练 TensorFlow 物体检测吗？</strong></h3>



<p>可以。你可以使用自己的标记数据集对预训练模型进行微调，以检测自定义物体。</p>



<h2 id="author"><strong>关于作者</strong></h2>


    <div>
        <div>
            <div>
                                                            <div>
                            <img src="https://blog.jetbrains.com/wp-content/uploads/2026/04/Iulia-Feroli-e1775558363746.png" alt="">
                        </div>
                                        <div>
                                                    <h4>Iulia Feroli</h4>
                                                <p><b>Iulia Feroli</b><span> 是 </span><a href="https://youtube.com/@BackToEngineering" rel="noopener noreferrer"><span>YouTube 上的 Back To Engineering 社区</span></a><span> 的创始人，在那里她构建机器人、探索物理人工智能，并使复杂的工程主题变得易于理解且有趣。她拥有数据科学、人工智能、云架构和开源背景。</span></p>
                    </div>
                            </div>
        </div>
    </div><p><em>由 mimo-v2.5 模型翻译，花费 12382 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/pycharm/2026/05/build-a-live-object-detection-app-for-reachy-mini-with-tensorflow-and-pycharm/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&amp;p=709771</guid>
      <pubDate>Wed, 27 May 2026 14:06:31 +0000</pubDate>
    </item>
    <item>
      <title>2026年构建应用程序的顶级智能体框架</title>
      <dc:creator>Evgenia Verbina</dc:creator>
      <category>data-science</category>
      <category>ai-agents</category>
      <category>langchain</category>
      <description>[AI 摘要] 该文章评估了2026年用于构建人工智能应用程序的主要智能体框架，并根据其编排模型和适用场景进行了比较。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 该文章评估了2026年用于构建人工智能应用程序的主要智能体框架，并根据其编排模型和适用场景进行了比较。</div><p>2026年，人工智能世界正以前所未有的速度变化。人工智能系统仅处理单一提示交互的时代即将结束。相反，这些模型正在演变为<a href="https://www.jetbrains.com/pycharm/data-science/" rel="noopener noreferrer">智能体系统</a>——由智能体框架驱动的、长期运行、目标导向的软件，它正在成为现代应用架构的关键一层。</p>

<p>这一快速转变意味着，构建自主系统的Python开发者越来越多地依赖智能体框架来管理推理、记忆、工具和多个智能体之间的协作。</p>

<p>你可能已经听说过一些最流行的框架。LangChain和AutoGen已经声名鹊起，但还有数十种框架，其中许多是开源的，只有一到两年的历史。面对如此多承诺不同智能体能力的框架，真正的挑战在于知道哪些最适合你要构建的应用类型。</p>

<p>让我们更深入地了解2026年市场上一些最重要的智能体框架，比较它们各自的优势，并根据我们的关键比较标准进行评分，以帮助你发现哪个最适合你的项目。</p>

<h2>什么是人工智能智能体？</h2>

<p><a href="https://www.jetbrains.com/pycharm/features/ai/" rel="noopener noreferrer">人工智能智能体</a>是一种能够自主推理、设定目标并代表用户或另一个系统执行任务的软件。顾名思义，人工智能智能体具有一定程度的自主性，能够学习、适应并独立做出决策。这意味着它们可以改进自身的行为，并随着时间的推移，自主选择行动以实现特定目标或成果。</p>

<p>人工智能智能体遵循感知、推理、行动、反思（PRAR）循环，使他们能够：</p>

<ul>
<li><strong>感知：</strong> 观察环境，包括用户输入、系统状态、工具和记忆，以理解任务的当前上下文和约束。</li>

<li><strong>推理：</strong> 使用大型语言模型或混合逻辑进行规划、决策和选择行动。</li>

<li><strong>行动：</strong> 执行调用工具、更新记忆或触发工作流等行动。</li>

<li><strong>反思：</strong> 评估先前行动的结果，并调整未来的决策、计划或提示以改进结果。</li>
</ul>

<p>人工智能智能体依赖于大型语言模型的自然语言处理能力，但与传统的LLM和AI聊天机器人不同，它们不需要持续的用户输入来执行任务。智能体是主动的，基于指定的一套规则和参数自主工作以实现目标。</p>

<h2>什么是智能体框架？</h2>

<p>智能体框架提供了大规模<a href="https://lp.jetbrains.com/pycharm-for-ai-engineers/" rel="noopener noreferrer">构建、运行和控制AI智能体</a>所需的基础设施。大多数现代框架提供三个核心能力：</p>

<ul>
<li><strong>编排：</strong> 控制智能体如何排序、协调或被允许协作。</li>

<li><strong>工具：</strong> 定义智能体如何与API或数据库等外部系统交互。</li>

<li><strong>记忆：</strong> 规定智能体如何跨步骤或会话保留和检索信息。</li>
</ul>

<p>虽然没有框架也可以构建智能体，但它们对于确保智能体可靠、可扩展和安全至关重要。</p>

<p>智能体框架通过促进以下方面，帮助将实验性的智能体构建转变为可维护的软件：</p>

<ul>
<li><strong>多智能体协调：</strong> 多个智能体进行通信以计划、协作并在任务的不同领域各司其职。</li>

<li><strong>人在回路检查点：</strong> 故意设置的暂停点，人类可以在智能体执行操作之前进行审查。</li>

<li><strong>可观察性、控制性和可重现性：</strong> 能够观察智能体正在做什么、引导智能体行为，或重新运行智能体并获得相同结果。</li>
</ul>

<h2>核心编排范式</h2>

<p>在比较具体框架之前，了解它们如何运作很重要。让我们看看2026年最常用的三种编排模型。</p>

<h3>基于图的编排</h3>

<p>基于图的编排通过将智能体和工具组织为有向图中的节点来提供最大的控制力。它不是让智能体自由决定下一步做什么，而是明确定义智能体被允许遵循的流程。</p>

<h4>优势</h4>

<ul>
<li><strong>更确定的控制：</strong> 可预测的行为对于需要可靠结果的生产系统至关重要。</li>

<li><strong>更容易调试：</strong> 得益于清晰的检查点和边界，可以精确定位哪个节点失败。</li>

<li><strong>生产级可靠性：</strong> 这种方法非常适合面向客户的应用程序、企业系统或受监管的环境。</li>
</ul>

<h4>局限性</h4>

<ul>
<li><strong>需要更多前期设计：</strong> 工作流必须预先定义，这会减慢初始开发速度。</li>

<li><strong>较少的“涌现”行为：</strong> 智能体受到图的约束，留给实验和创造的空间较少。</li>
</ul>

<h3>基于角色的编排</h3>

<p>当优先考虑简单性时，基于角色的编排最为有效。智能体被分配特定角色，如“规划者”、“研究员”或“构建者”，并通过相互发送消息进行协作。</p>

<h4>优势</h4>

<ul>
<li><strong>直观的心智模型：</strong> 这种类型的运作易于理解，因为它有效地反映了人类团队的工作方式。</li>

<li><strong>快速原型设计：</strong> 需要最少的设置，允许有更多时间探索结果。</li>
</ul>

<h4>局限性</h4>

<ul>
<li><strong>行为更难约束：</strong> 因为智能体有自由决定下一步做什么，很难强制执行严格的执行路径。</li>

<li><strong>确定性有限：</strong> 相同的输入可能产生不同的结果，使得重现结果和实现一致性变得棘手。</li>
</ul>

<h3>基于链的编排</h3>

<p>基于链的编排，也称为自适应编排，可以说提供了最大的灵活性。此模型中的智能体在动态链或循环中运行，自主决定下一步骤。</p>

<h4>优势</h4>

<ul>
<li><strong>灵活的工作流：</strong> 智能体不受预定义路径的约束，可以自由探索不同的策略。</li>

<li><strong>适合创意任务：</strong> 这种方法非常适合研究、发现和实验，因为智能体可以迭代地探索想法、调整策略并改变方法。</li>
</ul>

<h4>局限性</h4>

<ul>
<li><strong>可预测性较低：</strong> 测试和调试更具挑战性，因为执行路径更难重现和追踪。</li>

<li><strong>大规模治理更困难：</strong> 随着任务变得更加复杂，这种不可预测性会加剧。</li>
</ul>

<h2>最适合您项目的智能体框架</h2>

<p>现在我们已经熟悉了智能体框架的关键编排范式，是时候比较2026年市场上一些最受欢迎的框架了。下面，我们根据我们的关键比较标准评估每个框架的性能：</p>

<ul>
<li>主要的编排模型。</li>
<li>多智能体支持。</li>
<li>记忆能力。</li>
<li>人在回路支持。</li>
<li>最佳适用场景。</li>
</ul>

<figure><table><tbody><tr><td><strong>框架</strong></td><td><strong>编排模型</strong></td><td><strong>多智能体支持</strong></td><td><strong>记忆能力</strong></td><td><strong>人在回路支持</strong></td><td><strong>最佳用途</strong></td></tr><tr><td><strong>LangChain</strong></td><td>基于链</td><td>部分支持</td><td>中等</td><td>有限到中等</td><td>快速LLM应用开发</td></tr><tr><td><strong>LangGraph</strong></td><td>基于图</td><td>支持</td><td>强</td><td>强</td><td>生产级智能体工作流</td></tr><tr><td><strong>LlamaIndex</strong></td><td>以检索为中心</td><td>有限</td><td>强</td><td>中等</td><td>知识密集型智能体</td></tr><tr><td><strong>Haystack</strong></td><td>基于管道/模块化</td><td>中等</td><td>强</td><td>中等</td><td>生产级RAG和上下文密集型AI系统</td></tr><tr><td><strong>AutoGen</strong></td><td>基于角色</td><td>强</td><td>中等</td><td>有限</td><td>对话式多智能体系统</td></tr><tr><td><strong>CrewAI</strong></td><td>基于角色</td><td>强</td><td>轻量</td><td>有限</td><td>面向任务的智能体团队</td></tr><tr><td><strong>Semantic Kernel</strong></td><td>基于规划器</td><td>中等</td><td>中等</td><td>强</td><td>企业级AI</td></tr><tr><td><strong>smolagents</strong></td><td>极简主义</td><td>有限</td><td>轻量</td><td>极少</td><td>轻量级实验</td></tr><tr><td><strong>OpenAI Agents SDK</strong></td><td>基于图</td><td>支持</td><td>托管管理</td><td>强</td><td>托管的智能体应用</td></tr><tr><td><strong>Phidata</strong></td><td>以智能体为中心</td><td>有限到中等</td><td>强</td><td>中等</td><td>数据和工具密集型智能体</td></tr></tbody></table></figure>

<p>让我们更深入地了解每个框架的优势和劣势，以及它们最适合的应用场景。</p>

<h3>LangChain</h3>

<ul>
<li><strong>核心设计：</strong> 基于链的编排。</li>
<li><strong>理念：</strong> 开发者速度与灵活性。</li>
</ul>

<p><a href="https://www.langchain.com/" rel="noopener noreferrer">LangChain</a> 于2022年推出，由于其广泛的集成生态系统，已成为采用最广泛的框架之一。它几乎是任何LLM的便捷接口，是爱好者或初创公司探索智能体AI的理想起点。虽然严格来说不是“智能体优先”，但它为智能体行为提供了构建模块。</p>

<p>LangChain比其他框架提供的控制较少，但它仍然是进入智能体系统的绝佳入口，尤其适用于速度和创造力优先于强制执行严格工作流的项目。</p>

<h4>优势</h4>

<ul>
<li>庞大的生态系统。</li>
<li>易于工具集成。</li>
<li>快速原型设计。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>比基于图的系统控制少。</li>
<li>随着复杂性增加，智能体逻辑可能难以理解。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>智能体功能的原型设计。</li>
<li>工具增强型聊天机器人。</li>
<li>LLM驱动的后端服务。</li>
</ul>

<p>如果你想深入了解基础内容，请阅读我们的<em><a href="https://blog.jetbrains.com/pycharm/2026/02/langchain-tutorial-2026/" rel="noopener noreferrer">LangChain Python教程：2026年完全指南</a></em>。它更深入地探讨了LangChain提供的功能，并引导您了解在Python中构建AI智能体的实际用例。</p>

<h3>LangGraph</h3>

<ul>
<li><strong>核心设计：</strong> 基于图的编排。</li>
<li><strong>理念：</strong> 对智能体行为的显式控制。</li>
</ul>

<p><a href="https://www.langchain.com/langgraph" rel="noopener noreferrer">LangGraph</a> 已成为生产级智能体系统的领先标准。它构建在LangChain之上，用显式图取代了隐式链，对工作流提供了严格控制，并通过中断机制提供了出色的HITL支持。</p>

<p>虽然图结构本身实际上可以通过清晰地映射智能体和工具的交互方式来简化调试，但LangGraph确实有学习曲线。大部分复杂性来自于设计图和管理节点之间的显式状态。一旦理解了这些概念，该框架就成为构建可预测和可控智能体系统的强大选择。</p>

<h4>优势</h4>

<ul>
<li>确定性工作流。</li>
<li>原生状态管理。</li>
<li>通过中断机制提供出色的HITL支持。</li>
<li>适用于受监管或关键任务系统。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>需要更多的前期设计工作。</li>
<li>由于显式图和状态管理，学习曲线更陡峭。</li>
<li>对于开放式任务，灵活性有所降低。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>自主客户支持系统。</li>
<li>AI驱动的DevOps工作流。</li>
<li>多步骤决策引擎。</li>
</ul>

<h3>LlamaIndex</h3>

<ul>
<li><strong>核心设计：</strong> 以检索为中心的编排。</li>
<li><strong>理念：</strong> 数据优先的智能体。</li>
</ul>

<p><a href="https://www.llamaindex.ai/" rel="noopener noreferrer">LlamaIndex</a> 是一个Python框架，旨在帮助AI系统理解、存储和从大量文档和数据中检索信息。</p>

<p>它不是从智能体开始然后再添加数据，而是采取相反的方法——从数据开始，然后围绕数据构建智能体行为。这就是为什么它通常被描述为数据优先或以检索为中心。</p>

<p>由于它以这种方式运行，LlamaIndex在索引、记忆和检索方面表现出色，非常适合构建智能，其智能依赖于访问正确信息而非执行复杂行动的智能体。</p>

<h4>优势</h4>

<ul>
<li>高级文档索引。</li>
<li>强大的长期记忆模式。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>不适合复杂的、行动密集型的编排。</li>
<li>对多智能体编排的支持有限。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>研究助理。</li>
<li>知识库智能体。</li>
<li>企业文档智能。</li>
</ul>

<h3>Haystack</h3>

<ul>
<li>核心设计：模块化管道编排。</li>
<li>理念：上下文工程和生产就绪的AI系统。</li>
</ul>

<p><a href="https://haystack.deepset.ai/" rel="noopener noreferrer">Haystack</a> 是由 deepset 创建的一个开源AI编排框架，用于构建生产就绪的AI智能体、检索增强生成系统和多模态应用程序。</p>

<p>Haystack并非纯粹关注智能体行为，而是将应用程序构建为由检索器、路由器、记忆层、工具、评估器和生成器组成的显式管道。这种模块化架构让你能控制信息如何在系统中流动，允许独立测试和改进每个组件。</p>

<p>Haystack 在检索信息的质量决定模型输出质量的应用中尤其强大。其设计也非常适合需要生产系统透明度和可靠性的企业环境。</p>

<h4>优势&nbsp;</h4>

<ul>
<li>高度模块化的管道架构。</li>
<li>对RAG和文档处理有出色的支持。</li>

<li>强大的生态系统，特别是在搜索和面向RAG的企业用例中。</li>
<li>与模型和向量数据库的灵活集成。</li>
</ul>

<h4>局限性&nbsp;</h4>

<ul>
<li>比轻量级框架需要更多的基础设施和设置。</li>
<li>不太注重涌现式的多智能体协作。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>检索增强生成系统。</li>
<li>企业文档智能。</li>
<li>数据密集型AI应用程序。</li>
<li>需要强大上下文控制的生产级AI管道。</li>
</ul>

<h3>AutoGen</h3>

<ul>
<li><strong>核心设计：</strong> 基于角色的多智能体协作。</li>
<li><strong>理念：</strong> 对话驱动的自主性。</li>
</ul>

<p><a href="https://www.microsoft.com/en-us/research/project/autogen/" rel="noopener noreferrer">AutoGen</a> 是微软的一个开源框架，它普及了智能体通过结构化对话进行协作的理念，将系统组织成智能体团队，每个智能体都有其特定角色。与其他框架不同，这里没有中央控制器强制执行严格的执行路径——协作本身推动了进展。</p>

<p>这种方法使得AutoGen非常适合探索性、创造性和研究驱动的多智能体系统，代价是可预测性、HITL和严格的执行控制。</p>

<h4>优势&nbsp;</h4>

<ul>
<li>自然的多智能体交互。&nbsp;</li>
<li>最小的编排开销。&nbsp;</li>
<li>适合涌现式问题解决。&nbsp;</li>
</ul>

<h4>局限性&nbsp;</h4>

<ul>
<li>有限的执行控制。</li>
<li>较弱的HITL支持。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>编码智能体。</li>
<li>头脑风暴系统。</li>
<li>AI研究实验。</li>
</ul>

<h3>CrewAI</h3>

<ul>
<li><strong>核心设计：</strong> 基于角色的任务委托。</li>
<li><strong>理念：</strong> 专业化的智能体团队。</li>
</ul>

<p><a href="https://www.crewai.com/" rel="noopener noreferrer">CrewAI</a> 围绕构建简单、结构化的多智能体系统而设计。它与AutoGen类似，将AI智能体建模为“团队”的成员，每个智能体都有明确定义的角色。目标是让多智能体系统易于上手，即使你是智能体AI新手。</p>

<p>CrewAI优先考虑简单性和速度，而非深度记忆和生产控制，使其易于学习，并成为原型和小型团队的不错选择。然而，其有限的用于大规模可观测性、HITL和错误处理的工具集使其不太适合更大的系统。</p>

<h4>优势</h4>

<ul>
<li>非常平易近人的API。</li>
<li>清晰的角色分离。</li>
<li>快速设置。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>轻量级记忆。</li>
<li>有限的生产控制。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>内容管道。</li>
<li>市场研究自动化。</li>
<li>简单工作流智能体。</li>
</ul>

<h3>Semantic Kernel</h3>

<ul>
<li><strong>核心设计：</strong> 基于规划器的编排。</li>
<li><strong>理念：</strong> 企业级AI集成。</li>
</ul>

<p><a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/" rel="noopener noreferrer">Semantic Kernel</a> 是微软的另一个开源框架，旨在构建与现有企业系统集成的AI驱动应用程序。</p>

<p>它从一开始就是为生产环境设计的，强调治理、安全、可观测性和人类监督。它不是最大化智能体自主性，而是专注于使AI可预测、可控和可审计。</p>

<p>通过将结构化工作流与LLM推理相结合，它以牺牲灵活性和涌现行为为代价，换取了信任、安全性和运营可靠性。</p>

<h4>优势</h4>

<ul>
<li>强大的HITL支持。</li>
<li>企业友好的架构。</li>
<li>良好的可观测性。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>需要更重的前期结构设计。</li>
<li>对于开放式自主性，灵活性较差。</li>
<li>学习曲线更陡峭。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>内部企业工具。</li>
<li>AI副驾驶。</li>
<li>业务流程自动化。</li>
</ul>

<h3>smolagents</h3>

<ul>
<li><strong>核心设计：</strong> 极简的基于链。</li>
<li><strong>理念：</strong> 简单性优于规模。</li>
</ul>

<p><a href="https://huggingface.co/docs/smolagents/en/index" rel="noopener noreferrer">smolagents</a> 是一个极简框架，旨在使智能体AI尽可能简单和透明。它优先考虑简单、可读的代码，无需学习大型框架即可轻松理解智能体的工作方式。</p>

<p>smolagents 旨在通过保持最小的抽象和透明的逻辑，使智能体行为易于理解和实验。它提供了对基于代码和工具调用智能体的一流支持、广泛的模型和工具兼容性，以及轻量级CLI工具，同时有意牺牲大规模编排和生产功能以换取简单性和清晰性。</p>

<h4>优势</h4>

<ul>
<li>极轻量级的设计。</li>
<li>高度透明。</li>
<li>快速实验。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>不适合扩展</li>
<li>最少的生产功能。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>教育项目。</li>
<li>概念验证。</li>
<li>轻量级本地智能体。</li>
</ul>

<h3>OpenAI Agents SDK</h3>

<ul>
<li><strong>核心设计：</strong> 托管的、工作流驱动的编排（通常基于图）。</li>
<li><strong>理念：</strong> 托管的、生产就绪的智能体。</li>
</ul>

<p>得益于ChatGPT的爆炸式流行，我们都知道OpenAI。<a href="https://developers.openai.com/api/docs/guides/agents-sdk/" rel="noopener noreferrer">Agents SDK</a> 是该公司为提供构建和运行智能体的托管平台而付出的努力，无需维护自己的编排基础设施。</p>

<p>无需从头开始组装智能体，你只需定义智能体行为和工作流，而OpenAI则提供编排、记忆管理、监控和安全控制。这使得Agents SDK对于想要快速获得生产就绪智能体的团队特别有吸引力。</p>

<h4>优势</h4>

<ul>
<li>最小的基础设施负担。</li>
<li>内置的安全性和可观测性。</li>
<li>强大的多智能体支持。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>自定义和控制减少。</li>
<li>不适合实验性研究。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>SaaS智能体功能。</li>
<li>面向客户的自主系统。</li>
<li>优先考虑速度而非定制性的团队。</li>
</ul>

<h3>Phidata</h3>

<ul>
<li><strong>核心设计：</strong> 以智能体为中心，工具密集型。</li>
<li><strong>理念：</strong> 面向现实世界数据任务的实用智能体。</li>
</ul>

<p><a href="https://docs.phidata.com/introduction" rel="noopener noreferrer">Phidata</a> 旨在构建实用的、工具驱动的AI智能体，用于处理现实世界的数据。</p>

<p>它不关注抽象的编排模式，而是将智能体置于与API、数据库和内部服务等系统直接交互的中心。</p>

<p>它的设计反映了这样一个事实：许多智能体大部分时间都花在获取、转换和操作数据上。</p>

<h4>优势</h4>

<ul>
<li>强大的工具集成能力。</li>
<li>适合以数据为中心的工作流。</li>
</ul>

<h4>局限性</h4>

<ul>
<li>不太强调编排。</li>
<li>有限的多智能体功能。</li>
</ul>

<h4>最佳应用</h4>

<ul>
<li>数据分析智能体。</li>
<li>金融和运营自动化。</li>
<li>工具驱动的决策系统。</li>
</ul>

<h2>选择合适的框架</h2>

<p>现在您已经熟悉了2026年许多流行的框架，是时候为您的项目选择合适的框架了。让我们看看一些关键用例，以及最适合它们的框架。</p>

<figure><table><tbody><tr><td><strong>编排模型</strong></td><td><strong>使用场景</strong></td><td><strong>推荐框架</strong></td></tr><tr><td>基于图</td><td>涉及复杂分支逻辑，需要高可靠性、可审计性和控制性的项目。</td><td>LangGraph, OpenAI Agents SDK</td></tr><tr><td>基于角色</td><td>需要快速开发和直观设计，受益于智能体之间涌现式协作的项目。</td><td>AutoGen, CrewAI</td></tr><tr><td>基于链</td><td>需要最大灵活性，智能体需动态适应并自主确定下一步骤的项目。</td><td>LangChain</td></tr><tr><td>基于检索</td><td>深度、可靠地访问知识比高度自主性更重要的项目。</td><td>LlamaIndex, Haystack</td></tr><tr><td>面向企业</td><td>强治理和人在回路流程是不可妥协要求的项目。</td><td>Semantic Kernel</td></tr><tr><td>轻量级</td><td>快速原型设计、教育用途和简单本地智能体，其中透明度和控制比编排复杂性更重要。</td><td>smolagents</td></tr><tr><td>以工具为中心</td><td>构建主要与API、数据库和外部系统交互的生产级智能体，而非复杂的多步骤编排。</td><td>Phidata</td></tr></tbody></table></figure>

<p>2026年，智能体框架已从实验性工具演变为许多应用程序的基础设施。关键决策不再是否使用智能体，而是你的系统需要多少控制、自主性和治理。</p><p><em>由 mimo-v2.5 模型翻译，花费 12884 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/pycharm/2026/06/top-agentic-frameworks-for-building-applications-2026/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&amp;p=711444</guid>
      <pubDate>Tue, 2 Jun 2026 12:12:37 +0000</pubDate>
    </item>
    <item>
      <title>Toolbox App 3.5：更优的远程开发可观测性、更可靠的企业配置与更顺畅的日常交互</title>
      <dc:creator>Lana Novikova</dc:creator>
      <category>jetbrains-toolbox</category>
      <category>toolbox-app</category>
      <description>[AI 摘要] Toolbox App 3.5 增强了远程开发可观测性，提升了企业配置可靠性，并优化了日常交互体验与错误修复。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> Toolbox App 3.5 增强了远程开发可观测性，提升了企业配置可靠性，并优化了日常交互体验与错误修复。</div><p>Toolbox App 3.5 致力于让日常工作更顺畅，并使托管开发环境更易于监控。该应用现在支持使用熟悉的快捷键进行界面缩放，为企业远程开发连接提供 OpenTelemetry 指标，并更妥善地处理了一些长期存在的可靠性问题。</p>

<h2>远程开发可观测性</h2>

<p>Toolbox App 现在会为远程开发连接的延迟和可靠性发出 OpenTelemetry 指标。您可以将它们发送到 Grafana、Datadadog、Prometheus 或其他兼容 OTEL 的技术栈，以监控整个开发者舰队的连接健康状况。</p>

<h2>缩放控制</h2>

<p>您现在可以使用熟悉的键盘快捷键缩放 Toolbox App 界面：Cmd/Ctrl + 放大，Cmd/Ctrl – 缩小，Cmd/Ctrl 0 重置。该设置会跨重启保持，因此您偏好的缩放级别将被保留。</p>

<h2>更清晰的更新进度</h2>

<p>检查更新不再隐藏在通用旋转图标后面。您现在会看到应用正在检查什么、正在解压什么以及进度如何——提供更清晰的进度感。</p>

<h2>企业配置</h2>

<p>对于使用 JetBrains IDE Services 的企业客户，Toolbox App 现在在与后端服务通信时会同时发送静态和动态头信息。头信息更新也会自动推送到正在运行的 IDE——无需重启 IDE 即可获取新的头信息。</p>

<h2>错误修复</h2>

<ul>
<li>基于 IntelliJ 的 IDE 不再从 Toolbox App 主视图中随机消失。</li>
<li>Android Studio 和其他别名 IDE 在更新后会保留其显示名称。</li>
<li>KDE Plasma 6.6 上的任务栏图标以及 Pop!__OS 上的托盘和应用程序图标现在能可靠地显示。</li>
</ul>

<h2>远程开发修复</h2>

<ul>
<li>SSH 规范化失败不再中断连接。</li>
<li>当 canCreateNewEnvironments 标志设置时，远程开发环境列表不再显示空白页。</li>
</ul>

<div>
    <div>
        <a href="https://www.jetbrains.com/toolbox-app/" rel="noopener noreferrer">下载最新版本</a>
    </div>
</div>

<p>我们非常希望听到您对 Toolbox App 3.5 的看法！您的反馈帮助我们改进产品，因此请在评论中分享您的体验。</p>

<p>JetBrains Toolbox App 团队</p><p><em>由 mimo-v2.5 模型翻译，花费 1613 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/toolbox-app/2026/06/toolbox-app-3-5-better-remote-development-observability-more-reliable-enterprise-configuration-and-smoother-everyday-interactions/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=toolbox-app&amp;p=711171</guid>
      <pubDate>Tue, 2 Jun 2026 14:37:45 +0000</pubDate>
    </item>
    <item>
      <title>WPF热重载现已上线：在Rider中编辑XAML即可实时查看更新</title>
      <dc:creator>Sasha Ivanova</dc:creator>
      <category>net-tools</category>
      <category>rider</category>
      <category>eap</category>
      <category>hot-reload</category>
      <category>xaml-2</category>
      <description>[AI 摘要] Rider 2026.2 EAP 2新增WPF热重载功能，允许在调试时实时编辑XAML并立即预览更改。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> Rider 2026.2 EAP 2新增WPF热重载功能，允许在调试时实时编辑XAML并立即预览更改。</div><p>自2026.2 EAP 2版本起，Rider现已支持WPF热重载功能。您可以在调试器运行应用程序时编辑XAML，并立即看到更改生效，无需重新构建、无需重启、也不会丢失应用程序中的当前位置。结合Rider中已有的C#热重载支持，这完善了WPF的“编辑并继续”工作流。</p>

<p>这个功能酝酿已久，我们对此直言不讳。<a href="https://youtrack.jetbrains.com/issue/NETPM-371/Support-Hot-Reload-for-WPF-projects-in-Rider" rel="noopener noreferrer">在Rider中支持WPF热重载的请求</a>是Rider历史上获得最多投票的问题之一。我们仔细阅读了您的每一条评论，我们今天发布的功能正是基于这些反馈直接塑造而成。</p>

<p>在继续之前，先做一个简短而诚实的说明：这是一个<strong>测试版</strong>。WPF的涵盖面很广，某些场景尚未完全覆盖。我们将在下文清晰地列出这些场景，以便您确切了解可以期待什么。</p>

    <div>
        <div>
                                                <a href="https://www.jetbrains.com/rider/nextversion/" rel="noopener noreferrer">下载 Rider 2026.2 EAP</a>
                                                    </div>
    </div>

<h2>WPF热重载能做什么</h2>

<p>当您在Rider调试器下运行WPF应用程序时，现在可以修改XAML并将保存的更改反映到实时运行的应用程序中。调整边距、重新设计按钮样式、微调<code>DataTemplate</code>、更改颜色、重新设计布局——然后保存，用户界面就会即时更新。应用程序保持当前状态。您无需再通过五个屏幕导航回到您正在处理的视图。您无需重新构建。您只需继续迭代。</p>

<h2>为何这对您的工作方式很重要</h2>

<p>WPF用户界面开发有一个自然的节奏：更改内容，查看效果，然后调整。没有热重载时，这个节奏不断被重新构建和点击返回到目标屏幕的操作所打断。有些情况让这种摩擦尤其令人沮丧，不幸的是，这些情况并不少见。</p>

<p><strong>大型、长期维护的WPF应用程序。</strong> 对许多团队而言，WPF并非遗留技术。它是现在和未来路线图的核心，许多应用程序在未来十年或更长时间内将持续被多位开发者活跃开发。在这样的代码库中，用户界面迭代速度不是一个可选项；它每天都在每位开发者身上累积效应。热重载将WPF用户界面工作中最重复的循环——更改、重新构建、返回导航、检查——进行了压缩。</p>

<p><strong>具有复杂用户界面结构的应用程序。</strong> 这正是变得有趣的地方。热重载在真正复杂的设置中表现良好，包括在一个共享的WPF库中包含自定义控件的控件模板的XAML资源字典。这正是在其他热重载实现中容易出问题的那种结构。如果您的用户界面由分层的样式、模板化的自定义控件和共享的资源字典构建，那么热重载很可能是您工作流程中至关重要的一块缺失拼图。</p>

<p><strong>双IDE设置。</strong> 团队常见的情况是主要使用Rider进行工作，但专门保留Visual Studio用于实时XAML循环。为一项任务维护两个IDE是没人想要的额外负担。热重载消除了频繁切换的需要，对许多开发者来说，这是保留第二个IDE安装的唯一剩余原因。</p>

<h2>支持的目标框架</h2>

<p>当前EAP版本支持最新版本的.NET和.NET Framework。<code>net9.0-windows</code>和<code>net10.0-windows</code>可按预期工作，同时也支持.NET Framework目标。</p>

<h2>如何尝试</h2>

<ol>
<li>下载<strong>Rider 2026.2 EAP 2</strong>或更高版本。</li>

<li>打开您的WPF项目，并从Rider启动一个<em>调试</em>会话。</li>

<li>编辑并保存您的XAML更改，无论是样式、模板、布局还是资源。</li>

<li>观看正在运行的应用程序即时更新。</li>
</ol>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/06/RD-262-Hot-reload-WPF.gif" alt=""><figcaption><em>在此示例中，我们使用对天气应用程序用户界面的更改来展示Rider 2026.2中WPF项目无缝的热重载体验</em></figcaption></figure>

<p>这就是整个循环。无需额外配置，无需启用单独的模式。</p>

<h2>已知限制</h2>

<p>以下是值得仔细阅读的部分：这些是当前热重载不会原地应用更改的情况，以及可能的解决方法。对于其中一些限制，我们暂时没有立即的开发计划，而另一些将在即将发布的版本中得到解决。以下是测试版的当前状态。</p>

<ul>
<li><strong>添加、移除或更新NuGet包。</strong> 请还原包，然后重启调试会话。</li>

<li><strong>在应用程序运行时向项目添加新的控件、窗口、页面或其他文件。</strong> 重启调试会话以加载它们。</li>

<li><strong>更改已加载XAML文件的根类型或<code>x:Class</code></strong>（例如，将<code>Window</code>变为<code>Page</code>）。</li>

<li><strong>对运行时创建的资源或运行时切换的主题字典进行更改。</strong> 重启调试会话以应用它们。</li>

<li><strong>添加依赖于静态注册的新WPF类成员</strong>，例如DependencyProperty、附加属性或RoutedEvent。注意：这仅适用于注册发生在静态字段初始化器中的情况；在方法中稍后为该字段赋值可能也会生效。</li>

<li><strong>在XAML中添加新的<code>x:Name</code>值。</strong> 这一项是部分的：XAML更新本身会实时应用，但新名称仅在重启调试会话后才能从您的C#代码后台访问。</li>

<li><strong>更改由一次性触发器（如<code>Loaded</code>上的<code>EventTrigger</code>）启动的动画。</strong> 更新的动画不会重新启动，直到视图再次加载。</li>
</ul>

<h2>帮助我们确定优先级</h2>

<p>以上大部分限制都在YouTrack中跟踪，并计划在即将发布的版本中解决。每个限制都有自己的工单，您可以在其中投票并添加来自您设置的详细信息。我们收到的关于哪些限制阻碍您工作的信号越多，就越容易确定它们的优先级：</p>

<ul>
<li>[<a href="https://youtrack.jetbrains.com/issue/RIDER-138349" rel="noopener noreferrer">RIDER-138349</a>] <em>附加到进程</em>后的热重载</li>

<li>[<a href="https://youtrack.jetbrains.com/issue/RIDER-138659" rel="noopener noreferrer">RIDER-138659</a>] 对运行时创建的资源或运行时切换的主题字典的更改</li>

<li>[<a href="https://youtrack.jetbrains.com/issue/RIDER-138874" rel="noopener noreferrer">RIDER-138874</a>] 添加依赖于静态注册的新WPF类成员</li>

<li>[<a href="https://youtrack.jetbrains.com/issue/RIDER-138348/" rel="noopener noreferrer">RIDER-138348</a>] 更改由一次性触发器启动的动画</li>
</ul>

<p></p>

<p>一些场景更远期，包括附加到已运行进程后的热重载。在我们可以承诺特定的实施方法之前，这些需要进行更多的研究。如果您的架构依赖于其中任何一种，请打开一个工单，提供来自您真实设置的具体重现细节。</p>

<h2>告诉我们您的体验</h2>

<p>Rider中的WPF热重载之所以存在，是因为开发者反复且具体地告诉了我们他们的需求，因此您现在能做的最好的事情就是继续提供反馈。在您的真实项目上尝试它，而不仅仅是示例，并通过此处的评论、<a href="https://x.com/JetBrainsRider" rel="noopener noreferrer">X平台</a>或我们的<a href="https://youtrack.jetbrains.com/projects/RIDER" rel="noopener noreferrer">问题跟踪器</a>告诉我们它的效果如何。</p>

<p>我们将在EAP周期内及之后继续扩展框架覆盖范围并逐步解决上述限制。感谢所有投票、评论和等待的人，也欢迎首次尝试此功能的用户。</p>

    <div>
        <div>
                                                <a href="https://www.jetbrains.com/rider/nextversion/" rel="noopener noreferrer">下载 Rider 2026.2 EAP</a>
                                                    </div>
    </div><p><em>由 mimo-v2.5 模型翻译，花费 4847 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/dotnet/2026/06/03/wpf-hot-reload-in-jetbrains-rider/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=dotnet&amp;p=711537</guid>
      <pubDate>Wed, 3 Jun 2026 08:59:33 +0000</pubDate>
    </item>
    <item>
      <title>RustWeek 2026：我们的收获、遇到的伙伴以及Rust的未来展望</title>
      <dc:creator>Irina Mihajlovic</dc:creator>
      <category>community</category>
      <category>rustrover</category>
      <category>conference</category>
      <category>event</category>
      <category>rust</category>
      <category>rust-ide</category>
      <description>[AI 摘要] 本文总结了RustWeek 2026大会的关键收获、社区对话及未来趋势，重点关注嵌入式Rust增长和Rust在专业开发中的普及。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文总结了RustWeek 2026大会的关键收获、社区对话及未来趋势，重点关注嵌入式Rust增长和Rust在专业开发中的普及。</div><p>2026年RustWeek大会在荷兰乌得勒支吸引了超过900名Rust开发者、教育者和维护者，通过为期数天的演讲、走廊交流、社区聚会、黑客马拉松和研讨会，全方位探讨Rust相关话题。</p>

<p>作为连续第三年的金牌赞助商，RustRover团队参会旨在支持Rust生态系统，与开发者面对面交流，并深入了解人们当下如何运用Rust进行开发。</p>

<p>会议期间，我们与多位Rust社区成员进行了系列快速采访，探讨Rust的未来。本文将分享这些对话、观察到的趋势以及让我们印象深刻的社区瞬间。</p>

<h2>RustRover参与RustWeek 2026的初衷</h2>

<p>开发者大会是少数能完全脱离工单、问题追踪器和发布说明进行交流的场合之一。对我们的团队而言，RustWeek 2026是一个机会，让我们暂时离开屏幕，与每天使用Rust构建真实项目的开发者共度时光。</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/IMG_2837.jpg" alt="RustRover团队在RustWeek 2026"><figcaption>                                                     RustRover团队在RustWeek现场                                                     </figcaption></figure>

<p>我们带来了产品演示、贴纸、问答环节、一些新奇的奖品创意，以及RustRover的最新功能更新，包括ACP、Cargo nextest支持和调用层级功能。</p>

<p>我们还携带了摄像机和五个针对Rust社区成员的快速问题。目标很简单——捕捉那些以不同方式塑造、教授和使用Rust的人们的真实观点——我们认为我们成功了。</p>

<h2><strong>展位现场</strong></h2>

<p>RustWeek之前我们低估了一件事：Rust开发者在问答环节的竞争会如此激烈。</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/image-27.png" alt=""></figure>

<p>会议期间我们每天举办一场Rust知识问答，参会者测试自己的知识。问答结束后，获胜者可以从展位上选择奖品，这莫名地让贴纸、猫耳朵和Francesco Ciulla的《<a href="https://www.amazon.it/dp/B0CW1DMW4K?tag=itlinktagbk-21&amp;geniuslink=true" rel="noopener noreferrer">The Rust Programming Handbook</a>》变得极具吸引力。</p>

<p>祝贺我们的两位Rust问答冠军Nikolai Golub和Mateusz Mackowski，他们在所有权问题、异步编程知识问答以及日益激烈的观众反应中脱颖而出！</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/WinnerRustQuiz.png" alt=""></figure>

<p>对话自然而然地转向人们当下使用Rust构建的项目类型。嵌入式系统的出现频率远超预期，尤其是围绕<kbd>probe-rs</kbd>、远程工作流和调试设置的讨论。其他参会者分享了入门经历、编辑器偏好，或者仅仅是过来聊聊他们学习Rust的体验。</p>

<h3>我们听到的关于RustRover最常见的问题</h3>

<p>许多参会者好奇RustRover如何融入现有的Rust工作流，特别是对于已经在使用VS Code、Vim或Zed的开发者。对话通常围绕调试、Cargo集成、嵌入式开发以及帮助新开发者入门Rust展开。</p>

<p>一些参会者还对远程工作流、自定义工具链以及RustRover如何处理大型多语言项目感兴趣。</p>

<h2>5个问题，来自Rust社区的3种视角</h2>

<p>RustWeek最精彩的部分之一，是听到Rust生态系统中不同的人对这门语言及其未来的看法。为了捕捉这些观点，我们在会议间隙采访了三位Rust社区成员，进行一对一交谈。相同的问题，三个非常不同的视角。</p>

<p>我们的嘉宾：来自RustRover团队的Vlad Beskrovny，来自Rust基金会的Lori Lorusso，以及Rust教育者兼作家Stefan Baumgartner。</p>

<h3>“五年后的Rust将会是___”</h3>

<p>我们询问每位嘉宾，他们如何看待Rust在未来几年的演变。</p>

    <div>
                    <blockquote><p>“Rust会变成一门‘无聊’的语言，而这正是件好事。当它被所有人采纳时，就会变得‘无聊’。”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-e1780049958874.jpg" alt="Vladislav Beskrovny，JetBrains RustRover团队">
                                <div>
                                            <strong>Vladislav Beskrovny</strong>
                                                                <span>JetBrains RustRover团队</span>
                                    </div>
            </div>
            </div>

    <div>
                    <blockquote><p>“被比你想象中更多的公司所采用。”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/lori.jpeg" alt="Lori Lorusso，RustWeek演讲者">
                                <div>
                                            <strong>Lori Lorusso</strong>
                                                                <span>Rust基金会</span>
                                    </div>
            </div>
            </div>

<figure><div>

</div><figcaption>RustWeek 2026 Lori Lorusso完整采访 </figcaption></figure>

<h3>“你对Rust有什么不受欢迎的观点？”</h3>

    <div>
                    <blockquote><p>“所有人都说它很难学，但关键只是找到正确的入门路径。它其实没那么难”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/lori-1.jpeg" alt="">
                                <div>
                                            <strong>Lori Lorusso</strong>
                                                        </div>
            </div>
            </div>

    <div>
                    <blockquote><p>“Rust是一门容易学的语言。只是很难摆脱旧的习惯”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/stefan.jpeg" alt="Stefan Baumgartner，Rust教育者及作家">
                                <div>
                                            <strong>Stefan Baumgartner</strong>
                                                        </div>
            </div>
            </div>

<figure><div>

</div><figcaption>Stefan Baumgartner完整采访</figcaption></figure>

<h3>“在RustWeek 2026上你最期待什么？”</h3>

    <div>
                    <blockquote><p>“每转一个弯，都能见到一年没见的人。”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/stefan.jpeg" alt="Stefan Baumgartner，Rust教育者及作家">
                                <div>
                                            <strong>Stefan Baumgartner</strong>
                                                        </div>
            </div>
            </div>

    <div>
                    <blockquote><p>“我很期待我的演讲。场地是个电影院，这感觉太棒了。”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/lori-1.jpeg" alt="">
                                <div>
                                            <strong>Lori Lorusso</strong>
                                                        </div>
            </div>
            </div>

    <div>
                    <blockquote><p>“我非常期待与Lukas Wirth关于IDE引擎的演讲。”</p></blockquote>
            <div>
                                    <img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/vlad-e1780049958874.jpg" alt="Vlad Beskrovny，JetBrains RustRover团队">
                                <div>
                                            <strong>Vlad Beskrovny</strong>
                                                        </div>
            </div>
            </div>

<figure><div>

</div><figcaption>Vlad Beskrovny完整采访 </figcaption></figure>

<p>RustRover团队在会议上的一个亮点是Vlad与Lukas Wirth的演讲，他们探讨了IDE架构、开发者工具以及现代Rust语言支持背后的理念。</p>

    <div>
        <div>
                                                <a href="https://www.youtube.com/live/VbdD1c3owKc" rel="noopener noreferrer"> 观看直播回放</a>
                                                                <a href="https://blog.jetbrains.com/rust/2026/05/29/how-rust-ides-understand-code/" rel="noopener noreferrer">阅读博客文章</a>
                                    </div>
    </div>

<h2>Rust开发者在RustWeek 2026上讨论的热点话题</h2>

<p>贯穿整个会议，有几个主题在展位和走廊交谈中反复出现。</p>

<p>嵌入式Rust正在增长。<a href="https://www.espressif.com/" rel="noopener noreferrer">Espressif</a>展位一直很繁忙，多位参会者前来讨论嵌入式工作流、no_std开发和调试设置。虽然许多项目仍处于实验或爱好阶段，但对嵌入式Rust的兴趣持续稳步增长。</p>

<figure><img src="https://blog.jetbrains.com/wp-content/uploads/2026/05/IMG_9017-1.jpg" alt=""></figure>

<p>除了嵌入式，大量对话围绕工具链、学习资源以及开发者如何将Rust整合到现有工作流展开。与往年相比，似乎也有更多人在专业领域使用Rust，而不仅仅是作为业余爱好进行尝试。</p>

<p><cite>最重要的是，RustWeek充满了活力。每一次走廊交谈似乎都会引发另一个推荐、一场辩论，或是对某人最新项目的自发深入探讨。</cite></p>

<h2><strong>RustRover团队在RustWeek 2026上的收获</strong></h2>

<p>RustWeek强化了我们团队已经相信但并非总能亲身体验的一点：最有用的反馈往往来自你没有计划的对话。在演讲间隙、问答环节，或者当某人在等待贴纸时——真正的洞见就在此刻浮现。</p>

<p>离开乌得勒支时，我们对Rust生态系统的走向有了更清晰的认识：对嵌入式的兴趣更浓，更多开发者在专业领域使用Rust，以及对工具链和开发者体验的关注度日益提升。我们还带着一长串的想法、功能请求以及希望继续进行的对话离开。</p>

<p>RustWeek提醒我们，Rust社区在不断壮大的同时，依然保持着其独特之处：好奇心、乐于助人的意愿，以及那种让你想在回家的火车上就开始新项目的热情。</p><p><em>由 mimo-v2.5 模型翻译，花费 7512 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/rust/2026/06/03/rustrover-at-rustweek-2026/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=rust&amp;p=710508</guid>
      <pubDate>Wed, 3 Jun 2026 11:46:01 +0000</pubDate>
    </item>
    <item>
      <title>Kotlin 2.4.0 版本发布</title>
      <dc:creator>Sarah Haggarty</dc:creator>
      <category>releases</category>
      <description>[AI 摘要] Kotlin 2.4.0 版本发布，包含语言特性更新、标准库稳定化及多平台编译器改进。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> Kotlin 2.4.0 版本发布，包含语言特性更新、标准库稳定化及多平台编译器改进。</div><p>Kotlin 2.4.0 版本已发布！以下是主要亮点：</p>

<ul>
<li><strong>语言</strong>：稳定的上下文参数、显式支持字段以及用于注解使用点目标的多种新功能。</li>
<li><strong>标准库</strong>：UUID API 的支持已稳定，并新增了检查排序顺序的支持。</li>
<li><strong>Kotlin/JVM</strong>：支持 Java 26，并且默认启用元数据中的注解支持。</li>
<li><strong>Kotlin/Native</strong>：支持将 Swift 包作为依赖项、Swift 导出的更新，并且默认启用 CMS 垃圾回收。</li>
<li><strong>Kotlin/Wasm</strong>：默认启用增量编译，并支持 WebAssembly 组件模型。</li>
<li><strong>Kotlin/JS</strong>：支持值类导出以及在 JS 代码内联中使用 ES2015 特性。</li>
<li><strong>Gradle</strong>：与 Gradle 9.5.0 兼容。</li>
<li><strong>Maven</strong>：Java 和 JVM 目标版本之间自动对齐。</li>
<li><strong>Kotlin 编译器</strong>：在 <code>.klib</code> 编译期间，内联函数的行为更加一致。</li>
</ul>

<p>要查看完整的变更列表，请参阅 <a href="https://kotlinlang.org/docs/whatsnew24.html" rel="noopener noreferrer">Kotlin 2.4.0 新功能介绍</a> 或 <a href="https://github.com/JetBrains/kotlin/releases/tag/v2.4.0" rel="noopener noreferrer">GitHub 上的发布说明</a>。</p>

<h2>如何安装 Kotlin 2.4.0</h2>

<p>Kotlin 的最新版本包含在最新版本的 <a href="https://www.jetbrains.com/idea/download/" rel="noopener noreferrer">IntelliJ IDEA</a> 和 <a href="https://developer.android.com/studio" rel="noopener noreferrer">Android Studio</a> 中。</p>

<p>要更新到新的 Kotlin 版本，请在您的构建脚本中 <a href="https://kotlinlang.org/docs/releases.html#update-to-a-new-kotlin-version" rel="noopener noreferrer">将 Kotlin 版本更改为 2.4.0</a>。</p>

<p>如果您需要命令行编译器，请从 <a href="https://github.com/JetBrains/kotlin/releases/tag/v2.4.0" rel="noopener noreferrer">GitHub 发布页面</a> 下载。</p>

<p><strong>如果您遇到任何问题：</strong></p>

<ul>
<li>在 <a href="https://app.slack.com/client/T09229ZC6" rel="noopener noreferrer">Slack</a> 上获取帮助（<a href="https://surveys.jetbrains.com/s3/kotlin-slack-sign-up" rel="noopener noreferrer">获取邀请</a>）。</li>
<li>向我们的问题跟踪器 <a href="https://youtrack.jetbrains.com/issues/KT" rel="noopener noreferrer">YouTrack</a> 报告问题。</li>
</ul>

<div>
<p>通过在文章底部填写表格来订阅接收 Kotlin 更新，以了解最新的 Kotlin 功能！ ⬇️</p>
</div>

<h2>YouTrack 上的主要问题报告者</h2>

<p><a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20gamalik" rel="noopener noreferrer">Andreas Malik</a>（21 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20rnett" rel="noopener noreferrer">Ryan Nett</a>（20 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20dramaix" rel="noopener noreferrer">Julien Dramaix</a>（18 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20kyay10" rel="noopener noreferrer">Youssef Shoaib</a>（17 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20jsjeon" rel="noopener noreferrer">Jinseong Jeon</a>（14 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20ZacSweers" rel="noopener noreferrer">Zac Sweers</a>（11 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20clovisai" rel="noopener noreferrer">Ivan Canet</a>（10 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20joseefort" rel="noopener noreferrer">Jose Enrique Estremadoyro Fort</a>（9 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20l.wasylkowski" rel="noopener noreferrer">Łukasz Wasylkowski</a>（8 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20Caleb_Brandt" rel="noopener noreferrer">calebbrandt77</a>（8 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20lppedd" rel="noopener noreferrer">Edoardo Luppi</a>（8 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20vadim.shabanov" rel="noopener noreferrer">Vadim Shabanov</a>（7 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20azefsw" rel="noopener noreferrer">Asapha</a>（6 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20louis.cad" rel="noopener noreferrer">Louis CAD</a>（6 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20vanniktech" rel="noopener noreferrer">Niklas Baudy</a>（6 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20rickclephas" rel="noopener noreferrer">Rick Clephas</a>（6 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20rlnt" rel="noopener noreferrer">rlnt</a>（5 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20marczeugs" rel="noopener noreferrer">marc</a>（5 个问题）、<a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20Turansky" rel="noopener noreferrer">Victor Turansky</a>（5 个问题）以及 <a href="https://youtrack.jetbrains.com/issues/?q=project:KT,%20KTIJ%20created:%202025-12-16%20..%20*%20created%20by:%20dlatt" rel="noopener noreferrer">Dirk Lattermann</a>（5 个问题）。</p>

<h2>外部贡献者</h2>

<p>我们感谢所有贡献者，他们的拉取请求已包含在此版本中：</p>

<p><a href="https://github.com/AndreyBozhko" rel="noopener noreferrer">AndreyBozhko</a>、<a href="https://github.com/ArseniySukhanov" rel="noopener noreferrer">ArseniySukhanov</a>、<a href="https://github.com/bennyhuo" rel="noopener noreferrer">bennyhuo</a>、<a href="https://github.com/BraisGabin" rel="noopener noreferrer">BraisGabin</a>、<a href="https://github.com/dmaclach" rel="noopener noreferrer">dmaclach</a>、<a href="https://github.com/kralliv" rel="noopener noreferrer">kralliv</a>、<a href="https://github.com/loadkrnis" rel="noopener noreferrer">loadkrnis</a>、<a href="https://github.com/migmacdev" rel="noopener noreferrer">migmacdev</a>、<a href="https://github.com/NeonMika" rel="noopener noreferrer">NeonMika</a>、<a href="https://github.com/nyksans" rel="noopener noreferrer">nyksans</a>、<a href="https://github.com/oscarArismendi" rel="noopener noreferrer">oscarArismendi</a>、<a href="https://github.com/rickclephas" rel="noopener noreferrer">rickclephas</a>、<a href="https://github.com/stefanhaustein" rel="noopener noreferrer">stefanhaustein</a>、<a href="https://github.com/Stream29" rel="noopener noreferrer">Stream29</a>、<a href="https://github.com/tcmulcahy" rel="noopener noreferrer">tcmulcahy</a> 和 <a href="https://github.com/ZacSweers" rel="noopener noreferrer">ZacSweers</a>。</p>

<h2>特别感谢我们的 EAP 冠军</h2>

<ul>
<li><a href="https://x.com/noraltavir" rel="noopener noreferrer">Alexander Nozik</a></li>
<li><a href="https://github.com/BoD" rel="noopener noreferrer">Benoit Lubek</a></li>
<li><a href="https://github.com/JesusMcCloud" rel="noopener noreferrer">Bernd Prünster</a></li>
<li><a href="https://github.com/HagamosVideojuegos" rel="noopener noreferrer">David Lopez</a></li>
<li><a href="https://github.com/dayanruben" rel="noopener noreferrer">Dayan Ruben</a></li>
<li><a href="https://github.com/molikuner" rel="noopener noreferrer">Florian Schreiber</a></li>
<li><a href="https://ivan.canet.dev/" rel="noopener noreferrer">Ivan Canet</a></li>
<li><a href="https://jakewharton.com/" rel="noopener noreferrer">Jake Wharton</a></li>
<li><a href="https://www.linkedin.com/in/johannessvensson/" rel="noopener noreferrer">Johannes Svensson</a></li>
<li><a href="https://www.linkedin.com/in/lukasz-wasylkowski/" rel="noopener noreferrer">Łukasz Wasylkowski</a></li>
<li><a href="https://github.com/MohamedRejeb" rel="noopener noreferrer">Mohamed Rejeb</a></li>
<li><a href="https://github.com/Zordid" rel="noopener noreferrer">Olaf Gottschalk</a></li>
<li><a href="https://github.com/rickclephas" rel="noopener noreferrer">Rick Clephas</a></li>
<li><a href="https://github.com/msotho" rel="noopener noreferrer">Sechaba Mofokeng</a></li>
<li><a href="https://github.com/seregamorph" rel="noopener noreferrer">Sergey Chernov</a></li>
<li><a href="https://sterlingalbury.com/" rel="noopener noreferrer">Sterling Albury</a></li>
<li><a href="https://github.com/ychescale9" rel="noopener noreferrer">Yang</a></li>
<li><a href="https://www.linkedin.com/in/yuri-geronimus/" rel="noopener noreferrer">Yuri Geronimus</a></li>
<li><a href="https://bsky.app/profile/zacsweers.dev" rel="noopener noreferrer">Zac Sweers</a></li>
</ul>

<h2>延伸阅读</h2>

<ul>
<li><a href="https://kotlinlang.org/docs/whatsnew24.html" rel="noopener noreferrer">Kotlin 2.4.0 新功能文档</a></li>
<li><a href="https://kotlinlang.org/docs/compatibility-guide-24.html" rel="noopener noreferrer">Kotlin 2.4.0 兼容性指南</a></li>
<li><a href="https://blog.jetbrains.com/kotlin/2022/11/eap-champions/" rel="noopener noreferrer">Kotlin EAP 冠军</a></li>
</ul><p><em>由 mimo-v2.5 模型翻译，花费 8217 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/kotlin/2026/06/kotlin-2-4-0-released/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=kotlin&amp;p=711421</guid>
      <pubDate>Wed, 3 Jun 2026 10:14:55 +0000</pubDate>
    </item>
    <item>
      <title>IntelliJ IDEA 2025.3.6 发布！</title>
      <dc:creator>Julia Shashkova</dc:creator>
      <category>releases</category>
      <description>[AI 摘要] IntelliJ IDEA 2025.3.6 发布，包含 Java 21 关键安全更新和错误修复。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> IntelliJ IDEA 2025.3.6 发布，包含 Java 21 关键安全更新和错误修复。</div><p>IntelliJ IDEA 2025.3.6 现已发布，包含适用于 Java 21 的最新 Oracle 关键补丁更新。此次更新包含相应的 JetBrains Runtime 变更，并修复了问题 [<a href="https://youtrack.jetbrains.com/issue/IDEA-389015/jbr25.0.3-resourcemanager.sketchImporter.ResourceFileGeneratorTest-throws-ComparisonFailure" rel="noopener noreferrer">IDEA-389015</a>]，提供了改进的可靠性和安全性。</p>

<p>您可以在 IDE 内部通过 <a href="https://www.jetbrains.com/toolbox-app/" rel="noopener noreferrer">Toolbox App</a> 更新到此版本，Ubuntu 用户可以使用 snaps 进行更新。您也可以从我们的<a href="https://www.jetbrains.com/idea/download/" rel="noopener noreferrer">网站</a>下载。</p>

<p>有关修复的详细概述，请参阅<a href="https://youtrack.jetbrains.com/articles/IDEA-A-2100662688" rel="noopener noreferrer">发行说明</a>。如果您发现任何问题，请通过<a href="https://youtrack.jetbrains.com/issues/IDEA" rel="noopener noreferrer">问题跟踪器</a>告知我们。</p>

<p>祝开发愉快！</p><p><em>由 mimo-v2.5 模型翻译，花费 1057 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/idea/2026/06/intellij-idea-2025-3-6/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&amp;p=711482</guid>
      <pubDate>Wed, 3 Jun 2026 13:12:33 +0000</pubDate>
    </item>
    <item>
      <title>异步VFS内容写入——插件作者须知</title>
      <dc:creator>Jakub Chrzanowski</dc:creator>
      <category>intellij-platform</category>
      <category>plugins</category>
      <category>plugin-development</category>
      <category>vfs</category>
      <description>[AI 摘要] 本文介绍了IntelliJ平台异步VFS写入的变化，要求插件作者在通过外部进程或NIO直接读取磁盘文件前必须刷新待写入内容。</description>
      <content:encoded><![CDATA[<div style="background:#f0f4f8;border-left:3px solid #3b82f6;padding:12px 16px;border-radius:6px;margin:12px 0;font-size:14px;color:#555"><strong>[AI 摘要]</strong> 本文介绍了IntelliJ平台异步VFS写入的变化，要求插件作者在通过外部进程或NIO直接读取磁盘文件前必须刷新待写入内容。</div><p>某些插件代码遵循以下模式：</p>

<ol>
<li>保存打开的文档。</li>
<li>获取文件或目录路径。</li>
<li>将该路径传递给IDE外部工具，如格式化器、linter、编译器、版本控制命令、语言服务器或自定义CLI工具。</li>
</ol>

<p>过去，人们通常可以合理地假设：一旦保存操作完成，磁盘上的文件就已经包含了最新的编辑器文本。</p>

<p>这种假设现在不再成立。</p>

<p>IntelliJ平台现在可以先更新VFS，稍后在后台完成磁盘写入。通过IntelliJ平台文件API读取文件的代码仍能立即看到新内容。而通过<code>Path</code>、<code>File</code>、<code>Files.*</code>或外部进程读取同一文件的代码，可能需要在数据交接前显式刷新。</p>

<p>官方SDK文档在<a href="https://plugins.jetbrains.com/docs/intellij/virtual-file.html#when-are-virtualfile-changes-persisted-on-disk-and-loaded-from-disk-to-vfs" rel="noopener noreferrer">VirtualFile变更何时持久化到磁盘并从磁盘加载到VFS？</a>中涵盖了此契约。</p>

<h2><strong>此机制存在的原因</strong></h2>

<p>对<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java" rel="noopener noreferrer"><code>VirtualFile</code></a>的写入必须在写操作下执行。至今为止，保存文件通常意味着在写操作仍开启时执行实际的文件系统写入。</p>

<p>当文件系统响应缓慢、位于远程或通过WSL/Docker挂载时，这种做法开销很大。将磁盘写入移出写操作，旨在减少文档保存时的卡顿。</p>

<h2><strong>规则</strong></h2>

<p>如果您使用IntelliJ平台文件API保存和读取文件，可能无需更改任何内容。以下方式是可行的：</p>

<ul>
<li>通过<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/openapi/fileEditor/FileDocumentManager.java#L94-L116" rel="noopener noreferrer"><code>FileDocumentManager</code></a>保存文档</li>
<li>稍后通过<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java" rel="noopener noreferrer"><code>VirtualFile</code></a>读取</li>
<li>使用VFS API如<code>contentsToByteArray</code>、<code>getInputStream</code>或<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/analysis-api/src/com/intellij/openapi/vfs/VfsUtil.java" rel="noopener noreferrer"><code>VfsUtil</code></a></li>
</ul>

<p>VFS的表现如同写入已发生。例如，在写操作之后启动的读操作，通过VFS读取时应能看到新内容。</p>

<p>如果您的代码即将直接读取物理文件，或即将路径传递给另一进程，请先使用<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/analysis-api/src/com/intellij/openapi/vfs/newvfs/ManagingFS.java#L92-L112" rel="noopener noreferrer"><code>ManagingFS</code></a>刷新待处理的VFS写入：</p>

<pre>import com.intellij.openapi.vfs.newvfs.ManagingFS

FileDocumentManager.getInstance().saveAllDocuments()

// 在写操作外刷新；这可能会等待磁盘I/O。
ManagingFS.getInstance().flushPendingUpdates()

commandLine.createProcess()</pre>

<p>如果您知道具体文件，可使用更精确的版本：</p>

<pre>FileDocumentManager.getInstance().saveDocument(document)

// 在写操作外刷新；这可能会等待磁盘I/O。
ManagingFS.getInstance().flushPendingUpdates(virtualFile)

val textOnDisk = Files.readString(virtualFile.toNioPath())</pre>

<p>抛出异常的变体可能会等待I/O并抛出<code>IOException</code>，因此请在即将访问磁盘的边界处调用。不要仅仅为了安全起见在每次保存后添加刷新。</p>

<p>对于用户触发的操作，若使用IDE通知比在代码中处理异常更合适，请使用：</p>

<pre>ManagingFS.getInstance().flushPendingUpdatesOrNotify()</pre>

<p>例如，一个在浏览器中打开生成或保存文件的操作，可以在<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncher.kt" rel="noopener noreferrer"><code>BrowserLauncher</code></a>将其交给浏览器前刷新：</p>

<pre>FileDocumentManager.getInstance().saveAllDocuments()

ManagingFS.getInstance().flushPendingUpdatesOrNotify()

BrowserLauncher.instance.browse(url, browser, project)</pre>

<p>如果保存操作发生在更早之前，请保持相同的思路：在外部读取器访问文件系统之前立即刷新。</p>

<h2><strong>值得检查的地方</strong></h2>

<p>脆弱环节在于从VFS写入的文件到直接磁盘读取器的数据交接。这可能表现为读取过时内容、外部工具看到旧内容，或测试变得不稳定（因为它们通过VFS写入但通过NIO断言）。</p>

<p>平台代码库已针对许多此类过渡进行了调整，但插件可能仍有自己的场景。常见示例：</p>

<ul>
<li>启动格式化器、linter、编译器、测试运行器、版本控制命令或语言服务器</li>
<li>通过<code>Files.readString</code>、<code>Files.newInputStream</code>、<code>Path</code>或<code>File</code>读取</li>
<li>将项目目录或文件路径传递给CLI工具</li>
<li>通过VFS写入但通过NIO断言的测试</li>
<li>调度后续磁盘I/O的VFS监听器</li>
</ul>

<p>对于VFS监听器，应在实际发生磁盘访问的地方刷新。如果监听器仅是将任务入队，请不要在同步监听器内部刷新。那会将等待放回写操作下。</p>

<p><a href="https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/AsyncableLocalFileSystemImpl.kt#L324-L335" rel="noopener noreferrer">当前平台代码</a>可能会在某些<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java#L147" rel="noopener noreferrer"><code>VirtualFile.toNioPath()</code></a>路径上刷新待写入内容，因为路径转换后通常会进行NIO访问或启动进程。不要在插件代码中使用路径转换作为同步点。如果磁盘可见性很重要，请显式调用刷新API。</p>

<h2><strong>选择加入与故障排除</strong></h2>

<p>此功能<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/vfs/impl/async/AsyncableFileSystemWrapper.kt#L34" rel="noopener noreferrer">默认启用</a>，但并非每个<code>getOutputStream()</code>调用都会自动变为异步。</p>

<p>传递给<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java#L542-L561" rel="noopener noreferrer"><code>VirtualFile.getOutputStream(requestor)</code></a>的请求者必须选择加入。目前，重要的路径是编辑器保存：<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java#L121" rel="noopener noreferrer"><code>FileDocumentManagerImpl</code></a>已选择加入，因此从编辑器保存的文件可以通过新分支处理。</p>

<p>选择加入标记本身<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/executor/AsyncFileContentWriteRequestor.java" rel="noopener noreferrer"><code>AsyncFileContentWriteRequestor</code></a>目前是内部API，因此大多数第三方插件不应急于直接采用异步写入。更紧迫的任务是审查围绕<code>saveAllDocuments()</code>和直接磁盘访问的假设。</p>

<p>要检查问题是否与此行为相关，可临时禁用：</p>

<pre>-Dvfs.async-content-write.enabled=false</pre>

<p>使用IntelliJ平台Gradle插件运行插件时，通过<code>runIde</code>任务将标志传递给IDE进程：</p>

<pre>import org.gradle.process.CommandLineArgumentProvider
tasks {
  runIde {
    jvmArgumentProviders += CommandLineArgumentProvider {
      listOf("-Dvfs.async-content-write.enabled=false")
    }
  }
}</pre>

<h2><strong>可能出现的测试失败</strong></h2>

<p>此类测试可能变得不稳定：</p>

<pre>writeThroughVfs(virtualFile)

assertEquals("expected", Files.readString(virtualFile.toNioPath()))</pre>

<p>测试通过文件系统的一个视图写入，通过另一个视图读取。请明确界定边界：</p>

<pre>writeThroughVfs(virtualFile)

ManagingFS.getInstance().flushPendingUpdates(virtualFile)

assertEquals("expected", Files.readString(virtualFile.toNioPath()))</pre>

<p>如果断言通过VFS读取，则不需要刷新。</p><p><em>由 mimo-v2.5 模型翻译，花费 5761 tokens</em></p>]]></content:encoded>
      <link>https://blog.jetbrains.com/platform/2026/06/async-vfs-content-writes-what-plugin-authors-need-to-know/</link>
      <guid isPermaLink="false">https://blog.jetbrains.com/?post_type=platform&amp;p=711620</guid>
      <pubDate>Wed, 3 Jun 2026 20:20:10 +0000</pubDate>
    </item>
  </channel>
</rss>
