让 AI 直接连接并操作当前 R 工作空间:VS Code / Trae / CodeBuddy进行R语言编程的进阶方式

前言

本文将介绍如何用 Posit官方开发的接口把AI Agent 和 R 真正连接起来。

这里说的“连接 R”,并不是让 AI:

  1. 临时生成一段 R 代码;启动新的 Rscript;执行后返回结果。

  2. 生成代码后复制粘贴运行

而是希望 AI 直接接入我当前正在使用的 R Session

查看 analysis_data
      ↓
基于它继续数据处理
      ↓
生成 analysis_data_new
      ↓
直接保存在当前 .GlobalEnv
      ↓
继续拟合模型
      ↓
生成 ggplot

BTW是 Posit 开发的一套 R 与大语言模型连接工具

它不仅可以把 R 对象整理成文本发给 LLM,还可以通过 MCP(Model Context Protocol)把 R 的能力暴露给支持 MCP 的 AI 客户端。

 官方参考包括 btw MCP 文档、run_r 文档与 mcptools Session 文档。btw 官方项目 btw MCP 文档 mcptools MCP Session 文档

使用 btw + MCP 后,可以变成:

AI
 ↓
MCP
 ↓
当前正在运行的 R Session
 ↓
.GlobalEnv

AI 可以直接访问当前 R 中已经存在的:

adsl
adrs
analysis_data
fit
p1

并继续创建数据、建模和绘图。

vscode搭建r语言的环境的教程参考VSCode 配置 R 语言开发环境保姆级教程(从RStudio无痛迁移)-CSDN博客

 最终效果展示

完成配置后 可以直接告诉 AI:

查看当前 analysis_data。

按 treatment 分组做描述统计。

生成新的 summary_data。

基于当前数据建立 Cox 模型。

把模型保存为 fit_cox。

根据结果生成 ggplot。

所有对象都可以继续保存在当前 .GlobalEnv 中。

用 Trae code 和 R自带数据集mtcars做效果展示

analysis_data <- mtcars %>%as_tibble()

可看到工作空间 图形输出均无问题。


1. 安装btw

install.packages("btw")

检查:

packageVersion("btw")
packageVersion("mcptools")

如果提示缺少:

coro
later
tomledit

单独安装:

install.packages(
  c("coro", "later", "tomledit"),
  repos = "https://cloud.r-project.org",
  type = "binary"
)

2. 注册当前 R Session

在 VS Code 当前正在使用的 R Console 中执行:

btw::btw_mcp_session()

这样 MCP Server 就可以访问当前 .GlobalEnv

例如当前:

analysis_data <- mtcars

fit <- lm(
  mpg ~ wt + hp,
  data = analysis_data
)

btw::btw_mcp_session()

之后 AI 就可以直接访问:

analysis_data
fit

而不需要重新读取数据。


3. 配置 VS Code MCP

项目中创建:

.vscode/mcp.json

内容:

{
  "servers": {
    "r-btw": {
      "type": "stdio",
      "command": "Rscript",
      "args": [
        "-e",
        "btw::btw_mcp_server(list('docs','env','run','sessioninfo','cran'))"
      ]
    }
  }
}

几个核心工具:

env         查看当前 Workspace
run         执行 R 代码
docs        查询 R 文档
sessioninfo 查看 R 和包版本
cran        查询 CRAN 包

其中最重要的是:

run

它允许 Agent 在当前 R Session 中真正执行 R 代码。


4. 测试是否成功

让 AI 执行:

testdata <- head(mtcars)

然后在当前 R 中检查:

exists(
  "testdata",
  envir = .GlobalEnv
)

如果返回:

TRUE

说明 AI 确实正在操作当前 R Session。


5. 启动 R 时自动连接

不想每次手动运行:

btw::btw_mcp_session()

可以加入 .Rprofile

if (
  interactive() &&
  Sys.getenv("RSTUDIO") == "" &&
  requireNamespace("btw", quietly = TRUE)
) {

  try(
    btw::btw_mcp_session(),
    silent = TRUE
  )
}

以后:

启动 VS Code
 ↓
启动 R
 ↓
读取 .Rprofile
 ↓
自动注册 MCP Session

6.补充增强

6.1 Workspace 不自动刷新的问题

有时 AI 创建:

testdata <- mtcars

对象实际已经存在:

exists("testdata")
# TRUE

但 VS Code 左侧 Workspace 没有显示。

这是因为:

.GlobalEnv 已更新
        ↓
VS Code-R Workspace UI 没有同步

在我的环境中执行:

.vsc$update_workspace()

即可立即刷新。

注意:

.vsc$update_workspace()

属于 VS Code-R 内部接口,并不是 btw 官方 API。


6.2 为什么 AI 画图后 Plot 窗口不显示?

例如 Agent 创建:

p <- ggplot2::ggplot(
  mtcars,
  ggplot2::aes(wt, mpg)
) +
  ggplot2::geom_point()

对象:

p

已经存在,但 VS Code Plot Viewer 可能不会自动显示。

原因是 btw 会使用独立图形设备捕获图片,然后把图片通过 MCP 返回给 AI:

