关于 IPs-LE
IPs-LE 从文档中提取 IPv4、IPv6、CIDR 网段和 MAC 地址,规范化地址并分类其范围。它面向审查配置、日志或项目文件的开发者;对有歧义的格式会说明原因,不会默默作出解释,也不进行网络查询。
主要功能
- 01提取 IPv4、IPv6、CIDR 和 MAC
- 02规范化 IPv6 地址
- 03分类地址范围
- 04计算 CIDR 网络与地址数量
- 05说明歧义格式的原因
适用场景
- 01规范化允许列表中的地址
- 02检查日志中的对端和上游地址
- 03扫描项目文件中的地址
MCP 相关
MCP 服务通过 stdio 提供 `extract_ips`,接收内容及可选格式、类型和类别筛选参数。它以数据形式返回结果和无法判定项,不读取文件或发起网络请求。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| kind | array | 否 | 仅报告这些类型。拒绝结果始终会报告。 |
| class | array | 否 | 仅报告这些类别。拒绝结果始终会报告。 |
| format | enum | 否 | 文档格式。可选 — 仅决定结果是否包含键路径,不影响是否能找到结果。json | yaml | toml | ini | env | csv | tsv | log |
| content | string | 是 | 要扫描的文档文本。 |
| filename | string | 否 | 用于在 `format` 未指定时推断格式的文件名,例如 "app.yaml" 或 "access.log"。 |
| maxResults | integer | 否 | 返回的问题数上限(默认 500)。meta.truncated 报告是否有结果被省略。 |
详细介绍
IPs-LE:一个地址,一种写法
在文档中查找所有 IP 地址、CIDR 网段和 MAC 地址,规范化并分类,并明确指出有歧义的地址
IPv4 · IPv6 (RFC 5952) · CIDR · MAC — 不进行 DNS 查询,不查找,不建立套接字
有用吗? 点赞或评分能帮助其他开发者找到它 — ★ GitHub · ★ Open VSX · ★ Marketplace
它的功能
审查允许列表时,需要确认 2001:0db8::0001 是否已在列表中。列表中写的是 2001:db8::1。对原始文本进行差异比较会认为它们是两个地址,但实际上是同一个。
打开文档,运行 IPs-LE: Extract Addresses,文档中的所有 IPv4 和 IPv6 地址、CIDR 网段及 MAC 地址都会按类型列出,并标明其行号、列号、所属键、规范形式和用途——环回、私有、链路本地、文档专用等。CIDR 网段还会显示其网络地址、最后一个地址以及包含的地址数量。报告会在编辑器旁打开。支持 VS Code,以及 Cursor 和 VSCodium 等基于 VS Code 的编辑器(可从 Open VSX 安装)。
- 审查配置或允许列表 — 每个地址只有一种写法,并将私有地址标明为私有
- 阅读日志 — 找出每个对端和上游地址,即使它们位于 URL 或
[host]:port中 - 信任
010.1.1.1之前 — 根据解析者的不同,它可能表示两个不同的主机 - 检查整个项目 — 在一张表中列出每个包含地址的文件及其地址数量
无法明确读取的文本会连同原因一起报告,绝不猜测。 它不会解析地址、查询任何内容或改写文本。
安装
| 位置 | 可获得的内容 | 安装 |
|---|---|---|
| VS Code | 在编辑器中按一下键即可提取 | Marketplace |
| Cursor, VSCodium, Windsurf | 同一扩展 | Open VSX |
| 终端或 CI 步骤 | 整棵树及退出代码 | cargo install ips-le · crates.io |
| 通过 Node 使用终端 | 与 Rust 版本逐字节完全一致的同一命令行 | npx ips-le · npm |
| 通过 Node 使用任意 MCP Agent | 通过 stdio 调用 extract_ips | npx ips-le-mcp · npm |
它能回答什么
一个地址,一种形式。 IPv6 按照
RFC 5952 进行规范化——
2001:0db8:0000:0000:0000:0000:0000:0001、2001:db8:0:0:0:0:0:1、
2001:0db8::0001 和 2001:DB8::1 最终都会变成 2001:db8::1。
对原始文本排序会得到四个地址;对规范化后的形式排序则只会得到一个。
四种类型。 ipv4、ipv6、cidr、mac。
十种类别,范围封闭。无法命名的类别,就不会声称能够识别。
| 类别 | IPv4 | IPv6 |
|---|---|---|
loopback | 127.0.0.0/8 | ::1 |
private | 10/8、172.16/12、192.168/16 | — |
link-local | 169.254/16 | fe80::/10 |
cgnat | 100.64/10 | — |
multicast | 224/4 | ff00::/8 |
broadcast | 255.255.255.255 | —(IPv6 没有广播地址) |
documentation | 192.0.2/24、198.51.100/24、203.0.113/24 | 2001:db8::/32 |
unique-local | — | fc00::/7 |
reserved | 0/8、192.0.0/24、198.18/15、240/4 | ::、2001::/23、100::/64 |
global | 其他所有地址 | 其他所有地址 |
IPv4 映射的 IPv6 地址归入 IPv4 类别,因此 ::ffff:127.0.0.1
属于 loopback,而不是 global——这正是审查允许列表时要找出的遗漏。
地址所在位置。 行、列,以及它所属的键:JSON、YAML、
TOML、INI、dotenv、CSV 和日志都会提供这些信息。其他所有内容也仍会被扫描——
搜索直接针对字节进行,因此 .tf、.rules 或轮转后的 access.log.1 也能找到其中的地址,只是不会返回键路径。
网段及其计算结果。 CIDR 结果包含 prefix、
network、broadcast(仅限 IPv4——IPv6 没有广播地址)、last 和 hosts。
hosts 是十进制字符串,因为 ::/0 包含 2^128 个地址,比 u128 的最大值还多一个,也远超 JSON 数字的范围。
{
"kind": "cidr",
"text": "10.0.0.0/8",
"normalized": "10.0.0.0/8",
"class": "private",
"cidr": {
"prefix": 8,
"network": "10.0.0.0",
"broadcast": "10.255.255.255",
"last": "10.255.255.255",
"hosts": "16777216"
}
}
它拒绝处理的内容
当文本有不止一种合理解读时,ips-le 会报告该文本,说明存在的歧义,然后停止。
六种原因,每一种都表示两种解读同样站得住脚:
| reason | fires on |
|---|---|
octal_hazard | 010.1.1.1、0177.0.0.1、192.168.001.1 |
ambiguous_version | 10.0.1、1.2.3——除非键名指明这是版本 |
integer_form | 地址键下的 2130706433 |
malformed_address | 256.1.1.1、2001:db8:::1、12345::1 |
prefix_out_of_range | 10.0.0.0/33、2001:db8::/129 |
mac_ambiguous | deadbeefcafe |
最重要的两种情况:
- 不会对
010.1.1.1作出判定。 有些解析器会把前导零八位组视为八进制,另一些则视为十进制,因此这段文本指代两个不同的主机。输出中不会出现任一种解读——如果工具擅自选定一种,反而会掩盖问题。 - 只有与标志一起出现时,才会解码
2130706433。 在地址键下,它会被报告为integer_form,拒绝消息中会注明127.0.0.1。你绝不会看到一个环回地址悄悄出现在地址列表中,而标志却消失了。
拒绝处理是一项发现,而不是失败。 它不会改变退出代码,也没有任何过滤器能隐藏它——即使筛选 private,你仍会看到八进制歧义,因为过滤后的报告最不应该遗漏的正是这项发现。需要未解决的歧义中止构建的流水线可以使用 --strict。
crate/SPEC.md 明确说明了每种原因的触发条件。
它从不访问网络
不进行 DNS 查询、地理定位、ASN 查询、WHOIS 查询、可达性检查或遥测。既不会藏在某个标志后面,也绝不会进行。分类依据位运算和 IANA 注册表;如果进行查询,结果就会取决于审计者所处的网络。
它也绝不会改写文件,更不会给出判定。它说明地址是什么,而不是地址是否应该出现在那里。
扫描文件夹或工作区
提取会读取你当前打开的文档。扫描会从磁盘读取许多文件,并生成一份报告。
- 整个工作区:在命令面板中运行
IPs-LE: Scan Workspace for Addresses。 - 一个文件夹:在资源管理器中右键单击该文件夹并选择
Scan Folder for Addresses,或运行IPs-LE: Scan Folder for Addresses并选择一个文件夹。
报告打开后,会先显示一个表格,列出所有包含内容的文件,然后按文件分别显示一个部分:
# IPs-LE workspace report
`my-project` · 113 file(s) read · 2 address(es), 1 could not be read
| File | Addresses | Could not be read |
|---|---|---|
| `deploy/config.json` | 2 | 1 |
## `deploy/config.json` · json (2)
- **3:14** · `2001:0db8:0000:0000:0000:0000:0000:0001` · ipv6 · → `2001:db8::1` · documentation · key `upstream.host`
- **5:12** · `192.0.2.10` · ipv4 · documentation · key `peer`
> 2 file(s) larger than the safety limit were not read.
扫描会读取什么。 文件从磁盘读取,因此未保存的编辑不会被看到。超过安全大小的文件,或不是 UTF-8 文本的文件,都不会被读取。扫描最多处理 5,000 个文件或列出 10,000 个地址。报告末尾会为每个未纳入的项目添加一行,因此即使报告很短,也不会被误认为项目中没有问题。
会跳过什么,以及如何更改。 默认启用三个开关,每个开关都可以在设置中单独关闭:
| 开关 | 跳过的内容 |
|---|---|
scanUseDefaultExcludes | 依赖文件夹、构建输出、工具缓存和锁文件。完整列表见下文 |
scanRespectGitignore | 项目 .gitignore 文件跳过的所有内容 |
scanSkipBinaryFiles | 图片、字体、存档及其他非文本文件 |
两个列表可以调整扫描结果,而无需关闭开关。若要跳过更多内容,请将模式添加到 scanExcludes。若要读取开关原本会跳过的内容,请将其添加到 scanAlwaysInclude:
{
// Also skip the test fixtures.
"ips-le.workspace.scanExcludes": ["**/fixtures/**"],
// Read the vendored code, though the built-in list skips it.
"ips-le.workspace.scanAlwaysInclude": ["**/vendor/**"]
}
IPs-LE: Open Settings 会在设置编辑器中打开所有这些设置。
内置列表
无论出现在何处,以下文件夹都会被跳过:
.git、.hg、.svn、node_modules、bower_components、jspm_packages、.pnpm-store、.yarn、vendor、site-packages、Pods、Carthage、dist、build、out、target、_build、_site、dist-newstyle、zig-out、storybook-static、cdk.out、DerivedData、CMakeFiles、.next、.nuxt、.output、.svelte-kit、.angular、.astro、.docusaurus、.vuepress、.expo、.turbo、.parcel-cache、.cache、.sass-cache、.jekyll-cache、.dart_tool、.pub-cache、.gradle、.kotlin、.cxx、.externalNativeBuild、captures、ephemeral、.symlinks、.swiftpm、.build、.bundle、.stack-work、.zig-cache、.godot、elm-stuff、.vercel、.netlify、.serverless、.aws-sam、.terraform、.venv、venv、__pycache__、.tox、.nox、.mypy_cache、.pytest_cache、.ruff_cache、.ipynb_checkpoints、.eggs、coverage、htmlcov、.nyc_output、.vscode-test、.idea、.vs、xcuserdata、*.egg-info
无论出现在何处,以下文件都会被跳过:
*.min.js、*.min.css、*.map、*.snap、*.lock、package-lock.json、pnpm-lock.yaml、npm-shrinkwrap.json、go.sum、*.pbxproj、*.iml、local.properties、output-metadata.json、.flutter-plugins、.flutter-plugins-dependencies、.packages、Generated.xcconfig、flutter_export_environment.sh、GeneratedPluginRegistrant.*、fastlane/report.xml、fastlane/test_output/**、doc/api/**
以下内容不在列表中,因为它们在许多项目里都是普通文件夹:bin、obj、tmp、logs、public、generated。会生成这些内容的项目会在 git 中忽略它们,扫描也会读取 .gitignore。
无法读取的内容。 一个项目中可能有很多此类内容,而且会挤占其他结果。因此,扫描会在表格中按文件统计数量,而不逐项列出。ips-le.workspace.scanIncludeRefusals 会列出每一项及其原因;ips-le.workspace.scanProblemsEnabled 也会将它们放入“问题”面板,每一项都可以单击跳转。
影响扫描的设置位于设置中。
位置
每行开头都会显示地址所在的行和列:
- **3:14** · `2001:0db8:0000:0000:0000:0000:0000:0001` · → `2001:db8::1` · documentation · key `upstream.host`
关闭 ips-le.showPositions 后,同一行会显示为:
- `2001:0db8:0000:0000:0000:0000:0000:0001` · → `2001:db8::1` · documentation · key `upstream.host`
其他内容都不会改变:行数相同,计数也相同。剪贴板中的内容由另一个设置单独控制:ips-le.clipboardIncludesPositions。因此,报告可以在屏幕上显示位置,而复制粘贴的内容不包含位置。这两个设置对扫描和提取同样适用。
在 AI Agent 中使用
相同的引擎也可作为 MCP 服务运行,因此 Agent 可以直接调用它,而不必凭肉眼判断两种拼写是否指向同一个地址。
| 编辑器 | 使用方法 |
|---|---|
| VS Code 1.101+ | 无需安装任何内容——该扩展会在 Agent 模式下注册 extract_ips |
| Claude Code | claude mcp add ips-le -- npx -y ips-le-mcp |
| Cursor、Windsurf 及其他工具 | 将其指向 npx ips-le-mcp |
extract_ips(content, format?, filename?, kind?, class?, maxResults?)
它会以数据形式返回编辑器所呈现的发现结果(包括拒绝结果),默认上限为 500 条,并通过 meta.truncated 标示是否截断。它不会读取任何文件,也不会发出网络请求。它以 ips-le-mcp 的名称发布在 npm 上,也以 io.github.nolindnaidoo/ips-le 的名称发布在 MCP 注册表中。它的回答与 Rust CLI 服务完全一致:两者使用同一语料库运行;差分测试会将数千份各种格式的生成文档(包括损坏的 JSON)分别输入两者,并比较每一条回答——对于损坏的 JSON,两者都会报告解析器自身给出的错误信息和位置。
手动配置 — 适用于任何带有 MCP 配置文件的宿主
{
"mcpServers": {
"ips-le": {
"command": "npx",
"args": ["-y", "ips-le-mcp"]
}
}
}
也可以通过 npm install -g ips-le-mcp 一次性安装,然后将其指向 ips-le-mcp。它不需要环境变量、API 密钥或任何自行配置。检查方法如下:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | npx -y ips-le-mcp
CLI
同样的提取功能也可以在终端或 CI 步骤中对整个目录树运行:Rust CLI 位于 crate/,并与扩展共享同一语料库——crate/fixtures/——确保两者对地址的读取结果永远一致。同一命令行工具也以 ips-le 的名称发布在 npm 上,适用于安装了 Node 但没有 Rust 的机器;CI 会在三种操作系统上逐字节验证它与 crate 的一致性。
从 npm 安装(ips-le)或从 crates.io 安装(ips-le)。两种方式都会安装同一个 ips-le 命令。
npm install -g ips-le # or run it once: npx ips-le
cargo install ips-le
ips-le . # every address in the tree; one JSON line per file on stdout when piped
ips-le --class private --class loopback . # what should not be reachable
ips-le --kind cidr infra/ # every block, with its arithmetic
ips-le --strict config/ # exit 2 on any ambiguity
ips-le mcp # extract_ips and ips_le_scan over MCP on stdio
退出码遵循 grep 规则——至少命名了一个地址时为 0,没有地址时为 1,问题格式错误时为 2。拒绝结果不会改变退出码;使用 --strict 可让流水线将拒绝结果视为失败。
命令
| 命令 | 描述 |
|---|---|
IPs-LE: Extract Addresses | 提取当前文档中的所有地址 |
IPs-LE: Scan Workspace for Addresses | 从工作区中的每个文件提取地址,每个文件为一个分节 |
IPs-LE: Scan Folder for Addresses | 对单个文件夹执行相同操作。在资源管理器中的文件夹上也可使用 |
IPs-LE: Open Settings | 打开 IPs-LE 设置 |
IPs-LE: Help & Troubleshooting | 内置文档 |
默认情况下,没有命令绑定快捷键。可在编辑器的键盘快捷方式中为任意命令设置快捷键。
设置
| 设置项 | 默认值 | 说明 |
|---|---|---|
ips-le.kinds | [] | 仅报告这些类型;为空时报告所有类型。拒绝始终会报告 |
ips-le.classes | [] | 仅报告这些类别;为空时报告所有类别。拒绝始终会报告 |
ips-le.openResultsSideBySide | true | 在当前编辑器旁打开报告 |
ips-le.showPositions | true | 显示每个地址所在的行和列 |
ips-le.copyToClipboardEnabled | false | 同时将报告复制到剪贴板 |
ips-le.clipboardIncludesPositions | true | 在复制内容中包含行和列 |
ips-le.workspace.scanPatterns | ["**/*"] | 文件夹或工作区扫描读取的文件 |
ips-le.workspace.scanUseDefaultExcludes | true | 跳过依赖文件夹、构建输出、缓存和锁文件 |
ips-le.workspace.scanRespectGitignore | true | 跳过项目的 .gitignore 文件所排除的内容 |
ips-le.workspace.scanSkipBinaryFiles | true | 跳过图片、字体、归档文件及其他非文本文件 |
ips-le.workspace.scanExcludes | [] | 通过 glob 模式指定要跳过的其他文件 |
ips-le.workspace.scanAlwaysInclude | [] | 即使上述三项设置会跳过,也要读取这些文件 |
ips-le.workspace.scanMaxFiles | 5000 | 单次扫描读取的文件数上限 |
ips-le.workspace.scanMaxResults | 10000 | 停止读取前,单次扫描列出的地址数上限 |
ips-le.workspace.scanIncludeRefusals | false | 列出每次无法读取的运行记录,而不只是每个文件的数量 |
ips-le.workspace.scanProblemsEnabled | false | 同时在「问题」面板中显示无法读取的运行记录 |
ips-le.safety.enabled | true | 从大文件中提取内容前发出警告 |
ips-le.safety.fileSizeWarnBytes | 1000000 | 达到此大小时开始发出警告 |
ips-le.notificationsLevel | silent | all = 所有通知,important = 警告 + 错误,silent = 仅错误 |
ips-le.statusBar.enabled | true | 显示状态栏项 |
ips-le.telemetryEnabled | false | 仅保存在本地的事件日志(参见「隐私」) |
语言
除英语外还支持 12 种语言:
德语 · 西班牙语 · 法语 · 印度尼西亚语 · 意大利语 · 日语 · 韩语 ·
葡萄牙语(巴西) · 俄语 · 乌克兰语 · 越南语 · 简体中文
涵盖两部分内容:清单(命令标题、设置名称和说明),以及扩展运行时显示的所有内容(通知、状态栏和报告标题)。拒绝详情使用引擎的英文,与 CLI 中的内容一致。
隐私与安全
- 不访问网络。 扩展绝不会向任何地方发送数据:没有 DNS、地理位置查询或任何类型的查找。
telemetryEnabled设置只会将事件写入你可以检查的本地输出通道(IPs-LE)。 - MCP 服务同样恪守这一原则。 它接收内容作为参数并返回数据:不访问文件系统、不进行网络调用,也不收集遥测数据。
- 错误通知会对主目录和类似凭据的片段进行脱敏处理。
- 评分提示最多出现两次。 第 3 次成功使用时,扩展会询问你是否愿意评分;如果你选择 稍后 或关闭提示,则会在第 20 次使用时再询问一次。选择 不再询问 会结束提示。将
notificationsLevel设置为important或silent也会关闭此提示。计数保存在 VS Code 的扩展存储中,不会向任何地方发送数据;选择 评分 会在浏览器中打开你安装扩展时所用的目录页面——VS Code Marketplace 或 Open VSX。
文档
| 内容 | 位置 |
|---|---|
| 工具允许说什么——类型、类别、拒绝响应、输出约定、非目标 | crate/SPEC.md |
| 扩展的构建与组成方式——架构、不变条件、工具链、发布 | AGENTS.md |
| CLI 的构建与组成方式 | crate/AGENTS.md |
| 变更内容 | CHANGELOG.md · crate/CHANGELOG.md |
| 工具页面及其他 15 个工具 | letools.dev/tools/ips-le |
性能
| 输入 | 大小 | 找到数量 | 耗时 | 速率 | 扫描速度 |
|---|---|---|---|---|---|
| 访问日志 | 3.42 MB | 80,000 | 155.25 ms | 515,312/sec | 22 MB/s |
| JSON 配置 | 2.34 MB | 60,000 | 100.48 ms | 597,123/sec | 23.3 MB/s |
| 不含地址的散文 | 2.51 MB | 40,000 | 70.2 ms | 569,780/sec | 35.7 MB/s |
在 Apple M5 Pro、24 GB RAM、Node 24.3.0 上预热后运行 7 次所得的中位数。输入由 scripts/benchmark.ts 生成,而非检入仓库,因此上述大小就是实际测量值。使用 bun run benchmark 复现。
这些数据因机器而异,不会在 CI 中作出保证——以基准测试作为构建门槛,只能说明运行器当时有多忙。
测试
| 指标 | 覆盖率 |
|---|---|
| 语句 | 76.05% |
| 分支 | 70.36% |
| 函数 | 78.91% |
| 行 | 77.91% |
18 个文件中共 213 个测试用例,另有一个在真实 VS Code 扩展宿主中运行的集成测试套件,以及一个将构建出的 .vsix 安装到全新配置文件中的端到端测试。
数据由 scripts/coverage-readme.js 根据真实运行结果——coverage/coverage-summary.json 和 coverage/test-results.json——生成;如果本节内容发生偏差,CI 将失败。使用 bun run test:coverage 复现,用例数量以 vitest 输出为准。
LE 系列的更多工具
为每个模型眼前的工作打造的 16 款单一用途工具。每款都附带一个 Rust CLI 和一个 MCP 服务。一页尽览:letools.dev
提取
- String-LE — 提取代码库中的所有字符串,方便人工阅读
- Numbers-LE — 提取代码库中所有硬编码数字,方便人工检查
- Units-LE — 提取所有带单位的数量并统一格式,遇到含义不明确的数量时会按名称拒绝处理
- Dates-LE — 提取所有日期和时间戳,并给出每个日期和时间戳解析出的确切时刻
- IDs-LE — 提取所有 UUID、ULID、NanoID、ObjectId 和 Snowflake,并解码其中的时间
- IPs-LE — 提取所有 IP 地址、CIDR 网段和 MAC 地址,统一格式并按范围分类
- URLs-LE — 提取代码库中的所有 URL,并给出协议和确切位置
- Paths-LE — 提取代码库中的所有文件路径,并说明它们是否仍指向实际内容
- Colors-LE — 提取代码库中的所有颜色,并指出哪些颜色不在你的调色板中
检查
- Regex-LE — 查找代码库中的所有正则表达式,并报告哪些可能导致灾难性回溯
- Versions-LE — 查找某个依赖项在仓库的各个清单文件中受到不同约束的位置
- i18n-LE — 识别项目使用的 i18n 库,然后依据该库的规则审查其目录
- Scrape-LE — 在编写抓取器之前检查网页能否抓取,并说明何时无法判断
防护
- Secrets-LE — 查找代码库中的硬编码凭据,绝不在报告中打印凭据
- EnvSync-LE — 比较目录树中的 dotenv 文件,并指出哪些文件缺少哪些键
- Unicode-LE — 查找那些隐藏含义的 Unicode 字符——双向控制字符、不可见字符、同形异义字符、混合文字系统
每款工具都独立运行:没有共享的 crate,也没有发布的核心库。若有两款工具得出相同结论,那是因为这个答案经得起两次验证。
联系 — nolindnaidoo.com · GitHub · LinkedIn
nolindnaidoo 的其他项目
Rust — pixelcoords 和 pixelactions 构成一个工作循环:pixelcoords 回答 位置,pixelactions 在那里执行操作。它们有各自的工具、各自的风格——不属于 LE 系列。
- pixelcoords — 冻结屏幕,标记区域,获取像素级精确的坐标和裁剪图像 pixelcoords.dev · crates.io · docs.rs
- pixelactions — 使用经过人工验证的坐标执行交互,并确认操作已生效 pixelactions.dev · crates.io · docs.rs
许可证
MIT © nolindnaidoo
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。