亚马逊 Skills 中到底应该用 MCP 还是用 CLI

说明:本文讨论的是亚马逊运营 Skill 在真实业务场景里的工具选择:什么时候用 MCP 探索,什么时候用 CLI + Python 数据管道交付。它不是在比较 MCP 和 CLI 的协议设计、通信机制或底层实现原理。

最近我翻了一下自己写过的亚马逊运营 Skill。

市场调研,用 Sorftime CLI。

竞品监控,用 Sorftime CLI。

VOC 报告,也还是用 Sorftime CLI。

这就带出一个很实际的问题:既然 Sorftime 已经提供 MCP,那为什么我开发的 Skill 还是首选 CLI,而不是直接把 MCP 当主干呢?

我一开始也觉得这事有点反直觉。

MCP 明明更像给大模型准备的入口。Claude、Codex、Cursor 这类客户端接上以后,运营用自然语言提问,模型自己找工具、调接口、组织答案。听起来更顺,也更符合大家对 Agent 工作流的想象。

但后来我真正在写 Skill 的时候,关注点慢慢变了。

我不只是在意“这一次能不能查到数据”。我更在意:这次到底调了哪些接口,花了多少额度,原始 JSON 存在哪里,失败后能不能接着跑,换一个 ASIN 或换一个类目时,还能不能按同一套口径再来一遍。

这些问题一冒出来,选择就没那么纠结了。MCP 很适合把模型和外部数据连起来,帮人快速看方向;CLI 则更适合被脚本、缓存、manifest、DuckDB 和报告模板接住,变成一条可执行的数据管道。

Skill 真正要解决的,往往不是“今天能不能问到一个答案”,而是“同一套运营动作,下周、下个月、换一个类目、换一个 ASIN,还能不能稳定跑出来”。

MCP 与 CLI 在 Skill 中的位置

图:MCP 更适合做探索入口,CLI 更适合做执行管道,Skill 负责把业务 SOP 编排成可复用流程。

先把两个东西放回业务现场

MCP 是什么?

在我们的场景里,可以先把它理解成“让大模型客户端直接看见外部数据能力的一层协议”。比如 Sorftime MCP 暴露了产品详情、评论、趋势、流量词、关键词拓展这些能力,Codex 接上以后,用户发一句“帮我分析这个 ASIN”,模型就能根据工具 schema 选择要调用的能力。

这个体验很顺。运营把问题讲清楚,模型就会试着把数据拉回来。

CLI 又是什么?

CLI 是命令行入口。它看起来没有 MCP 那么“会聊天”,但它有一个很硬的特点:容易被脚本、文件系统、缓存层、manifest、数据库和定时任务接住。

真正有价值的是,CLI 很容易被 Python、Shell、Node、DuckDB 这些东西封装成一条稳定的数据管道。

比如 Sorftime CLI 的命令形态很直接:

sorftime api ProductSearch '{"keyword":"coffee grinder","page":1}' --domain 1

前者服务对话,后者服务工程流程。问题不在于哪个更先进,而在于你此刻要完成的是“问答”还是“生产”。

如果把两者压成一张表格,大概是这样:

维度 MCP CLI
全称/性质 Model Context Protocol,模型上下文协议 Command Line Interface,命令行界面
核心对象 AI 应用、大模型、Agent 程序员、Shell 脚本、自动化任务
主要目标 让 AI 发现、选择并调用外部工具或数据 让用户或脚本用文本命令调用程序
交互方式 自然语言 -> 大模型理解 -> MCP 工具调用 明确命令 -> 参数/选项 -> 程序执行
典型使用 AI 助手查数据、调工具、生成初步分析 批量查询、定时任务、ETL、调试、CI/CD
控制重点 工具权限、Schema、上下文、Human-in-the-loop 命令精确性、权限控制、日志、脚本稳定性
最适合 多工具、多步骤、自然语言驱动的 Agent 工作流 稳定、可复现、可批量执行的工程任务

这张表解决的是定义问题。真正落到 Skill 里,还得看数据怎么进来、怎么留下来、怎么被下一步继续使用。

Sorftime 的 product_report 很有意思

这次让我重新想清楚这件事的,是一个很小的真实调用。

我在 Codex 里输入了一句:

B0FCYFS3LW,单个产品分析报告

