AGENTS.md@infrastructure/llm/prompts/compositions · git:20260612.8db3bb9 · 2026-06-12 · sha256 7b1f29da193340f7

AGENTS.md@infrastructure/llm/prompts/compositions git:20260612.8db3bb9A

Immutable. This exact content is served forever at /api/v1/blob/7b1f29da193340f7.

# LLM Prompt Compositions

## Overview

The `infrastructure/llm/prompts/compositions/` directory is a **data-only** directory
containing JSON files. There is no Python package here. Compositions are pre-written
reinforcement strings loaded by `PromptFragmentLoader.load_composition()` and
prepended to an existing prompt by `PromptComposer.add_retry_prompt()` when an LLM
response fails a quality or relevance check.

## Directory Structure

```mermaid
flowchart LR
    C[infrastructure/llm/prompts/compositions/]
    C --> AG[AGENTS.md]
    C --> RP[retry_prompts.json<br/>Error recovery &amp; format enforcement]

    classDef d fill:#0f172a,stroke:#0f172a,color:#fff
    classDef code fill:#1e3a8a,stroke:#0f172a,color:#fff
    classDef doc fill:#0f766e,stroke:#0f172a,color:#fff
    class C d
    class RP code
    class AG doc
```

## Public API

Compositions are accessed through `PromptFragmentLoader` and `PromptComposer`,
both exported from `infrastructure.llm.prompts`:

```python
from infrastructure.llm.prompts import PromptFragmentLoader, PromptComposer

loader = PromptFragmentLoader()
composer = PromptComposer(loader=loader)

# Load a composition entry by reference
entry = loader.load_composition("retry_prompts.json#off_topic_reinforcement")
# → {"version": "1.0", "content": "IMPORTANT: You must review..."}

# Prepend off-topic reinforcement to an existing prompt
reinforced = composer.add_retry_prompt(base_prompt, retry_type="off_topic")

# add_retry_prompt constructs: f"retry_prompts.json#{retry_type}_reinforcement"
# and prepends the "content" value before base_prompt.
# If the key is not found, base_prompt is returned unchanged.
```

## `retry_prompts.json` — Schema and Entries

### Top-level schema

```json
{
  "entry_name": {
    "version": "1.0",
    "content": "Ready-to-prepend reinforcement text.\n\n"
  }
}
```

Nested sub-objects share the same `{"version", "content"}` shape and are
addressed via dot-notation: `"retry_prompts.json#format_enforcement.executive_summary"`.

### `off_topic_reinforcement`

```json
{
  "off_topic_reinforcement": {
    "version": "1.0",
    "content": "IMPORTANT: You must review the ACTUAL manuscript text provided below. Do NOT generate hypothetical content, generic book descriptions, or unrelated topics. Your review must reference specific content from the manuscript.\n\n"
  }
}
```

**When to use:** the LLM generates generic or hypothetical content instead of
analyzing the supplied manuscript. Selected by `add_retry_prompt(prompt, retry_type="off_topic")`.

### `format_enforcement` (nested)

Four sub-keys for targeting specific review types. Selected via
`loader.load_composition("retry_prompts.json#format_enforcement.<sub_key>")`:

| Sub-key | Instruction prepended |
| --- | --- |
| `executive_summary` | Use exact headers: Overview, Key Contributions, Methodology Summary, Principal Results, Significance and Impact |
| `quality_review` | Include `**Score: [1-5]**` in every scoring section |
| `methodology_review` | Include all required sections with proper markdown headers |
| `improvement_suggestions` | Each improvement must include WHAT (the issue) / WHY (why it matters) / HOW (how to address it) |

## How `add_retry_prompt` Works

`PromptComposer.add_retry_prompt(base_prompt, retry_type)` follows this logic:

1. Constructs the reference `f"retry_prompts.json#{retry_type}_reinforcement"`.
2. Calls `self.loader.load_composition(reference)` — raises `LLMTemplateError` if
   the key does not exist.
3. Extracts the `"content"` field from the returned dict (or coerces to string).
4. If content is non-empty, returns `f"{content}\n\n{base_prompt}"`.
5. On `LLMTemplateError`, logs a debug message and returns `base_prompt` unchanged.

## Caching

`PromptFragmentLoader` caches all loaded JSON files in `_fragment_cache` (keyed by
absolute file path). Repeated calls to `load_composition` for the same file do not
re-read disk. Call `loader.clear_cache()` to invalidate.

## Adding New Compositions

1. Add a new entry to `retry_prompts.json` (or create a new `*.json` file in this
   directory) using the `{"version": "...", "content": "..."}` schema.
2. Load it with `loader.load_composition("retry_prompts.json#your_key")`.
3. To integrate with `add_retry_prompt`, name the entry `{retry_type}_reinforcement`
   and pass `retry_type="your_type"` — no Python changes required.
4. For a new file, pass the new filename in the loader reference string. The loader
   resolves it relative to `infrastructure/llm/prompts/compositions/`.

## Integration in the Pipeline

The LLM review scripts (e.g. `scripts/06_llm_review.py`) construct prompts via
`PromptComposer.compose_template()` and may call `add_retry_prompt()` on a second
pass if the first LLM response is rejected by the validation layer. The composition
content is prepended so it appears before the manuscript text in the final prompt.

## See Also

**Related Documentation:**

- [`../AGENTS.md`](../AGENTS.md) - Prompts module overview
- [`../fragments/AGENTS.md`](../fragments/AGENTS.md) - Fragment components
- [`../templates/AGENTS.md`](../templates/AGENTS.md) - Template system
- [`README.md`](README.md) - Compositions quick reference

**System Documentation:**

- [`../../../../AGENTS.md`](../../../../AGENTS.md) - system overview
- [`../../../../docs/operational/troubleshooting/llm-review.md`](../../../../docs/operational/troubleshooting/llm-review.md) - LLM troubleshooting guide