Authoring Agents#

Warning

Project Neo is currently in Technical Preview. Features described in this section may change before general availability.

This page covers what you can change about an agent and when: how to write its instructions, and the full definitions of preferences, constraints, and pins. For the broader configuration model, typed resources, and how a request resolves across the project, agent, user, and request layers, see the Overview page.

An agent is defined by:

  • Instructions

    What it does. The prose the model runs as its system prompt, authored as the markdown body of a .md agent.

  • Preferences

    Soft defaults for the model, the reasoning effort, the active traits, and the retrieval profile. A user can override a preference.

  • Constraints

    Hard restrictions. Values outside the allowed set cannot be used for the agent. A user cannot override a constraint.

  • Pins

    Absolute locks. Identity-level decisions that nothing can override.

  • Extends

    Inherit configuration from another agent.

For the complete catalog of preference paths you can set, with the type and allowed values for each, see the Preference Reference page.

Write the Instructions#

The instructions are the markdown body of the .md agent, everything below the block enclosed by the --- lines at the top of the file. The Squirro instance passes that body to the model as its system prompt, essentially as written. Nothing strips or reformats it, so the structure you author is the structure the model receives.

The one exception is the <when> tag. The character sequences <when ...> and </when> are reserved for the model-specific macro described in the Model-Specific Text section. The platform interprets them instead of passing them through, and a sequence that is not a valid tag fails genai lint. Do not write <when or </when> in your instructions unless you mean the macro.

Because the model receives the raw content, use markdown to make the instructions clear and to emphasize what matters:

  • Bold for the rules that must not be missed.

  • Bulleted or numbered lists for steps, checks, and output formats.

  • Headings to separate concerns, such as tone, scope, and what to refuse.

  • Inline code for exact tool names, field names, or literal values.

A short, well-structured prompt usually outperforms a long, unstructured one. Lead with the role and the task, then the rules, then the output format.

---
kind: Agent
schema: v1
metadata:
  id: research
entrypoint: true
---

You are a research analyst for the compliance team.

**Always:**

- Cite every claim with its source.
- Flag anything you cannot verify rather than guessing.

## Output

Return a one-paragraph summary, then a bulleted list of findings.

Model-Specific Text#

To include a fragment of the prompt only for certain models, wrap it in a <when> tag. The <when> tag matches on the resolved model and includes its body only when the pattern matches:

Answer concisely.

<when model="claude-opus-*">
For this model, show your reasoning step by step before the final answer.
</when>

The tag accepts one of two keys, model or provider, and the pattern is a glob, so claude-opus-* matches every Opus version. A tag whose pattern does not match contributes nothing to the rendered prompt.

Several sibling tags are allowed, and the bodies of the ones that match are included in the order they appear. The same key and pattern can appear more than once, and each matching tag contributes its body at its own position, so repeated tags accumulate rather than override one another. For a request resolved to an Opus model, the following renders both extra lines, the reasoning line first and the citation line second:

Answer concisely.

<when model="claude-opus-*">
Show your reasoning step by step before the final answer.
</when>

<when provider="anthropic">
Cite the source for every claim.
</when>

Tags cannot be nested. A <when> tag inside another <when> tag fails genai lint:

<when provider="anthropic">
Cite your sources.
<when model="claude-opus-*">
Show your reasoning.
</when>
</when>

Each <when> tag takes a single key, either model or provider, with one pattern. You cannot put both keys in one tag, and because tags cannot be nested, there is no way to require a model and a provider at once. Sibling tags match independently, so use a model pattern when you need a specific model and a provider pattern when you need a whole provider family.

To share prose across several agents rather than conditioning it on the model, use a trait, described in the Compose Prose With Traits section.

Set a Default Model#

Use prefers to set the default model for the agent:

---
kind: Agent
schema: v1
metadata:
  id: research
prefers:
  inference.model: claude-sonnet
---

You are a careful research analyst...

If the user does not choose a model, the agent uses Sonnet. If the user picks a different published model, that choice wins.

prefers is the least restrictive primitive: it sets the starting value but leaves every choice open to the user. Constraints and pins, covered next, restrict what the user can select.

Restrict the Model Set#

Use constrains to restrict which values are allowed:

constrains:
  inference.model:
    in:
      - claude-sonnet
      - claude-opus

A user can switch between Sonnet and Opus but cannot pick any other model for the agent. An attempt to pick a model outside the allowed set falls through to a value the constraint allows.

For the full predicate syntax, such as in, not_in, and matches, see the Predicate Syntax for Constraints section. For the resolution model behind it, see the Overview page.

Lock a Value with a Pin#

Use pins when a value is part of what the agent is and must never change:

pins:
  source.documents.enabled: false

A pin fixes the value. Nothing can change it, and nothing else needs to set it.

