Drug Procurement — a study whose run happens somewhere else¶
studies/drug_procurement/ · reference: true · external MARL pipeline
(AgentProcure)
How do government, enterprise and hospital policies optimize centralized drug
procurement (集采) strategy? The case is one procurement game cell of China's
second national volume-based procurement batch: adefovir dipivoxil tablets
(drug_68), round 2, three competing manufacturers. But the reason this page
exists is not the pharmacology. It is the mechanism: a study whose actual run
belongs to a heavy external pipeline, and which still plugs into the same
artifacts, the same discovery fields and the same dashboard. Everything is
materialized here except the training itself.
P / E / B¶
| what it is | where it comes from | |
|---|---|---|
| P | 5 agents — government, hospital, enterprise_1..3 — all with learnable: true and their own policy_group. Enterprise attributes are real (anonymized) workbook rows for the adefovir case: shared Pmax 1.08 yuan/tablet, Q0 2893.17, Qe 3471.804, but heterogeneous beta_i / omega (0.265/0.50, 0.470/2.00, 0.265/0.50). Interaction is explicit_network (government↔each enterprise, government↔hospital, hospital↔each enterprise). |
population/population.json |
| E | Four layers, no scheduled events, no broadcasts — plus a case-specific game block. |
environment/environment.json |
| B | RL policies, not LLM agents. Government Box(3) (Pmax scale, rho, omega); enterprise Box(1) (a normalized bid mapped into (cost_i, Pmax)); hospital Box(2) (report-next ratio, execution rate). |
the game block + model.py |
| layer | modality | scope | dynamics | content |
|---|---|---|---|---|
policy_variables |
physical | macro | scheduled | government-side Pmax, rho, x, omega |
enterprise_market_state |
physical | local | endogenous | cost, beta_i, bid, profit, selection status |
hospital_execution_state |
physical | local | endogenous | hospital execution / reporting variables |
procurement_notice |
information | macro | scheduled | evidence-bound case and run-mode notes |
The game block is case-specific, not part of the common schema
Alongside the standard layers / scheduled_events /
information_program, this study's environment.json carries a game
object: game_type: general_sum, timing: simultaneous,
observability: partially_observable, api_target:
pettingzoo_parallel, gamma_default: 0.99, plus typed roles,
observations, action spaces, rewards and a termination clause. Treat it as
a case-specific extension — the environment schema every other study
uses does not include it. It is what makes a Markov game expressible
inside the same artifact.
Rewards are deliberately in three different units: social welfare (utility) for government, profit (yuan) for enterprises, execution utility for the hospital. They must be read per-role, never summed — that is what "general-sum, mixed-motive" means in practice.