按直觉,很多人会以为 Sorftime MCP 里的 product_report 会直接返回一份完整报告。

但实际不是。

Codex 只调用了一次 product_report,参数是:

{
  "tool": "mcp__sorftime.product_report",
  "arguments": {
    "asin": "B0FCYFS3LW",
    "amzSite": "US"
  }
}

它返回的不是产品数据,也不是最终报告,而是一段“分析方法”:

Codex MCP 调用示例

# 源代码
{
  "callId": "call_awMs7drh6j9tx7qSG3f7cqug",
  "invocation": {
    "server": "sorftime",
    "tool": "product_report",
    "arguments": {
      "asin": "B0FCYFS3LW",
      "amzSite": "US"
    }
  },
  "durationMs": 308,
  "result": {
    "type": "success",
    "content": [
      {
        "type": "text",
        "text": "分析一个产品的方法如下,请调用对应的工具组合数据分析:\r\n1. 查询这个产品的详情。\r\n- 获取产品销量,价格,品牌等核心要素。\r\n- 通过标题,产品描述提取产品特点。\r\n2. 通过类目Top100实时数据报告,分析产品所处类目市场的竞争环境。\r\n3. 分别拉取产品的好评和差评,分析产品的优劣势。\r\n4. 通过类目产品特点调研目前市场上这类产品的特点,然后对比产品特点。\r\n5. 通过这个产品反查关键词,分析这个产品通过关键词获取平台流量的特点。\r\n6. 通过核心关键词挖掘延伸关键词,给与关键词投放建议。\r\n7. 综上信息,进行分析总结。并给我Listing文案优化建议、广告关键词建议、产品打造建议。"
      }
    ],
    "structuredContent": null,
    "raw": {
      "content": [
        {
          "type": "text",
          "text": "分析一个产品的方法如下,请调用对应的工具组合数据分析:\r\n1. 查询这个产品的详情。\r\n- 获取产品销量,价格,品牌等核心要素。\r\n- 通过标题,产品描述提取产品特点。\r\n2. 通过类目Top100实时数据报告,分析产品所处类目市场的竞争环境。\r\n3. 分别拉取产品的好评和差评,分析产品的优劣势。\r\n4. 通过类目产品特点调研目前市场上这类产品的特点,然后对比产品特点。\r\n5. 通过这个产品反查关键词,分析这个产品通过关键词获取平台流量的特点。\r\n6. 通过核心关键词挖掘延伸关键词,给与关键词投放建议。\r\n7. 综上信息,进行分析总结。并给我Listing文案优化建议、广告关键词建议、产品打造建议。"
        }
      ],
      "structuredContent": null,
      "_meta": null
    }
  }
}

这个返回就很有意思。

它不像一个“数据接口”,更像一个“流程提示器”。它告诉模型:如果你要分析单个产品,不要只看产品详情,还要看评论、类目、关键词、流量来源,再给 Listing、广告和产品建议。

也就是说,product_report 这个 MCP 工具并没有自动完成完整报告。它只是把分析路径递给了模型。

后面真正发生的是另一件事:模型根据这段提示,继续主动调用其他 MCP 工具。

在那次会话里,后续调用过的 Sorftime MCP 工具有这些:

[
  "product_detail",
  "product_variations",
  "product_reviews(Positive)",
  "product_reviews(Negative)",
  "product_trend(SalesVolume)",
  "product_trend(Price)",
  "product_trend(Rank)",
  "product_traffic_terms",
  "competitor_product_keywords",
  "keyword_detail",
  "keyword_trend",
  "keyword_extends",
  "keyword_search_results"
]

工具链调用

所以这里要拆开看。

MCP 里预置的是工具、schema、工具说明,以及像 product_report 这种流程提示。至于后面怎么串起来、哪些工具真的要调、调几次、缺哪个能力时怎么降级,这部分不是 MCP 协议自动替你完成的,而是模型在当前对话里现场编排。

这就是 MCP 很舒服的地方,也是它不适合直接当生产型 Skill 主干的地方。

舒服在于:你只说了一句“单个产品分析报告”,模型就能顺着工具名和返回提示往下走。

不适合直接当主干在于:如果每一次都让模型现场决定工具链,那这次可能调 12 个工具,下次可能调 8 个工具;这次先看评论,下次先看关键词;这次缺类目 Top100 就跳过,下次可能换一种补法。

