【免费下载链接】rocketride-server
High-performance AI pipeline engine with a C++ core and 50+ Python-extensible nodes. Build, debug, and scale LLM workflows with 13+ model providers, 8+ vector databases, and agent orchestration, all from your IDE. Includes VS Code extension, TypeScript/Python SDKs, and Docker deployment.
本篇技术指南聚焦 RocketRide 仓库中的tool_word节点(nodes/src/nodes/tool_microsoft_365/word/README.md),它把 Microsoft Graph drive content API 与python-docx封装成一组 Agent 可调用的工具函数:读取文档文本、从段落列表新建.docx、追加文本、查找替换、导出 PDF。读完本文,你将掌握该节点的配置方式(Entra 应用 / 用户 OAuth 两种认证)、六个word_*工具的调用语义,以及它基于If-MatcheTag 的并发安全往返机制与单遍文本替换算法,可直接在自己的 RocketRide 管道中接线使用。
节点定位:无 lane 的 Agent 工具节点
tool_word是一个典型的tool 节点——数据不经过任何 lane(数据流通道)流动,每个操作都是注册给 Agent 的@tool_function,由 Agent 按需调用(word/IInstance.py 中的装饰器即注册入口)。它操作的.docx文件位于“acting user”(执行主体用户)的 OneDrive 中。
两个关键设计约束贯穿全部工具:
- 操作目标是调用参数,不是节点配置:文件路径 / item id、新建文档的目标路径永远由 Agent 在每次调用时传入,节点配置中不存任何文件地址(IInstance.py)。
- 输出是清洗后的形状,不是原始 Graph JSON:工具只返回
id、name、webUrl、文本等字段,绝不把原始 docx/PDF 字节塞回工具通道(word/client.py 的clean_item实现)。
核心机制:无持久会话的 docx 往返(round-trip)
该节点没有持久的 Word Online 编辑会话。每一次写操作都是完整的“下载—本地编辑—回传”往返:
GET获取文件元数据,拿到当前 eTag;GET .../content下载二进制 docx;- 在进程内用
python-docx编辑; PUT .../content整体回传,请求头携带If-Match: <eTag>。
client.py中的两个辅助函数精确实现了这一模式:
download_docx(auth, base, file) -> (bytes, etag):先取元数据拿 eTag,再取二进制内容(client.py);upload_docx(auth, base, file, blob, etag):携带If-Match回传;eTag 为空时(例如全新文件)省略该头(client.py)。
并发冲突是显式失败而非静默覆盖:如果下载与回传之间文件被他人修改,Graph 会返回409/412,graph_client.request将其包装成GraphError,错误信息明确指出冲突,提示调用方“re-read and retry”,而不是 last-writer-wins 覆盖别人的编辑(graph_client.py)。冲突后的标准处理是先用word_read_text重新读取,再重试编辑。
值得注意的细节:word_replace_text在零匹配时会跳过上传(IInstance.py),这样一次无操作替换不会无谓地推进 item 的 eTag / mtime——这是源码层面的一个微妙优化。
配置与访问层级(Access Tier)
节点字段
| 字段 | 说明 |
|---|---|
microsoft.authType | service(Entra 应用,客户端凭据流)或user(用户 OAuth),默认service |
microsoft.tenantId/microsoft.clientId/microsoft.clientSecret | service认证用的 Entra 应用凭据 |
microsoft.userPrincipalName | service认证下“acting user”的 UPN(应用专用调用以/users/{upn}为目标) |
microsoft.oAuthButton/microsoft.userToken | 用户 OAuth:点击登录以填充访问令牌,由 broker 自动刷新 |
word.access | readonly或write(默认),由core/microsoft_access.py中共享的WORD规范解析,scope 从不手工填写 |
字段模式定义见 services.word.json,其预置配置(preconfig)默认authType: "service"、access: "write"。
访问层级 → Graph Scopes
| 层级 | Scope | 能力 |
|---|---|---|
readonly | Files.Read | 仅读取文档文本 |
write | Files.ReadWrite | 完整读写(默认) |
WORD规范定义在 nodes/src/nodes/core/microsoft_access.py:scopes={'readonly': ['Files.Read'], 'write': ['Files.ReadWrite']}, default='write'。节点的validateConfig、check_connection工具与build_auth三个检查点共用同一个token_scope_report判定,保证三处不会漂移(graph_client.py)。readonly层级的写工具调用会在运行时抛出MicrosoftAccessError(microsoft_access.py 的require_write)。
注意:本节点没有破坏性操作的门控开关(destructive gate flags)——所有写操作都由 Graph 自身的If-Match前置条件守护,而非节点侧的安全开关。
六个 Agent 工具
| 工具 | 对应 Graph 调用 | 用途 |
|---|---|---|
word_read_text | GET .../content | 读取文档正文段落与表格单元格文本,换行拼接 |
word_create_document | PUT /drive/root:/{path}:/content | 从段落文本列表新建.docx |
word_append_text | GET/PUT .../content(If-Match) | 在文档末尾追加段落 |
word_replace_text | GET/PUT .../content(If-Match) | 跨正文段落与表格单元格查找替换,返回替换计数 |
word_export_pdf | GET .../content?format=pdf、PUT .../content | 服务端转 PDF,上传到源文件旁 |
word_check_connection | GET /drive+ scope 报告 | 诊断:连接与 scope 覆盖情况 |
各工具的实现要点
word_read_text:下载 docx →docx.Document(BytesIO(content))在内存解析 → 依次收集doc.paragraphs的正文段落,再遍历doc.tables中每个单元格的段落文本,最后以'\n'.join(parts)返回{text}(IInstance.py)。
word_create_document:用doc.add_paragraph(text)逐段建文档后直接PUT /drive/root:/{path}:/content,不带If-Match——新建文件尚无 eTag 可匹配,且本义就是创建(或有意覆盖)文件而非合并旧版本(IInstance.py)。
word_append_text:先download_docx取内容与 eTag → 追加段落 →upload_docx带If-Match回传(IInstance.py)。
word_export_pdf:GET {item}/content?format=pdf拿服务端转换的 PDF 字节 → 以“源文件名去掉.docx加.pdf”命名,上传到源文件所在目录,返回的是新 PDF 的元数据而非字节——避免把数 MB 内容塞回工具通道(IInstance.py)。
word_check_connection:以GET {base}/drive探测连接,并复用共享的 scope 报告逻辑确认已授予的 OAuth scope 覆盖配置的访问层级(IInstance.py)。
文件寻址:路径与 item id 的智能区分
工具接受两种文件参数:OneDrive 路径(如Docs/report.docx)或 drive item id。it()辅助函数用正则[A-Za-z0-9!]{15,}$判断:形如 item id(或root别名)的走/drive/items/{id},其余按root:/{path}:形式寻址,并对路径逐段做百分号编码(safe='/'保留分隔符)——未编码的空格会触发http.client.InvalidURL,未编码的#会截断路径导致寻址到错误文件(client.py)。
底层共享机制:graph_client.py
Word 与 Excel、OneDrive、Outlook Mail、Outlook Calendar 共用同一份 Graph 凭据与请求机制(graph_client.py,各服务client.py通过functools.partial绑定,word 服务在其第 38-41 行声明GraphService(product='Word', superset_scopes={'Files.ReadWrite.All'})):
- 凭据安全:token 端点仅允许
login.microsoftonline.com,broker 刷新 URL 仅允许https且 host 在信任名单(oauth2.rocketride.ai/oauth.rocketride.ai,自托管可用环境变量RR_OAUTH_BROKER_URL追加),篡改的存储 token 无法把凭据重定向到第三方主机(graph_client.py); - 两种认证实现:
AppOnlyAuth(client_credentials,进程内缓存至过期前 60 秒)与BrokerUserAuth(broker 刷新,非 200 拒绝、200 无 access_token 视为契约违规、过期前 60 秒刷新)(graph_client.py); - 重试策略:
429/5xx指数退避重试(退避序列 1s/2s/4s,最多 4 次尝试,Retry-After被钳制在 0–30s);429对所有方法重试,5xx仅对幂等方法(GET/HEAD/PUT/DELETE)重试,避免重放 POST 造成重复副作用(graph_client.py); - 错误语义:
401/403快速失败并给出可读的授权修复提示(如AADSTS65001表示需要管理员同意);409/412快速失败并提示“re-read and retry”(graph_client.py)。
还有一个对 Word 至关重要的实现细节:Graph 对/content下载常返回 302 跳转到预授权的下载 CDN 主机,_AuthStrippingRedirectHandler会在跨主机跳转时剥离Authorization头——否则外域 CDN 会以 401 拒绝携带陌生 bearer token 的请求(graph_client.py,注释明确记录该问题“found live on word_read_text”)。
word_replace_text的单遍替换算法
替换发生在两个层面:正文段落与表格单元格。_iter_all_paragraphs先产出doc.paragraphs,再遍历每个表格的每行每单元格的段落(IInstance.py)。
每个段落的处理由_replace_in_paragraph完成(IInstance.py),核心原则是单遍扫描、基于原始文本计算:
original = paragraph.text(该段落所有 run 文本的拼接)只扫描一次,替换计数与替换结果都从这个永不改变的原串计算;- 这正是为了避免“先 run 后段落”双遍扫描的缺陷:若
replace值本身包含find(如find='foo'、replace='foobar'作用于'foo is here'),双遍扫描第一遍会替换成'foobar is here',第二遍又在新写入的'foobar'里再找到'foo',最终错误计数为 2 且文本被破坏成'foobarbar is here'; - 零匹配的段落完全不动——任何 run 与格式都不受扰动;
- 至少一个匹配的段落:新文本写入第一个 run,该段落其余 run 清空。第一个 run 的格式对整个合并文本生效——多 run 段落一旦命中替换,段内的格式边界(粗体/斜体跨度等)会丢失。这是朴素 read-modify-write 文本替换的已知取舍,并非真实 Word 编辑会话的替代品。
接线方式
这是tool节点:通过control(classType: "tool")接到 Agent 旁边,同时 Agent 需要自己的memory节点:
{ "id": "tool_word_1", "provider": "tool_word", "config": { "type": "tool_word" }, "control": [{ "classType": "tool", "from": "agent_rocketride_1" }] }Agent 会自行发现word_*工具并按指令调用。在数据流层面,services.word.json的lanes为空对象,确认了该节点纯工具属性。
认证设置:两种模式与所需 Graph 权限
认证方式二选一:
- Entra 应用(
service):填microsoft.tenantId/microsoft.clientId/microsoft.clientSecret/microsoft.userPrincipalName,走 client-credentials 流; - 用户 OAuth(
user):点击登录按钮填充microsoft.userToken,由 broker 自动刷新。
完整的 Entra 应用 / 授权设置见 docs/docusaurus/apps/vscode/microsoft-oauth.md(所有 Microsoft 365 工具服务共用)。应用所需 Graph 权限取决于认证模式:
| 认证模式 | 层级 | Graph 权限 |
|---|---|---|
| 用户 OAuth(委派) | readonly | Files.Read |
| 用户 OAuth(委派) | write | Files.ReadWrite |
| Entra 应用(application,需管理员同意) | readonly | Files.Read.All |
| Entra 应用(application,需管理员同意) | write | Files.ReadWrite.All |
应用权限(application permissions)一律需要管理员同意。与 Excel workbook API 不同(其 API 不接受 application-only token),Word 的 drive-item 下载/上传/转换调用支持 app-only 模式,因此两种认证模式都被完整支持。service模式下所有 Graph 调用以/users/{userPrincipalName}为目标(user_base的实现见 graph_client.py),UPN 即选定凭据作用的数据归属用户。
限制与边界
- 并发:
word_append_text、word_replace_text是往返编辑,依赖If-Match;eTag 过期(下载期间文件被他人改动)会以命名冲突的GraphError形式暴露。工具不会自动重试——先用word_read_text重读,再重试编辑。 - PDF 导出:
word_export_pdf依赖 Graph 服务端格式转换(?format=pdf);超大或非常规文档可能转换更久,甚至上游转换失败。 - 格式坍缩:
word_replace_text将匹配段落的文本写入首 run 并清空其余 run,段内格式坍缩为首 run 样式——适合纯文本编辑,不能替代真实 Word 编辑会话;未匹配段落不受影响。 - 限流:限流按 Entra 应用 / 租户维度计数,节点对
429/5xx响应以指数退避重试。
端到端示例
一个 Agent 创建报告,随后查找替换占位符:
word_create_document { "path": "Docs/report.docx", "paragraphs": ["Q3 Report", "Status: DRAFT"] } word_replace_text { "file": "Docs/report.docx", "find": "DRAFT", "replace": "FINAL" }word_create_document在Docs/report.docx生成两段文本的新文档并返回{id, name, webUrl};word_replace_text将DRAFT替换为FINAL(带If-Match回传),返回{replacements: 1}。若期间文件被他人修改,第二次调用会收到 409/412 冲突的GraphError,此时先调用word_read_text获取最新内容再重试即可。
故障排查速查表
- Scope / 403 错误:调用
word_check_connection;若 scope 缺失——用户认证下断开并重新连接 Microsoft 账户,服务认证下按所需层级授予 / 同意 Entra 应用权限。 access为只读:写工具抛出MicrosoftAccessError;把word.access提升到write。- 冲突 / 409 / 412 错误:文件在下载后发生了变化(他人编辑,或前一次调用的上传已推进 eTag)。调用
word_read_text获取当前内容后重试编辑。 word_replace_text返回 0:确认目标文本精确匹配——find是字面量(区分大小写)子串匹配,不是正则。
【免费下载链接】rocketride-server
High-performance AI pipeline engine with a C++ core and 50+ Python-extensible nodes. Build, debug, and scale LLM workflows with 13+ model providers, 8+ vector databases, and agent orchestration, all from your IDE. Includes VS Code extension, TypeScript/Python SDKs, and Docker deployment.
相关推荐
如何将通用 MCP 客户端通过 Streamable HTTP 连接到 MCP Toolbox 的 /mcp 端点
如何将通用 MCP 客户端通过 Streamable HTTP 连接到 MCP Toolbox 的 /mcp 端点 如果你的 MCP 客户端(Claude De
RocketRide `tool_excel` 节点:基于 Microsoft Graph 的 Excel 工作簿 Agent 工具深度指南
RocketRide tool_excel 节点:基于 Microsoft Graph 的 Excel 工作簿 Agent 工具深度指南 本篇技术指南围绕 Ro
SmartDNS 3步本地DNS部署教程:让家里所有设备自动访问最快IP
SmartDNS 3步本地DNS部署教程:让家里所有设备自动访问最快IP 你有没有觉得,明明宽带是300兆,打开某些网页还是要转好几秒?很多时候锅不在带宽,而在
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考