The decision rule:

  • prefers

    Sets a default value that the user can still override. The least restrictive primitive. Use it whenever the user should be free to choose. For example, set inference.model to a default model the user can swap, or trait.style: concise as a starting tone the user can change.

  • constrains

    Limits which values are allowed. An agent can narrow the set further than the project, but can never widen it beyond what a parent allows. Use it to keep choices within a safe or supported range. For example, limit inference.model to the two models you have vetted, so a user cannot select an unsupported one.

  • pins

    Locks the value so nothing can change it, not a user, an agent, or a request. Use it only for a value that is part of what the agent is, rather than a setting the user should be able to touch. For example, pin source.documents.enabled: false on an agent that must never read from documents.

Share Configuration with Extends#

When several agents should share the same model constraints, capability set, and retrieval defaults, factor the shared configuration into a .yaml template:

# agents/research_template.yaml
kind: Agent
schema: v1
metadata:
  id: research_template

constrains:
  inference.model:
    in:
      - claude-sonnet
      - claude-opus

prefers:
  capability.code_interpreter.enabled: true
  source.documents.enabled: true

The template is a .yaml file rather than a .md file because it holds configuration only. A .yaml agent has no markdown body, so it carries no instructions, which is exactly why instructions are never inherited through extends.

The name research_template is only an example. You are free to name the template anything, and the platform gives the name no special meaning. The id and the file name are yours to choose, as long as the id follows the identifier rules, and a child references the template by that same id in its extends list.

Then use extends from each agent that needs it:

---
# agents/deep_research.md
kind: Agent
schema: v1
metadata:
  id: deep_research
extends: [research_template]
prefers:
  inference.model: claude-sonnet
  inference.reasoning_effort: high
---

You are a deep research analyst. Take your time. Cite your sources.

extends inherits configuration only, meaning the preferences, constraints, and pins of the parent. It never inherits instructions: a child agent does not pick up the parent prose, so each runnable .md agent writes its own instructions. To share prose across agents instead, use a trait, described in the Compose Prose With Traits section.

A preference that falls outside a constraint inherited through extends does not fail the deploy. At runtime, resolution records the value as invalid and falls through to the next source. Use genai explain to spot a preference that never takes effect, then either pick a value the parent allows or relax the parent constraint.

Compose Prose With Traits#

A TraitDimension is a named dimension where each entry contributes a prompt fragment when active. Use it to reuse the same prose across several agents:

# trait-dimensions/style.yaml
kind: TraitDimension
schema: v1
metadata:
  id: style

traits:
  concise:
    prompt_fragment: |
      Keep your responses tight. No preamble, no recap.
  detailed:
    prompt_fragment: |
      Be exhaustive. Walk through your reasoning and cite each step.

An agent activates a trait through preferences. Here, the research agent, a .md file, selects the concise entry of the style dimension:

---
kind: Agent
schema: v1
metadata:
  id: research
entrypoint: true
prefers:
  trait.style: concise
---

You are a research analyst for the compliance team.

When the agent instructions are rendered, the fragment for the active trait is appended. Multiple dimensions compose freely. Your configuration can also set a baseline trait on the Project resource, which applies to every agent that does not set its own value. The Project resource is the project manifest, a YAML file named project.yaml:

# project.yaml
kind: Project
schema: v1
title: Compliance workspace

prefers:
  inference.model: claude-sonnet
  trait.style: detailed

With this manifest in place, every agent uses the detailed style by default. An agent that sets its own trait.style overrides that baseline. The research agent above sets trait.style: concise, so it stays concise rather than falling back to the project detailed.

Entrypoints and Delegates#

An agent can be user-selectable, a subagent target, both, or neither:

  • Entrypoint

    An agent the user picks in the conversation interface. Set entrypoint: true.

    ---
    kind: Agent
    schema: v1
    metadata:
      id: assistant
    entrypoint: true
    ---
    
    You are a helpful assistant.
    
  • Delegate

    A subagent that another agent can call, not user-selectable. Set delegate: true and provide a description that a calling agent reads to decide whether to call it. A calling agent needs capability.subagents.enabled: true to invoke a delegate at runtime.

    ---
    kind: Agent
    schema: v1
    metadata:
      id: citation_checker
    delegate: true
    description: Verifies that quoted passages match their cited sources. Returns a list of mismatches.
    ---
    
    You verify citations. Given a document and a list of quotes, check each quote against the source...
    
  • Both

    An agent that is both user-selectable and callable as a subagent. Set both flags.

    ---
    kind: Agent
    schema: v1
    metadata:
      id: research
    entrypoint: true
    delegate: true
    description: Researches a question and returns a sourced summary.
    ---
    
    You are a research analyst for the compliance team.
    
  • Neither

    There is no neither flag. When the envelope mentions neither entrypoint nor delegate, both fields take their default of false, and the agent is neither user-selectable nor callable as a subagent, so it is never invoked directly. This is therefore the default state of any agent that does not opt in. Its only role is to be an extends parent: a shared template, such as the research_template above, that other agents inherit configuration from. Because it is never invoked, it needs no instructions and is typically a configuration-only .yaml file. To make an agent reachable, set entrypoint: true, delegate: true, or both.

    kind: Agent
    schema: v1
    metadata:
      id: research_template
    
    constrains:
      inference.model:
        in:
          - claude-sonnet
          - claude-opus
    

