# Prompts and templates

> PromptType enum, embedded .tmpl templates, validation via validatePrompt, user prompt CRUD, and how agent templates bind to execution context variables.

- Repository: vxcontrol/pentagi
- GitHub: https://github.com/vxcontrol/pentagi
- Human docs: https://grok-wiki.com/public/docs/vxcontrol-pentagi-eca0cf4de5f5
- Complete Markdown: https://grok-wiki.com/public/docs/vxcontrol-pentagi-eca0cf4de5f5/llms-full.txt

## Source Files

- `backend/pkg/templates/templates.go`
- `backend/pkg/templates/prompts`
- `backend/pkg/graph/schema.graphqls`
- `backend/pkg/server/services/prompts.go`
- `frontend/src/pages/settings/settings-prompts.tsx`
- `backend/docs/prompt_engineering_pentagi.md`
- `backend/pkg/controller/prompter.go`

---

---
title: "Prompts and templates"
description: "PromptType enum, embedded .tmpl templates, validation via validatePrompt, user prompt CRUD, and how agent templates bind to execution context variables."
---

PentAGI drives agents and system helpers through Go `text/template` files under `backend/pkg/templates/prompts/*.tmpl`, selected by a shared `PromptType` string. Embedded defaults ship in the binary; per-user overrides live in the `prompts` table and merge at flow/assistant start. GraphQL is the primary management surface (Settings UI); REST exposes a parallel CRUD path under `/api/v1/prompts`.

## Architecture

```text
  Settings UI / GraphQL mutations          Flow or Assistant start
  ───────────────────────────────          ───────────────────────
  validatePrompt(type, template)           newUserPrompter(userID)
           │                                        │
           ▼                                        ▼
  validator.ValidatePrompt                 LoadDefaultPromptsMap()
  (syntax, allowed vars, dry-run)          + DB GetUserPrompts overlay
           │                                        │
           ▼                                        ▼
  CreateUserPrompt / UpdateUserPrompt      templates.NewFlowPrompter
                                                    │
  Agent / helper runtime                            ▼
  ─────────────────────                    prompter.RenderTemplate(type, params)
  handlers + helpers build map[string]any  → Go text/template Execute
  keys ⊆ PromptVariables[type]
```

| Layer | Role |
| --- | --- |
| `templates.PromptType` | Canonical type strings (`primary_agent`, `question_pentester`, …) |
| `//go:embed prompts/*.tmpl` | Compiled-in default bodies |
| `PromptVariables` | Whitelist of top-level `{{.Var}}` names per type |
| `Prompter` | `GetTemplate` / `RenderTemplate` / `DumpTemplates` |
| `defaultPrompter` | Reads embed FS only |
| `flowPrompter` | Map of defaults + user overrides for a session |
| `validator.ValidatePrompt` | Pre-save checks for custom bodies |
| `prompts` table | One row per `(user_id, type)` customization |

Graphiti uses a separate embed (`graphiti/*.tmpl`) via `ReadGraphitiTemplate`; those files are not user-editable `PromptType` entries.

## PromptType catalog

Types are declared in Go (`templates.PromptType`), GraphQL (`enum PromptType`), Postgres (`PROMPT_TYPE`), and as `prompts/<type>.tmpl` filenames. Current set (39 templates):

| Category | Types |
| --- | --- |
| System-only agents | `primary_agent`, `assistant`, `summarizer` |
| Specialist system + human | `pentester` / `question_pentester`, `coder` / `question_coder`, `installer` / `question_installer`, `searcher` / `question_searcher`, `memorist` / `question_memorist`, `adviser` / `question_adviser`, `generator` / `subtasks_generator`, `refiner` / `subtasks_refiner`, `reporter` / `task_reporter`, `reflector` / `question_reflector`, `enricher` / `question_enricher`, `toolcall_fixer` / `input_toolcall_fixer` |
| Tools / descriptors | `flow_descriptor`, `task_descriptor`, `execution_logs`, `full_execution_context`, `short_execution_context`, `image_chooser`, `language_chooser` |
| Tool-call ID probes | `tool_call_id_collector`, `tool_call_id_detector` |
| Supervision | `question_execution_monitor`, `question_task_planner`, `task_assignment_wrapper` |

