# 采购审批 Flow 节点配置清单

目标：这份清单按斑头雁 BetterYeah Flow 当前官方手册整理，目的是让你先把一个可运行的采购审批演示 Flow 搭出来，再去微调话术。

边界：本练习不接真实 OA、预算、采购或供应商系统；采购申请、预算样例、历史价样例、供应商风险、审批规则、输出模板全部来自下载资料和知识库。

先准备两样资料：
- `18-采购审批单据样例.docx`：作为开始节点输入时的单据样本。
- `19-采购审批知识库Word资料包.zip`：解压后把 5 份 Word 全部上传到同一个知识库。
- `22-采购审批规则.docx`：独立规则版，方便单独发放或单独上传。

知识库内应包含：
- 采购单摘要：CG-2026-001、项目、科目、金额、拟选供应商、附件清单。
- 预算样例：项目可用余额、差额、预算例外口径。
- 历史价样例：同规格历史采购区间、样本数量、差异率口径。
- 供应商风险样例：准入状态、风险等级、延期交付记录、更新时间。
- 审批规则：字段完整性、预算不足、价格异常、供应商风险、越权请求。
- 输出模板：审批建议、依据、风险点、补件项、人工确认项。

## 1. 统一约定

先把节点名称改成下面这一组，后面所有变量和逻辑条件都按这些名字写：
- `start`：开始节点，系统默认。
- `llm_parse`：抽取采购单字段。
- `kb_lookup`：查询知识库。
- `llm_review`：判断建议动作和路由。
- `branch_route`：逻辑分支。
- `llm_draft_continue`：生成“可继续审批”草稿。
- `llm_draft_supplement`：生成“补件后再审”草稿。
- `llm_draft_manual`：生成“转人工重点复核”草稿。
- `output`：输出节点，系统默认。

为什么要先改节点名：官方文档说明，开始节点的字段名就是变量名，节点输出结果默认用节点名称作为变量名；普通节点里直接用变量名或属性路径，例如 `purchase_request`、`llm_parse.query_text`；逻辑节点里同样直接写变量名，不要再加 `{{}}`。

## 2. 开始节点 `start`

开始节点选择 `表单类型`，不要选 Webhook。默认自带的 `消息` 字段直接改造成下面第 1 个字段。

| 字段名称 | 变量名称 | 字段类型 | 是否必填 | 占位符 / 选项 |
|---|---|---|---|---|
| 采购申请摘要 | `purchase_request` | 文本域 | 是 | 粘贴采购申请摘要，至少包含项目、采购物料、金额、拟选供应商、附件情况 |
| 当前办理角色 | `operator_role` | 单选下拉框 | 是 | 采购经办人 / 财务复核 / 业务负责人 |

调试时，可以直接把 `18-采购审批单据样例.docx` 里的正文内容粘贴进 `purchase_request`。

## 3. 节点总连线

`start -> llm_parse -> kb_lookup -> llm_review -> branch_route`

再从 `branch_route` 分出三条线：
- `continue -> llm_draft_continue -> output`
- `supplement -> llm_draft_supplement -> output`
- `Else -> llm_draft_manual -> output`

注意：官方文档说明 `output` 节点本身不支持自定义配置，只会把前一个节点的结果直接传出去。所以这里不要想着在 `output` 节点里再改字段，只要把三个草稿节点分别连到 `output` 即可。

## 4. 节点逐项照填

### 4.1 `llm_parse`：采购单抽取节点

节点类型：`LLM`
模型：任选一个支持 `JSON` 输出的文本模型；优先 `qwen-plus` 或 `deepseek-V3`。
输出格式：`JSON`
建议把创造性 / 温度调低，目标是抽取稳定，不是发挥。