One step, and the horizon¶
n_steps: 50 is one procurement episode, and the axis is a training
episode, not calendar time. That is the sharpest departure from every other
case on these pages: a step advances the game, and the whole 50-step episode is
replayed thousands of times while the policies learn.
Training config (simulation/simulation.json): PPO on RLlib, ctde: true,
gamma: 0.99, train_episodes: 10000, eval_episodes: 1000, one policy per
role and one per enterprise. Winner selection uses soft_topk during training
(differentiable, learnable) and hard_topk at evaluation (lowest bids win) —
which is exactly why selection_accuracy exists as its own metric rather than
being averaged into returns.
Results¶
There are none — and that is the point
reports/report.md is explicitly a pre-run, setup-only report:
0 training episodes, 0 evaluation episodes. There is no trajectory/
directory and no study.duckdb. The external AgentProcure checkout and
its heavy dependencies (pettingzoo / ray[rllib] / gymnasium / torch) are
not installed in this workspace.
What ships instead is a bilingual run_note on study.yaml, which the
workflow surfaces to the reader:
This case runs through the external AgentProcure multi-agent RL pipeline (ray + torch, a heavy training job). The hosted service does not run the training — to produce a real run, clone the repo and run it locally (
AgentProcure/run_training.py), then import the trajectory viastudies/drug_procurement/adapter/socioverse_export.py.
So the workflow for this study is: materialize here → train there → import
back. Everything a reader needs to judge the study — the game structure, the
t=0 roster, the grounding ledger, the figures — is committed and reviewable
before a single GPU-hour is spent. When the training does run, the exported
trajectory lands in the same study.duckdb shape as every other study, and the
same report and dashboard paths apply, with the four display_metrics
(government_return, enterprise_return, hospital_return,
selection_accuracy).
The setup report still carries findings, because structural findings do not
need a trajectory: the population size is not arbitrary but pinned by the
≥3-manufacturer eligibility rule; enterprise_2 is the structural outlier on
both beta_i and omega simultaneously, which predicts two separable
strategy paths (high-quality/high-cost vs low-price/high-volume) to be
confirmed or refuted by an actual run.
Grounding¶
Five sourced facts, one proxy fact, three assumptions, two implementation
references — plus something no other study in the catalog has: an evidence[]
array inside resources.json, carrying typed engineering evidence
(PettingZoo parallel API contract, Gymnasium spaces, RLlib multi-agent policy
mapping) with supports[], confidence and retrieved_at fields, referenced
by evidence_ids from inside the game block.
| entry | basis | role |
|---|---|---|
| ≥3 manufacturers required for a variety to enter the batch (NHSA) | sourced |
pins role_counts.enterprise = 3 and n_applicants = 3 |
| average price cut of winning products: 53% | sourced |
the empirical envelope for how aggressive a learned bid may plausibly be |
| maximum single-variety price cut: 93% | sourced |
the upper edge of that envelope |
| 32 varieties / 100 products won in the batch | sourced |
scope context: this study is one cell of that batch |
| winning results took effect 2020-04 | sourced |
anchors round 2 to the Jan-2020 bid / Apr-2020 execution cycle |
| adefovir being among the second-batch varieties | proxy |
drug identity and qualitative price level come from an industry-press roundup; no official public bulletin for this specific winning bid was found |
| enterprise cost / elasticity / quality parameters from the anonymized workbook | assumed |
real bid costs are not public; values are simulator inputs, not claims about real firms |
a single illustrative drug_68 × round 2 cell |
assumed |
a readable whole game, not a batch-wide panorama |
| results are valid only as within-simulator paired differences | assumed |
the strongest of the three: no real-world causal policy conclusion may be drawn |
This is the catalog's clearest basis: proxy
sourced means an authoritative source states the value. assumed means
nobody states it and the study declares its own choice. proxy is the
middle case that people usually skip: the claim is supported, but only by
a substitute of lower evidentiary standing — here, press coverage standing
in for an official bulletin. Recording it as proxy rather than quietly
promoting it to sourced is the whole discipline in one field. See
Grounding & Provenance.
Note also how the third assumption constrains interpretation, not just inputs. A study can declare the limits of its own conclusions in the same ledger it declares its constants.
The single case sits inside a much larger research dataset — the AgentProcure
project's NVBP corpus of several hundred drugs across procurement rounds,
firm types and bidding outcomes — which is what drug_id / round_id /
focus_drugs select from:

What you can fork¶
| param | the question it opens |
|---|---|
drug_id |
the same game machinery on another variety — does enterprise heterogeneity look structurally similar across drugs? |
round_id |
the same variety across procurement batches. |
timestep |
where inside the 50-step episode you slice. |
focus_drugs |
a multi-cell comparison rather than a single game. |
data_path |
point the case resolver at a different workbook. |
shock_id |
the counterfactual axis — see below. |
--shock-id all runs 15 counterfactual scenarios: 6 enterprise-cost shocks
(enterprise_cost_down__cost_scale_{1p5,1p2,1p1,0p9,0p8,0p5}), 6
hospital-demand shocks (hospital_demand_up__q0_scale_{…} with the same six
factors), and 3 government single-winner scenarios
(gov_single_winner__single_winner_x_{1,2,3}). The last group is the direct
mechanism-design experiment: how do all three roles' returns move when the
number of winners changes? Read every one of those as a paired difference
inside the simulator — that limit is declared in the grounding ledger, not a
footnote.