R
 ↓
btw
 ↓
捕获 Plot
 ↓
PNG
 ↓
MCP Client

而不是直接:

R
 ↓
VS Code Plot Viewer

因此手动:

print(p)

后图形才会出现在 VS Code。


6.3 可选增强:自动刷新 Workspace + 自动显示 Plot

下面是我针对:

btw 1.4.0
VS Code-R

设计的一个本地增强方案。需要安装r包 png

在用户目录.Rprofile文件中添加以下代码


      if (base::requireNamespace("btw", quietly = TRUE)) {
        patch_btw_vscode <- function() {
          ns <- base::asNamespace("btw")
          current_impl <- base::get(
            "btw_tool_run_r_impl",
            envir = ns,
            inherits = FALSE
          )

          if (base::isTRUE(base::attr(current_impl, "vscode_auto_output"))) {
            return(base::invisible(TRUE))
          }

          original_impl <- current_impl

          refresh_workspace <- function() {
            vsc <- base::get0(
              ".vsc",
              envir = .GlobalEnv,
              inherits = TRUE
            )

            if (
              base::is.environment(vsc) &&
              base::is.function(vsc$update_workspace)
            ) {
              base::try(vsc$update_workspace(), silent = TRUE)
            }

            base::invisible(NULL)
          }

          show_plot <- function(result) {
            last_value <- base::tryCatch(
              result@extra$data,
              error = function(e) NULL
            )

            if (
              base::inherits(last_value, "ggplot") ||
              base::inherits(last_value, "grob") ||
              base::inherits(last_value, "gtable") ||
              base::inherits(last_value, "recordedplot")
            ) {
              base::try(
                {
                  if (base::inherits(last_value, "recordedplot")) {
                    grDevices::replayPlot(last_value)
                  } else {
                    base::print(last_value)
                  }
                },
                silent = TRUE
              )

              return(base::invisible(TRUE))
            }

            contents <- base::tryCatch(
              result@extra$contents,
              error = function(e) base::list()
            )

            if (!base::length(contents)) {
              return(base::invisible(FALSE))
            }

            is_image <- base::vapply(
              contents,
              function(x) {
                base::tryCatch(
                  S7::S7_inherits(x, ellmer::ContentImageInline),
                  error = function(e) FALSE
                )
              },
              logical(1L)
            )

            if (!base::any(is_image)) {
              return(base::invisible(FALSE))
            }

            img <- contents[[utils::tail(base::which(is_image), 1L)]]
            image_type <- base::tryCatch(
              img@type,
              error = function(e) ""
            )
            image_data <- base::tryCatch(
              img@data,
              error = function(e) ""
            )

            if (
              base::identical(image_type, "image/png") &&
              base::nzchar(image_data) &&
              base::requireNamespace("jsonlite", quietly = TRUE) &&
              base::requireNamespace("png", quietly = TRUE)
            ) {
              shown <- base::tryCatch(
                {
                  raster <- png::readPNG(jsonlite::base64_dec(image_data))
                  grid::grid.newpage()
                  grid::grid.raster(raster)
                  TRUE
                },
                error = function(e) FALSE
              )

              return(base::invisible(shown))
            }

            base::invisible(FALSE)
          }

          patched_impl <- function(
            code,
            .envir = rlang::global_env(),
            show_last_value = TRUE
          ) {
            base::on.exit(refresh_workspace(), add = TRUE)

            result <- original_impl(
              code,
              .envir = .envir,
              show_last_value = show_last_value
            )

            show_plot(result)
            result
          }

          base::attr(patched_impl, "vscode_auto_output") <- TRUE

          # .Rprofile 执行时通常只有 base 已附加,因此必须显式使用
          # utils 命名空间。
          utils::assignInNamespace(
            "btw_tool_run_r_impl",
            patched_impl,
            ns = "btw"
          )

          base::invisible(TRUE)
        }

        base::tryCatch(
          patch_btw_vscode(),
          error = function(e) {
            base::message(
              "未能安装 btw 的 VS Code 补丁: ",
              base::conditionMessage(e)
            )
          }
        )

        base::tryCatch(
          btw::btw_mcp_session(),
          error = function(e) {
            base::message(
              "未能启动 btw MCP 会话: ",
              base::conditionMessage(e)
            )
          }
        )
      } else {
        base::message("未安装 btw;已跳过 btw MCP 启动。")
      }
    }
  })
}

保存文档 重启r工作空间


注意安全

run 工具可以执行任意 R 代码。

也就是说 AI 理论上可以运行:

rm(list = ls())

甚至:

system(...)

因此更适合:

个人电脑
可信项目
本地开发环境

总结

核心只需要记住三层:

AI Agent
    ↓
MCP Server
    ↓
当前 R Session

R 中:

btw::btw_mcp_session()

VS Code 中:

.vscode/mcp.json

开启:

env + run

就可以让 AI 直接围绕当前 R Workspace 连续完成数据处理、统计建模和绘图。

相比传统“AI 写代码 → 手动复制执行”,最大的提升是:

AI 可以直接操作正在运行的 R Session,而不是每次重新启动一个新的 R 环境。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值