提示词直接粘贴：
```text
你是采购审批流程里的“单据抽取节点”。
你的任务只有两个：
1. 从采购申请摘要中抽取字段。
2. 判断哪些关键字段缺失。

你不是审批人，不做“通过/驳回/转人工”的最终结论。

输入信息：
- 当前办理角色：operator_role
- 采购申请摘要：purchase_request

请严格输出 JSON，对象字段固定如下：
{
  "doc_no": "",
  "project_name": "",
  "budget_subject": "",
  "purchase_item": "",
  "amount": 0,
  "currency": "CNY",
  "supplier_name": "",
  "attachment_list": [],
  "missing_fields": [],
  "query_text": "",
  "input_risk_hints": []
}

字段要求：
1. `amount` 只保留数字，不要带人民币符号。
2. `attachment_list` 只放附件名称字符串数组。
3. `missing_fields` 只列关键缺失项，例如：预算依据、技术验收标准、比价记录、供应商材料。
4. `query_text` 用一句中文搜索串，把项目、采购物料、预算科目、供应商和风险线索串起来，供知识库节点直接查询。
5. 没有的信息留空字符串、0 或空数组，不要编造。
6. 不要输出 JSON 以外的任何解释。
```

这个节点跑完后，重点检查 `llm_parse.query_text` 是否可读、`missing_fields` 是否真的被抽出来。

### 4.2 `kb_lookup`：知识库查询节点

节点类型：`知识库节点`
知识库：选择刚才上传 5 份 Word 的那个采购审批演练库。
标签：可空。
查询方式：`混合查询`
查询关键词：`llm_parse.query_text`
最大结果数：`6`
结果重排：`开启`
输出方式：`文本`

这里故意选 `文本` 输出，不选 `JSON`。原因很简单：这一步只是给下一个 LLM 节点读规则、样例和模板，文本更容易直接粘过去用，现场也更稳。

### 4.3 `llm_review`：建议动作判断节点

节点类型：`LLM`
模型：任选一个支持 `JSON` 输出的文本模型；尽量和 `llm_parse` 用同一类稳定模型。
输出格式：`JSON`

提示词直接粘贴：
```text
你是采购审批流程里的“建议动作判断节点”。
你要依据采购申请抽取结果和知识库命中的规则/样例，给出建议动作，但你不能替代正式审批。

输入 1：采购申请抽取结果
llm_parse

输入 2：知识库命中内容
kb_lookup

请严格输出 JSON，对象字段固定如下：
{
  "route": "continue",
  "risk_level": "low",
  "summary": "",
  "basis": [],
  "risk_points": [],
  "missing_items": [],
  "manual_confirm_points": [],
  "budget_gap": 0,
  "price_delta_pct": 0,
  "supplier_risk_level": "unknown"
}

判断要求：
1. `route` 只能是 `continue`、`supplement`、`manual` 三个值之一。
2. 如果关键材料缺失，或者知识库规则明确要求补充资料后再判断，`route` 输出 `supplement`。
3. 如果命中高风险、预算明显不足、价格异常、供应商风险较高、越权请求，`route` 输出 `manual`。
4. 只有在资料完整、风险可控、且没有触发人工断点时，`route` 才能输出 `continue`。
5. `risk_level` 只能是 `low`、`medium`、`high`。
6. `basis`、`risk_points`、`missing_items`、`manual_confirm_points` 一律输出字符串数组。
7. `budget_gap` 用数字；预算不足时填差额值，拿不到就填 0。
8. `price_delta_pct` 用数字；拿不到就填 0。
9. `summary` 用一句完整中文概括当前建议动作和原因。
10. 不要输出“已审批通过”“已付款”“已定标”之类越权结论。
11. 不要输出 JSON 以外的任何解释。
```

### 4.4 `branch_route`：逻辑分支节点

节点类型：`逻辑分支`

按下面顺序建 3 个分支：

分支 1 名称：`continue`
```javascript
llm_review.route === "continue"
```

分支 2 名称：`supplement`
```javascript
llm_review.route === "supplement"
```

分支 3：保留系统默认 `Else`

为什么这么写：官方手册明确说逻辑分支里的条件必须是 JavaScript 表达式，而且变量不要加 `{{}}`。所以这里直接写 `llm_review.route`，不要写成 `{{llm_review.route}}`。