`GetDefaultPrompts()` groups these into `AgentsPrompts` and `ToolsPrompts` for the Settings UI and GraphQL `settingsPrompts` query.

<Note>
REST `models.PromptType.Valid()` currently accepts the historical set through `tool_call_id_detector` but not the three supervision types. Prefer GraphQL for editing `question_execution_monitor`, `question_task_planner`, and `task_assignment_wrapper`.
</Note>

## Embedded templates and rendering

Templates use standard Go `text/template` syntax (`{{.Var}}`, `{{if}}`, `{{range}}`, etc.). Bodies are loaded from the embed FS:

```go
//go:embed prompts/*.tmpl
var promptTemplates embed.FS
```

| API | Behavior |
| --- | --- |
| `NewDefaultPrompter()` | Always reads embed FS by `prompts/<type>.tmpl` |
| `LoadDefaultPromptsMap()` | Fresh map of all defaults (safe to mutate for overlay) |
| `NewFlowPrompter(map)` | Session map after user overlay |
| `RenderPrompt(name, body, params)` | `template.Parse` + `Execute` into a buffer |
| `GetTemplate` miss | `ErrTemplateNotFound` |

Empty user rows are skipped during overlay so a blank DB body cannot replace a default and surface as a missing template mid-agent.

## Allowed variables (`PromptVariables`)

Each `PromptType` has an authorized top-level variable list. Custom templates may use **only** those names. Common system-agent variables:

| Variable | Typical content |
| --- | --- |
| `*ToolName` fields | Concrete registry names (`terminal`, `search`, `pentester`, result tools, barrier tools) |
| `ExecutionContext` | Rendered short/full task–subtask narrative |
| `DockerImage`, `Cwd`, `ContainerPorts` | Sandbox environment |
| `Lang`, `CurrentTime` | Engagement language and clock |
| `UserFiles` | Listing of `/work` uploads and resources |
| `SummarizationToolName`, `SummarizedContentPrefix` | Summarizer protocol markers |
| `GraphitiEnabled`, `GraphitiSearchToolName` | Optional knowledge-graph tools |
| `ToolPlaceholder` | Trailing tool-schema injection marker |
| `AskUserEnabled` / flow-manager tool names | Feature-gated assistant/orchestrator tools |

Human / question templates usually take a small set (`Question`, `Task`, `Subtasks`, `Code`, `Output`, …). Context builders (`full_execution_context`, `short_execution_context`) take `Task`, `Tasks`, `Subtask`, `CompletedSubtasks`, `PlannedSubtasks` as real `database.Task` / subtask structs (field access like `{{.Task.Input}}`).

Missing a variable from `PromptVariables` fails validation as `unauthorized_variable`. Using a variable the runtime never fills is allowed at save time only if it is on the whitelist; dry-run uses dummy data that includes the full mock map.

## Validation (`validatePrompt`)

`validator.ValidatePrompt(promptType, prompt)` runs before GraphQL create/update:

1. **Empty** — reject blank/whitespace templates (`empty_template`).
2. **Parse** — AST parse with common template funcs registered; syntax failures → `syntax_error`.
3. **Variable whitelist** — extract top-level fields; any name not in `PromptVariables[type]` → `unauthorized_variable`.
4. **Dry-run render** — `RenderPrompt` with `CreateDummyTemplateData()` (tool name constants, sample tasks, Docker fields, barrier tools). Failure → `rendering_failed` (or type-mismatch messaging).

GraphQL maps those to `PromptValidationErrorType`:

