Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ AI-Q has two distinct kinds of skill, separated by audience:
| :-- | :-- | :-- |
| **Audience** | Developers changing the AI-Q repo | Users calling a running AI-Q server |
| **Location** | `.agents/skills/` (this directory) | top-level `skills/` |
| **Examples** | `aiq-add-data-source`, `aiq-add-tool`, `aiq-release-qa`, `aiq-prepare-pr` | `aiq-deploy`, `aiq-research` |
| **Examples** | `aiq-add-data-source`, `aiq-add-tool`, `aiq-release-qa`, `aiq-prepare-pr`, `aiq-customize-prompts-models`, `aiq-maintain-ci` | `aiq-deploy`, `aiq-research` |
| **Assumes** | A repo checkout and dev toolchain | A reachable AI-Q backend |

Consumer skills under `skills/` are authored to be self-contained and exportable
Expand Down
118 changes: 118 additions & 0 deletions .agents/skills/aiq-customize-prompts-models/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
name: aiq-customize-prompts-models
description: Use when customizing AI-Q agent behavior through Jinja2 prompt templates or per-agent model selection — editing prompts under src/aiq_agent/agents/*/prompts/, adding template variables, or assigning/swapping LLMs per agent role via config (the llms section plus per-agent fields like orchestrator_llm, planner_llm, researcher_llm, writer_llm, source_router_llm).
license: Apache-2.0
compatibility: Claude Code, Codex, Cursor, OpenCode, and Agent Skills-compatible tools.
metadata:
version: "0.1.0"
source-repo: "NVIDIA-AI-Blueprints/aiq"
tags: "aiq nemo-agent-toolkit prompts models jinja2 customization"
allowed-tools: Read Bash Edit
---

# Customize AI-Q Prompts and Models

Use this skill when a developer wants to change *how* an AI-Q agent reasons or
*which* model it uses — by editing a Jinja2 prompt template or by assigning a
different LLM to an agent role — usually without touching agent code. AI-Q agent
behavior is driven by prompts and config, so most tuning is a template or YAML
change. The one exception is adding a brand-new template, which needs a one-line
`load_prompt` wiring in the agent (see the prompt-templates reference).

## Start Here

- Confirm the change is prompt or model customization, not new tool/agent logic.
For a new retrieval source use `aiq-add-data-source`; for a new tool use
`aiq-add-tool`.
- Read the authoritative docs and the existing templates/config below first.
- Prefer editing an existing template or config field over adding new machinery.
- Keep the prompt's STRICT citation rules intact, and never hard-code a model
name where an `llms:` ref belongs.
- Keep templates general-purpose: don't hard-code specific queries, domains, or
source/tool names — those come from the user's request and the
`data_source_registry` at runtime.

## Authoritative References

- `docs/source/customization/prompts.md`: prompt guide — template inventory,
`load_prompt(path, name)`, `render_prompt_template(template, ...)`, the
documented template variables, the STRICT citation rules, and "Creating a New
Template". Note it does not document every template's variables (e.g.
`source_router.j2`, `writer.j2`, `source_registry.j2`) — the `.j2` files are
authoritative for the variables they actually use.
- `docs/source/customization/swapping-models.md`: choosing hosted vs. self-hosted
NIMs and pointing config at them.
- `docs/source/customization/configuration-reference.md`: the `llms` section and
each agent's config fields (`deep_research_agent`, `clarifier_agent`, …).
- `src/aiq_agent/common/prompt_utils.py`: `load_prompt` and
`render_prompt_template`.
- `src/aiq_agent/common/llm_provider.py`: `LLMRole` and `LLMProvider.configure`,
which bind a resolved LLM to an agent role (used by the deep research agent).
- Templates to model on: `src/aiq_agent/agents/deep_researcher/prompts/*.j2`
(orchestrator, planner, researcher, source_router, writer) and
`src/aiq_agent/agents/clarifier/prompts/*.j2`. Other agents have prompts too
(e.g. `shallow_researcher`, `chat_researcher`) — check
`src/aiq_agent/agents/*/prompts/`.

Longer procedures live in this bundle:

- [references/prompt-templates.md](references/prompt-templates.md): where templates
live, how they load and render, template variables, citation rules, and how to
edit or add one safely.
- [references/model-selection.md](references/model-selection.md): the `llms`
section, per-agent LLM fields, role binding via `LLMProvider`, and swapping models.

## Workflow

1. Identify the target agent and whether the change is a prompt or a model.
2. For a prompt: edit the relevant `src/aiq_agent/agents/<agent>/prompts/*.j2`
template; keep its variables and citation rules intact (see the references).
3. For a model: add or point an `llms:` entry in the config and set the agent's
role field (e.g. `orchestrator_llm`, `planner_llm`, `researcher_llm`,
`writer_llm`, `source_router_llm`) to that ref — do not edit Python to swap a
model.
4. Keep token cost in mind: prefer reordering static instructions before dynamic
content (KV-cache reuse) and a cheaper model for low-stakes roles.
5. Validate (below): lint any changed Python, run the agent's tests, and
smoke-run the CLI against the config you changed.
6. Summarize changed files and paste the validation evidence.

## Validation

Run the narrowest checks first; broaden only if you touched shared code.

```bash
uv run ruff check src/aiq_agent # only if you changed Python
uv run pytest tests/aiq_agent/agents/<agent> # the agent's tests (a prompt-only edit may have none)
./scripts/start_cli.sh --config_file <your config> # smoke against the config you edited
```

Expected: the agent loads its templates without a Jinja2 error and runs with the
configured model. A bare `./scripts/start_cli.sh` uses the fixed default
(`configs/config_cli_default.yml`), so pass `--config_file` to exercise your
change. For a prompt-only edit (which often has no dedicated unit test), the
smoke run is the real check; a config/prompt-only change needs no Python lint.

## Common Mistakes

- Breaking a template variable or the STRICT citation rules in
`docs/source/customization/prompts.md`, which degrades report grounding.
- Hard-coding specific queries, domains, or source/tool names into a template,
which biases the agent toward one task and breaks generalization. Source/domain
selection is data-driven (`data_source_registry`, `source_router.j2`); keep
prompts task-agnostic.
- Hard-coding a model name in Python instead of using an `llms:` ref and the
agent's role field, so the model can no longer be swapped from config.
- Changing an agent's default model when you meant a single sub-role. The deep
research agent's default is `orchestrator_llm` (there is no generic `llm`
field); the clarifier's default is `llm`. Editing the default shifts every
unset role.
- Introducing a large dynamic prefix that defeats KV-cache reuse and raises cost.
- Pointing an agent's role at a model whose entry is not defined in `llms:`.

Comment thread
zhongxuanwang-nv marked this conversation as resolved.
## Related Skills

- `aiq-add-tool`
- `aiq-add-data-source`
- `aiq-release-qa`
- `aiq-prepare-pr`
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!--
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# Model selection

Authoritative sources: `docs/source/customization/swapping-models.md` and the
`llms` section of `docs/source/customization/configuration-reference.md`.

## Define models once, reference them by name

Declare each model in the config `llms:` section, then reference it by name from
an agent. Do not hard-code model names in Python.

```yaml
llms:
nemotron_super_llm:
_type: nim
model_name: <a capable model>
gpt_oss_llm:
_type: nim # `openai` is also supported (also takes model_name)
model_name: <a cheaper model>
```

## Assign a model to an agent role

Agents expose per-role LLM fields, but the two agents wire them differently — so
check the agent you are editing.

**Deep research agent** (`src/aiq_agent/agents/deep_researcher/register.py`)
defines `orchestrator_llm` (required) plus `source_router_llm`, `researcher_llm`,
`planner_llm`, and `writer_llm` (`LLMRef | None`). It seeds the provider default
from `orchestrator_llm` (`LLMProvider.set_default(...)`) and binds each set role
with `LLMProvider.configure(LLMRole.<ROLE>, llm)`
(`src/aiq_agent/common/llm_provider.py`). Field → role:

| Config field | `LLMRole` |
| :-- | :-- |
| `orchestrator_llm` (required) | `ORCHESTRATOR` — also the provider default |
| `source_router_llm` | `ROUTER` |
| `researcher_llm` | `RESEARCHER` |
| `planner_llm` | `PLANNER` |
| `writer_llm` | `REPORT_WRITER` |

An unset role falls back to the provider default (the `orchestrator_llm` model).
There is **no** generic `llm` field on the deep research agent.

**Clarifier** (`src/aiq_agent/agents/clarifier/register.py`) defines `llm` (its
default) and `planner_llm`. It does **not** use `LLMProvider.configure` for the
role — it passes `planner_llm` straight to the agent constructor, and `planner_llm`
falls back to `llm` when unset.

```yaml
functions:
deep_research_agent:
_type: deep_research_agent
orchestrator_llm: nemotron_super_llm # required; also the default for unset roles
source_router_llm: nemotron_super_llm
researcher_llm: nemotron_super_llm
planner_llm: gpt_oss_llm # cheaper model for planning
writer_llm: gpt_oss_llm
```

This mirrors the real configs (for example
`configs/config_domain_routing_and_skills.yml`); copy field names from there
rather than guessing.

## Swapping to a self-hosted NIM

Follow `swapping-models.md`: run the NIM locally, then point the `llms:` entry's
endpoint/model at it. Mind the hosted-API limitations and mitigations the doc
lists.

## Validation

```bash
./scripts/start_cli.sh --config_file <the config where you set the role LLMs> # agent starts with the assigned models
uv run pytest tests/aiq_agent/agents/deep_researcher
```

Expected: the agent starts with the configured models and its tests pass. A bare
`start_cli.sh` runs the fixed default config, so pass the config you edited. Every
role ref must resolve to an entry in `llms:`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!--
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# Prompt templates

Authoritative sources: the `.j2` files themselves (for the variables a template
actually uses) and `docs/source/customization/prompts.md` (for the documented
workflow and many template variables). Note `prompts.md` does not cover every
template — e.g. `source_router.j2`, `writer.j2`, and `source_registry.j2` have no
variable section there yet — so read the `.j2` file when in doubt.

## Where templates live

Each agent owns its Jinja2 templates under
`src/aiq_agent/agents/<agent>/prompts/*.j2`. For example, the deep researcher has
`orchestrator.j2`, `planner.j2`, `researcher.j2`, `source_router.j2`, `writer.j2`,
and `source_registry.j2`; the clarifier has `plan_generation.j2` and
`research_clarification.j2`; `shallow_researcher` and `chat_researcher` have their
own as well.

## How templates load and render

- `load_prompt(path, name)` in `src/aiq_agent/common/prompt_utils.py` reads a
template file as a string.
- `render_prompt_template(template, **kwargs)` (same module) renders it with
Jinja2, injecting the variables. `prompts.md` has a "Template Variables" section
for many agents; for a template it doesn't list, read the `.j2` to see which
variables it references.

## Editing a template safely

1. Keep every `{{ variable }}` the agent passes in; removing one breaks rendering
or silently drops context.
2. Preserve the **Citation Rules (STRICT)** section in `prompts.md` — report
grounding depends on the model emitting citations exactly as instructed.
3. Prefer putting static instructions before dynamic content so the KV cache is
reused across calls (lower latency and token cost).
4. Keep the template task-agnostic — don't hard-code specific queries, domains, or
source/tool names; source/domain routing is data-driven (`data_source_registry`,
`source_router.j2`), and hard-coding it bypasses that routing.
5. Editing an existing `.j2` needs **no** code change.

## Adding a new template

This is the one prompt change that touches Python. Follow "Creating a New
Template" in `prompts.md`:

1. Add the `.j2` under the agent's `prompts/` directory.
2. Write the template (keep variable names consistent with what the agent passes).
3. **Wire it in the agent's Python** — load it with `load_prompt` and render it
with `render_prompt_template` (this is `prompts.md` Step 3). Without this step
the new template is never used.

## Validation

```bash
./scripts/start_cli.sh --config_file <a config using that agent> # template loads (no Jinja2 error)
uv run pytest tests/aiq_agent/agents/<agent> # if the agent has tests
```

Expected: the agent starts and renders the template without error. A bare
`start_cli.sh` runs the fixed default config, so pass the config that exercises
the agent whose template you changed.
109 changes: 109 additions & 0 deletions .agents/skills/aiq-maintain-ci/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: aiq-maintain-ci
description: Use when changing AI-Q continuous integration, pre-commit, or contributor governance — editing .github/workflows/ (ci, ui, skills-eval, request-nvskills-ci), .pre-commit-config.yaml hooks, .github/CODEOWNERS, .coderabbit.yaml, copy-pr-bot, or the .github/skill-eval harness — and validating those changes without breaking the gate.
license: Apache-2.0
compatibility: Claude Code, Codex, Cursor, OpenCode, and Agent Skills-compatible tools.
metadata:
version: "0.1.0"
source-repo: "NVIDIA-AI-Blueprints/aiq"
tags: "aiq ci github-actions pre-commit governance skill-eval"
allowed-tools: Read Bash Edit
---

# Maintain AI-Q CI and Governance

Use this skill when a developer changes AI-Q's CI, pre-commit hooks, or
contributor governance — the GitHub Actions workflows, the pre-commit config,
CODEOWNERS, the CodeRabbit review config, the copy-pr-bot mirror, or the
product-level skill-eval harness. These surfaces gate every PR, so a change must
keep the gate working and must not weaken security or review rules.

## Start Here

- Identify the surface: a workflow (`.github/workflows/`), a pre-commit hook
(`.pre-commit-config.yaml`), governance (`.github/CODEOWNERS`,
`.coderabbit.yaml`, `.github/copy-pr-bot.yaml`), or the skill-eval harness
(`.github/skill-eval/`).
- Read the authoritative files below and `CONTRIBUTING.md` "CI and Bot Workflow"
before editing — the bot/mirror flow is easy to break.
- Make the smallest change; do not weaken secret detection, auth gating, or
code-owner review without a prior design discussion (see `AGENTS.md`).
- Remember CI runs on the copy-pr-bot mirror after `/ok to test`, not on push.

## Authoritative References

- [CONTRIBUTING.md](../../../CONTRIBUTING.md): "CI and Bot Workflow" — copy-pr-bot
mirroring to `pull-request/<N>`, `/ok to test`, `/nvskills-ci`, `/merge`.
- [AGENTS.md](../../../AGENTS.md): "Git and PR hygiene" and the validation
commands CI mirrors.
- `.github/workflows/ci.yml`: jobs `pre-commit`, `test` (pytest + coverage),
`helm-lint`, `test-scripts`. The `pre-commit` job runs Ruff separately and
`SKIP=ruff-check,ruff-format,pytest,helm-lint pre-commit run --all-files` — so
pytest/helm-lint run as their own jobs, not via the hook.
- `.github/workflows/ui.yml`: jobs `install`, `lint`, `type-check`, `unit-test`,
`build`.
- `.github/workflows/skills-eval.yml`: the Skills Eval gate (push +
`workflow_dispatch`; `detect-changes` path gate → `generate-datasets` spec
validation → `harbor-eval` on the self-hosted `aiq-eval` runner).
- `.github/workflows/request-nvskills-ci.yml`: comment-triggered NVSkills CI.
- `.pre-commit-config.yaml`: the hook set. Note `pytest` and `helm-lint` are
`stages: [push]` (see the reference for what that means locally).
- `.github/CODEOWNERS`, `.coderabbit.yaml`, `.github/copy-pr-bot.yaml`: review
routing, path-scoped automated review, and the PR mirror.

Longer procedures live in this bundle:

- [references/workflows-and-hooks.md](references/workflows-and-hooks.md): the
workflows, their jobs/triggers, the copy-pr-bot mirror flow, and the pre-commit
hook inventory (incl. the push-stage hooks).
- [references/skill-eval-harness.md](references/skill-eval-harness.md): how the
`.github/skill-eval/` regression gate finds specs, runs adapters, and verifies.

## Workflow

1. Locate the exact workflow, hook, or governance file and read it plus the
relevant `CONTRIBUTING.md` section.
2. Make the smallest scoped change; keep job names, triggers, and the
`detect-changes` path gate intact unless that is the change.
3. Lint the change: validate YAML and, for workflows, run `actionlint` if it is
installed.
4. Reproduce the affected gate locally where possible — run the pre-commit hooks
or the job's underlying command (see the references).
5. Note that the real CI run happens on the copy-pr-bot mirror after a maintainer
comments `/ok to test`.
6. Summarize changed files and the local validation evidence.

## Validation

```bash
uv run pre-commit run --all-files # default-stage hooks (NOT pytest/helm-lint)
uv run pre-commit run --all-files --hook-stage push # adds the push-stage pytest + helm-lint hooks
uv run pre-commit run --files <changed> # faster, during iteration
actionlint .github/workflows/<file>.yml # if actionlint is installed
```

Expected: hooks pass (or only auto-fix) and any edited workflow is valid YAML.
`pytest` and `helm-lint` are push-stage, so the default `--all-files` run skips
them — CI runs them as the dedicated `test` and `helm-lint` jobs. For skill-eval
changes, see the harness reference: full Harbor runs need the self-hosted runner,
so validate spec/adapter shape locally and rely on the mirrored CI run.

## Common Mistakes

- Adding a trigger `paths:` filter to `skills-eval.yml` instead of using the
`detect-changes` job — the comment in that workflow explains why path-filtering
the trigger is wrong here.
- Weakening `detect-secrets`, auth gating, or code-owner review to make CI pass.
- Expecting CI to run on push; it runs on the copy-pr-bot mirror after
`/ok to test`.
- Assuming `pre-commit run --all-files` reproduces the whole gate — `pytest` and
`helm-lint` are `stages: [push]`, so they do not run at the default stage. Use
`--hook-stage push` (or run them directly), and remember CI runs them as
separate jobs.
- Editing `.github/CODEOWNERS` without updating the paths it routes, so reviews
go to the wrong owners.

## Related Skills

- `aiq-release-qa`
- `aiq-prepare-pr`
Loading
Loading