--- name: goofish-publish-item description: | 闲鱼发商品 skill。何时激活:用户说"帮我发到闲鱼 / 挂个闲置 / 把 XX 上架 / 发布一件 / 新增商品 / 上新";或说"这个怎么写标题 / 类目该选啥 / 图片够了吗"。 功能:从 raw 描述 / 图片 → 确认类目 → 生成标题候选 → 风控预检 → 批量传图 → 拿默认地址 → 调用 `item_publish` 一键发布。核心约束:**发送前必须让用户确认文案**, 不自动提交;调用 `goofish-risk-guard` 做发布前扫描。 metadata: author: goofish-cli version: "0.3.0" tags: [xianyu, publish, listing] allowed-tools: - mcp__goofish__auth_status - mcp__goofish__category_recommend - mcp__goofish__media_upload - mcp__goofish__location_default - mcp__goofish__item_publish - mcp__goofish__item_get --- # 闲鱼发商品 ## 工具命名 正文使用 `auth_status` 这类逻辑名。OpenClaw 调 `goofish__<逻辑名>`; Claude Code / Cursor 调 `mcp__goofish__<逻辑名>`。frontmatter 的 `allowed-tools` 保留 Claude 权限预批准格式,OpenClaw 不读取该字段。 ## 触发姿势 用户典型输入: - "帮我把这个耳机发到闲鱼,300,9 成新" - "我拍了几张图,给我写个标题和描述" - "上架这个手办,可以议价,底线 200" 当你从用户消息中能提取 **(商品本体 + 至少一张图 或 对物品的文字描述)** 就启动本流程。信息不足时**问用户**而不是瞎猜。 ## 标准流程(7 步) ### Step 1 · 登录态自检(必做) ``` 调 auth_status valid=true → 继续 valid=false → 不继续;告诉用户: "登录态失效了,请执行 goofish auth login(扫码走 --qr)",停。 ``` ### Step 2 · 信息收集 从对话和用户提供的素材里提取: | 字段 | 必填 | 来源 | |---|---|---| | 核心物品(是什么) | ✅ | 用户描述 / 图片识别 | | 品牌 + 型号 | ✅ | 用户描述 | | 成色(X 成新) | ✅ | 用户描述;没说就**问** | | 价格 | ✅ | 用户描述;没说就**问** | | 图片(URL 或本地路径) | ✅ | 用户提供 | | 瑕疵说明 | 推荐 | 问用户"有没有瑕疵要提一下的" | | 购买渠道 | 推荐 | 帮写描述更有信服力 | | 是否可议价 / 底线 | 可选 | 为后续 reply-buyer 做准备 | | 发货地 | ⚪️ | 不填就调 `location_default` 兜底 | **缺必填字段就停下来问,不要补默认值**。 ### Step 3 · 类目识别 ``` 调 category_recommend(title=候选标题, images_json="[]") → 拿到完整类目 DTO;已有上传收据时用 images_json=JSON.stringify(收据数组) ``` **结果校验**: - 如果 catName 和用户描述明显不符(比如用户说手机壳,返回"手机"),告诉用户并让他确认 - 如果置信度低(返回多个候选),展示前 3 个让用户选 ### Step 4 · 标题生成 按 **"品牌 + 核心词 + 属性 + 场景 + 情感"** 5 段式生成 3 个候选。详见 `references/title-formula.md`。 **然后**调用 `goofish-risk-guard` 扫描: - 绝对化词、外联词、侵权词命中 → 重新生成 - 命中"降价改标"嫌疑(标题含促销词+历史价格变动)→ 重新生成 把扫描过的候选展示给用户选 / 改。 ### Step 5 · 描述生成 结构参考 `references/title-formula.md` 的"描述骨架"段。重点: - **有瑕疵就大方说**(保护卖家对抗仅退款) - 说清**为什么卖**("升级新款"、"不常用",比"闲置"可信) - 说清**发货地 + 快递** + **是否包邮** - 议价政策写明("小刀友好 / 超过 XX 勿扰") ### Step 6 · 图片处理 ``` for image in 本地图片路径: 调 media_upload(path=image) 收集返回的 {url, width, height} 检查: 总图数 ≤ 9 ? 首图是 1:1 ?(如果不是,提示用户换首图) 张数 ≥ 3 ?(少于 3 张警告但不拦) ``` ### Step 7 · 用户最终确认 + 发布 **展示给用户**(强制): ``` 【标题】...(最终版) 【类目】catName (catId=XXXX) 【描述】 (全文) 【价格】XXX 元 【图片】9 张,首图 1:1 ✅ 【发货地】XX 市 / 默认 【风控扫描】通过 ✅ 是否确认发布? ``` **等待用户明确回复 "发 / 发吧 / 确认 / OK / 是 / 嗯"**。含糊的 "看看" / "嗯" 单字不算确认。 确认后: ``` 调 item_publish( title=..., desc=..., price=..., images_json=JSON.stringify([{url, width, height}, ...]), category_json=JSON.stringify(category_recommend 的完整 DTO), location_json=JSON.stringify(location_default 的完整 DTO) ) ``` 发布也可用 `images=[本地路径, ...]` 自动上传,不能把 CDN URL 当本地路径。已提前上传时复用 `images_json`,避免重复上传。类目 DTO 必须包含 cat_id/cat_name/channel_cat_id/tb_cat_id,地址采用 selected 字段。缺少身份的类目不能猜补。模型分数不是校准概率,未知值为 null。 成功返回 accepted 与 item_id 后,用 item_get 回读确认。失败返回的 images/category/location 可复用;submission_unknown 必须先核对自己的商品列表,禁止盲目重试。发布和下架共用 item.write 默认 1 次/分钟,上传独立 media.write 默认 9 次/分钟。 ## 反模式(不要做) ❌ **跳过 category_recommend**,自己猜 catId ❌ **先发后改**(建议用户"先发出去再慢慢改" → 发布后频繁编辑会丢流量池权重) ❌ **省略风控扫描**(标题含绝对化词直发 → 100% 降权) ❌ **未确认就发**(写操作不可逆,多花 5 秒等用户确认) ❌ **批量发十件不停手**(令牌桶 1 件/分钟,连发会触发限流) ## 批量发布场景 用户说"我有 10 件都要发": 1. 先让他确认"是 10 件完全不同的,还是 10 件同款?" 2. 同款:合并为 1 件的多 SKU 描述,不是 10 次发布 3. 不同款:逐件走完整流程,**每件单独确认**,不要"一次性确认 10 件"(用户看不过来) 4. 告诉用户"受速率限制,大约 12-15 分钟" ## 发布后 - 返回 `item_id` 展示给用户 - **提示流量池窗口**:"商品已发布,前 24 小时是初始推荐窗口,这段时间内不要改标题/降价改标" - 记录 item_id(如果用户后续要诊断或下架) ## 相关 skill - `goofish-risk-guard` — 发布前扫描(标题/描述/图片) - `goofish-reply-buyer` — 发布后回买家消息 - `goofish-shop-diagnosis` — 如果发布后流量差