Contents
01Why build this
The single biggest change in pharma promotional-material work over the last 18 months: the first draft is now written by AI. ChatGPT, Claude, Gemini, in-house LLMs — whichever stack — MR-facing materials, HCP explanations, patient IP, internal training material all increasingly start from an AI prompt.
Three inefficiencies have piled up across the industry, however.
- Duplicate reinvention — every company is independently iterating on roughly the same prompt for roughly the same goal. Thirty companies running side-by-side at the bottom of the learning curve
- Good prompts buried — the prompts that experienced reviewers have honed to balance compliance and quality sit in private chats and personal notes, lost when those people leave
- Slow propagation of industry discipline — the latest interpretation of the Pharmaceutical Act, the Sales-Information-Provision Guideline, or the JPMA Code is not shared in prompt form, so violation patterns are independently rediscovered company by company
The conclusion up front. Prompts are "structural thinking", not company secrets. The output (the actual material) is still reviewed under each company's own responsibility, so opening the prompts does not lower compliance. It lifts compliance sensitivity across the whole industry. This proposal is for a system that operates prompt openness and continuous improvement as a shared public good for the industry.
02What we are building
A web platform with six elements.
Prompt catalog
Searchable, taggable, browsable catalog of industry-shared prompts. Each prompt written in Markdown.
Version control
Each prompt has a Git-like history. Who changed what, when, traceable. Forkable.
Discussion threads
A discussion thread on each version. Compliance feedback, improvement proposals, field reports meet here.
Rating system
Votes, reactions, adoption reports. Community collective intelligence selects quality.
Moderation
Automatic scan for clear violations (Pharmaceutical Act §66/§68, etc.) plus human flags. Policy detailed below.
AI improvement
(Later phase) The AI learns from discussions and generates improvement proposals. A distillation of industry knowledge.
These elements together form a continuous, industry-wide discussion cycle started from a prompt.
03Why it can be opened
The first objection: "We won't share ours. It's competitive advantage." Three-step answer.
Step 1 — where the advantage actually lives: a pharma company's competitive advantage lives not in the prompt itself but in (a) clinical data, (b) understanding of the target patient, (c) MR field knowledge, (d) internal review for output materials. The prompt is just the vessel that expresses these. Sharing the vessel does not share what's inside.
Step 2 — gains from opening: a company that publishes its prompt gets feedback from the entire industry. Compliance angles, expression improvements, unseen risks. One head works inside one company; a hundred heads work on a published prompt.
Step 3 — managing the risk of opening: confidential parts can be abstracted with variables like {{PRODUCT_X}}. The platform's guidelines forbid embedding specific pre-approval information, trial data, or commercial secrets. Automatic pre-publication scans catch lapses.
04Use scenarios
Scenario A — veteran reviewer publishes their own prompt
Y, a veteran at pharma X, publishes "a prompt that structures MR-facing explanations without implying off-label use." Y has three years of internal use without a §66 violation flag. On publication, Y attaches an anonymized operational log summary.
Scenario B — a newcomer learns from an industry-shared prompt
W, a newcomer at pharma Z, has no company prompt of their own. On Prompt Commons they compare 12 prompts under the "MR foundational material" tag, read the top three by vote, and follow the discussion threads. The discussion threads become the textbook.
Scenario C — a compliance lead feeds back industry discipline
V, a JPMA Code interpretation lead, comments on the discussion thread of prompt P-218: "The expression here conflicts with the latest interpretation of JPMA Code §3.1." The original author and other users discuss; a forked improvement, P-218.v2, appears. Industry discipline propagates not as a document but as a prompt diff.
Scenario D — an AI developer maps industry needs
U, a developer at an LLM provider, follows discussions tagged "Pharmaceutical Act compliant" and "Sales-Info Guideline compliant" to see what the industry wants from AI and where it is stuck. The next-generation model's fine-tuning direction tracks industry need.
05Core features
Functions organized by role.
| Area | Capabilities | Role |
|---|---|---|
| Catalog | Search, tag filter, sort (popular / new / active discussion), related-prompt recommendation | Everyone |
| Authoring | New prompt, edit, Markdown preview, variable definition, sample-output attachment | Author |
| Versioning | Edit history, diff view, branch (fork), merge proposal (PR-equivalent) | Author, editor |
| Discussion | Threaded comments, Markdown support, quoting, reactions (👍 / ⚠️ / 💡) | Everyone |
| Rating | Vote (+/-), adoption report ("in production", "piloting", "modified", "declined"), reputation | Everyone |
| Compliance | Automatic scan (§66/§68 pattern detection), community flag, moderator action, removal log | Moderator, all |
| Notifications | Watch, email / web push, new comment / new version | Everyone |
| User | Pseudonym account, optional org disclosure, COI declaration, reputation history | Everyone |
| API | REST + WebSocket, external tool integration, data export | Developer |
06Architecture
When picking the tools (the technology), three things came first: (a) it can run on the server we already rent (a VPS, meaning a single machine rented for our own use), (b) we can start small and add capacity later, (c) the monthly running cost stays low.
What follows is a diagram split into three layers — the screen, the processing, and the data storage — and the tools used in each. Some of the names will be unfamiliar, but each comes with a one-line note on what it is for. If a name is hard to place, skip it; picking up the role is enough.
Overall
┌─────────────────────────────────────────────────────────────┐ │ Frontend (Next.js 14 App Router, TypeScript, Tailwind) │ │ - SSR for SEO + Client interactivity │ │ - Markdown editor (CodeMirror 6) │ │ - Diff viewer (react-diff-viewer-continued) │ └──────────────────┬──────────────────────────────────────────┘ │ HTTPS / WebSocket ┌──────────────────┴──────────────────────────────────────────┐ │ API (FastAPI + Pydantic, Python 3.12) │ │ - REST endpoints │ │ - WebSocket (real-time comments) │ │ - Background jobs (Celery + Redis) │ └──────────────────┬──────────────────────────────────────────┘ │ ┌──────────────────┴──────────────────────────────────────────┐ │ Storage Layer │ │ - PostgreSQL 16 (primary data) │ │ - Redis 7 (cache, sessions, job queue) │ │ - S3-compatible object store (attachments, exports) │ │ - Meilisearch (full-text search; Phase 2) │ └─────────────────────────────────────────────────────────────┘ Compliance Scan Service (separate microservice) - Pattern matching (regex + rules) - LLM-based review (Phase 3+) Notification Service - Email (SES / Resend) - Web Push (VAPID) Auth: NextAuth + Email OTP / OIDC (pseudonym supported)
A word on the less familiar names in the diagram. WebSocket keeps a line open between the server and the browser so that a new comment reaches the screen the instant it is posted (ordinary traffic only answers when asked; this one speaks up on its own). Redis is a small fast holding area that keeps frequently used data close at hand for quick retrieval. Celery is the worker that handles slow jobs (sending email, running automatic checks) in the background. None of these are visible to the user; they are the stagehands that keep the display fast and the waiting short.
Hosting
- Frontend: Vercel or a Node process on the same VPS behind nginx
- API + DB: existing VPS via Docker Compose at first; split out at scale
- CDN: reuse the existing Cloudflare configuration
- Auth: self-hosted NextAuth
Why these choices
| Choice | Why |
|---|---|
| Next.js | The tool for building the screen. It assembles the page on the server first (this is SSR) and sends it, so search engines can read the content easily. Once it arrives, it comes to life in the browser (this is hydration, the step that adds interactive behavior to a still page), so buttons and inputs feel smooth. Since this is a place meant to be found through search, that fit matters. |
| FastAPI | The tool for building the counter (the API) that connects the screen and the data. Deciding the "shape" of the data up front cuts mistakes, and a spec sheet (a list of what counters exist) is generated automatically. |
| PostgreSQL | The box that stores the data (the database). Prompts, their versions, and comments link together in tangled ways, so we chose one that is good at such linking. It also has soft-shaped data storage (JSONB) and whole-text searching (full-text search) built in, which is plenty at launch scale. |
| Meilisearch | A search-only tool. It can split a Japanese sentence into words to look things up, so it is added in a later stage (Phase 2). |
| Markdown | A light way of writing using only simple marks. It is already widely used for writing prompts and pairs well with change-history tracking. |
07Data model (DB schema)
This is the design for which "tables" the data gets split into. There are eleven main tables, placed on top of the storage box mentioned earlier (PostgreSQL 16). The code below is the instruction sheet for creating those tables (written in SQL, the way you give commands to a database). If you are not an engineer, you do not have to read the code itself. Picking up the comments on the right of each line (the users table, the prompt-body table, the discussion table, and so on) is enough to grasp the overall picture of what gets stored and how.
-- Users and organizations -- CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), pseudonym TEXT NOT NULL UNIQUE, -- public name (pseudonym OK) email_hash TEXT NOT NULL UNIQUE, -- SHA-256 of email (login key) display_name TEXT, -- optional real name org_id UUID REFERENCES orgs(id), -- optional org disclosure coi_text TEXT, -- COI declaration reputation INTEGER NOT NULL DEFAULT 0, role TEXT NOT NULL DEFAULT 'member', -- member / mod / admin created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), status TEXT NOT NULL DEFAULT 'active' -- active / suspended / banned ); CREATE TABLE orgs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL UNIQUE, type TEXT, -- pharma / consulting / academic / regulator verified BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Prompt -- CREATE TABLE prompts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug TEXT NOT NULL UNIQUE, title TEXT NOT NULL, summary TEXT, current_version_id UUID, owner_user_id UUID NOT NULL REFERENCES users(id), fork_of_prompt_id UUID REFERENCES prompts(id), license TEXT NOT NULL DEFAULT 'CC-BY-4.0', status TEXT NOT NULL DEFAULT 'published', view_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_prompts_owner ON prompts(owner_user_id); CREATE INDEX idx_prompts_status ON prompts(status) WHERE status = 'published'; CREATE TABLE prompt_versions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), prompt_id UUID NOT NULL REFERENCES prompts(id), version_label TEXT NOT NULL, content_md TEXT NOT NULL, changelog TEXT, parent_version_id UUID REFERENCES prompt_versions(id), author_user_id UUID NOT NULL REFERENCES users(id), sample_output_md TEXT, variables_json JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(prompt_id, version_label) ); CREATE INDEX idx_versions_prompt ON prompt_versions(prompt_id); -- Discussion -- CREATE TABLE discussions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), prompt_version_id UUID NOT NULL REFERENCES prompt_versions(id), title TEXT NOT NULL, opened_by_user_id UUID NOT NULL REFERENCES users(id), status TEXT NOT NULL DEFAULT 'open', category TEXT, -- compliance / improvement / question / report comment_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE comments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), discussion_id UUID NOT NULL REFERENCES discussions(id), parent_comment_id UUID REFERENCES comments(id), author_user_id UUID NOT NULL REFERENCES users(id), content_md TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'visible', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), edited_at TIMESTAMPTZ ); CREATE INDEX idx_comments_discussion ON comments(discussion_id); -- Rating -- CREATE TABLE votes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), target_type TEXT NOT NULL, target_id UUID NOT NULL, user_id UUID NOT NULL REFERENCES users(id), value SMALLINT NOT NULL CHECK (value IN (-1, 1)), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(target_type, target_id, user_id) ); CREATE TABLE reactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), target_type TEXT NOT NULL, target_id UUID NOT NULL, user_id UUID NOT NULL REFERENCES users(id), emoji TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(target_type, target_id, user_id, emoji) ); CREATE TABLE adoptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), prompt_id UUID NOT NULL REFERENCES prompts(id), user_id UUID NOT NULL REFERENCES users(id), status TEXT NOT NULL, -- 'in_production' / 'piloting' / 'modified' / 'declined' notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Tags -- CREATE TABLE tags ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL UNIQUE, type TEXT NOT NULL -- 'topic' / 'regulation' / 'material_type' / 'language' ); CREATE TABLE prompt_tags ( prompt_id UUID NOT NULL REFERENCES prompts(id), tag_id UUID NOT NULL REFERENCES tags(id), PRIMARY KEY(prompt_id, tag_id) ); -- Moderation -- CREATE TABLE moderation_flags ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), target_type TEXT NOT NULL, target_id UUID NOT NULL, reporter_user_id UUID REFERENCES users(id), -- NULL when from auto-scan reason_code TEXT NOT NULL, reason_text TEXT, status TEXT NOT NULL DEFAULT 'open', resolved_by_user_id UUID REFERENCES users(id), resolution_notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), resolved_at TIMESTAMPTZ ); -- Audit log (append-only) -- CREATE TABLE audit_log ( id BIGSERIAL PRIMARY KEY, actor_user_id UUID REFERENCES users(id), action TEXT NOT NULL, target_type TEXT NOT NULL, target_id UUID NOT NULL, payload JSONB, ip_hash TEXT, user_agent TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_audit_target ON audit_log(target_type, target_id);
Key invariants
- Audit log is append-only. No deletes or updates (enforced by PostgreSQL role)
- Reputation is recomputable from SUM(votes WHERE target == user's content). Stored as cache
- prompts.current_version_id points only to a published version. Drafts are tracked separately
- Removed content uses status='removed'. No physical delete, so discussion history stays coherent
08API design
The counter through which outside tools and apps exchange data with this service is called an "API". Two kinds are provided here. One is REST, a very ordinary set of manners for the exchange (it fixes "go to this address and this data comes back"). The other is the WebSocket mentioned earlier, used to deliver new discussion comments on the spot. The list of counters (which address returns what) is written out automatically as a spec sheet (in a shared format called OpenAPI 3.1).
Endpoints (REST)
Prompts
Versions
Discussion
Rating
Moderation
WebSocket (real time)
# Connect wss://prompt-commons.example.com/ws # Subscribe (when a discussion page opens) { "type": "subscribe", "channel": "discussion:<discussion_id>" } # Pushed events { "type": "comment.created", "data": {...} } { "type": "comment.edited", "data": {...} } { "type": "reaction.added", "data": {...} } { "type": "version.published", "data": {...} }
Auth flow
- Sign-up: email → OTP → pick a pseudonym → done
- Login: email → OTP → session cookie (HttpOnly, Secure, SameSite=Lax)
- API auth: session cookie (web) or JWT bearer (external tools)
- Org verification: optional; auto-verified via org-domain email
Rate limits
| Class | Anon | Logged in |
|---|---|---|
| GET (reads) | 60 req/min | 300 req/min |
| POST (authoring) | — | 10 req/min, 100 req/day |
| POST (rating) | — | 60 req/min |
| POST flag | — | 5 req/hour |
09Moderation
Three layers: automatic → community → moderator. Machines catch the clearly violative, the community surfaces the ambiguous, moderators decide the close calls.
Layer 1 — automatic scan (pre-publication)
On new prompt or comment, run:
- Regulatory patterns: Pharmaceutical Act §66 (exaggerated advertising) and §68 (pre-approval advertising) — regex + a term dictionary
- Hard-coded product names: prompt the author to use variables
- Unapproved-info mentions: combination of "unapproved" / "off-label" tokens
- PII / clinical-data leaks: SSN, email, CT.gov IDs, etc.
- Spam / duplication: hash + Jaccard similarity
Behavior on hit:
- Certain violation (high confidence): block, show reason
- Suspected (medium): warn + save as draft. The author can fix or push through (push-through goes to moderator queue)
- Minor (low): publish but mark for review
Layer 2 — community flag
Anyone can flag content via POST /flags. Reasons:
| Code | Meaning | Handling |
|---|---|---|
| pharma_act_66 | Pharmaceutical Act §66 (exaggeration) | To moderator queue |
| pharma_act_68 | §68 (pre-approval advertising) | Immediate moderator queue + temporary unpublish |
| jpma_code | JPMA Code concern | Queue + redirect to discussion |
| off_topic | Unrelated to pharma promo | Queue |
| spam | Spam | Auto-hide after 3 flags; moderator review |
| personal_data | PII embedded | Immediate mask + moderator alert |
| other | Other | Queue |
Layer 3 — moderator decisions
Moderators may:
- Remove (status='removed'): hide content; preserve discussion history. URL returns 410 Gone
- Edit: only to remove PII or clearly violative tokens. No editorial revision
- Warn: notice to the author
- Suspend: time-bounded account restriction
- Ban: only for severe and repeated violation
- Receive appeals: authors may appeal moderator decisions; a different moderator re-reviews
audit_log. Monthly statistics (removal counts, flag counts, breakdown) are published.Moderator selection
- Initially: 3 moderators chosen by the editorial team (Advertising Regulation in Japan (Promotional Material Review) and Artificial Intelligence)
- Phase 2+: 5–7, balanced across reputation + industry experience (pharma / regulator / academia)
- Term: 12 months, renewable
- COI: moderators cannot act on content they are connected to
10Governance & license
License
Default license: CC BY 4.0 (free use with attribution). Authors may choose CC0 (public domain) instead. Commercial use is allowed under both — consistent with the "industry shared good" concept.
Operating body
Initially run by Advertising Regulation in Japan (Promotional Material Review) and Artificial Intelligence (this site's editorial). Phase 2+ moves to a cross-industry steering committee:
- 3 from pharma (mixed company sizes)
- 1 from a regulator/related agency (PMDA-style observer)
- 1 from academia
- 2 from the operations team
COI policy
- User profiles include a COI field (optional)
- If a prompt has a specific commercial promotional intent, the author must mark it
- Moderators cannot act on related content
Privacy & data protection
- Real names are optional; pseudonym operation is the default
- Email lives only inside (SHA-256 hash used for matching)
- GDPR / Japan APPI compliant
- Deletion requests (GDPR Art. 17) are honored
11Phased rollout
Launch in stages; bring in real-use feedback at every stage.
MVP — static catalog + discussions on existing Mattermost
Stand up a sub-section /prompt-commons/catalog/ inside this very site. Prompts as Markdown files (Git). Discussions link out to an existing Mattermost channel. Zero spend, used to validate demand.
- Catalog 10 seed prompts
- Each prompt links to its Mattermost thread
- Adoption reports collected via Google Form
Basic features — web authoring + version control
Minimum Next.js + FastAPI + PostgreSQL stack stood up. Authoring, versions, and search work.
- Auth (OTP), profiles
- Prompt CRUD, tags
- Version control + diff view
- Simple full-text search (Postgres FTS)
- Ops: existing VPS + Docker
Discussion + community
Discussion threads, votes, reactions, adoption reports, community flags.
- Threaded comments with Markdown
- Vote / Reactions / Adoptions
- WebSocket for real-time
- Notifications (email + web push)
- Community flag + moderator dashboard
- Meilisearch (JA morphological analysis)
Compliance automation + AI improvement
Production-grade auto-scan (rule + LLM). Discussion-aware AI improvement suggestions.
- Structured rule DB (§66/§68, Sales-Info Guideline, JPMA Code)
- Two-stage scan (regex + LLM) on submission
- Discussion summarizer (long-thread digest)
- Similar-prompt recommendation (embeddings)
- "Suggest an improvement to this prompt"
Fine-tune integration and distillation
Approved high-quality prompts + discussion logs feed an industry-specialized LLM. Hosted LLM API offered as a (paid) option to interested companies.
- Annotate the high-quality corpus
- Domain-adapt a base LLM (e.g., Llama 4)
- Dedicated API gateway
- Publish benchmarks (§66 compliance rate, expression quality)
- Monetization: paid hosted API — funds operations
12Risks & responses
| Risk | Impact | Response |
|---|---|---|
| Confidential leak | High | Pre-publication auto-scan (PII, trial data, pre-approval info) + clear guidelines + recommended variable templating |
| Regulatory-violating prompts | High | Auto-scan + community flag + moderator. Clear breaches: immediate unpublish. Ambiguous: visible discussion |
| Conformity pressure (one prompt becomes "the answer") | Medium | Vote labeled as guidance not verdict; minority views surfaced; fork encouraged |
| Misuse by competitors | Low | Open by design; "misuse" concept is weakened. License keeps attribution |
| Continuity of operations | Medium | Move to cross-industry committee at Phase 2+. Portable data (Markdown + JSON export) |
| Failure to acquire initial users | High | Phase 0: 10 high-quality seed prompts authored by editorial. Decision gate: 50 posts in 3 months |
| Insufficient or biased moderators | Medium | 3 starters → expand by transparent selection. Monthly stats published. Appeals lane |
| Regulator concerns | Medium | Consult PMDA / JPMA at launch. Invite observer participation |
| Low-quality posts | Low | Reputation + Vote naturally sort. Author trustworthiness made visible |
13Success metrics & cost
Success metrics by phase
| Metric | End of Phase 1 | End of Phase 2 | End of Phase 3 |
|---|---|---|---|
| Registered users | 50 | 300 | 1,500 |
| Published prompts | 30 | 200 | 800 |
| Discussion threads | 20 | 500 | 3,000 |
| Avg comments per prompt | 2 | 5 | 10 |
| Adoption reports | 10 | 100 | 500 |
| Fork rate (% of published) | 10% | 25% | 40% |
| Participating orgs (declared) | 5 | 20 | 60 |
Development & operating cost
| Item | Phase 0-1 | Phase 2 | Phase 3+ |
|---|---|---|---|
| Dev effort (person-months) | 0.5 | 2 | 4 |
| VPS / infra | existing (~$0) | $20-40 / mo | $100-200 / mo |
| External APIs (LLM) | 0 | $50 / mo | $500 / mo |
| Moderation labor | editorial | editorial | industry committee (paid) |
| Approximate total | ~ $0 | $70-90 / mo | $600-700 / mo |
Initial decision checkpoint
At Phase 0 end (~ 1 month in), assess:
- 10 seed prompts attracted ≥ 30 total discussion comments?
- ≥ 3 companies expressed "considering adoption"?
- No negative signals from regulators or industry bodies?
If not, defer Phase 1 and re-design or stop. Phase 0's goal is not "build" but "verify it's worth building".
Promotional-material work in pharma has been "a closed-loop craft of expertise inside each company". The AI-prompt era opens a chance to turn that closed loop into shared public good across the industry.
The Prompt Commons proposed here is technically modest — a standard web stack, PostgreSQL relationships, Markdown and Git-like versioning. The hard part is not technical. It is the first few companies showing that opening is possible. Once that lift happens, network effects do the rest.
If, after reading this, you find one prompt at your own company that could be opened, that is the first Phase 0 post. Beginnings are always one post at a time.