For the complete documentation index, see llms.txt. This page is also available as Markdown.

Template Authoring Guide

What you'll learn: How to create a custom behavioral template that maps your framework's events to Kyvvu's 12 atomic behavior types.


When you need a custom template

The SDK ships four built-in templates:

  • decorator -- for Python agents using @kv.step decorators.

  • langchain -- for LangChain agents using the callback handler.

  • langgraph -- for LangGraph graph executions.

  • crewai -- for CrewAI agents.

You need a custom template when you're integrating a framework that none of the built-ins cover and isn't using @kv.step decorators. Examples: a custom HTTP-based agent, a Microsoft AutoGen agent, or any framework that emits its own event types. The template engine (loading, matching, interpolation) lives in kyvvu_engine.templates; the YAML files live with their integration.

Template YAML anatomy

A template is a YAML file with three top-level keys and an ordered list of rules. Here's an annotated excerpt from the built-in decorator template:

name: decorator                    # Template identifier
version: "1.0"                     # Version string (for your own tracking)
description: |                     # Optional human-readable description
  Maps Python decorator annotations to v0.05 Kyvvu atomic behaviors.

rules:                             # Ordered list of matching rules

  - id: model_call                 # Unique rule identifier
    description: |                 # What this rule matches (for documentation)
      AI model inference: LLM completion, chat, embedding, vision, etc.
    match:                         # Conditions -- all must be true for this rule to fire
      step_type: "step.model"
    behavior:                      # Output fields when the rule matches
      step_type: "step.model"
      verb: "POST"
      step_name: "{{ function_name }}"
      properties:
        model:
          name: "{{ kwargs.model | default('unknown') }}"
          provider: "{{ kwargs.provider | default('unknown') }}"

  - id: unknown                    # Catch-all fallback rule (should be last)
    match:
      step_type: "step.unknown"
    behavior:
      step_type: "step.unknown"
      step_name: "{{ function_name }}"

Each rule has:

Key
Required
Purpose

id

Recommended

Unique identifier for the rule (for debugging and documentation).

description

No

Human-readable description.

match

Yes

Conditions dict. All conditions must pass (implicit AND).

behavior

Yes

Output dict with step_type, verb, step_name, properties.

Note: Do not set scope in a template. The behavior's scope is derived automatically from step_type at emit time (task.*task, everything else → step) by the decorator and every callback adapter, so a scope: key in the YAML is ignored.

The context dict

When your integration fires, it builds a context dict and passes it to template.match(context). The template's rules match against this context.

The context keys depend on your integration. Here are the two built-in examples:

Decorator context keys:

LangChain context keys:

Designing context keys for a new framework:

  1. Identify what events your framework emits (e.g. http_request, llm_call, tool_invoke).

  2. Choose a primary discriminator key (e.g. event or event_type).

  3. Include any metadata your rules need to distinguish sub-types (e.g. HTTP method, model name, tool name).

  4. Include data you want to extract into properties (e.g. URL, model parameters).

Match semantics

Rules are evaluated top to bottom in list order. The first rule whose match block fully matches the context wins. Evaluation stops immediately -- no further rules are checked.

Exact string matching

Each key in match is compared to the same key in the context dict. All keys must match (implicit AND):

Nested keys (dot-path)

Use dot notation to match nested dict values:

Regex patterns

Two special condition keys support regex matching:

  • name_pattern -- regex match against context["name"] (case-insensitive)

  • function_name_pattern -- regex match against context["function_name"] (case-insensitive)

Why rule order matters

Because of first-match-wins, specific rules must come before general ones. For example, in the LangChain template, tool rules are ordered: read-pattern tools, then write-pattern tools, then the catch-all tool default. If the catch-all were first, it would match every tool and the specific patterns would never fire.

The fallback/default rule

A rule with an empty match block matches everything:

Every template should end with a fallback rule so that unrecognized events produce a step.unknown behavior instead of returning None (no match).

Property extraction

String values in the behavior block support {{ }} interpolation from the context dict.

Simple lookup

Resolves context["function_name"]. If the key is missing, produces an empty string.

Default values

If context["kwargs"]["model"] is missing or None, produces "unknown".

Nested paths

Traverses context["kwargs"]["url"] using dot-path notation.

Mapping to Kyvvu's property schema

Properties are grouped by purpose. The standard groups (from the Behaviours reference) are:

Group
Used with
Example keys

target

step.resource

host, resource_type, system, table, domain, trust

model

step.model

name, provider, temperature, parameters

guard

step.gate

check_type, result

message

step.message

channel, recipient, sender

auth

step.credential

credential_name, provider, scope

data

any

classification, fields

exec

step.exec

runtime, isolation, side_effect_class

usage

step.model outputs

prompt_tokens, completion_tokens, cost_usd

Example property extraction in a rule:

Testing your template

Use BehaviorTemplate.from_path() to load your template and match() to verify mappings:

Test each event type your framework emits. Test edge cases: missing fields, unknown events, events with None values. Verify the fallback rule catches unrecognized events.

Worked example: HTTP agent template

Suppose you have an agent that emits these events:

Event
Data

http_request

method, url

llm_call

model, prompt

approval_requested

approver

task_started

--

task_completed

--

Step 1: Task lifecycle rules

Step 2: LLM call rule

Step 3: HTTP request rule

Step 4: Approval gate

Step 5: Fallback rule

Step 6: Test it


Next steps

  • Templates overview -- deep-merge semantics and built-in template details

  • Atomic Behaviours -- the 12 behavior types and property groups

  • Writing a New Integration -- the FrameworkAdapter base class for hooking into frameworks

  • Pattern templates -- reference snippets for common agent architectures (RAG, ReAct, human-in-the-loop, multi-agent)

Last updated