| Enum value | Meaning |
| --- | --- |
| `syntax_error` | Unclosed `}}`, bad actions, etc. |
| `unauthorized_variable` | Variable not declared for this type |
| `rendering_failed` | Execute failed against mock data |
| `empty_template` | No body |
| `variable_type_mismatch` | Reserved / mapped from type mismatch |
| `unknown_type` | Fallback / unknown prompt type |

Permissions: `settings.prompts.edit` for `validatePrompt`, `createPrompt`, `updatePrompt`, `deletePrompt`; `settings.prompts.view` for `settingsPrompts`.

<Warning>
REST `PUT /api/v1/prompts/{promptType}` does **not** call `ValidatePrompt`. It only checks type membership and non-empty body via model validators. Prefer GraphQL (or validate first) when editing agent templates.
</Warning>

## User prompt CRUD

### GraphQL (Settings UI)

| Operation | Args | Effect |
| --- | --- | --- |
| `settingsPrompts` | — | `default` tree (agents + tools with `type`, `template`, `variables`) + `userDefined[]` |
| `validatePrompt` | `type`, `template` | Dry validation only |
| `createPrompt` | `type`, `template` | Insert after validation |
| `updatePrompt` | `promptId`, `template` | Update owned row after validation |
| `deletePrompt` | `promptId` | Remove customization (falls back to default) |

`UserPrompt` fields: `id`, `type`, `template`, `createdAt`, `updatedAt`.

UI routes:

- `/settings/prompts` — agent and tool tables, Custom/Default badges, reset via delete
- `/settings/prompts/:promptId` — system/human tabs, variable chips, validate, save (create or update), diff vs default, reset

### REST (`/api/v1/prompts`)

| Method | Path | Privilege |
| --- | --- | --- |
| `GET` | `/prompts/` | `settings.prompts.view` |
| `GET` | `/prompts/{promptType}` | `settings.prompts.view` |
| `PUT` | `/prompts/{promptType}` | `settings.prompts.edit` (upsert body `{ "prompt": "..." }`) |
| `POST` | `/prompts/{promptType}/default` | `settings.prompts.edit` (write embed default into DB) |
| `DELETE` | `/prompts/{promptType}` | `settings.prompts.edit` |

Scope is always the authenticated `user_id`. List supports table query filters (`type`, `prompt`, timestamps) and optional group-by.

### Overlay at runtime

On flow and assistant worker start:

```go
prompter, err := newUserPrompter(ctx, db, userID)
// LoadDefaultPromptsMap + GetUserPrompts → NewFlowPrompter
```

Database load failure aborts session creation (no silent fallback to defaults-only). Overrides apply immediately to **new** flows/assistants; in-flight workers keep the prompter they were given at start.

## How templates bind to execution context

Providers hold a `Prompter` and call `RenderTemplate` with maps whose keys match `PromptVariables`. Typical pattern in agent handlers:

1. Build **system** params (tool names, Docker, `ExecutionContext`, language, flags).
2. Build **human/question** params (`Question`, task fields, code/output, …).
3. Render both templates.
4. Pass rendered strings into the LLM message chain for that agent type.

Execution context itself is produced by templates:

| Template | Used for |
| --- | --- |
| `full_execution_context` | Rich task/subtask state for summarization-style consumers |
| `short_execution_context` | Compact narrative injected as `ExecutionContext` into specialists |
| `execution_logs` | Format message-log history for refiner/reporter human prompts |

Example specialist binding (conceptual):

```go
system, _ := prompter.RenderTemplate(templates.PromptTypePentester, map[string]any{
  "HackResultToolName": tools.HackResultToolName,
  "TerminalToolName":   tools.TerminalToolName,
  // ... other PromptVariables for pentester ...
  "ExecutionContext":   shortContext,
  "Lang":               language,
  "DockerImage":        image,
  "Cwd":                docker.WorkFolderPathInContainer,
  "UserFiles":          userFilesListing,
})
human, _ := prompter.RenderTemplate(templates.PromptTypeQuestionPentester, map[string]any{
  "Question": question,
})
```