聊天里没问题,生产里就麻烦了。

同一个需求,如果换成 CLI

如果把同一个需求换成 CLI,事情就变了。

CLI 不会因为你说“单个产品分析报告”,就自己理解要查哪些数据。它更像一个沉默的执行入口,你给它什么命令,它就执行什么命令。

所以开发者要先做一件事:把“单个产品分析报告”拆成一份请求清单。

B0FCYFS3LW 这个 ASIN 为例,我会先把需求写成这样:

{
  "target": "分析 US 站点 ASIN B0FCYFS3LW",
  "deliverable": "单品分析报告",
  "coreQuestions": [
    "产品是什么,价格、销量、评分、类目位置怎么样",
    "好评和差评分别集中在哪些点",
    "销量、价格、排名有没有趋势变化",
    "自然流量来自哪些关键词",
    "核心关键词的搜索量、CPC、竞争情况怎么样",
    "Listing、广告和产品定位应该怎么调整"
  ]
}

然后再把它拆成一份稳定请求计划:

步骤 要拿的数据 Sorftime CLI 接口 输出文件
1 产品档案、价格、评分、Listing 资产、近 15 天趋势 ProductRequest(trend=1) raw/product-request.json
2 亚马逊公布销量历史 AsinSalesVolume raw/asin-sales.json
3 近 30 天流量词、自然位、广告位 ASINRequestKeywordv2 raw/asin-keywords-page-001.json
4 差评样本 ProductReviewsQuery(star=10,pageIndex=1) raw/reviews-negative-page-001.json
5 好评样本 ProductReviewsQuery(star=11,pageIndex=1) raw/reviews-positive-page-001.json
6 核心关键词搜索量、CPC、商品数 KeywordRequest raw/keyword-detail/*.json
7 关键词拓展词 KeywordExtends raw/keyword-extends/*.json
8 核心词下 ASIN 自然/广告排名 ASINKeywordRanking raw/asin-keyword-ranking/*.json

这张清单才是 CLI 进入 Skill 的关键。

注意,这里不是让模型“想起来什么就调什么”。而是开发者先把业务问题拆成稳定动作,再让脚本按这个动作执行。

比如某几个请求可以长这样:

sorftime api ProductRequest '{"asin":"B0FCYFS3LW","trend":1}' --domain 1 \
  > raw/product-request.json

sorftime api ProductReviewsQuery '{"asin":"B0FCYFS3LW","star":10,"pageIndex":1}' --domain 1 \
  > raw/reviews-negative-page-001.json

sorftime api ASINRequestKeywordv2 '{"asin":"B0FCYFS3LW","pageIndex":1,"pageSize":200}' --domain 1 \
  > raw/asin-keywords-page-001.json

sorftime api KeywordRequest '{"keyword":"50000mah power bank"}' --domain 1 \
  > raw/keyword-50000mah-power-bank.json

评论这里还要更细一点。如果要刷新最新评论,Sorftime 不是简单查一次 ProductReviewsQuery 就完事,它还有 ProductReviewsCollectionProductReviewsCollectionStatusQueryProductReviewsQuery 这条异步链路。这个细节在聊天里可以临场解释,但写进 Skill,就必须变成明确的采集状态、轮询次数、超时策略和缺口说明。

命令本身不神奇。

真正有用的是命令背后的那套管道:输入校验、请求计划、缓存判断、raw JSON 落盘、manifest 记录、失败重试、数据清洗、报告渲染。

到这里,区别就出来了。

MCP 更像把一组工具递给模型,让它在对话里现场编排;CLI 更像先把业务需求拆成请求清单,再交给脚本稳定执行。

一个偏探索,一个偏执行。真正落到生产型 Skill 里,问题就不再是入口长什么样,而是谁能把后面的缓存、落盘、复跑和审计接住。

product_report 暴露了一个很关键的事实

product_report 这个例子,其实把 MCP 的优缺点都照出来了。

它的优点是,MCP 工具可以把业务方法藏在工具返回里。用户不用知道要调哪些接口,只要说“单个产品分析报告”,模型就能拿到一段分析路径。

这对探索非常友好。

比如我之前不知道分析这个 B0FCYFS3LW 应该从哪里下手,product_report 一返回,我至少知道要看产品详情、评论、类目竞争、产品特点、流量关键词、关键词拓展。

这就像有人先把白板画出来了。

但它的问题也在这里:这块白板不是最终产物。

白板上写了“通过类目Top100实时数据报告,分析产品所处类目市场的竞争环境”,可当前 MCP 能力里我没看到类目 Top100 工具。那模型要怎么办?

它可以跳过,可以用产品排名替代,可以找关键词搜索结果补,也可以在报告里说明这个缺口。

这几种处理方式,在聊天里都能接受。

但如果我要把它写成一个可复用的 Skill,我就不能每次靠模型现场发挥。我需要在 Skill 里写清楚:

{
  "condition": "category_top100_unavailable",
  "actions": [
    "不生成类目 Top100 竞争结论",
    "改用 keyword_search_results 作为搜索页竞争参考",
    "在报告中标注数据缺口"
  ],
  "manifest": {
    "skipped_reason": "category_top100_unavailable"
  }
}

这才是工程化。

模型可以聪明,但生产流程不能只靠聪明。

业务现场通常不按演示走。今天工具缺一个字段,明天接口返回为空,后天某个 ASIN 没评论。聊天可以临场解释,Skill 要能稳定兜住。

还有一个容易被忽略的成本:上下文

这里还有一个很关键的点,很多人一开始容易忽略。

对话式 MCP 调用,默认会把工具返回交给模型看。模型要看见结果,才能继续判断下一步调什么、怎么分析、怎么组织回答。

少量调用没问题。

但一到亚马逊报告,事情就不一样了。产品详情、子体、评论、趋势、流量词、关键词详情、拓展词、搜索结果,一轮跑下来,返回内容会非常多。如果这些结果都进入上下文,成本就不是一点点。

第一个成本是 token。

你让模型看完整评论、完整关键词列表、完整趋势数组,它就要消耗上下文窗口。很多数据其实只是中间材料,并不需要每一行都进模型脑子里。就像仓库收货,不能每箱货都搬到会议桌上。会议桌只需要样品、数量和问题点。

第二个成本更隐蔽:频繁压缩以后,Agent 会“失忆”。

长对话跑久了,系统会压缩上下文。压缩不是完整备份,它更像把一堆现场记录整理成会议纪要。大方向还在,但很多细节会被折叠掉。某个接口原始字段、某一页评论、某个请求是否命中缓存、某个异常返回,可能就这样淡掉了。

这对聊天影响不大,对报告型 Skill 很要命。

因为报告后面经常要追问:这个结论来自哪条评论?这个关键词数据是哪一次拉的?这个 ASIN 当时有没有缺字段?这次到底消耗了多少请求?

如果所有东西只留在上下文里,那上下文一压缩,证据链就开始松。

CLI 管道的思路不一样。

它可以先不把结果塞给模型,而是直接落盘:

sorftime api ProductRequest '{"asin":"B0FCYFS3LW","trend":1}' --domain 1 \
  > raw/product-request.json

也可以写进 DuckDB 或数据库。

模型真正需要参与判断时,只读取摘要、指标、证据片段和文件路径。大文件留在磁盘里,结论进入上下文里。

这样上下文就不会被 raw JSON 撑爆,Agent 也不需要靠记忆保存证据。它只需要知道:原始数据在什么地方,需要哪块时再去拿。

这里也要说严谨一点。MCP 不是一定会浪费上下文,如果 MCP 服务端设计成返回文件句柄、资源 URI 或摘要,也可以避免大数据直接进对话。CLI 也不是天然节省上下文,如果你把完整 JSON 贴给模型,它一样会烧 token。

差别还是使用方式。

对话式 MCP 更容易让“数据返回”和“模型阅读”绑在一起。

CLI + 数据管道更容易把“数据采集”和“模型分析”拆开。

这也是为什么我更愿意让 CLI 负责采集,让文件和数据库负责保存,让模型只在真正需要判断的地方进场。

缓存是什么,为什么它和成本有关

给不写代码的同学讲,缓存可以理解成“查过一次,就把结果先放进本地抽屉”。

第一次查 B0FCYFS3LW,我们真的去 Sorftime 调接口。

查完以后,把返回的 JSON 存成文件。

下一次如果还是 US 站点、同一个 ASIN、同一个接口、同一组参数,就先看本地有没有这个文件。有,就直接拿来用;没有,再去调用接口。

这就是缓存。

它不是为了炫技,也不是为了让代码看起来复杂。它的核心作用只有三个。

一是控制调用次数。

一份单品报告,不是只调一个接口。光这次 B0FCYFS3LW,MCP 会话里就涉及产品详情、子体、正评、差评、销量趋势、价格趋势、排名趋势、流量词、竞品关键词、关键词详情、关键词趋势、关键词拓展、搜索结果。

如果每次改报告文案、重跑图表、修一个字段,都重新调接口,请求次数会很快被烧掉。

二是控制成本。

很多数据服务的接口不是无限免费的。它可能按额度、套餐、点数或调用次数计费。CLI 管道里可以把每次真实请求记录进 manifest,后面复盘时就能知道这份报告到底花了多少次请求。

三是保证报告可复盘。

同一份报告里,分析结论应该基于同一批原始数据。如果上午拉一次,下午因为改模板又重新拉一次,数据源内容变了,报告前后可能对不上。缓存能让一次运行里的原始数据固定下来,后面所有步骤都围绕这批数据加工。

比如缓存路径可以设计成这样:

cache/sorftime/product_detail/US/B0FCYFS3LW.json
cache/sorftime/product_reviews/US/B0FCYFS3LW/positive-page-001.json
cache/sorftime/product_reviews/US/B0FCYFS3LW/negative-page-001.json
cache/sorftime/product_request/US/B0FCYFS3LW/trend-1.json
cache/sorftime/keyword_detail/US/50000mah-power-bank.json

每次准备调用前,先检查这个文件是否存在。

存在,就记录 cache_hit=true,直接读取。

不存在,就执行 CLI,保存 raw JSON,再记录 cache_hit=false 和这次请求的接口、参数、时间、输出路径。

MCP 不是不能做缓存,但它通常需要再包一层存储逻辑:把工具调用参数哈希成 key,把工具响应保存下来,再判断下次是否复用。能做,只是它已经不再是单纯的对话式 MCP 了,而是在给 MCP 外面补一套小型数据管道。

这也是我在 Skill 里更偏向 CLI 的核心原因:报告不是只要拿到答案,还要控制请求、控制成本、保留证据。

接口审计示例一

接口设计示例二

为什么我的亚马逊 Skill 基本都选 CLI

这里不需要再开一个新例子。回到我已经做过的几个 Skill,答案其实很清楚。

亚马逊市场调研,底层是 Sorftime CLI 加一组 Python 脚本。collect_amazon_market_data.py 负责 dry-run、固定请求计划、真实采集和缓存;build_report_data.py 再把 Top100、关键词、品牌、卖家、VOC 和代表 ASIN 趋势合成 report_data.json,再用模板渲染成离线 HTML。

竞品监控也是同样的思路。collect_monitoring_data.py 不是简单查一次 ASIN,而是按同一批 ASIN、同一个日期、同一套接口,把数据写进 raw/,再生成 manifest.json。后面的 build_monitoring_report_data.py 会做快照、找最近一次历史快照、生成 snapshots/{date}.jsonevents/{date}.jsonreport_data/{date}.json

VOC 洞察报告更典型。fetch_sorftime_reviews.py 调 Sorftime CLI 拉评论,写成 JSONL 放到运行目录的 raw/。接着 run_voc_pipeline.py 把评论导入 DuckDB,清洗、去重、打标签、做聚合,再由 build_insight_facts.pybuild_report_data.py 生成事实包和报告数据。

你看,这几个 Skill 表面上都叫“亚马逊分析”,实际都不是让模型临场发挥。

它们是在用 Python 脚本把 Sorftime CLI 包成一条数据管道。

已经做过的 Skill Python 脚本接住什么 数据怎么保存 二次分析做什么
市场调研报告 固定请求计划、dry-run、Top100 与代表 ASIN 补采 request JSON、raw response、normalized response、manifest.jsonreport_data.json 款式分层、品牌集中度、卖家结构、关键词/VOC/趋势合并
竞品监控 每日 ASIN 快照、关键词、销量、评论、Buybox 信号 raw/manifest.jsonsnapshots/events/report_data/ 对比历史快照,生成价格、排名、评论、关键词变化事件
VOC 洞察报告 评论分页采集、raw JSONL 导入、标签流水线 raw/amazon_voc.duckdbllm_call_audit、事实包、report_data.json 清洗评论、标签归因、星级/时间/变体/场景聚合、行动建议证据化

如果把这几个 Skill 里的脚本动作抽象一下,大概就是下面这几段伪代码。它不是源码,只是把“CLI 适合批量处理”这件事讲清楚。

市场调研和竞品监控里,最常见的是先查缓存,再决定要不要真实调用:

params = {
    "domain": "US",
    "endpoint": "ProductRequest",
    "asin": "B0FCYFS3LW",
    "trend": 1,
}

cache_key = stable_hash(params)
cache_file = run_dir / "raw" / "product_request" / f"{cache_key}.json"

if cache_file.exists() and not force_refresh:
    raw = read_json(cache_file)
    manifest.add(
        params=params,
        cache_hit=True,
        request_consumed=0,
        output=cache_file,
    )
else:
    raw = sorftime_cli("ProductRequest", params)
    write_json(cache_file, raw)
    manifest.add(
        params=params,
        cache_hit=False,
        request_consumed=raw["requestconsumed"],
        request_left=raw["requestleft"],
        output=cache_file,
    )

VOC 报告里,更典型的是按页拉评论,把每页结果追加到 JSONL,同时记录停止原因:

page = 1

while True:
    raw = sorftime_cli(
        "ProductReviewsQuery",
        {
            "asin": "B0FCYFS3LW",
            "star": "1,2,3,4,5",
            "pageIndex": page,
        },
    )

    reviews = normalize_reviews(raw["data"])

    if not reviews:
        manifest.stop_reason = "empty_page"
        break

    append_jsonl(run_dir / "raw" / "reviews.jsonl", reviews)

    current, total = parse_item_index(reviews[-1]["ItemIndex"])
    manifest.pages.append(
        {
            "page": page,
            "count": len(reviews),
            "current": current,
            "total": total,
        }
    )

    if current >= total:
        manifest.stop_reason = "reached_total"
        break

    page += 1

到了报告阶段,脚本继续把 raw 数据导入 DuckDB,生成事实包,再把模型需要看的内容压缩到很小:

raw_files = list_files(run_dir / "raw")
duckdb_path = import_reviews_to_duckdb(
    raw_files,
    run_dir / "amazon_voc.duckdb",
)

facts = build_insight_facts(duckdb_path)
agent_input = pick_summary_metrics_and_evidence(facts)
agent_result = ask_model(agent_input)

report_data = merge(facts, agent_result, manifest)
validate_report_data(report_data)
write_json(run_dir / "report_data.json", report_data)
render_html(report_data, reports_dir / "voc-report.html")

这几段看着很朴素,但它们正是 CLI 对 Skill 有价值的地方:每一步都有文件路径、状态、成本、停止原因和可复跑入口。模型不需要记住每一页评论,也不需要吞下全部 raw JSON。它只拿事实包、证据片段和最终需要判断的部分。

看到这里,你可能会冒出一个很自然的判断:这些活,MCP 是不是做不了?

这个直觉很正常。

但如果直接说“MCP 做不了”,就有点把 MCP 冤枉了。

MCP 不是被焊死在“聊天窗口”里的。你如果愿意继续往下做,在 MCP 服务端后面接缓存、任务队列、文件存储、数据库、manifest、分页状态和报告渲染,它一样能把这套流程跑起来。

但到那一步,我们聊的就不是“把一个 MCP 接进 Codex,然后让模型自己调工具”了。

真正托住流程的,是 MCP 后面的那条数据管道。MCP 负责把工具暴露给模型,管道负责把原始数据接住、记账、复跑、审计和交付。

普通对话式 MCP 客户端的问题就在这里。它很适合把结果递给模型,让模型继续往下想。但它默认不会替你决定:这页评论要不要落盘,缓存是否命中,分页跑到哪里停,这次消耗了多少 request,失败后从哪里接着跑,半年后谁能把证据翻出来。

这些活都要有人写进流程里。

所以这不是“MCP 行不行”的问题,而是“谁来接住生产流程”的问题。我的体感是,CLI 更容易被 Python 脚本、文件系统、DuckDB 和 manifest 接住,所以它更适合放在这些 Skill 的主干里。

说回这几个 Skill,图里真正想表达的是一句话:CLI 返回数据只是开始,Python 层还要把缓存、落盘和二次分析接住。

我的 Skill 数据管道

图:Sorftime CLI 负责固定请求清单,Python 负责编排、缓存、落盘和事实包生成,Agent 只读取事实包并渲染离线 HTML。

这也是我说 CLI 更适合写进 Skill 的原因。

Skill 和 Prompt 不一样。Prompt 更像一次对话里的提醒:你要注意哪些点,你要怎么分析,你要输出什么结构。Skill 更像一个小型工作流:输入是什么,调用什么数据,过程怎么走,中间在哪里确认,失败怎么处理,产物放哪里,交付格式是什么。

进入 Skill 之后,命令行就不只是命令行了。

它变成了真实数据入口、请求计划载体、原始证据来源、流程稳定器、复跑基础和交付桥梁。说得再直白一点,CLI 在这里干的是系统里的脏活累活:分页、重试、缓存、存盘、记账、校验、渲染。

这些活看起来都很细碎,但亚马逊运营里的很多结论,最后都会落到选品、备货、广告、Listing 改版和利润测算上。数据链路不清楚,报告写得再漂亮,也很难变成动作。

MCP 并不弱,它只是边界不同

写到这里,容易把文章读成“CLI 好,MCP 不行”。这不是我的意思。

MCP 的价值很明确,而且在亚马逊运营里会越来越常用。像这次 product_report,它没有直接给出完整报告,却把“单品报告应该看什么”递给了模型:产品详情、类目 Top100、好评差评、类目特点、流量词、核心关键词和 Listing 建议。

这对探索很有用。一个新想法还没沉淀成 Skill 时,运营突然想看一个关键词、一个类目、一个 ASIN,也不想先写脚本,这时直接在 Claude、Codex 或 ChatGPT 里问,先把方向摸出来,体验会很顺。

但边界也在这里。生产型 Skill 要关心的是固定请求计划、运行目录、缓存命中、预算确认、失败恢复、接口审计、离线交付和半年后的复盘。普通对话式 MCP 客户端默认不会替你处理这些工程细节。要处理当然也可以,但那时托住流程的已经是 MCP 后面的数据管道。

product_report 与 CLI 请求计划对比

图:product_report 更像探索入口,帮助确认分析方向;CLI 请求计划把 raw、manifest、DuckDB 和 report_data.json 串成可复跑的生产链路。

所以我不会把 MCP 放到对立面。

更合理的方式是:先用 MCP 摸清问题,再把稳定流程沉淀成 CLI 请求计划,再封装进 Skill。

我现在会怎么选

我的选择标准,其实就一个问题:

这份结论能不能离开聊天窗口?

如果答案只是“用户当场看一下就行”,MCP 往往够用。

如果答案要进入报告,要发给团队,要下周复盘,要沉淀成 SOP,要对数据来源负责,那就应该把 CLI + Python 数据管道放进主流程。

如果把这个判断画成流程,会比继续列一张表更直观:

MCP 还是 CLI?看交付要求

图:临时探索选 MCP;需要批量采集、审计复跑和证据沉淀,选 CLI;要交付成长期 Skill,就先用 MCP 探索,再把稳定流程固化进 CLI + Python 数据管道。

这张图放在这里,是为了把前面那些例子收成一个操作标准:看交付要求。越往右,交付越重,数据就越应该从上下文里退出来,进入文件、数据库和报告产物。

回到开头那个问题。

以后看 Sorftime 这类数据能力时,我不会再问“它有 MCP 还是 CLI”,而会先问它要服务哪种工作:临时探索,还是长期执行。前者交给 MCP,后者交给 CLI + 数据管道。

一个好的亚马逊运营 Skill,真正要做的不是展示接口字段,也不是炫耀模型会调用多少工具。它要把业务 SOP、数据接口、分析口径和报告模板,封装成一套可重复执行的自动化工作流。

所以在市场调研、竞品监控、VOC 报告这些场景里,我会继续把 CLI + 数据管道放在主干,把 MCP 放在探索和辅助的位置。

工具可以换,数据源可以换,模型也会换。但运营自动化里最值钱的东西,始终是可复用的流程、可解释的证据,以及能落到人手上的动作。

以上,既然看到这里了,如果觉得不错,随手点个赞、在看、转发三连吧,如果想第一时间收到推送,也可以给我个星标⭐~谢谢你看我的文章,我们,下次再见。

作者:Jax丰哥