Implementing Effective Safety Filters in a Game-Based LLM Application
jobifai · Media & Entertainment · 2025 · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.6 (schema v2)
Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.
Problem
Domain: interactive LLM game safety, structured output reliability, and production error handling
JOBifAI needed to turn free-form player input into structured in-game outcomes while handling malformed JSON, schema failures, and opaque safety-filter refusals that all surfaced as similar API errors.
Constraints:
- players can enter arbitrary and potentially unsafe text
- the game expects JSON output that can be parsed and routed into gameplay logic
- LLM responses can be malformed or fail schema validation
- safety-filter rejections return the same 400-style failure as technical output errors
- interactive gameplay cannot simply show users frequent backend errors
- retrying failures increases cost and can be abused by adversarial users
- the project was a free proof of concept rather than a full commercial product
Architecture
JOBifAI structures each player interaction as a prompt containing game context, the player's action, potential action categories, and an instruction to return a JSON dictionary of the form {"choice": c, "sentence": s}. The application validates the result, attempts type casting for minor schema deviations, and retries failed calls up to three times because invalid JSON, schema mismatch, and safety-filter refusal are not distinguishable enough for tailored handling. The game also encodes safety at the application/narrative layer: if a player asks for something wildly unsafe, the secretary character calls security and the game ends, so inappropriate behavior has an in-world consequence rather than relying solely on model-level safety filters.
Diagram confidence: reconstructed (reconstructed from prose).
flowchart LR Player_input["Player input"] --> Prompt_builder["Prompt builder"] Prompt_builder --> LLM_API["LLM API"] Provider_safety_filter["Provider safety filter"] --> LLM_API LLM_API --> JSON_parser_and_schema_validator["JSON parser and schema validator"] JSON_parser_and_schema_validator --> Retry_controller["Retry controller"] Retry_controller --> LLM_API JSON_parser_and_schema_validator --> Game_outcome_router["Game outcome router"] Game_outcome_router --> Narrative_safety_handler["Narrative safety handler"] Narrative_safety_handler --> Player_input
Components
| Component | Role | Interacts with |
|---|---|---|
Player input |
Free-form action text entered by the player during the interview scenario. | Prompt builder |
Prompt builder |
Assembles context, player action, allowed action categories, and structured-output instruction. | LLM API |
LLM API |
Generates the choice and sentence used by the game, subject to provider safety filters and output unreliability. | JSON parser and schema validator |
JSON parser and schema validator |
Parses the model response, tries minor casts, and checks whether the output matches the expected schema. | Retry controller, Game outcome router |
Retry controller |
Retries failed calls up to three times because technical failures and safety rejections are hard to distinguish. | LLM API |
Game outcome router |
Applies the parsed choice to gameplay or routes unacceptable behavior to an in-world safety consequence. | Narrative safety handler |
Narrative safety handler |
In-game secretary/security response that ends the game when the player crosses social/safety boundaries. | Player input |
Provider safety filter |
Opaque upstream filter that can reject unsafe-looking queries with the same error class as technical failures. | LLM API, Retry controller |
Data flow
A player enters an action. The game prompt combines the current scenario, the action, allowed outcome categories, and a JSON schema instruction. The LLM returns a candidate choice and sentence. The application parses JSON, casts minor type mismatches where possible, checks schema conformance, and either applies the outcome to the game or retries up to three times on failure. For clearly unacceptable player behavior, game logic routes the result to a narrative safety outcome such as security ejecting the player.
"The prompts have this general form:
- Here are the potential actions that can happen:"
Why this architecture
The system uses structured outputs because game logic needs a machine-readable action category and narrative sentence. It uses retries because LLM output and safety filters fail often enough that failing immediately would degrade the user experience. It uses application-level safety consequences because the game can respond more predictably to unacceptable player behavior than an opaque provider refusal can.
3 supporting quotes
Models used: LLM-based gameplay model · provider safety filters · possible simple word-vector pre-filter heuristic
Scale
- The retry policy attempts failed queries up to three times.“the current mechanism to bypass these problems is to retry asking 3 times.”article, stated
- The authors report approximate playtesting success rates of 75%, 90%, and 99% for one, two, or three attempts.“Answer success rate, corresponding to 1, 2, or 3 retries, is roughly 75, 90, 99%.”article, stated
- The game was released as a free proof of concept.“But it is only a Proof of Concept that we released for free”article, stated
Alternatives considered (roads not taken)
Trust the first LLM response
Rejected because: LLM output is unreliable enough that a single attempt would produce frequent invalid JSON, schema failures, or safety-filter errors.
Return an error to the player whenever a call fails
Rejected because: Technical failures were too frequent and would significantly degrade the user experience.
Rely solely on provider safety filters
Rejected because: Safety rejections are opaque and indistinguishable from technical failures, while the game's narrative can handle unsafe behavior directly.
Retry indefinitely for technical failures and reject safety failures
Rejected because: The API does not provide enough granular error information to separate technical failures from safety rejections.
Patterns
structured-output-contract (data-contract)
The prompt asks the model to return a JSON dictionary with a specific choice/sentence schema that game logic can process.
fallback-degradation (reliability)
The application retries failed LLM calls up to three times to mask invalid JSON, schema failures, and transient safety-filter behavior.
safety-policy-enforcement (safety)
Unsafe player behavior is handled by game logic as a socially realistic game-over state rather than asking the LLM to produce unsafe content.
cost-aware-routing (operations)
The developers reason explicitly about retries multiplying cost and consider simpler heuristics to pre-filter adversarial inputs.
Tradeoffs
Three retries instead of immediate failure
Chose bounded retry loop over showing an error after the first failed LLM call.
Rationale: Retries improve observed success from roughly 75% to 99%, preserving playability despite extra cost.
Narrative safety consequence
Chose secretary calls security and instant game over over letting the model answer or relying only on provider refusal.
Rationale: The in-game response is realistic for the scenario and avoids needing the LLM to provide unsafe content.
Structured action categories
Chose predefined action categories and JSON output over free-form generated gameplay outcomes.
Rationale: Game logic needs a bounded choice field to route the player's action into a concrete outcome.
Heuristics left as possible future pre-filtering
Chose retry-based handling for the proof of concept over adding word-vector safety pre-filters immediately.
Rationale: Simple heuristics might reduce adversarial load but introduce complexity and remain imperfect.
Gotchas (what bit them)
A 400 error could mean multiple unrelated failure modes.
Root cause: Invalid JSON, schema non-conformance, and safety-filter rejection all produce similar API responses.
Resolution: Retry failures up to three times, while arguing for more granular API error codes in future provider implementations.
Structured output instructions do not guarantee valid structured output.
Root cause: The model can emit invalid JSON or valid JSON that does not conform to the schema.
Resolution: Parse, cast minor type deviations where possible, validate schema, and retry failed generations.
Safety filters can be less predictable than ordinary model answers.
Root cause: The article claims safety triggers are often unreliable and can fire on queries that merely discuss unsafe content.
Resolution: Use application-level behavior handling and request provider-side error-code clarity rather than treating all safety outcomes as reliable moderation signals.
Retries create cost uncertainty and abuse risk.
Root cause: Adversarial users can trigger safety filters repeatedly, causing extra API calls and unpredictable cost.
Resolution: Consider pre-filter heuristics and account for retries in business/cost planning, though the article notes heuristics add complexity.
The proof of concept was shippable, but not trusted enough for a full product.
Root cause: The developers considered the LLM foundations too unreliable for full-fledged commercial development.
Resolution: Release as a free proof of concept and treat the reliability limitations as a product boundary.
Evaluation
The article reports playtesting observations rather than a rigorous benchmark. It estimates answer success rates after one, two, and three attempts, observes frequent technical and safety-filter failures, and qualitatively assesses that the proof of concept worked but was too unreliable for full product development.
Metrics: roughly 75% success after one attempt · roughly 90% success after two attempts · roughly 99% success after three attempts · three main failure modes · proof-of-concept release decision
3 supporting quotes
Outcomes
- JOBifAI shipped as a working free proof of concept.
- The retry mechanism achieved approximately 99% observed success after three attempts.
- The game handled unacceptable player input with an in-world game-over consequence.
- The developers concluded the unreliable LLM/API foundations deterred them from building a full-fledged product.
The case has small-project and proof-of-concept scope. Its most reusable value is the explicit taxonomy of LLM output failures and the warning that cost and UX planning must account for retries and safety-filter opacity.