Appearance
Supplier due diligence
Due diligence on a new supplier before the first purchase order, decided by rules, a model and a person together: a questionnaire covers the company, what it will supply, its owners and, for software suppliers, security certification; rules check the company against the sanctions list you keep, its declared owners, a country risk list and the contract value; unless they decline, a model reads the supplier's own description of what it will supply; findings and any supplier the model is not sure of go to procurement. Every rule result, model answer and decision is on the run's record. Pack: examples/policies/supplier-due-diligence/.
A pattern for expressing a supplier and third-party risk policy, not compliance guidance: the thresholds, lists, questions and fields are placeholders chosen to make the example run; a real policy comes from the business's own rules. Its model step calls one install, chatgpt (The chatgpt install). The model never screens the company or its owners: the list and the rules do.
The decision it automates
Whether to approve the supplier, send it to procurement, or decline it, when the questionnaire is submitted. Only the rules decline: the model can send a supplier the rules approve to procurement, never decline one or approve one the rules sent to procurement.
Data expected
- The supplier as a subject (kind
merchant, a business); nothing is read from its data. - The questionnaire's answers under
submission:legalName,country,category,supplyDescription(what it will supply, in its own words: 20 to 2,000 characters),annualValue(USD),ownersDeclaredwithownerNameandownershipPct, and the security answers for software suppliers. - The
supplier_sanctions_listandsupplier_high_risk_countrieslists, which the pack creates with a placeholder row each. - An install named
chatgptof the platform'schatgptapp, with your OpenAI API key.
Steps and rules
| Step | Type | Why |
|---|---|---|
collect | wait_for_collection | The supplier-questionnaire flow; software suppliers get the security step. |
rules | evaluate_rules | The supplier-due-diligence rule set under max_severity. |
gate | branch | rules.outcome == reject goes straight to the decision, without the model; any other supplier goes to supply_check. |
supply_check | call_app | decide through chatgpt on the supplier's description: two questions, answered under supplyCheck. |
route | branch | rules.outcome == manual_review opens a case, and so does supplyCheck.answers.fits_category.probability under 0.8; supplyCheck.answers.no_data_access.probability of 0.8 or more approves; the rest open a case. |
review | create_case | procurement_review, 5-day SLA. |
decide | emit_decision | From the rules, or the reviewer's decision. |
The rule set supplier-due-diligence (useCase: onboarding) uses max_severity: a failed block rule rejects, a failed warn rule sends the run to review, and the weights add up to the score.
| Rule | Type | Severity, weight | Fails when |
|---|---|---|---|
supplier_sanctions_clear | list lookup | block, 100 | legalName is on supplier_sanctions_list |
supplier_ownership_declared | comparison | block, 100 | ownersDeclared is no |
supplier_country_risk | list lookup | warn, 30 | country is on supplier_high_risk_countries |
supplier_contract_value | score threshold | warn, 20 | annualValue is above $500,000 |
The model step
supply_check writes the model a sentence (its stateTemplate) from two of the questionnaire's answers, the category the supplier chose and its supplyDescription, each with a literal default, and asks two questions, each phrased so that a yes lets the supplier through:
| Question | Answer | Asks | Read by |
|---|---|---|---|
fits_category | yes or no, threshold 0.8 | Does the description fit the category the supplier chose? A hosted service, an app, an integration with your systems or the maintenance of software is software, whatever the supplier chose. | route: under 0.8 opens a case |
no_data_access | yes or no, threshold 0.8 | Will the supplier do the work without personal data of your customers or staff and without access to your systems, networks or facilities? A description that says too little to tell is a no. | route: 0.8 or more approves; less opens a case |
The category decides which questions the supplier answers: a software vendor that picks Services skips the security step, and fits_category catches it. A supplier that will reach personal data or your systems, or a description the model cannot tell from, goes to procurement rather than through (Routing on an answer). The model never declines a supplier, never approves one the rules sent to procurement, and is never asked about sanctions, owners, company registries or politically exposed persons: those stay with the lists and the rules.
What the vendor sees
Through the install, OpenAI receives the step's sentence and its two questions: the category and the supplier's description. Nothing else reaches it: not the legal name, the country, the owners, the contract value or the certificate; no document or image reaches a model (Data protection). A supplier the rules decline is never sent. The one-click install calls OpenAI's Responses API with storing off.
The supplier writes the description and may write instructions into it: at worst, an answer the model was talked into lets through a supplier every rule approved, as the rules alone would (Prompt injection).
The chatgpt install
A pack never carries an install: its steps call one by name, so the pack declares it, "installs": { "chatgpt": "chatgpt" }, the platform's ChatGPT (OpenAI API). The import reports chatgpt missing (--publish publishes all the same), and the admin console's import result installs it in one click, with a preset configuration (an OpenAI connection through the Responses API, the console's first suggested model, OpenAI's hosts approved) and a sample key in place of yours. Store your OpenAI API key on the install's page (Configuration › Keys › openaiApiKey) before the workflow runs. Through the API, create the install as Installing a model app shows, named chatgpt.
Until you do, the run fails: OpenAI refuses the sample key, so every supplier the rules do not decline fails at supply_check, with no case and no decision. A model call that fails once the key is in does the same: an outage that outlasts the retries, an exhausted quota or a refusal fails the run at supply_check, and so does a workspace without any install named chatgpt. The vendor's answer shows in the invocation record and under the run's app calls; once the call can succeed, replay the run from supply_check (Operate › Runs, Replay from step), and the supplier is not asked to fill in the questionnaire again. To ask Claude or Gemini instead, install that app under a name of your own and name it in the step's app and in the pack's installs: every model app answers decide in the same shape.
What a reviewer sees
A procurement_review case with the questionnaire, each rule's result and the model's answers (supplyCheck, each with its probability) in the run context; the model's call is under Vendor checks. An export of the audit trail with its digest serves the audit later.
How to adapt it
Ask the model what your procurement policy weighs, such as subcontractors or offshore work, keep each question phrased so that a yes lets the supplier through, and move the 0.8 in route together with the question's threshold. Load the sanctions lists you screen suppliers against into supplier_sanctions_list, or screen through a vendor: install the platform's http-api for it, add a call_app step between collect and rules on submission.legalName and submission.country with outputKey: "companyScreening", and make supplier_sanctions_clear a comparison that passes when the vendor's verdict, such as companyScreening.body.hit, is false (Adding a vendor step). Screen each owner too, add a call_app step that creates the supplier in your ERP through an http-api install for its API once approved, and have your own scheduler start a screening run for every active supplier each year.
Run it
bash
pnpm policy:import examples/policies/supplier-due-diligence --publishThe check runs two fixtures against the model stub (scripts/ai-stub.mjs), whose answers a cue word in the supplier's description picks. expect.json registers Polar Supplies Inc., a goods supplier incorporated in Antarctica (the placeholder on the country list), with declared owners: the company is not on the sanctions list and the country-risk rule fails, so the supplier goes to procurement whatever the model says; cued high, the stub answers both questions yes at 0.95, a procurement_review case opens, the check approves it and the run completes with a manual approve. expect-approved.json registers Cascade Packaging Co., which ships boxes to a distribution center in Reno, Nevada: every rule passes, the stub answers both questions yes at 0.95, and the run completes with an automated approve, no case opened.