How an Agent Appears in Chat#

When you deploy a configuration, the entrypoint agents become the assistants a user can select in the Chat dashboard. What you author determines what the user sees and how the conversation starts.

In the picker:

  • Only entrypoint agents are offered, in the order set by entrypoint_order and then by id. An agent that is not an entrypoint, such as a delegate, is never shown.

  • Each assistant shows a title and, when set, a short summary. The title is the agent title when you set one, otherwise a readable label derived from the agent id, so deep_research appears as Deep Research.

The settings a user can change for the selected agent follow from what you authored: a preference appears as the default, a constraint narrows the choices to the allowed set, and a pin appears as a fixed value the user cannot change. A capability whose backing resource kind your configuration does not publish is shown but cannot be turned on.

Once an agent is selected:

  • When the agent defines an opening message, the assistant shows it as the first message of the conversation, before the user has typed anything. Use it to greet the user or explain what the agent can help with.

  • When the agent sets pin_to_conversation, choosing it locks the conversation to this agent. Every later turn stays with it, and any attempt to switch to a different agent is ignored for the rest of that conversation. Without this flag, a user can switch agents mid-conversation.

For the agent fields behind this, see the Agent resource on the Resource Reference page.

Lock the Model Set Project-Wide#

To restrict the models for every agent in the project, set the constraint on the Project resource:

# project.yaml
constrains:
  inference.model:
    in:
      - claude-sonnet
      - claude-opus

Agents can narrow further, for example constraining to just Sonnet, but they cannot widen. The active set is the intersection of the project restriction and the selected agent restriction.

Keep Long Sessions in Budget#

A long, tool-heavy conversation can grow past what a model accepts in a single turn. The context_budget on the Project resource keeps each turn within the model window by trimming what is sent to the model. Because it is project policy rather than a preference, neither a user nor a request can turn it off:

# project.yaml
context_budget:
  tool_output_pruning: true
  tool_output_pruning_trigger_tokens: 100000
  turn_truncation: true

The two behaviors are independent and can be used together or on their own:

  • Tool-output pruning clears older tool results once they cross tool_output_pruning_trigger_tokens, an absolute token count. Set it to match the models you run: a higher value for large-window models, a lower one for small ones.

  • Turn truncation is a safety fallback. It drops the oldest whole turns as the input approaches the model context window. For a smaller-window model, set context_window on its Model resource so that truncation engages before the provider rejects the request.

For the complete context_budget field set, with the type and default of each field, see the Resource Reference page.

Common Pitfalls#

  • No shared retrieval master switch

    There is no single setting that turns all retrieval on or off. Each retrieval family has its own switch, and you set each one independently: source.documents.enabled for document retrieval and source.knowledge_graph.enabled for knowledge graph retrieval. Turning one off has no effect on the other, so to turn off retrieval entirely you must set every family switch to false.

  • A capability exposes every published resource of its kind

    Turning a capability on does not let you pick which resources it reaches. It exposes every resource of that capability’s backing kind that your configuration publishes. For example, turning on the knowledge graph capability gives the agent access to every KnowledgeGraph resource you have published, not a chosen subset. The only way to narrow what an agent can reach is to publish fewer resources of that kind, so keep the ones an agent should not use out of the configuration it runs under.

  • inference.model has no default

    Most settings fall back to a built-in default when nothing sets them, but inference.model does not: there is no default model, so every configuration must choose one. Set it on the Project resource, under prefers for a default the user can change or under pins for a fixed value. If neither is set, genai lint fails and the configuration cannot deploy.

  • Pins on the same setting must agree

    A pin is an absolute lock, so two pins on the same setting cannot both hold unless they name the same value. The Project resource and an agent may both pin a setting, but only if they pin it to the same value. If they pin it to different values, the conflict cannot be reconciled and the deploy fails. When you pin a setting in more than one place, make sure every pin uses the same value, or pin it in only one place.

Next Steps#

  • For the complete catalog of preference paths, with the type and default of each, see the Preference Reference page.

  • For the field-by-field reference of every resource kind, see the Resource Reference page.

  • For every genai command and flag, see the CLI Reference page.

  • For the configuration model and how a request resolves across sources, see the Overview page.