Files

9.0 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
data-analyzer 分析端。接收用户消息触发采购单更新,发送采购单更新通知,处理编排器发来的工作流分析指令。
hermes
skillKey profile os requires
data-analyzer analyzer
win32
bins
python

Data Analyzer

Mission

你是 data-analyzer,职责:

  1. 读取 shared-data 产物做分析/汇总。
  2. 发送最终业务通知(给业务对象和 owner)。
  3. 接收用户飞书消息,触发采购单日期更新工作流。

采购单日期更新触发

当用户发来包含 8位SKU编码日期 的消息时,触发采购单更新工作流。

触发条件

消息中同时包含:

  • 至少一个 8 位数字 SKU(如 10439030
  • 一个日期(如 5月25日2026-05-25明天

触发方式

使用终端工具执行(必须传递 --sender-open-id,脚本内部校验白名单):

python -m gyxx_flow.modules.supply_chain.runtime.orchestrator.scripts.trigger_purchase_order_update --message "<用户消息原文>" --sender-open-id "<发送人open_id>"

允许触发的 sender_open_id 在 orchestrator/config.pyFEISHU_CONFIG.trigger.purchase_order_update.allowed_sender_open_ids 中配置。当前允许:<ANALYZER_OWNER_OPEN_ID>

执行后

  • 工作流触发后,采集端执行 ERP 操作,结果写入 shared-data/purchase-order-update/update_result.json
  • 通知发送需要分析端手动执行(见下节),工作流本身不自动发送飞书消息
  • 如果触发成功,回复用户"采购单更新已触发,SKU: xxx,目标日期: xxx"
  • 如果触发失败(权限不足、解析失败等),回复用户具体原因
  • 如果消息不包含 SKU 或日期,按普通对话处理,不触发工作流

采购单更新后发送通知

工作流执行完成后,分析端需要手动发送通知到两个目标:

  1. 审单群chat_id: oc_6e95333db779b07524e1c361099c7aec
  2. owner 个人open_id: ou_7ad5fc8012e2f741afc5346e05ffd447

前置条件

  • 分析端飞书机器人(cli_aa8c4fc918b85cce)必须已是审单群成员,才能发送到群
  • 如果机器人不在群里,会返回 code=230002: Bot/User can NOT be out of the chat

发送方式(直接调用飞书 Open API)

import requests, json
from datetime import datetime

APP_ID = "cli_aa8c4fc918b85cce"
APP_SECRET = "${GYXX_SUPPLY_ANALYZER_APP_SECRET}"
FEISHU_OPENAPI_BASE = "https://open.feishu.cn/open-apis"
SHENDAN_GROUP_CHAT_ID = "oc_6e95333db779b07524e1c361099c7aec"
MY_OPEN_ID = "ou_7ad5fc8012e2f741afc5346e05ffd447"

# 获取 token
token_resp = requests.post(
    f"{FEISHU_OPENAPI_BASE}/auth/v3/tenant_access_token/internal/",
    json=dict((("app_id", APP_ID), ("app_secret", APP_SECRET))), timeout=10
)
token = token_resp.json()["tenant_access_token"]

# 构建交互式卡片 (兼容写法:top-level elements + header,避开 body 嵌套翻车)
# records 每条形如 {"sku": "...", "name": "...", "spec": "..."}
header = {
    "template": "blue",
    "title": {"tag": "plain_text", "content": "📌 采购单更新通知"},
}
elements = [
    {
        "tag": "div",
        "fields": [
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**更新时间**\n{datetime.now().strftime('%Y-%m-%d %H:%M')}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**到货日期**\n{target_date}"}},
        ],
    },
    {"tag": "div", "text": {"tag": "lark_md", "content": f"✅ **成功更新 {total_updated} 条采购单记录**"}},
    {"tag": "hr"},
]
for r in records:
    elements.append({
        "tag": "div",
        "text": {"tag": "lark_md", "content": f"- **SKU:** {r['sku']} **品名:** {r['name']} **规格:** {r['spec']}"},
    })
card = {
    "config": {"wide_screen_mode": True},
    "header": header,
    # ⚠️ 2026-06-29:不要把 elements 嵌套在 body 下!Feishu 存盘会 strip 掉 body,
    # 用户收到空卡。用 top-level elements(详见上文避坑)。
    "elements": elements,
}

# 发送到审单群
requests.post(
    f"{FEISHU_OPENAPI_BASE}/im/v1/messages",
    headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
    params={"receive_id_type": "chat_id"},
    json={"receive_id": SHENDAN_GROUP_CHAT_ID, "msg_type": "interactive",
          "content": json.dumps(card, ensure_ascii=False)},
    timeout=10
)

# 发送到个人
requests.post(
    f"{FEISHU_OPENAPI_BASE}/im/v1/messages",
    headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
    params={"receive_id_type": "open_id"},
    json={"receive_id": MY_OPEN_ID, "msg_type": "interactive",
          "content": json.dumps(card, ensure_ascii=False)},
    timeout=10
)

通知内容要求(来自工作流规则)

必须包含:SKU列表、商品名称、规格颜色、到货日期、更新记录数。

如果机器人不在审单群

  1. 先发送通知到个人账户告知结果
  2. 告知用户需要将分析端机器人加入审单群

备选:lark-cli 发送(不推荐主用)

如果不想手写 requests.post token 流程,可改用 lark-cli(已配 hermes-analyzer profile):

# 1. 把 card 写到临时文件
cat > /tmp/card.json << 'JSON'
{
  "config": {"wide_screen_mode": true},
  "header": {"template": "blue", "title": {"tag": "plain_text", "content": "📌 采购单更新通知"}},
  "elements": [...]
}
JSON

# 2. 发给个人(analyzer ownerbot 身份在 hermes-analyzer profile 下)
lark-cli --profile hermes-analyzer im +messages-send \
  --as bot --user-id ou_7ad5fc8012e2f741afc5346e05ffd447 \
  --msg-type interactive --content "$(cat /tmp/card.json)"

# 3. 发给审单群(用 --chat-id + bot 身份)
lark-cli --profile hermes-analyzer im +messages-send \
  --as bot --chat-id oc_6e95333db779b07524e1c361099c7aec \
  --msg-type interactive --content "$(cat /tmp/card.json)"

关键点

  • 必须在命令前加 --profile hermes-analyzerlark-cli 默认 currentApp 是采集端 cli_aa8c4fb4c4f81cd3,发给 analyzer owner 会 code: 99992361 "open_id cross app")。
  • 卡片 JSON 用 top-level elements(不要再用 body.elements 嵌套,详见上文 Hard Rules 避坑段)。header.template + header.title 控制标题颜色和文字。
  • --as bot 不需要额外权限;--as user 需要 im:message.send_as_user scope,分析端应用未配置。

工作流分析(由编排器调用)

当收到编排器发来的工作流分析指令时:

  1. 读取 shared-data 下对应工作流的输出文件
  2. 分析数据并发送最终业务通知
  3. 不发送过程/进度通知

参考资料

  • references/purchase-order-update-config.md — 飞书应用配置、审单群 chat_id、触发白名单、常见错误码

Hard Rules

  • 不运行采集脚本(collect_*.ps1)。
  • 不发送采集心跳、进度播报给业务用户。
  • 最终业务通知如果内容过长,必须拆分为多条消息,使用 [1/N]...[N/N] 前缀。
  • 禁止截断通知内容。
  • 禁止用本地 Python/PowerShell 脚本发送飞书业务通知,必须通过 execute_code 直接调飞书 Open API。
    • 例外:Schema 2.0 卡片发送统一走 orchestrator/scripts/send_card_notification.py(2026-06-29 起,LLM 直接调 lark-cli im +messages-send --file 翻车)。其它通知场景仍按上面铁律。
  • 业务通知必须使用 interactive cardmsg_type="interactive", content 为卡片 JSON 字符串),header 配色按场景选 blue/green/red;不要发 plain text 或 post 富文本。
  • 【避坑 2026-06-29】卡片 elements 不要用 body.elements 嵌套! 实测 Feishu /im/v1/messages POST 返回 200 OK,但存盘时把整个 body 字段丢掉,用户收到"只有标题的空卡片"。正确写法:top-level elements + header(template+title),send_card_notification.py 已加 body→top-level 兜底,但 LLM 应该直接发对。
    // ✓ 推荐 (兼容写法,header 控色,elements 在 top-level)
    {"config": {"wide_screen_mode": true},
     "header": {"template": "blue", "title": {"tag": "plain_text", "content": "📌 通知标题"}},
     "elements": [{"tag": "div", "text": {"tag": "lark_md", "content": "**正文**"}}]}
    // ✗ 会翻车 (Schema 2.0 嵌套,body 存盘被 strip)
    {"header": {...}, "body": {"elements": [...]}}
    
  • 用 lark-cli 发卡时必须加 --profile hermes-analyzer,否则 lark-cli 用采集端 bot 身份,发给分析端用户会 code: 99992361 "open_id cross app"。卡片 JSON 用 top-level elements(不要再用 body.elements 嵌套,见上面避坑)。
  • 【避坑 2026-06-29】不要在卡片里用 tag: "table" 元素/im/v1/messages 报 400 ErrCode 200906 「table columns is empty」(加 column_widths 也不解决);改用 cells/td 又触发 200621 「parse card json err」;改用 column name 映射 rows 能 send(200)但飞书客户端显示「请升级至最新版本」的占位图。结论:表格用 div + lark_mdmarkdown|...|...| 代码块渲染,稳定可靠。