07: English screenshots, School news version 2 with Checker and Gatekeeper

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido van Dijk
2026-09-21 09:22:57 +02:00
parent f5761da782
commit edf987f58a
70 changed files with 36 additions and 40 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 56 KiB

@@ -2,7 +2,7 @@
FOR THE EDITOR
Guide 7: self-critique and a gatekeeper, a sequel to guide 3. Screenshots in screenshots/,
captured on 20 September 2026 (Dify cloud, Dutch build of the same workflow).
captured on 21 September 2026 (Dify cloud, English interface).
Convert to HTML, PDF and Word: python3 build.py
(python-markdown, google-chrome and libreoffice; layout and logo in _tools/build_guide.py)
@@ -15,7 +15,7 @@ Formatting conventions (see also README.md):
- Blockquote starting with **Your choice** = a choice for the participant (green box)
The Python code for the Gatekeeper is in the text; participants paste it with Ctrl+V into the Code node.
Published example app (Dutch, version 2 of School news): https://udify.app/workflow/jJclFyX9BlteuHYi
Published example app (version 2 of School news): https://udify.app/workflow/E2CHtVPKe3PLg7GF
-->
# School news with checks: self-critique and a gatekeeper
@@ -32,7 +32,7 @@ In guide 3 you built School news: one core message becomes a piece for the websi
The difference between those two is the heart of this guide. The Checker is a model reviewing a model: good for form and tone, but it can talk itself into things. The Gatekeeper is six lines of Python that cannot be talked into anything: 81 words is too many, full stop. Together they deliver what a communications officer wants: less checking work, and certainty that the rules that really matter really apply.
This guide follows the cloud version of Dify of 20 September 2026. If your screen differs, follow what you see. The screenshots show the Dutch build of this workflow (Dutch block names such as Controleur and Poortwachter, Dutch texts); the screens and clicks are the same.
This guide follows the cloud version of Dify of 20 September 2026. If your screen differs, follow what you see.
**How to read this guide**
@@ -77,11 +77,11 @@ Before we started clicking, this card was filled in (the blank card is in the De
This is what School news looks like before you start:
![Starting point: the workflow from guide 3](screenshots/01-uitgangspunt.jpg)
![Starting point: the workflow from guide 3](screenshots/01-starting-point.jpg)
And this is what the tail looks like when you are done:
![The goal: Checker, Gatekeeper, IF/ELSE and two exits](screenshots/27-hele-keten.jpg)
![The goal: Checker, Gatekeeper, IF/ELSE and two exits](screenshots/27-whole-chain.jpg)
---
@@ -100,15 +100,15 @@ The new blocks go between Social and Output. So you first disconnect Output.
![Change, Disconnect, Delete](screenshots/03-disconnect-menu.jpg)
![Social now has no next step](screenshots/04-losgekoppeld.jpg)
![Social now has no next step](screenshots/04-disconnected.jpg)
### Step 2. Add the Checker
1. Under **NEXT STEP** click **Select next step** and choose **LLM**.
![The list of blocks](screenshots/05-nodes-lijst.jpg)
![The list of blocks](screenshots/05-nodes-list.jpg)
![The new, empty LLM block](screenshots/06-llm4-leeg.jpg)
![The new, empty LLM block](screenshots/06-llm4-empty.jpg)
2. **MODEL**: gpt-5-mini. **CONTEXT**: Knowledge Retrieval / result (the style guide).
3. Paste the prompt below into **SYSTEM**. For `{Context}` type a forward slash and choose **Context**; for `{Parent app/text}` choose the item **text** under **PARENT APP**.
@@ -125,11 +125,11 @@ Style guide: {Context}
Draft message: {Parent app/text}
```
![The prompt, with the menu after a forward slash](screenshots/07-controleur-prompt.jpg)
![The prompt, with the menu after a forward slash](screenshots/07-checker-prompt.jpg)
4. Rename the block to `Checker`.
![Checker ready: context and draft message are connected](screenshots/08-controleur-klaar.jpg)
![Checker ready: context and draft message are connected](screenshots/08-checker-ready.jpg)
> "Work in two steps, but give only step 2." That is not a contradiction but a trick: the model assesses first (which improves the result) and you still get clean text back that you can pass on directly. If you leave step 1 in the answer, that explanation ends up in the parent app too.
@@ -145,7 +145,7 @@ Draft message: {Parent app/text}
1. At Checker: **NEXT STEP** → **Select next step** → under Transform: **Code**.
![The Code block with sample code](screenshots/09-code-standaard.jpg)
![The Code block with sample code](screenshots/09-code-default.jpg)
2. Click in the editor, select everything (Ctrl+A) and paste the code below.
@@ -171,7 +171,7 @@ def main(website: str, parent_app: str, social: str) -> dict:
}
```
![The code is in place](screenshots/10-code-ingevuld.jpg)
![The code is in place](screenshots/10-code-in-place.jpg)
What the code does, line by line in plain words:
@@ -191,7 +191,7 @@ What the code does, line by line in plain words:
1. Under **INPUT VARIABLES** rename `arg1` to `website` and `arg2` to `parent_app`. Click **+** for a third variable and call it `social`. The names must be exactly the same as the names in the first line of the code.
![Input variables renamed, third one added](screenshots/11-invoer-hernoemd.jpg)
![Input variables renamed, third one added](screenshots/11-inputs-renamed.jpg)
2. For each variable click **Set variable** and choose:
@@ -201,17 +201,17 @@ What the code does, line by line in plain words:
| parent_app | **CHECKER / text** (the improved version, not the raw one) |
| social | **SOCIAL / text** |
![The variable picker](screenshots/12-variabele-kiezen.jpg)
![The variable picker](screenshots/12-variable-picker.jpg)
![All three connected](screenshots/13-invoer-gekoppeld.jpg)
![All three connected](screenshots/13-inputs-connected.jpg)
3. Under **OUTPUT VARIABLES**: rename `result` to `ok` and set the type to **Number**. Click **+** and add `reasons`, type **String**.
![Choosing the type: Number](screenshots/14-uitvoer-type.jpg)
![Choosing the type: Number](screenshots/14-output-type.jpg)
4. Rename the block to `Gatekeeper`.
![Gatekeeper ready](screenshots/15-poortwachter-klaar.jpg)
![Gatekeeper ready](screenshots/15-gatekeeper-ready.jpg)
> The output names (`ok`, `reasons`) must literally match the names in the `return` line of the code. One letter different and the workflow stops with an error about a missing output.
@@ -225,21 +225,21 @@ What the code does, line by line in plain words:
1. At Gatekeeper: **Select next step** → under Logic: **IF/ELSE**.
![The empty IF/ELSE block](screenshots/16-ifelse-leeg.jpg)
![The empty IF/ELSE block](screenshots/16-ifelse-empty.jpg)
2. Under **IF** click **+ Add Condition** and choose **GATEKEEPER / ok**.
![The variable for the condition](screenshots/17-conditie-variabele.jpg)
![The variable for the condition](screenshots/17-condition-variable.jpg)
3. Leave the comparison on **=** and type `1` as the value.
![The condition: ok = 1](screenshots/18-conditie.jpg)
![The condition: ok = 1](screenshots/18-condition.jpg)
### Step 6. Exit 1: Approved
1. Under **NEXT STEP** under **IF**: **Select next step** → **Output**.
![The new Output block](screenshots/19-output2-leeg.jpg)
![The new Output block](screenshots/19-output2-empty.jpg)
2. Add three output variables:
@@ -251,7 +251,7 @@ What the code does, line by line in plain words:
3. Rename the block to `Approved`.
![Approved](screenshots/20-goedgekeurd.jpg)
![Approved](screenshots/20-approved.jpg)
### Step 7. Exit 2: to the communications officer
@@ -270,7 +270,7 @@ What the code does, line by line in plain words:
3. Rename the block to `To the communications officer`.
![The second exit](screenshots/22-naar-medewerker.jpg)
![The second exit](screenshots/22-to-officer.jpg)
> Two Output blocks may not have variables with the same name. If you call them `website` in both, Dify puts an orange "Duplicate variable" in the checklist and you cannot publish. Hence `draft_` in the second exit.
@@ -280,16 +280,14 @@ What the code does, line by line in plain words:
![The checklist: the old Output is connected to nothing](screenshots/23-checklist.jpg)
2. Click **Output** in the checklist. The old block is selected; it is hidden behind Checker.
2. Click the first line under **Output** in the checklist. The old block is selected and the panel opens.
![The old Output, hidden behind Checker](screenshots/24-oude-output.jpg)
![The old Output, selected from the checklist](screenshots/24-old-output.jpg)
3. In the panel click the three dots and then **Delete**.
4. Check that the checklist is empty (no number on the icon any more).
![After renaming to draft_: no duplicate variables any more](screenshots/25-concept-variabelen.jpg)
![The new tail of the workflow](screenshots/26-nieuwe-staart.jpg)
![The new tail of the workflow](screenshots/26-new-tail.jpg)
---
@@ -309,7 +307,7 @@ You always test a gatekeeper both ways. Testing only the good side proves nothin
2. **Start Run**. After about a minute the path via **IF** to **Approved** turns green.
![Test 1: the path runs via IF to Approved](screenshots/28-test1-resultaat.jpg)
![Test 1: the path runs via IF to Approved](screenshots/28-test1-result.jpg)
3. Click **DETAIL**: status SUCCESS, run time and tokens.
@@ -319,12 +317,10 @@ You always test a gatekeeper both ways. Testing only the good side proves nothin
![Tracing: every block with its time and tokens](screenshots/30-tracing.jpg)
![The Gatekeeper: ok 1, "All rules satisfied."](screenshots/31-poortwachter-ok.jpg)
![The Gatekeeper: ok 1, "All rules satisfied."](screenshots/31-gatekeeper-ok.jpg)
> Tracing is your friend. For every question "why did this come out?" you open the block here and see exactly what went in and what came out. This is also the screen on which you saw that the Checker had removed the sign-up deadline (see the orange box in step 2).
![The extended prompt of the Checker after test 1](screenshots/32-controleur-aangevuld.jpg)
### Step 10. Test 2: a message with gaps
1. **Test Run** again, now with a message without a date, without a time and with an abbreviation:
@@ -335,25 +331,25 @@ You always test a gatekeeper both ways. Testing only the good side proves nothin
| For which class or year group? | `Year 7` |
| Which tone fits this message? | **informative** |
![Test 2: the input](screenshots/33-test2-invoer.jpg)
![Test 2: the input](screenshots/33-test2-input.jpg)
2. **Start Run**. Now the path runs via **ELSE** to **To the communications officer**, and the reasons are at the top of the result.
![Test 2: rejected, with four reasons](screenshots/34-test2-afgekeurd.jpg)
![Test 2: rejected, with the reasons at the top](screenshots/34-test2-rejected.jpg)
Read the reasons: all three texts contain [FILL IN] (the models did not know the date and neatly did not invent one), and the social post contains "asap". That last one is instructive: the Social prompt asked to follow the style guide, and still the abbreviation from the core message slipped through. A prompt asks; the gatekeeper enforces.
Read the reasons: all three texts contain [FILL IN] (the models did not know the date and neatly did not invent one). In our Dutch run the social post also contained the abbreviation from the core message; in this run the model left it out. That difference is instructive: the Social prompt asked to follow the style guide, and whether the abbreviation slips through depends on the run. A prompt asks; the gatekeeper enforces, every time.
### Step 11. Publish
1. **Publish** → **Publish Update**. Dify creates version 2 of the app; with **Name** you can give a version a name, and via the clock icon at the top right you can always go back to version 1.
![Published as version 2](screenshots/35-gepubliceerd.jpg)
![Published as version 2](screenshots/35-published.jpg)
2. Open the webapp and repeat test 2. The colleague sees the reasons at the top and below them the drafts to complete.
![The webapp: rejected, with the reasons and the drafts](screenshots/36-webapp-afgekeurd.jpg)
![The webapp: rejected, with the reasons and the drafts](screenshots/36-webapp-rejected.jpg)
The link has stayed the same (Dutch version): https://udify.app/workflow/jJclFyX9BlteuHYi
The link has stayed the same: <https://udify.app/workflow/E2CHtVPKe3PLg7GF>.
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

+1 -1
View File
@@ -5,7 +5,7 @@ This is what changed per release.
## 2026-09-20, English screenshots
- Guides 01, 02 and 03 rebuilt with the English Dify interface and English apps (Text at level, School guide assistant, Parent letter assistant, School news; links in the README). The other guides follow.
- Guides 01, 02, 03 and 07 rebuilt with the English Dify interface and English apps (Text at level, School guide assistant, Parent letter assistant, School news with checks; links in the README). The other guides follow.
- New appendix 00-self-hosting: running Dify Community Edition yourself with Docker (Windows, Mac, Linux).
## 2026-09-20, first release
+1 -1
View File
@@ -57,7 +57,7 @@ The apps are live to try out:
- Text at level (01): https://udify.app/workflow/VHWboGL9462eA1KI
- School guide assistant (02, with the Reception mode from 03): https://udify.app/chat/6Y6WnP0m5Z3jSHRs
- Parent letter assistant (03): https://udify.app/chat/oB2H2mLaoKR6d4IC
- School news (03): https://udify.app/workflow/E2CHtVPKe3PLg7GF
- School news (03 and 07): https://udify.app/workflow/E2CHtVPKe3PLg7GF
- Test week planner (04; Dutch build): https://udify.app/agent/rKoqw8rbmp0IF86Q
- Transcription tool (05; Dutch build): https://udify.app/workflow/7N5dNg8fD9qXHA80
- Materials check (06; Dutch build): https://udify.app/agent/lkWKedkSW2d3nqBV