Merge pull request #2731 from qdrant/szabosteve/build-with-agent

Add Agentic Tools docs hub with prompts in the docs
This commit is contained in:
István Zoltán Szabó
2026-09-22 10:03:11 +02:00
committed by GitHub
27 changed files with 1163 additions and 3 deletions
+43
View File
@@ -0,0 +1,43 @@
name: Check documentation prompts
on:
pull_request:
paths:
- 'qdrant-landing/content/**/*.md'
# Every shortcode, not just the prompt ones: the Markdown variants are
# what keep prompt bodies out of the agent-facing output, and naming them
# individually is how prompt-index.markdown.md went unguarded.
- 'qdrant-landing/layouts/shortcodes/**'
- 'qdrant-landing/themes/qdrant-2024/layouts/shortcodes/**'
- 'automation/prompts/**'
- '.github/workflows/prompts.yml'
# A skill link breaks when a skill is renamed or unpublished in qdrant/skills,
# which does not touch this repository and so never triggers the pull request
# run. The weekly run is what catches that.
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:
jobs:
check-prompts:
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Source checks only: no Hugo, no Dart Sass, no npm install. The rules
# this guards are all visible in the source, so a full site build would
# cost minutes on every docs PR to verify something that takes seconds.
- name: Check prompts
run: automation/prompts/check-prompts.sh
check-skill-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Kept separate from check-prompts because this one reaches the network.
# An unreachable skills.qdrant.tech skips rather than fails, so the job
# only goes red on a skill that is genuinely not published.
- name: Check agent skill links
run: automation/prompts/check-skill-links.sh
+114
View File
@@ -0,0 +1,114 @@
# AGENTS.md
Guidance for AI coding agents working in this repository. This is the Hugo site
behind [qdrant.tech](https://qdrant.tech): the marketing pages, the blog, and
the documentation.
This file is about editing the repository. It is not the agent-facing entry
point for the published site, which is
[`qdrant.tech/llms.txt`](https://qdrant.tech/llms.txt), generated from
`themes/qdrant-2024/layouts/index.llms.txt`.
## Build and serve
```bash
./run.sh # npm install, then hugo serve on :1313
./install-and-build.sh # what Netlify runs: fetches Dart Sass, builds to public/
```
Three things about the build cause more lost time than everything else combined:
- **Hugo 0.160.1 exactly.** `install-and-build.sh` refuses any other version and
`run.sh` warns. A different version produces failures that look like content
bugs.
- **Dart Sass is required, and the npm package named `sass` is not it.** It is a
different implementation. Without Dart Sass on `PATH`, Hugo still reports a
successful build and serves stale CSS out of `qdrant-landing/resources/_gen`.
A panel that renders unstyled after an SCSS change almost always means this
rather than a mistake in the SCSS.
- **`hugo --quiet` hides build errors.** It has reported exit 0 through a pipe
while the site failed to render. Grep unpiped output for `ERROR` before
trusting a green build.
## Layout of the repository
| Path | What it holds |
|---|---|
| `qdrant-landing/content/` | All content. `documentation/` is the docs tree |
| `qdrant-landing/content/headless/`, `.../documentation/headless/` | Page bundles that are never published as URLs: code snippets, prompts |
| `qdrant-landing/themes/qdrant-2024/` | The theme: layouts, shortcodes, SCSS, JS |
| `qdrant-landing/layouts/` | Project layouts, which override the theme's |
| `automation/` | Checks and generators, each with its own README |
| `.github/workflows/` | CI |
`qdrant-landing/public/`, `qdrant-landing/resources/_gen/`, and `node_modules/`
are generated and git-ignored. Never edit them, and never cite a path under
`public/` as the source of anything.
Project layouts win over theme layouts, which is why `layouts/shortcodes/`
holds the per-output-format variants while the theme holds the HTML ones.
## Every page has two outputs
A content page renders as HTML and as Markdown at `<url>/index.md`. The Markdown
output is what AI agents consume, and `qdrant.tech/llms.txt` indexes it. It is
not a format conversion of the HTML: `layouts/_default/single.markdown.md`
prepends pointers to the skills catalog and rewrites internal links to their
`index.md` form.
So a change to a page changes two published artifacts. When a shortcode should
behave differently for agents, give it a `<name>.markdown.md` variant in
`layouts/shortcodes/` rather than trying to detect the format inside the HTML
one. `code-snippet`, `include`, `prompt`, and `prompt-index` all do this.
## Content conventions
- **`weight` in front matter orders the docs sidebar.** Adding a page without
one puts it in an unpredictable position.
- **Moving or renaming a page needs `aliases`** in the destination's front
matter. Old URLs are linked from blog posts, from articles, and from the
pointers prepended to every documentation `index.md`.
- **Code samples belong in `documentation/headless/snippets/`**, not written
inline. They are compilable sources that CI type-checks against the real
client libraries, and `automation/snippets/generate-md.py` derives the
Markdown. See `automation/snippets/README.md`.
- **Prompts belong in `documentation/headless/prompts/`**, one file per prompt,
included with `{{< prompt "id" >}}`. A prompt body written inline on a page
reaches the agent-facing `index.md`, where an instruction addressed to an
agent can displace the question that agent was actually asked. See
`automation/prompts/README.md`.
- **Agent skill links are `https://skills.qdrant.tech/<path>/SKILL.md`.** The
bare path without the suffix returns 404, meta skills live under `meta/`, and
a path that exists in the `qdrant/skills` repository is not necessarily
published. `https://skills.qdrant.tech/llms.txt` lists what is actually
served.
## Writing style
American English, Oxford commas, straight quotes. No em dashes. No directional
language: an element is not "below" or "above", because the Markdown output,
the mobile layout, and a screen reader all order things differently. Name the
thing instead.
## Before opening a pull request
```bash
automation/prompts/check-prompts.sh # prompt rules, source only, seconds
automation/prompts/check-skill-links.sh # skill links resolve, needs network
automation/snippets/generate-md.py # if you touched snippets, then commit the result
```
When you are not at the repository root, stage with `git add -A` or
`git add :/`. Plain `git add .` inside `qdrant-landing/` silently misses
changes to `automation/` and `.github/`.
## Two traps in the templates
- **`.Section` returns the top-level section, not the nested one.** For
`/documentation/agentic-tools/skills/` it is `documentation`. Gating an asset
on it loads that asset on every documentation page, several hundred of them.
Check the path instead.
- **`[data-theme='light'] &` does not work inside a compound selector.** Nested
under `&__a &__b` it compiles to `.a [data-theme=light] .b`, which puts the
attribute in descendant position where it can never match, because the theme
attribute sits on `<html>`. Write those rules out longhand.
+132
View File
@@ -0,0 +1,132 @@
# Documentation prompts
A prompt is a short, ready-to-paste instruction that gets a coding agent moving
on the task a documentation page is about. Prompts live in one folder,
`qdrant-landing/content/documentation/headless/prompts/`, one file each, and
pages include them by id.
## Adding one
1. Create `content/documentation/headless/prompts/<id>.md`:
```yaml
---
title: "Set up monitoring"
page: /documentation/ops-monitoring/monitoring/
skills:
- qdrant-monitoring/setup
---
Help me set up monitoring for my Qdrant deployment. Read
https://skills.qdrant.tech/qdrant-monitoring/setup/SKILL.md first, then ask
me whether I am self-hosted, on Qdrant Cloud, or on Hybrid Cloud.
```
- `title` is a plain task name: "Set up monitoring", not "Set up monitoring
with an AI agent". The collapsed element already carries a `Prompt` marker
placed by the shortcode, so working the word into the title says the same
thing twice. Write it as the task, starting with a verb.
- `skills` is **always a YAML sequence**, even for a single skill, so the
four templates that render it have one shape to handle rather than two.
The old scalar `skill:` key is rejected by CI rather than ignored. Each
entry is a path under `skills.qdrant.tech` without the `/SKILL.md` suffix.
List more than one when the prompt genuinely spans them, as
`design-a-collection` does across tenancy and sizing; the index renders
every entry. Omit the key entirely when no skill applies, rather than
reaching for the nearest one, because this value is what labels the prompt
in the index. Published paths are listed at
`https://skills.qdrant.tech/llms.txt`, and a path in the `qdrant/skills`
repo is not automatically published, so the repo is not a reliable source
for this value. Meta skills sit under `meta/`, as in `meta/qdrant-advisor`.
`check-skill-links.sh` verifies all of this, so there is no need to curl
by hand.
- `page` is where the prompt is included. CI checks that page really
includes it.
- `open: true` renders the element expanded. **In-page prompts are always
collapsed**, so leave this unset. It exists for a page whose whole purpose
is the prompt, which today means only the Agentic Tools hub. A prompt
feeling important is not a reason to set it: every prompt feels important
to its author, and the collapsed state is what keeps a docs page readable.
2. Add `{{< prompt "<id>" >}}` to that page, near the top of the section it
relates to rather than at the bottom.
3. Run both checks:
```bash
automation/prompts/check-prompts.sh # source rules, no build, seconds
automation/prompts/check-skill-links.sh # skill links resolve, needs network
```
`check-prompts.sh` also accepts a built site directory, to verify the real
output as well: `automation/prompts/check-prompts.sh qdrant-landing/public`.
## Writing one
**A few sentences.** Enough to get someone moving, not a specification.
**Name the skill inside the body,** with its full `SKILL.md` URL, so the agent
loads it rather than guessing. Prompts exist partly to surface skills.
**No variables.** Never ask the reader to fill something in before pasting. If
the task needs their configuration, tell the agent to ask for it.
**Do not restate the page.** The reader is already on it. A prompt that
re-explains the feature earns nothing.
**Ask for what the docs cannot give.** The useful instructions are the ones
that make an agent commit to a judgment call: which setup applies to the
reader's deployment, what to check first, what to ignore.
**Title it as the task.** The collapsed line reads `PROMPT | Set up
monitoring`: the marker says what kind of thing it is, the title says what it
does. That split is why titles stay short, and why the type signal is
consistent across every prompt rather than depending on each author phrasing it
the same way.
## Formatting a prompt body
**Do not hard-wrap prose.** Write each paragraph as one long line and let the
page wrap it. The element renders the body with `white-space: pre-wrap`, so any
line breaks you type are preserved, and a body wrapped at 80 columns keeps those
breaks in a content column that is narrower than that. `start-building` is the
exception: its numbered steps and indented commands are structure, so its line
breaks are content.
**A body can contain anything, including fenced blocks.** The shortcode passes
it to Chroma as a value rather than re-parsing it as markdown, so a prompt that
includes a YAML or JSON snippet renders correctly.
## Two rules that break silently
**Prompt bodies must never reach a built `index.md`.** Those files are consumed
by agents. A prompt is an instruction written in the imperative first person,
so an agent reading it inside a page it was told to fetch can follow the prompt
instead of answering the question it was actually asked. The shortcode's
Markdown variant drops the body and keeps the skill pointer. This is also why a
prompt is never written as a plain fenced block on the page.
**The `page:` declaration is what the index trusts.** Prompt files cannot know
who includes them, so a stale declaration would put a wrong link in the index,
and a prompt that is never included would still be listed.
`check-prompts.sh` guards both, plus a third rule that keeps them equivalent:
the Markdown variant of the shortcode must never render the body. Rule one is
checked in the source rather than in the built output, because pasting a body
inline is a source-level mistake and catching it there means CI needs no site
build.
## The third rule, which breaks without a commit
**A skill link rots on its own.** A skill renamed or unpublished in
`qdrant/skills` breaks every reference to it here without anything in this
repository changing, so no pull request run would ever notice.
`check-skill-links.sh` covers that. It checks both places a skill path appears,
the `skill:` front matter and the `SKILL.md` URLs written inside prompt bodies
and documentation prose, against the catalog at
`https://skills.qdrant.tech/llms.txt`, and it also runs weekly on a schedule.
It reaches the network, which is why it is a separate script and a separate CI
job. An unreachable catalog skips the run rather than failing it, so a docs pull
request never goes red because skills.qdrant.tech was down. A path missing from
the catalog is confirmed with a request before it is reported, since the catalog
can lag a freshly published skill.
+141
View File
@@ -0,0 +1,141 @@
#!/usr/bin/env bash
# Guards the rules that keep documentation prompts working. All of them fail
# silently otherwise, which is why they are checked rather than reviewed.
#
# 1. No prompt body is written inline on a page. Prompt bodies must not reach
# a built index.md, because those files are consumed by agents and a prompt
# is an instruction addressed to an agent, so it can displace the question
# the agent was actually asked. The shortcode's Markdown variant drops the
# body, so the only way a body reaches index.md is an author pasting it as
# a fenced block instead of using the shortcode. That is a source-level
# mistake, so it is caught in the source.
# 2. No Markdown variant renders the body. Rule 1 is only equivalent to "no
# body in index.md" while this holds, and it has to cover every template
# that can reach a prompt's content, not just the element's. The index
# ranges the same pages, so a body added there lands in the built
# index.md exactly the same way.
# 3. Every prompt's declared `page:` really includes it. The prompt files live
# in one folder and cannot know who includes them, so the declaration is
# what the index trusts. This also catches a prompt that is written but
# never used, which would otherwise appear in the index anyway.
#
# These are all source checks, so no site build is needed and the run takes
# seconds. Pass a built-site directory to additionally verify the real output:
#
# automation/prompts/check-prompts.sh # source checks only
# automation/prompts/check-prompts.sh qdrant-landing/public # and the build
set -Eeuo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
public_dir="${1:-}"
prompts_dir="$repo_root/qdrant-landing/content/documentation/headless/prompts"
content_dir="$repo_root/qdrant-landing/content"
# Every Markdown-output template that can reach a prompt page. Add to this list
# rather than writing a new check if another one appears.
md_shortcodes=(
"$repo_root/qdrant-landing/layouts/shortcodes/prompt.markdown.md"
"$repo_root/qdrant-landing/layouts/shortcodes/prompt-index.markdown.md"
)
failures=0
fail() {
printf 'FAIL: %s\n' "$1" >&2
failures=$((failures + 1))
}
if [[ ! -d "$prompts_dir" ]]; then
echo "No prompts directory at $prompts_dir; nothing to check."
exit 0
fi
# --- rule 2: no Markdown variant may render the prompt body ---
for md_shortcode in "${md_shortcodes[@]}"; do
rel="${md_shortcode#"$repo_root"/}"
if [[ ! -f "$md_shortcode" ]]; then
fail "missing $rel: without it, prompt bodies reach the agent-facing Markdown"
elif grep -qE '\.(RawContent|Content|Inner)\b' "$md_shortcode"; then
fail "$rel renders the prompt body; it must emit only titles, pages, and skill pointers"
fi
done
shopt -s nullglob
checked=0
for file in "$prompts_dir"/*.md; do
id="$(basename "$file" .md)"
[[ "$id" == "_index" ]] && continue
checked=$((checked + 1))
# First non-empty body line, used as the probe. Front matter is delimited by
# the first two '---' lines.
probe="$(awk '
/^---[[:space:]]*$/ { d++; next }
d >= 2 && NF { print; exit }
' "$file")"
if [[ -z "$probe" ]]; then
fail "$id: prompt file has no body"
continue
fi
# --- rule 1: the body must not appear inline anywhere in the content tree ---
if hits="$(grep -rlF "$probe" --include='*.md' "$content_dir" 2>/dev/null \
| grep -v "^$prompts_dir/" || true)" && [[ -n "$hits" ]]; then
fail "$id: prompt body is written inline instead of using the shortcode:"
printf ' %s\n' $hits >&2
printf ' Replace it with {{< prompt "%s" >}}\n' "$id" >&2
fi
# --- rule 3: the declared page must include the shortcode ---
page="$(awk -F': *' '
/^---[[:space:]]*$/ { d++; if (d >= 2) exit; next }
d == 1 && $1 == "page" { print $2; exit }
' "$file" | tr -d '"'"'"'')"
if [[ -z "$page" ]]; then
fail "$id: no page: declared in front matter"
continue
fi
rel="${page#/}"
rel="${rel%/}"
src=""
for candidate in "$content_dir/$rel.md" "$content_dir/$rel/_index.md"; do
[[ -f "$candidate" ]] && { src="$candidate"; break; }
done
if [[ -z "$src" ]]; then
fail "$id: declared page $page has no source file under content/"
continue
fi
if ! grep -qF "prompt \"$id\"" "$src" && ! grep -qF "prompt '$id'" "$src"; then
fail "$id: declared page $page does not include it"
printf ' Expected {{< prompt "%s" >}} in %s\n' "$id" "${src#"$repo_root"/}" >&2
fi
# --- optional: verify the real built output when a build is available ---
if [[ -n "$public_dir" ]]; then
if [[ ! -d "$public_dir" ]]; then
fail "built site not found at $public_dir"
public_dir=""
elif built="$(grep -rlF "$probe" --include='index.md' "$public_dir" 2>/dev/null || true)" \
&& [[ -n "$built" ]]; then
fail "$id: prompt body reached the agent-facing Markdown output:"
printf ' %s\n' $built >&2
fi
fi
done
if (( failures > 0 )); then
printf '\n%d prompt check failure(s) across %d prompt(s).\n' "$failures" "$checked" >&2
exit 1
fi
if [[ -n "$public_dir" ]]; then
printf 'Prompt checks passed (source and build): %d prompt(s).\n' "$checked"
else
printf 'Prompt checks passed: %d prompt(s).\n' "$checked"
fi
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env bash
# Verifies that every agent skill this repository points at is actually
# published on skills.qdrant.tech.
#
# Why this needs its own check. A skill reference breaks without anything in
# this repository changing: a skill gets renamed or unpublished in qdrant/skills
# and the link here rots in place. It is also easy to get wrong when writing,
# for two reasons that have each already produced a 404:
#
# - A path that exists in the qdrant/skills repository is not automatically
# served. The repository is not the source of truth; llms.txt is.
# - Meta skills live under meta/, as in meta/qdrant-advisor, not at the root
# alongside the domain skills.
#
# Two kinds of reference are checked, because both exist and they can disagree:
#
# 1. `skill:` in the front matter of a prompt, which the shortcode turns into
# a link and which the prompt index renders as a column.
# 2. Any https://skills.qdrant.tech/<path>/SKILL.md URL written in the content
# tree, which includes the URLs inside prompt bodies that tell the agent
# what to read, and ordinary prose links in the documentation.
#
# This check reaches the network, unlike check-prompts.sh, which is why it is a
# separate script and a separate CI job: skills.qdrant.tech being unreachable
# must not turn a documentation pull request red. An unreachable catalog skips
# the run; only a catalog that answers and does not list a path fails it.
#
# automation/prompts/check-skill-links.sh
set -Eeuo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
content_dir="$repo_root/qdrant-landing/content"
prompts_dir="$content_dir/documentation/headless/prompts"
catalog_url="${SKILLS_CATALOG_URL:-https://skills.qdrant.tech/llms.txt}"
base_url="https://skills.qdrant.tech"
failures=0
fail() {
printf 'FAIL: %s\n' "$1" >&2
failures=$((failures + 1))
}
# --- collect references, as "<skill path>\t<where it came from>" ---
refs="$(
# 1. skills: values in prompt front matter. Always a YAML sequence, even for
# a single skill, so there is one shape here and in all four templates.
# A file still using the old scalar `skill:` key emits a LEGACY marker
# rather than being silently skipped, which would leave it unchecked.
if [[ -d "$prompts_dir" ]]; then
for file in "$prompts_dir"/*.md; do
[[ -f "$file" ]] || continue
awk -v src="${file#"$repo_root"/}" '
/^---[[:space:]]*$/ { d++; if (d >= 2) exit; next }
d != 1 { next }
/^skill:[[:space:]]/ { print "LEGACY-SKILL-KEY\t" src; next }
/^skills:[[:space:]]*$/ { inlist = 1; next }
inlist && /^[[:space:]]*-[[:space:]]*/ {
v = $0
sub(/^[[:space:]]*-[[:space:]]*/, "", v)
gsub(/^["\x27[:space:]]+|["\x27[:space:]]+$/, "", v)
if (v != "") print v "\t" src " (skills: front matter)"
next
}
/^[^[:space:]#]/ { inlist = 0 }
' "$file"
done
fi
# 2. SKILL.md URLs written anywhere in the content tree. The path is
# everything between the host and the /SKILL.md suffix.
grep -roE "https://skills\.qdrant\.tech/[A-Za-z0-9._/-]+/SKILL\.md" \
--include='*.md' "$content_dir" 2>/dev/null \
| sed -E "s|^${repo_root}/||" \
| awk -F':' '{ src = $1; url = $0; sub(/^[^:]*:/, "", url)
sub(/^https:\/\/skills\.qdrant\.tech\//, "", url); sub(/\/SKILL\.md$/, "", url)
print url "\t" src " (SKILL.md link)" }' || true
)"
# A prompt still on the old scalar `skill:` key would contribute no references
# at all, so this check would pass while that prompt's link went unverified.
if legacy="$(grep -F 'LEGACY-SKILL-KEY' <<<"$refs" || true)" && [[ -n "$legacy" ]]; then
while IFS=$'\t' read -r _ src; do
[[ -n "$src" ]] && fail "$src uses the old scalar 'skill:' key; it must be a 'skills:' sequence"
done <<<"$legacy"
printf '\n%d prompt(s) on the legacy key.\n' "$failures" >&2
exit 1
fi
if [[ -z "$refs" ]]; then
echo "No agent skill references found; nothing to check."
exit 0
fi
# --- fetch the catalog of what is actually served ---
catalog=""
if ! catalog="$(curl -fsS --max-time 30 "$catalog_url" 2>/dev/null)" || [[ -z "$catalog" ]]; then
echo "Skipping: could not reach $catalog_url."
echo "This check is advisory and does not fail on an unreachable catalog."
exit 0
fi
# --- check each distinct path ---
checked=0
while IFS=$'\t' read -r path sources; do
[[ -n "$path" ]] || continue
checked=$((checked + 1))
url="$base_url/$path/SKILL.md"
if grep -qF "$url" <<<"$catalog"; then
continue
fi
# The catalog can lag a freshly published skill, so a miss is confirmed with
# a request before it is reported. Only a path that is absent from both the
# catalog and the live site is a failure.
status="$(curl -fsS -o /dev/null -w '%{http_code}' --max-time 30 "$url" 2>/dev/null || true)"
if [[ "$status" == "200" ]]; then
printf 'note: %s is served but missing from the catalog at %s\n' "$path" "$catalog_url"
continue
fi
fail "$path is not a published skill (${url} returned ${status:-no response})"
printf ' Referenced by: %s\n' "$sources" >&2
printf ' Published skills are listed at %s\n' "$catalog_url" >&2
printf ' Meta skills are under meta/, as in meta/qdrant-advisor.\n' >&2
done < <(
# Distinct paths, with their sources joined so one bad path reports once.
sort -u <<<"$refs" | awk -F'\t' '
{ if ($1 in seen) seen[$1] = seen[$1] ", " $2; else { seen[$1] = $2; order[++n] = $1 } }
END { for (i = 1; i <= n; i++) print order[i] "\t" seen[order[i]] }
'
)
if (( failures > 0 )); then
printf '\n%d broken skill link(s) across %d referenced skill(s).\n' "$failures" "$checked" >&2
exit 1
fi
printf 'Skill link checks passed: %d referenced skill(s).\n' "$checked"
@@ -241,7 +241,7 @@ client.create_collection(
![Decision tree for choosing the right bulk upload strategy: dense, sparse, or hybrid vectors, with memory, quantization, and sharding options](/articles_data/bulk-uploads-in-qdrant/choosing-the-right-mix.png)
Still deciding exactly what to configure for your workload? [Qdrant's Agent Skills](https://qdrant.tech/documentation/skills/) provide hands-on, scenario-based guidance that walks you through the specific settings for your situation.
Still deciding exactly what to configure for your workload? [Qdrant's Agent Skills](https://qdrant.tech/documentation/agentic-tools/skills/) provide hands-on, scenario-based guidance that walks you through the specific settings for your situation.
## It's Not One-Size-Fits-All
+1 -1
View File
@@ -132,7 +132,7 @@ We recommend upgrading versions one by one. Qdrant Cloud does this automatically
> If you still use the legacy `/search`, `/recommend`, or `/discover` endpoints, migrate to the [`/query` API](/documentation/search/search/#query-api) before upgrading to 1.19.
Need help with your upgrade? The [Qdrant Advisor agent skill](https://qdrant.tech/documentation/skills/) can help you navigate upgrades, troubleshoot configurations, and answer questions about your Qdrant setup, whether you're on Qdrant Cloud or self-hosting.
Need help with your upgrade? The [Qdrant Advisor agent skill](https://qdrant.tech/documentation/agentic-tools/skills/) can help you navigate upgrades, troubleshoot configurations, and answer questions about your Qdrant setup, whether you're on Qdrant Cloud or self-hosting.
## Engage
@@ -0,0 +1,36 @@
---
title: "Agentic Tools"
short_description: "Tools that help AI coding assistants work with Qdrant: a prompt to get started, agent skills that carry solutions architect knowledge, and prompts embedded across the documentation."
description: "Qdrant agentic tools for AI coding assistants. Start from a single prompt that runs Qdrant locally and installs the Qdrant Advisor, then use agent skills to diagnose and tune search, memory, scaling, and relevance in a running deployment."
weight: 303
partition: develop
---
# Agentic Tools
Qdrant provides tools and resources that help AI agents work with Qdrant more effectively.
Use them to give your agent task-specific guidance, troubleshoot existing deployments, design and evaluate search systems, and apply Qdrant best practices to your specific use case.
## Start Building
If you're new to Qdrant, the Start building on Qdrant prompt takes your coding assistant from an empty project to a working search query.
It stands up a local instance, installs the client with local embeddings, adds the [Qdrant Advisor](/documentation/agentic-tools/skills/#the-qdrant-advisor) skill, and then builds against your own use case.
{{< prompt "start-building" >}}
Qdrant runs in Docker and [FastEmbed](/documentation/fastembed/fastembed-quickstart/) generates embeddings on your machine, so you can go from nothing to a working search query without signing up for anything.
Collection configuration is important. A mismatched distance metric or a missing payload index can result in slower or lower quality search.
The prompt is written to make your assistant ask what you are building, then commit to those choices out loud before it writes any code.
## Agent Skills
[Agent skills](/documentation/agentic-tools/skills/) encode solutions architect knowledge as structured files your agent loads on demand.
They're organized around symptoms, so they answer "why is this happening, and what should I check first?"
Reach for skills when something is already running and behaving unexpectedly: slow search, growing memory, a stuck optimizer, poor relevance.
## Prompts in the Docs
Short, ready-to-use prompts appear directly on the documentation pages where they are relevant, so you can use the prompts when you need them instead of looking for them elsewhere.
Each prompt points to the agent skill your assistant should load. The prompt gets the task started; the skill provides the deeper Qdrant-specific guidance.
The [prompt index](/documentation/agentic-tools/prompts/) lists all available prompts.
@@ -0,0 +1,17 @@
---
title: "Prompts in the Docs"
short_description: "Every prompt in the Qdrant documentation, with the page it appears on and the agent skill it uses."
description: "An index of the ready-to-paste prompts embedded in the Qdrant documentation. Each one sits on the page it relates to, links the relevant agent skill, and is written to be handed straight to a coding assistant."
weight: 20
partition: develop
---
# Prompts in the Docs
Qdrant documentation includes short, ready-to-use prompts on the pages where they are relevant: setting up monitoring on the monitoring page, diagnosing memory issues on the optimization page, and so on.
Each prompt is collapsed by default, requires no additional input, and tells your assistant which [agent skill](/documentation/agentic-tools/skills/) to load. The prompt defines the task; the skill provides the Qdrant-specific guidance needed to complete it.
This page lists all available prompts. Follow a link to jump to the prompt on the page that provides its context.
{{< prompt-index >}}
@@ -2,7 +2,9 @@
title: "Agent Skills"
short_description: Qdrant agent skills encode solutions architect knowledge for AI coding assistants, helping them diagnose vector search problems, recommend the right configuration, and navigate to the exact documentation without installation.
description: Qdrant agent skills help AI coding assistants diagnose and tune vector search in production. Pass a skill URL from skills.qdrant.tech to your agent to get targeted guidance on scaling, search quality, performance, monitoring, and more.
weight: 303
weight: 10
aliases:
- /documentation/skills/
partition: develop
---
@@ -0,0 +1,16 @@
---
title: "Prompts"
# The parent headless bundle cascades `list: never`, which makes this section
# unreachable via site.GetPage and leaves the prompt index empty. `list: local`
# keeps these pages out of site-wide collections while still allowing the
# section's .RegularPages to be ranged over. They are never published as URLs.
build:
list: local
render: never
publishResources: false
cascade:
- build:
list: local
render: never
publishResources: false
---
@@ -0,0 +1,11 @@
---
title: "Design a collection"
page: /documentation/manage-data/collections/
# Two skills because the prompt genuinely spans both. Naming only one would
# label the whole prompt as that one thing, which is why this prompt had no
# skill at all before the body grew to cover sizing.
skills:
- qdrant-multitenancy
- qdrant-sizing
---
Help me design a Qdrant collection for my data. Ask me what I am searching over, roughly how many vectors I expect and how fast that will grow, which embedding model I am using, what I will filter on, and what query rate I need, before you write any code. If the use case has to support multiple users, customers, or organizations, decide on a multitenancy strategy by reading https://skills.qdrant.tech/qdrant-multitenancy/SKILL.md. Then derive the vector size from the embedding model rather than hardcoding it, name the distance metric the model was trained for and say why you picked it, and add a payload index for every field I will filter on. Size it before I create anything, reading https://skills.qdrant.tech/qdrant-sizing/SKILL.md: tell me how much memory the vectors and the index will need, how many shards to create and why, whether I need replication, and whether the vectors belong in memory or on disk. If quantization would change that answer, say which kind, what it saves, and what it costs me in recall. Finish by telling me which of these I cannot change later without recreating the collection.
@@ -0,0 +1,7 @@
---
title: "Diagnose high memory usage"
skills:
- qdrant-performance-optimization/memory-usage-optimization
page: /documentation/ops-optimization/optimize/
---
My Qdrant memory usage is higher than I expected, or a node was killed for running out of memory. Read https://skills.qdrant.tech/qdrant-performance-optimization/memory-usage-optimization/SKILL.md first, then ask me for my collection configuration before proposing anything. Tell me which component is actually consuming the memory, whether that is the vectors, the HNSW index, or the payload indexes, and give me the fixes in order of impact. Say which ones need a reindex, and what recall I would lose if I quantize.
@@ -0,0 +1,7 @@
---
title: "Review my deployment"
skills:
- qdrant-sizing
page: /documentation/production-checklist/
---
Review my Qdrant deployment before I put production traffic on it. Read https://skills.qdrant.tech/qdrant-sizing/SKILL.md, then ask me for my collection configuration, my expected vector count and query rate, and the hardware I have provisioned. Tell me which of my settings will not survive real load, ordered by how much it would hurt, and separate what I can change later from what needs the collection recreated. Where my numbers do not support a conclusion, say what to measure rather than guessing.
@@ -0,0 +1,7 @@
---
title: "Set up monitoring"
skills:
- qdrant-monitoring/setup
page: /documentation/ops-monitoring/monitoring/
---
Help me set up monitoring for my Qdrant deployment. Read https://skills.qdrant.tech/qdrant-monitoring/setup/SKILL.md first, then ask me whether I am self-hosted, on Qdrant Cloud, or on Hybrid Cloud, since the scrape setup differs. Give me the Prometheus scrape config, the health probes worth wiring up, and the three or four alerts that would actually have caught a real incident. Tell me which metrics to ignore.
@@ -0,0 +1,35 @@
---
title: "Start building on Qdrant"
page: /documentation/agentic-tools/
# Expanded because this prompt is the point of the hub page. Do not copy
# this to an in-page prompt; those are always collapsed.
#
# This is the one prompt whose line breaks are structure rather than wrapping,
# so it keeps them. Two rules follow from that, both about pre-wrap wrapping a
# second time on a narrow screen:
# - Commands go on one line, however long. A backslash continuation wraps
# again and lands the continuation under the wrong column.
# - Prose inside a step is one line per step or bullet, never hard-wrapped.
# A 65-column source wrap colliding with a 37-column screen wrap leaves
# orphan fragments on their own lines.
open: true
skills:
- meta/qdrant-advisor
---
Help me get started building on Qdrant.
1. Start Qdrant locally with Docker. No signup and no API key needed:
docker run -d -p 6333:6333 -p 6334:6334 -v "$(pwd)/qdrant_storage:/qdrant/storage:z" qdrant/qdrant
Confirm it's running at http://localhost:6333/dashboard
2. Ask me which language I want to build in, then install the Qdrant client for it. If I have no preference, use Python with local embeddings so I don't need an embedding provider key either:
pip install "qdrant-client[fastembed]"
3. Install the Qdrant Advisor agent skill:
npx skills add qdrant/skills/meta/qdrant-advisor
4. Then ask me what I'm building and what I'll search over before you write any code. Once you know, build the integration against my local instance, and get the collection right the first time:
- Derive the vector size from the embedding model itself. Never hardcode a dimension.
- Choose the distance metric the model was trained for, and say which one you picked and why.
- Create a payload index for every field I'll filter on, before I load data rather than after.
Then load a small sample, run a real query, and show me the results.
@@ -29,6 +29,10 @@ Qdrant supports these most popular types of metrics:
In addition to metrics and vector size, each collection uses its own set of parameters that controls collection optimization, index construction, and vacuum.
These settings can be changed at any time by a corresponding request.
To hand this to an agent instead, the Design a collection prompt makes it ask about your data and query shape first, then commit to a vector configuration and the payload indexes to create.
{{< prompt "design-a-collection" >}}
## Setting Up Multitenancy
**How many collections should you create?** In most cases, you should only use a single collection with payload-based partitioning. This approach is called [multitenancy](https://en.wikipedia.org/wiki/Multitenancy). It is efficient for most of users, but it requires additional configuration. [Learn how to set it up](/documentation/manage-data/multitenancy/).
@@ -21,6 +21,10 @@ The integration with Qdrant is easy to
[configure](https://prometheus.io/docs/prometheus/latest/getting_started/#configure-prometheus-to-monitor-the-sample-targets)
with Prometheus and Grafana.
If you would rather have an agent handle this, the Set up monitoring prompt asks it to select the right configuration for your deployment type and recommend the alerts worth wiring up.
{{< prompt "set-up-monitoring" >}}
## Metrics
Qdrant exposes various metrics in Prometheus/OpenMetrics format, commonly used together with Grafana for monitoring.
@@ -25,6 +25,10 @@ This guide will walk you three main optimization strategies:
This page covers the <code>memory</code> parameter introduced in Qdrant v1.19. If you're using an older version, see the <a href="/documentation/ops-configuration/memory-tiers/#legacy-settings">Legacy Settings</a> section for how to map the new parameter to the old ones.
</aside>
An agent can work through this with you. The Diagnose high memory usage prompt makes it find which component is actually using the memory before it suggests any changes.
{{< prompt "diagnose-memory-usage" >}}
## 1. High-Speed Search with Low Memory Usage
To achieve high search speed with minimal memory usage, you can store vectors on disk while minimizing the number of disk reads. Vector quantization is a technique that compresses vectors, allowing more of them to be stored in memory, thus reducing the need to read from disk.
@@ -10,6 +10,10 @@ weight: 147
A practical checklist to ensure Qdrant is optimized, stable, and ready to handle real-world load.
An agent can run the checklist with you. The Review my deployment prompt asks it for a prioritized list of what would break first under real load.
{{< prompt "review-before-launch" >}}
---
## 1. Distributed Deployment & Sharding
@@ -0,0 +1,23 @@
{{- /*
Markdown variant of the prompt index.
Titles, pages, and skill pointers only. Prompt bodies never appear here, for
the same reason the `prompt` shortcode drops them: an index.md is consumed by
agents, and a prompt is an instruction addressed to an agent.
Keep in step with prompt-index.html, which emits table markup instead of
pipes because shortcode output is not re-parsed as markdown.
Page links are absolute and index.md-suffixed to match every other link in
this output, which single.markdown.md rewrites that way. A bare site path
would be the one column an agent reading this table could not resolve.
*/ -}}
{{- $section := site.GetPage "documentation/headless/prompts" -}}
{{- if not $section -}}
{{- errorf "prompt-index: cannot resolve documentation/headless/prompts (see prompt-index.html for the cause)." -}}
{{- end -}}
| Prompt | Page | Agent skills |
|---|---|---|
{{ range $section.RegularPages.ByTitle -}}
| {{ .Title }} | {{ with .Params.page }}{{ with site.GetPage . }}{{ .Permalink }}index.md{{ else }}{{ . }}{{ end }}{{ else }}None{{ end }} | {{ with .Params.skills }}{{ range $i, $s := . }}{{ if $i }}, {{ end }}https://skills.qdrant.tech/{{ $s }}/SKILL.md{{ end }}{{ else }}None{{ end }} |
{{ end -}}
@@ -0,0 +1,22 @@
{{- /*
Markdown output variant of the `prompt` shortcode.
The prompt body is deliberately omitted. An index.md file is consumed by
agents, and a prompt is written in the imperative first person, so including
it risks an agent following the prompt instead of answering the question it
was actually asked. The skill pointer survives, because that is the part with
genuine value to an agent.
The title is always emitted, even when there is no skill. The prose around a
prompt refers to it by name, so a prompt that left no trace here would turn
that sentence into a reference to nothing, which is the failure this whole
variant exists to avoid creating.
*/ -}}
{{- $id := .Get 0 -}}
{{- $p := site.GetPage (printf "documentation/headless/prompts/%s" $id) -}}
{{- with $p }}
> Prompt: {{ .Title }}
{{- range .Params.skills }}
> Related agent skill: https://skills.qdrant.tech/{{ . }}/SKILL.md
{{- end }}
{{ end -}}
+9
View File
@@ -87,3 +87,12 @@
# ACORN blog post converted into an internals article
/blog/filtered-vector-search-acorn/ /articles/filtered-vector-search-acorn/ 301
# Agent Skills moved under the Agentic Tools hub. The page's `aliases:` front
# matter already covers the HTML, but Hugo writes an alias as index.html with
# no index.md beside it, so the agent-facing form of the old URL 404s. That URL
# is linked from the pointers prepended to every documentation index.md, so the
# .md case is the one that matters most here. Left unforced on purpose: the
# alias stub is a real file and so keeps serving the HTML, and this rule applies
# where there is no file to shadow it.
/documentation/skills/* /documentation/agentic-tools/skills/:splat 301
@@ -11,3 +11,4 @@
@import 'components/accordion';
@import 'components/course-card';
@import 'components/date';
@import 'components/prompt';
@@ -0,0 +1,266 @@
@use '../helpers/functions' as *;
// Both themes are declared here so a palette change is a one-block edit.
$prompt-border: $neutral-30;
$prompt-text: $neutral-98;
$prompt-text-secondary: $neutral-70;
// The site's interactive red. Used for the disclosure caret and the skill-line
// links, the only interactive marks in the component.
$prompt-accent: $primary-50;
// The dark card fill and the dark code block fill. Swapped from the usual
// arrangement: the card is the lighter $neutral-20 and the code block recesses
// to the $neutral-10 page tone.
$prompt-surface: $neutral-20;
$prompt-block-surface: $neutral-10;
// The code text color Chroma uses on this site. The block text is dark in both
// themes, so one value serves both.
$prompt-body-text: $neutral-94;
$prompt-border-light: $neutral-80;
$prompt-text-light: $neutral-10;
$prompt-text-secondary-light: $neutral-50;
// The light card fill, and the light code block's own darker fill on top of it.
$prompt-surface-light: $neutral-98;
$prompt-block-surface-light: $neutral-20;
.prompt {
// Dark: the card is the lighter $neutral-20 and the code block recesses to
// $neutral-10. Light overrides this below with its own card and code fills.
background: $prompt-surface;
border: 1px solid $prompt-border;
border-radius: pxToRem(12);
margin: pxToRem(24) 0;
overflow: hidden;
// Grayscale antialiasing for the summary text, so Mona Sans renders light and
// crisp rather than the heavier subpixel default on macOS.
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
&__summary {
display: flex;
align-items: center;
gap: pxToRem(8);
padding: pxToRem(12) pxToRem(16);
cursor: pointer;
font-size: $font-size-md;
font-weight: 600;
// The title inherits this; the kind marker overrides it with the secondary
// tier below.
color: $prompt-text;
list-style: none;
&::-webkit-details-marker {
display: none;
}
// Disclosure caret, drawn rather than an icon file. It sets its own color to
// the accent red and draws its borders in currentColor, so it stays red
// regardless of the summary text color.
&::before {
content: '';
flex: none;
width: pxToRem(8);
height: pxToRem(8);
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(-45deg);
transition: transform 0.15s ease;
color: $prompt-accent;
}
// On hover the kind marker lifts to the prominent color (the `&__kind` hover
// rule below) and the title is only underlined, not recolored. Same in both
// themes.
&:hover {
.prompt__summary-label {
text-decoration: underline;
}
}
&:focus-visible {
outline: 2px solid currentColor;
outline-offset: -2px;
}
}
&[open] &__summary::before {
transform: rotate(45deg);
}
// The category label. It shares the title's type and differs only in color,
// dropping to the secondary tier, with the divider rule setting it apart.
&__kind {
flex: none;
color: $prompt-text-secondary;
// Separates the marker from the title without a divider glyph.
padding-right: pxToRem(12);
border-right: 1px solid $prompt-border;
transition: color 0.15s ease;
}
// The one part that changes color on hover: the kind marker rises from the
// secondary tier to the title's prominent color, while the title only gains an
// underline. Light mode overrides the target below, where the prominent text
// is dark. Beats the base and light-theme `&__kind` colors on specificity.
&__summary:hover &__kind {
color: $prompt-text;
}
&__summary-label {
// A prompt title can be long; wrap rather than overflow.
min-width: 0;
transition: color 0.15s ease;
}
// `.documentation-article summary` sets margin-bottom: 1rem for the sidebar
// disclosures. Inside this element that margin lands below the summary, and
// `overflow: hidden` keeps it inside the box, so a collapsed prompt is 16px
// taller than its content and the title sits above center. Written as a
// descendant selector because the plain BEM class loses to that rule on
// specificity: one class against a class plus an element.
& &__summary {
margin-bottom: 0;
}
// The skill line is a <p>, and the same rule catches it: the shorthand on
// `&__skill` already sets its bottom margin to zero, but
// `.documentation-article p` outranks a single class and puts 1rem back,
// which then stacks on the body's bottom padding and pushes the line off
// centre. Anything in here that the docs article also styles by tag name
// needs this descendant form to win.
& &__skill {
margin-bottom: 0;
}
&__body {
// The bottom padding matches the skill line's top margin, so that line
// sits centred between the prompt block and the bottom edge of the card.
padding: 0 pxToRem(16) pxToRem(16);
}
// The block Chroma emits inside the body. The card is transparent, so this
// is the only filled surface; it takes the card's corner radius itself
// rather than borrowing it from the card's overflow clip.
.highlight {
margin: 0;
border-radius: pxToRem(12);
}
// A prompt is prose, not code. Its line breaks come from how the author
// wrapped the source file, so a fixed-width <pre> clips every line at any
// window narrower than the wrap width instead of reflowing. Sideways
// scrolling is a fair bargain for code and a bad one for a paragraph.
// pre-wrap keeps the deliberate line structure in prompts that have one.
// Chroma wraps every line in `.line > .cl`, and syntax-highlight.scss makes
// `.line` a flex container. A flex item will not shrink below its min-content
// width, so the `overflow-wrap` set on the pre never reaches a long token
// inside `.cl` and a docker command or a URL is clipped instead of wrapped.
// Letting the item shrink is what makes the wrapping rule take effect;
// setting `word-break` on the pre does not, because the constraint is here.
.highlight .cl {
min-width: 0;
}
// copy-code.js gives its button a $neutral-20 fill, which shows as a grey chip
// against the surface behind it. Only the icon is wanted, so the fill goes.
.copy-code {
background: transparent;
}
// Chroma sets `.line` to font-size 16px with line-height 16px, a ratio of
// exactly 1, and supplies the breathing room with a margin between source
// lines. That works for code, where a line is short and never wraps. A
// prompt is prose: one long source line that pre-wrap breaks into many
// visual lines, and every one of those gets the 1.0 ratio with no margin at
// all, so the text packs into a slab. A real line height fixes it, and the
// margin goes so wrapped lines and source lines share one rhythm. Blank
// source lines still separate the steps in a structured prompt.
.highlight .line {
font-size: pxToRem(16);
line-height: 1.625;
color: $prompt-body-text;
&:not(:last-of-type) {
margin-bottom: 0;
}
}
.highlight > pre {
// On the pre, not on .highlight: Chroma puts class="chroma" on this
// element and syntax-highlight.scss gives .chroma its own background, so a
// color set on the parent is painted over and the change is invisible.
// $neutral-10 in dark, so the block recesses below the lighter card; light
// gives it a $neutral-20 fill below.
background: $prompt-block-surface;
// The pre carries the same radius as its .highlight wrapper so the fill's
// own corners are rounded, not just clipped by the wrapper.
border-radius: pxToRem(12);
padding: pxToRem(16);
white-space: pre-wrap;
overflow-wrap: break-word;
// copy-code.js absolutely positions its button at the top right of
// .highlight. Wrapped text now reaches the full width, so the first line
// would run underneath it.
padding-right: pxToRem(56);
}
&__skill {
font-size: $font-size-s;
color: $prompt-text-secondary;
margin: pxToRem(16) 0 0;
line-height: 1.5;
code {
font-family: $font-family-code;
font-size: pxToRem(12);
}
a {
color: $prompt-accent;
text-decoration: none;
text-underline-offset: pxToRem(2);
&:hover {
text-decoration: underline;
}
}
}
& &__skill a code:not([class]) {
background: none;
padding: 0;
color: inherit;
}
[data-theme='light'] & {
background: $prompt-surface-light;
border-color: $prompt-border-light;
}
}
[data-theme='light'] .prompt__summary {
color: $prompt-text-light;
}
[data-theme='light'] .prompt__summary:hover .prompt__kind {
color: $prompt-text-light;
}
[data-theme='light'] .prompt__skill,
[data-theme='light'] .prompt__kind {
color: $prompt-text-secondary-light;
}
[data-theme='light'] .prompt__kind {
border-right-color: $prompt-border-light;
}
[data-theme='light'] .prompt .highlight > pre {
background: $prompt-block-surface-light;
}
@media (prefers-reduced-motion: reduce) {
.prompt__summary::before {
transition: none;
}
}
@@ -0,0 +1,61 @@
{{- /*
Generated index of every prompt in the docs.
Ranges the headless prompts section rather than scraping pages, so the table
cannot drift from the prompt files. Each row's page comes from the prompt's
own `page:` param; check-prompts.sh verifies that page really includes it.
This emits real table markup because a shortcode's output is inserted as HTML
and never re-parsed as markdown, so pipe rows would render as literal text.
The Markdown output format has its own variant, prompt-index.markdown.md,
which does use pipes. Keep the two in step.
*/ -}}
{{- $section := site.GetPage "documentation/headless/prompts" -}}
{{- if not $section -}}
{{- errorf "prompt-index: cannot resolve documentation/headless/prompts. The section needs an _index.md setting build.list to 'local'; the parent headless bundle cascades 'never', which makes this lookup nil." -}}
{{- end -}}
{{- /* Emitted without attributes on purpose: article-content.html matches a
bare `<table>`, wraps it in .table-responsive and adds `table mb-5`, so
this picks up the same styling and mobile scrolling as every other docs
table. Adding a class here would silently opt out of both. */ -}}
<table>
<thead>
<tr><th>Prompt</th><th>Page</th><th>Agent skills</th></tr>
</thead>
<tbody>
{{- range $section.RegularPages.ByTitle }}
{{- $p := . }}
<tr>
<td>
{{- with $p.Params.page -}}
<a href="{{ . }}#prompt-{{ $p.File.BaseFileName }}">{{ $p.Title }}</a>
{{- else -}}
{{ $p.Title }}
{{- end -}}
</td>
<td>
{{- with $p.Params.page -}}
{{- with site.GetPage . -}}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{- else -}}
<a href="{{ $p.Params.page }}">{{ $p.Params.page }}</a>
{{- end -}}
{{- else -}}
None
{{- end -}}
</td>
<td>
{{- $skills := $p.Params.skills -}}
{{- with $skills -}}
{{- range $i, $s := $skills -}}
{{- if $i }}, {{ end -}}
<a href="https://skills.qdrant.tech/{{ $s }}/SKILL.md"><code>{{ $s }}</code></a>
{{- end -}}
{{- else -}}
None
{{- end -}}
</td>
</tr>
{{- end }}
</tbody>
</table>
@@ -0,0 +1,51 @@
{{- /*
Renders one prompt from content/documentation/headless/prompts/<id>.md.
Usage: {{< prompt "set-up-monitoring" >}}
The prompt file's body is plain text. `highlight` hands it to Chroma as a
value, producing a real `.highlight > pre`, which copy-code.js finds and adds
a copy button to. That is why this element needs no JavaScript of its own.
It is deliberately not wrapped in a fence and run through markdownify. A body
containing its own ``` would close that outer fence early, truncating the
prompt and spilling the rest of it onto the page as loose markdown, with a
clean build and a green check. `highlight` never re-parses the body, so a
prompt can contain any characters at all.
The Markdown output format has its own variant, prompt.markdown.md, which
deliberately drops the body so an agent reading index.md is not handed an
instruction addressed to it.
*/ -}}
{{- $id := .Get 0 -}}
{{- $p := site.GetPage (printf "documentation/headless/prompts/%s" $id) -}}
{{- if not $p -}}
{{- errorf "prompt: no prompt file found for id %q (looked in content/documentation/headless/prompts/). Called from %s" $id .Page.RelPermalink -}}
{{- end -}}
<details class="prompt" id="prompt-{{ $id }}"{{ if $p.Params.open }} open{{ end }}>
<summary class="prompt__summary">
{{- /* Names the kind of thing this is while collapsed. The summary line is
the only discovery surface a prompt has, so the type marker is placed
here by the shortcode rather than left to each author to work into the
title. Prompt titles are therefore plain task names. */ -}}
<span class="prompt__kind">Agent Prompt</span>
<span class="prompt__summary-label">{{ $p.Title }}</span>
</summary>
<div class="prompt__body">
{{ highlight (trim $p.RawContent "\n") "text" "" }}
{{- /* `skills` is always a sequence, even with one entry, so there is one
shape to render rather than two to keep in step. */ -}}
{{- $skills := $p.Params.skills }}
{{- with $skills }}
<p class="prompt__skill">
Uses the
{{- range $i, $s := $skills -}}
{{- if $i }}{{ if eq (add $i 1) (len $skills) }} and{{ else }},{{ end }}{{ end }}
<a href="https://skills.qdrant.tech/{{ $s }}/SKILL.md"><code>{{ $s }}</code></a>
{{- end }}
agent skill{{ if gt (len $skills) 1 }}s{{ end }}.
<a href="/documentation/agentic-tools/skills/">What are agent skills?</a>
</p>
{{- end }}
</div>
</details>