### 4.5 `llm_draft_continue`：可继续审批草稿

节点类型：`LLM`
输出格式：`文本`

提示词直接粘贴：
```text
你要输出一段可直接放进 OA 的采购审批意见。

采购单抽取结果：
llm_parse

建议动作判断结果：
llm_review

请按下面结构输出，不要多写解释：
建议动作：可继续审批
依据：
1. ...
2. ...
风险提示：
- ...
人工确认项：
- ...

要求：
1. 语气客观、简短。
2. 只写审批建议，不写最终审批结论。
3. 如果没有明显风险，风险提示写“未见需立即中止的异常，但仍需按正式审批流程复核”。
```

### 4.6 `llm_draft_supplement`：补件通知草稿

节点类型：`LLM`
输出格式：`文本`

提示词直接粘贴：
```text
你要输出一段“补件后再审”的采购审批意见。

采购单抽取结果：
llm_parse

建议动作判断结果：
llm_review

请按下面结构输出，不要多写解释：
建议动作：补件后再审
缺失项：
1. ...
2. ...
依据：
1. ...
2. ...
补充完成后再检查：
- ...

要求：
1. 缺失项必须和 `llm_review.missing_items` 对齐。
2. 不要写成驳回，只写“补齐后再审”。
3. 不要输出最终审批语言。
```

### 4.7 `llm_draft_manual`：转人工重点复核草稿

节点类型：`LLM`
输出格式：`文本`

提示词直接粘贴：
```text
你要输出一段“转人工重点复核”的采购审批意见。

采购单抽取结果：
llm_parse

建议动作判断结果：
llm_review

请按下面结构输出，不要多写解释：
建议动作：转人工重点复核
触发原因：
1. ...
2. ...
依据：
1. ...
2. ...
建议由谁确认：
- ...
下一步动作：
- ...

要求：
1. 触发原因优先写预算异常、价格异常、供应商风险、越权请求等。
2. 明确“转人工重点复核”，不要代替人工做最终决定。
```

## 5. 最小调试方法

1. 先只跑 `llm_parse`：确认它真的输出了合法 JSON，而不是一段说明文字。
2. 再跑 `kb_lookup`：确认查询词不是空的，而且能命中预算、历史价、供应商风险、规则、模板这几类内容。
3. 再跑 `llm_review`：确认 `route` 只会出现 `continue / supplement / manual` 三个值。
4. 最后看 `branch_route`：如果分支没走通，先检查是不是把逻辑条件写成了 `{{llm_review.route}}`，这是错的。
5. 调试面板里优先看日志页：官方手册说明日志能看到每个节点的入参、出参和耗时；单节点运行时，除了开始 / 输出 / 逻辑节点，其他节点都可以单独调。

## 6. 这份配置单能不能做到“复制粘贴就搭出来”

可以做到 `80% 到 90%`：
- 可以直接复制的是：节点命名、表单字段、3 段 LLM 提示词、逻辑分支条件、知识库节点的查询方式和结果数。
- 仍要人工点选的是：具体模型下拉框、知识库资源本身、是否开启结果重排、分支连线。
- 如果现场有人完全照抄却还是跑不通，优先检查三件事：`llm_parse` / `llm_review` 有没有真的设成 `JSON` 输出；知识库是否已上传 5 份 Word；逻辑条件里有没有误加 `{{}}`。

## 7. 搭建顺序复盘

1. 建 `start` 表单。
2. 建 `llm_parse`，先单节点跑通。
3. 上传 5 份知识库 Word，建 `kb_lookup`。
4. 建 `llm_review`，确认 `route` 三选一稳定输出。
5. 建 `branch_route`，按 `continue / supplement / Else` 分线。
6. 建 3 个草稿节点，分别连到 `output`。
7. 用 `18-采购审批单据样例.docx` 和 `06-最小验证表.md` 逐条复跑。
