feat: complete production workflow migration

This commit is contained in:
2026-08-06 14:29:57 +08:00
parent 7f215e79c4
commit 8df5266abb
448 changed files with 56937 additions and 14619 deletions
@@ -0,0 +1,88 @@
---
name: data-collector-guardrail
description: "Guardrail for collector: collect + heartbeat only."
metadata:
hermes:
skillKey: data-collector-guardrail
profile: data-collector
os: ["win32"]
---
Collector Guardrail
You are the collector endpoint.
Do:
- Run collection scripts.
- Publish fresh artifacts to shared-data.
- (Heartbeat is now sent automatically by `orchestrator/mcp_workflow.py` as a Schema 2.0 interactive card to `ou_8ee224968aa26a74c7d30ba27fed5eeb`. Do NOT call send_message / curl / any Feishu API to relay heartbeats.)
- For **manual** ad-hoc heartbeat relay (operator asks in chat to forward a workflow report to the admin), reply directly with the same fields described in `Collector Heartbeat Notification Format` below — the gateway binding delivers the reply.
Do not:
- Send final business notifications.
- Run analyzer-stage actions.
- Mix analyzer app credentials (analyzer app: cli_aa8c4fc918b85cce, analyzer owner: ou_7ad5fc8012e2f741afc5346e05ffd447).
- Try to call send_message, curl, or external APIs to relay **automated** collector heartbeats — orchestrator already sends them as cards.
Credential path: The collector app secret (`2lAvcKK4gX2Qa6uCrhxgZedTzbc70a7U`) lives in two places:
- Hermes profile `.env`: `${HERMES_STATE_ROOT}\profiles\data-collector\.env` under `FEISHU_APP_SECRET` (for cron / LLM-direct paths).
- `orchestrator/config.py``FEISHU_CONFIG.apps.collector.app_secret` (env override `AUTOFLOW_COLLECTOR_APP_SECRET`). Used by `mcp_workflow.py:_send_collector_heartbeat` for the new automated card-based heartbeats.
It is NOT in `orchestrator/scripts/config.json``feishu.app_secret` / `feishu_analyzer.app_secret` (those are analyzer app credentials). Scripts like `send_collector_notify.py` that fall back to that file will fail with `code: 10014, app secret invalid`.
**Automated heartbeat (post-2026-06-27):** The orchestrator sends Schema 2.0 interactive cards via `mcp_workflow.py:_send_collector_heartbeat``requests.post(/open-apis/im/v1/messages)` with the collector app credentials, target `ou_8ee224968aa26a74c7d30ba27fed5eeb`. Phases: `start` (blue), `progress` (blue, every `HEARTBEAT_INTERVAL_SECONDS`=180s), `final` (green on success / red on failure). The collector LLM is NOT involved — do not duplicate.
**Manual relay (operator asks in chat to forward a report):** Reply directly with the format below. The gateway binding delivers it. No tool calls needed.
Pitfalls:
| Collector app owner open_id | `ou_8ee224968aa26a74c7d30ba27fed5eeb` — the admin/reporter who receives collector heartbeat notifications via collector Feishu app |
| Notification target (admin/reporter) | `ou_8ee224968aa26a74c7d30ba27fed5eeb` — this profile receives workflow heartbeat reports (NOT the analyzer app owner) |
| Analyzer app owner open_id | `ou_7ad5fc8012e2f741afc5346e05ffd447` — NOT the collector heartbeat target; this belongs to the analyzer app scope (`cli_aa8c4fc918b85cce`) |
- UTF-8 BOM manifest error: If a workflow fails with `Invalid run manifest: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1`, the manifest JSON file was saved with a BOM (common on Windows editors like Notepad). Fix: resave the manifest as UTF-8 without BOM, or strip the BOM (`\ufeff`) from the file's first bytes.
- batch_process.py scripts must be saved as UTF-8 encoding. GBK/CP936 encoding corrupts Chinese string literals and causes "unterminated string literal" SyntaxError on line 41.
- Yuanbao toolsets are listed in platform_toolsets.cli but the `yb` CLI binary and `yb_send_dm`/`yb_query_group_members` functions are NOT available on data-collector — `yb_send_dm: command not found` is the expected result. Do not search for the `yb` binary, attempt `hermes tools enable yuanbao` (it will succeed but expose nothing usable), or try to curl localhost:8123/yuanbao/send_dm. Yuanbao adapter is also absent from channel_directory.json (yuanbao: []). Always fall back to direct reply immediately — the API server delivers collector heartbeat reports without any Yuanbao integration.
- Cron jobs running collector workflows (deliver: local): output IS the notification — do NOT try to call Feishu REST APIs from cron. The cron output goes to the gateway chat; the gateway relays to Feishu. If you need to send Feishu from cron, you need the app_secret accessible in the cron env (`FEISHU_APP_SECRET`) — collector app secrets are often gateway-runtime-injected and NOT stored in the codebase. Use `FEISHU_APP_SECRET` (not `COLLECTOR_FEISHU_APP_SECRET`).
- **`execute_code` filesystem access is limited to the sandbox directory.** `execute_code` runs in an isolated sandbox — it cannot access host paths like `${GYXX_PROJECT_ROOT}\shared-data\` or `${HERMES_STATE_ROOT}\...`. Attempting to open host paths via `Path()` or `read_bytes()` will raise `FileNotFoundError`. Use `terminal` (which inherits the MSYS/Git-Bash environment with proper path mapping) for all file operations on host paths. The `execute_code` sandbox is only suitable for pure in-memory computation or accessing paths that the sandbox itself creates.
- **`execute_code` can make outbound HTTP calls** — the "cannot call Feishu REST APIs" restriction is outdated. `requests` + `FEISHU_APP_SECRET` works fine from `execute_code` in the agent process. The failure scenario is cron job scripts running in a subprocess (where `FEISHU_APP_SECRET` is not injected). Always check `os.environ.get("FEISHU_APP_SECRET")` before assuming it works or fails.
- **`terminal` + inline Python script** is the most reliable pattern for Feishu REST calls. `python -c "..."` via terminal inherits the shell env, works in both agent and cron contexts.
- **Manual relay only (operator asks in chat to forward a report) — reply DIRECTLY with no tool calls.** Your reply text IS the notification. Do NOT query channel_directory, search for yuanbao group codes, call send_message, or look up credentials. This is for **manual / ad-hoc** forwarding only — automated heartbeats are sent by the orchestrator as cards.
- Common mistake: trying to relay an automated heartbeat (orchestrator already sent it) or calling send_message for manual relay. Both are unnecessary.
- Cron jobs with **gateway Feishu binding** (deliver: origin/local): reply directly — the gateway's own Feishu binding delivers the reply. (For automated heartbeats the orchestrator sends the card itself, so this is now mostly relevant for operator-side messages, not workflow status.)
- Scripts run via **terminal** without gateway binding: use `python -c "..."` to call Feishu REST APIs with `FEISHU_APP_SECRET`. (Same caveat — only relevant for paths outside the orchestrator's automated heartbeat flow.)
**Cron job skills that don't exist cause a skip warning but don't fail the job.** When a cron job's `skills` list contains a nonexistent skill, the scheduler logs `WARNING Cron job 'X': skill not found, skipping — Skill 'feishu_doc' not found.` The job still runs with the remaining skills or no skills. If the cron job silently does nothing or hits an unexpected code path, check `logs/errors.log` for this warning — the missing skill is the likely cause. To fix: remove the nonexistent skill from the cron job's `skills` list, or create the skill.
- Example: A `purchase-confirmation` workflow monitor cron job listed `feishu_doc` in skills but the skill was deleted. The job's `feishu_doc_read` tool call failed because the skill wasn't loaded, producing a partial/incomplete result with no error surfaced to the user.
## Collector Heartbeat Notification Format
**Automated heartbeats are sent by the orchestrator as Schema 2.0 cards** (see "Automated heartbeat" under Credential path). The LLM does not need to format or send them.
**Manual relay only** (operator asks in chat to forward a workflow report) — include all key fields:
```
[采集端心跳] <工作流名称>
Workflow ID: <id>
运行 ID: <run_id>
状态: <status>
当前阶段: <phase>
采集尝试次数: <n>
分析尝试次数: <n>
执行结论: <conclusion>
```
Manual relay pattern (for owner-only reports):
- User asks: "notify me only, not business targets" → reply directly as your response text (API server delivers it).
- The reply content IS the notification — no extra tools needed.
- Include all key fields: workflow ID, run ID, phase, status, attempts, error details, collection summary.
- Do NOT also call send_message / lark-cli / lark-cli / Feishu REST API — that would double-send.
Reference: `references/feishu-open-id-cross-app.md` — Feishu open_id cross-app failure and correct routing. Collector heartbeat target is `ou_8ee224968aa26a74c7d30ba27fed5eeb` via collector app (`cli_aa8c4fb4c4f81cd3`). `ou_7ad5fc8012e2f741afc5346e05ffd447` is the analyzer app owner — NOT the collector heartbeat target. Full REST call pattern included.
Reference: `references/collector-notification-credentials.md` — Collector Feishu notification credential sources: direct-reply pattern vs REST API pattern, and the known bug where `send_collector_notify.py` reads the wrong app secret from `config.json`.
Reference: `references/workflow-state-querying.md` — How to query collector workflow state. **Primary source: `shared-data/` artifacts + agent logs.** The orchestrator's `state.db` (LangChain checkpoints) only stores `replenishment-alert` runs — NOT collector runs. See the reference file for the full state querying strategy.
**Reusable monitoring script:** `scripts/monitor_purchase_confirmation.py` — drop-in script that queries collector session DB + shared-data, then sends Feishu admin notification. Run with `python scripts/monitor_purchase_confirmation.py <RUN_ID>`. Handles `orchestrator/state.db` ≠ collector workflow source correctly.
Final check:
- Collection only.
- No business-user final notify.
- Shared-data contains only this run outputs.
@@ -0,0 +1,58 @@
---
name: purchase-confirmation-workflow
description: 触发采购确认通知工作流 (purchase-confirmation)
triggers:
- manual: 用户要求"执行/触发 purchase-confirmation"
- scheduled: 通过 cron 定时触发
---
# Purchase Confirmation Workflow
Trigger the `purchase-confirmation` (采购确认通知工作流) from the `${GYXX_PROJECT_ROOT}` orchestrator.
## ⚠️ Important Execution Notes
- **执行时间长**:此工作流包含浏览器自动化(Chrome CDP),采集脚本执行时间约 **5-15 分钟**,请耐心等待,**不要**超时后随意清理锁和进程
- **不要主动终止**:看到超时不要清理锁/杀进程,工作流可能仍在正常执行
- **判断完成**:检查 `shared-data/purchase-confirmation/` 目录中是否有 CSV 文件(如 `采购确认通知汇总_YYYYMMDD_HHMMSS.csv`
## Workflow Command
```bash
cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run purchase-confirmation
```
工作流通过 Hermes MCP 网关执行,包含采集端和分析端两个阶段。
## 创建定时任务
```python
cronjob(action='create',
prompt='执行 purchase-confirmation 工作流。运行命令:cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run purchase-confirmation',
schedule='0 9 * * *', # 每天早上9点
name='purchase-confirmation-daily')
```
## 通知规则(关键)
⚠️ **采集端通知规则**:采集端 Hermes 只通知运营,不通知业务目标用户。
- 采集端通知对象:运营(飞书 OpenID `ou_7ad5fc8012e2f741afc5346e05ffd447`
- 业务最终通知:由分析端 Hermes 负责
- 通知方式:通过当前会话直接响应(不调用 send_message/yuanbao 派)
当用户请求"只通知我(不要通知业务目标用户)"时:
1. 直接在当前会话中以文本形式输出报告
2. 说明通知范围:仅运营,业务目标用户由分析端 Hermes 负责
## 工作流配置参考
- **工作流名称**purchase-confirmation
- **显示名称**:采购确认通知工作流
- **采集脚本**`${GYXX_PROJECT_ROOT}/orchestrator/scripts/collect_confirmation.ps1`
- **输出文件**`采购确认通知汇总_*.csv`xlsx 格式)
- **通知目标**(业务用户):
- `ou_7ad5fc8012e2f741afc5346e05ffd447`
- `ou_339c1d396b397c97b08e1bf377963d40`
- `ou_54141d75b24d22ddc8c1c4d66e00b6e9`
- **采集端绑定应用**`cli_aa8c4fb4c4f81cd3`
- **分析端绑定应用**`cli_aa8c4fc918b85cce`
@@ -0,0 +1,34 @@
---
name: purchase-order-update-workflow
description: 触发采购单日期更新工作流 (purchase-order-update)
category: workflow
triggers:
- manual: 用户要求"执行/触发 purchase-order-update"
- scheduled: 通过 cron 定时触发
---
# Purchase Order Update Workflow
Trigger the `purchase-order-update`(采购单日期更新工作流)from the `${GYXX_PROJECT_ROOT}` orchestrator.
## Workflow Command
```bash
cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run purchase-order-update
```
## 工作流配置(来源 bytecode 反向提取)
| 字段 | 值 |
|------|-----|
| **工作流名称** | purchase-order-update |
| **显示名称** | 采购单日期更新工作流 |
| **采集脚本** | `collect_purchase_order_update.ps1` |
| **输出文件** | `update_result.json` |
| **分析端通知方式** | `plugin:feishu.im.send_message` |
| **采集端绑定应用** | `cli_aa8c4fb4c4f81cd3` |
| **分析端绑定应用** | `cli_aa8c4fc918b85cce` |
## ⚠️ config.py 源文件已丢失
此工作流配置从 bytecode 反向提取,如需修改通知目标,需先重建 `config.py`
@@ -0,0 +1,41 @@
---
name: replenishment-alert-workflow
description: 触发库存预警通知工作流 (replenishment-alert)
triggers:
- manual: 用户要求"执行/触发 replenishment-alert"
- scheduled: 通过 cron 定时触发
---
# Replenishment Alert Workflow
Trigger the `replenishment-alert` (库存预警通知工作流) from the `${GYXX_PROJECT_ROOT}` orchestrator.
## Trigger Conditions
- **手动触发**:用户明确要求"执行/触发 replenishment-alert"
- **定时触发**:通过 cron 任务定期运行
## Workflow Command
```bash
cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run replenishment-alert
```
工作流通过 Hermes MCP 网关执行,包含采集端和分析端两个阶段。
## 已知问题
⚠️ **通知重复问题**:如需修复,从 `config.py``notification_targets.threshold_alert` 中移除 analyzer owner。但注意:`config.py` 源文件已丢失(仅 pyc 缓存),修改前需先重建配置。
⚠️ **config.py 源文件已丢失**:仅存 `__pycache__/config.cpython-311.pyc`,如需修改通知目标,需从 bytecode 反向提取常量后再重建文件。
## 工作流配置参考
- **工作流名称**replenishment-alert
- **显示名称**:库存预警通知工作流
- **采集脚本**`${GYXX_PROJECT_ROOT}/orchestrator/scripts/collect_replenishment.ps1`
- **输出文件**`threshold_alert_data.json`
- **通知目标**(业务用户,仅 `threshold_alert`,来源 bytecode const[53]):
- `ou_b76e4cbdb24fe28cebd45ad091b60224`(黄坤平)
- **采集端绑定应用**`cli_aa8c4fb4c4f81cd3`
- **分析端绑定应用**`cli_aa8c4fc918b85cce`
@@ -0,0 +1,54 @@
---
name: replenishment-workflow
description: 触发补货建议工作流 (replenishment)
triggers:
- manual: 用户要求"执行/触发 replenishment"
- scheduled: 通过 cron 定时触发
---
# Replenishment Workflow
Trigger the `replenishment` (补货建议工作流) from the `${GYXX_PROJECT_ROOT}` orchestrator.
## ⚠️ Important Execution Notes
- **执行时间长**:此工作流包含浏览器自动化(Chrome CDP),采集脚本执行时间约 **5-15 分钟**,请耐心等待
- **判断完成**:检查 `shared-data/replenishment/` 目录中是否有 `pending_insert.json``notify_data.json`
- **后台执行**:建议使用 `background=true` + `notify_on_complete=true` 执行,避免超时
## Workflow Command
```bash
cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run replenishment
```
## 创建定时任务
```python
cronjob(action='create',
prompt='执行 replenishment 工作流。运行命令:cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run replenishment',
schedule='0 9 * * *', # 每天早上9点
name='replenishment-daily')
```
## 通知目标(分析端)
仅通知运营(`ou_7ad5fc8012e2f741afc5346e05ffd447`)。不通知黄坤平,不通知审单群。
## 工作流配置参考
- **工作流名称**replenishment
- **显示名称**:补货建议工作流
- **采集脚本**`${GYXX_PROJECT_ROOT}/orchestrator/scripts/collect_replenishment.ps1`
- **输出文件**`pending_insert.json`, `notify_data.json`
- **多维表**:应用 `cli_aa8c4fc918b85cce` 需对多维表 `Th0jbMHHQa7a8Lse3CicfSMBnxb` 有写入权限
- **采集端绑定应用**`cli_aa8c4fb4c4f81cd3`
- **分析端绑定应用**`cli_aa8c4fc918b85cce`
## 已知问题
⚠️ **多维表写入权限**:如遇以下错误,需在飞书开放平台为应用 `cli_aa8c4fc918b85cce` 开启多维表写入权限:
- `91403 Forbidden` — 无权限访问多维表
- `99992402 field validation failed` — 字段验证失败(常见于 app 无写入权限或字段类型不匹配)
写入失败**不影响通知发送**(符合 HARD RULES),但待处理数据需手动补充。
@@ -0,0 +1,57 @@
---
name: workflow-trigger-only
description: 工作流触发原则:只执行既有命令,不自主生成代码修改流程
triggers:
- manual: 用户要求将某个操作原则沉淀为 skill 时创建
---
# Workflow Trigger Only
## 核心原则
**只触发既有工作流,不自主生成或修改流程代码。**
## 执行方式
```
cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run <工作流名>
```
支持的 `<工作流名>`
- `replenishment-alert` — 库存预警通知
- `purchase-confirmation` — 采购确认通知
- `replenishment` — 补货建议
## 规则
1. **只运行既有命令** — 使用 `run.py mcp-run` 触发,不写 Python 脚本绕过后续流程
2. **不自主修改流程代码** — 不修改 `mcp_workflow.py`、通知脚本、采集脚本等,除非用户明确要求
3. **不自主生成新代码** — 不创建新脚本自行完成通知/写表等操作
4. **编排逻辑由既有代码决定** — 采集→分析→通知的分段流程和通知对象由 `mcp_workflow.py` 和分析端 Hermes 控制
## 例外:手动转发心跳/执行报告
"只触发既有命令"规则**仅适用于 orchestrator 工作流执行**。当用户要求将某个工作流的心跳/执行报告**手动转发**给运营时:
1. 构造心跳消息内容(包含工作流 ID、运行 ID、状态、错误信息等)
2. 通过飞书采集端应用(`cli_aa8c4fb4c4f81cd3`)将报告以 **Feishu DM** 形式发送给运营(`ou_8ee224968aa26a74c7d30ba27fed5eeb`
3. **不**通知业务目标用户,不走 yuanbao group
4. 使用 `feishu` skill 中的 temp 脚本模式发送(写临时脚本 → terminal 运行 → 删除),或直接 reply 让 gateway 转发
> 注意:**自动心跳已由 `orchestrator/mcp_workflow.py` 用 Schema 2.0 interactive card 发送**target 同样是 `ou_8ee224968aa26a74c7d30ba27fed5eeb`),本例外仅适用于**人工额外转发**场景(比如重发、转发给第三方等),不要重复发送已自动发出的心跳。
这是人工操作员在对话中转发报告的合法场景,与 orchestrator 自动执行工作流是两回事。
## 触发命令参考
| 工作流 | 命令 |
|--------|------|
| 库存预警通知 | `cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run replenishment-alert` |
| 采购确认通知 | `cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run purchase-confirmation` |
| 补货建议 | `cd ${GYXX_PROJECT_ROOT} && .venv/Scripts/python.exe run.py mcp-run replenishment` |
## 为什么这样做
- 工作流的编排逻辑(采集、分析、通知分段执行)由 `mcp_workflow.py` 控制
- 通知对象由 `config.py` 和分析端 prompt 决定,不应在触发层硬编码
- 自主生成代码容易出错且难以追踪