Tool-name variables stay provider-neutral: they are constants from the tools registry, not model-vendor strings. Feature flags (`GraphitiEnabled`, `AskUserEnabled`, `FlowManagerEnabled`) gate optional sections in templates via `{{if}}`.

## Template authoring conventions

In-repo guidance (`backend/docs/prompt_engineering_pentagi.md`) and the embedded agents share a structure:

- Role and authorization framing first
- Semantic XML sections (`<memory_protocol>`, `<container_constraints>`, `<team_specialists>`, …)
- Dual language policy: engagement log in `{{.Lang}}`, technical tool payloads in English
- Mandatory structured tool calls; result tools complete the subtask
- Summarization protocol: treat summaries as history, never invent tool output as plain text
- Use only whitelisted `{{.Var}}` names for the target `PromptType`

```markdown
# [AGENT ROLE]

## OPERATIONAL ENVIRONMENT
<container_constraints>
Image: {{.DockerImage}}
Cwd: {{.Cwd}}
</container_constraints>

## EXECUTION CONTEXT
{{.ExecutionContext}}

{{.ToolPlaceholder}}
```

Flow-level engagement text (what the user types when creating a flow) is separate from these agent templates; see sample engagement prompts under `examples/prompts/`.

## Permissions and storage

| Privilege | Capability |
| --- | --- |
| `settings.prompts.view` | Read defaults + user-defined list |
| `settings.prompts.edit` | Validate, create, update, delete, REST reset |
| `settings.prompts.admin` | Role seed privilege (admin bundle) |

Table: `prompts` (`id`, `type` as `PROMPT_TYPE`, `user_id`, `prompt`, timestamps). Enum migrations track template additions (tool-call ID types, then supervision types).

## Troubleshooting

| Symptom | Check |
| --- | --- |
| `unauthorized_variable: […]` | Variable not in `PromptVariables` for that type; remove or fix spelling |
| `syntax_error` / unclosed braces | Balance `{{` / `}}`; avoid unknown funcs |
| `rendering_failed` | Wrong field path on a struct (e.g. `.Task.Foo`); align with mock/`database` shapes |
| Create fails GraphQL, works REST | REST skips template validation — still fix variables before flows |
| Custom prompt not used | Confirm save succeeded; start a **new** flow/assistant; ensure non-empty body |
| Flow create fails “failed to load user prompts” | DB error on `GetUserPrompts` — fix DB rather than expecting default-only fallback |
| REST rejects supervision type | Use GraphQL for types missing from REST `Valid()` switch |
| Graphiti sections missing | `GraphitiEnabled` is false when the Graphiti client is disabled |

## Related pages

<CardGroup cols={2}>
  <Card title="Agents and supervision" href="/agents-and-supervision">
    Specialist agents, execution monitor, planning, and tool-call limits that consume these templates.
  </Card>
  <Card title="Flows, tasks, and subtasks" href="/flows-tasks-subtasks">
    Where execution context, generator/refiner human prompts, and assistant mode attach.
  </Card>
  <Card title="Tools reference" href="/tools-reference">
    Registry names injected as `*ToolName` template variables.
  </Card>
  <Card title="GraphQL API" href="/graphql-api">
    `settingsPrompts`, `validatePrompt`, and prompt mutations.
  </Card>
  <Card title="REST API" href="/rest-api">
    `/api/v1/prompts` list, get, put, reset, delete.
  </Card>
  <Card title="Sample pentest prompts" href="/sample-pentest-prompts">
    Flow input examples (engagement text), not agent `.tmpl` bodies.
  </Card>
  <Card title="Memory and knowledge" href="/memory-and-knowledge">
    Memory tools and summarizer budgets referenced from agent prompts.
  </Card>
  <Card title="Provider configuration schema" href="/provider-config-schema">
    Per-agent model settings paired with prompt types.
  </Card>
</CardGroup>
