*** title: Slot Best Practices position: 2 excerpt: Six slot anti-patterns that break in production and how to fix them. deprecated: false hidden: false metadata: title: Slot Best Practices | Agent Studio Best Practices description: >- Common slot mistakes in Agent Studio: bloated descriptions, wrong types for people, missing validation, timezone mapping, unnecessary resolvers, and impossible dependencies. Each pattern with bad example, explanation, and fix. robots: index ------------- Slots look simple in the UI. Pick a name, write a description, choose a type. But the wrong choices here cascade through every action and conversation that touches the slot. These six patterns are the ones that break in production, not in testing. ## Bloated Descriptions Slot descriptions tell the reasoning engine *what* a slot represents. They're semantic labels, not rule engines. When you stuff validation logic, formatting rules, or behavioral instructions into a description, none of it gets enforced. ### What it looks like ```yaml title="Bloated slot description" - name: due_date description: | The date when the task is due. MUST be in YYYY-MM-DD format. The date CANNOT be in the past. If user provides a past date, reject it and ask again. The date should be within the next 90 days. If the user says "tomorrow", calculate the date. data_type: string ``` ### Why it breaks The reasoning engine reads slot descriptions to understand *what* a slot represents, not *how* to handle it. When you pack rules into descriptions: 1. **Rules aren't enforced.** The engine might ignore them or hallucinate compliance. 2. **Context bloat.** Every character in the description consumes tokens in every reasoning call. 3. **Maintenance nightmare.** Rules scattered across descriptions instead of centralized in policies. ### Real consequence User submits "yesterday" as due date. The engine sees the rule in the description but has no enforcement mechanism. Bad data hits your API, which returns a cryptic error like `"Invalid date range"`. User sees a generic failure message. Support ticket filed. ### The fix ```yaml title="Clean description + validation policy" - name: due_date description: "The target completion date for this task. Expected format: YYYY-MM-DD." data_type: string validation_policy: rule: $PARSE_TIME(value) >= $TIME() error_message: "Due date must be today or in the future" ``` Description says *what* the slot is and *what format* to expect. Validation policy enforces the *rules*. The engine shows the `error_message` to the user and re-collects the slot automatically. Formatting hints like "Expected format: YYYY-MM-DD" are fine in descriptions — they help the engine collect the right shape of data. What doesn't belong is behavioral logic: "if the user says X, do Y" or "reject and ask again." **Rule of thumb:** Descriptions should say *what*, not *how*. Format hints are fine. Behavioral rules are not. If your description has instructions for the engine rather than context about the data, move those rules to validation policies, decision policies, or resolver strategies. Know the trade-off between deterministic enforcement (policies, DSL) and non-deterministic prompting (descriptions) — use the platform's built-in tools wherever the behavior should be consistent and reliable. *** ## Wrong Slot Type for People Using `string` to collect people is the single most common slot mistake. It forces the reasoning engine to parse structured data from free text, which is unreliable and skips the platform's built-in identity resolution. ### What it looks like ```yaml title="String slot for people" - name: attendees description: "Comma-separated list of attendee emails like john@company.com, jane@company.com" data_type: string ``` ### Why it breaks You're asking the engine to parse a comma-separated string into individual identities. This requires: 1. An LLM call to parse the string into a list 2. Another call to resolve each email to a user 3. Error handling for malformed input ("john at company dot com") 4. No fuzzy matching ("John" won't find "[john.smith@company.com](mailto:john.smith@company.com)") ### Real consequence User says "invite John and the marketing team". Your string slot captures `"John and the marketing team"`. The engine tries to parse this as emails. Fails. User tries `"john@company.com, marketing-team@company.com"`. Engine parses it. But marketing-team is a distribution list, not a user. API call fails with `"Invalid attendee"`. ### The fix ```yaml title="Correct slot type for people" - name: attendees description: "People to invite to the meeting" data_type: list[User] ``` The platform's `User` [resolver](/agent-studio/core-concepts/agentic-automation-engine/slot-resolvers): * Fuzzy matches "John" to "[john.smith@company.com](mailto:john.smith@company.com)" * Shows disambiguation UI if multiple matches * Validates users exist before proceeding * Handles display names, partial matches, and typos **Rule of thumb:** If you're collecting people, use `User` or `list[User]`. Always. *** ## Missing Validation Policy Free text slots without validation accept whatever the user provides. "Priority 99" passes through. "Start date: last Tuesday" passes through. Your API receives garbage, returns errors, and the user sees a generic failure. ### What it looks like ```yaml title="Slots without validation" - name: priority description: "Priority level 1-5 where 1 is highest" data_type: number - name: start_date description: "When the PTO should start. Must be today or later." data_type: string ``` ### Why it breaks Without a validation policy, the engine accepts whatever the user provides. The "1-5" constraint lives in the description only — which means it's a suggestion, not a rule. ### Real consequence HR system only accepts priority 1-5. User says "make it priority 10 — super urgent!" Engine passes `priority: 10`. API returns `400 Bad Request: priority must be between 1 and 5`. User sees "Something went wrong. Please try again." ### The fix ```yaml title="Slots with validation policies" - name: priority description: "Priority level" data_type: number validation_policy: rule: value >= 1 AND value <= 5 error_message: "Priority must be between 1 (highest) and 5 (lowest)" - name: start_date description: "When the PTO should start" data_type: string validation_policy: rule: $PARSE_TIME(value) >= $TIME() error_message: "Start date must be today or in the future" ``` When validation fails, the engine shows the `error_message` and re-collects the slot. The user gets a clear, specific prompt instead of a cryptic API error. ### Common Validation Patterns | Use Case | DSL Expression | | ---------------- | ------------------------------------ | | Date not in past | `$PARSE_TIME(value) >= $TIME()` | | Number in range | `value >= 1 AND value <= 100` | | Non-empty | `value != '' AND value != null` | | Minimum length | `value.$LENGTH() >= 10` | | Allowed values | `value IN ['low', 'medium', 'high']` | Validation policies use the same [DSL functions](/agent-studio/configuration-languages/moveworks-dsl-reference) available in output mappers. `$PARSE_TIME`, `$TIME`, `$LENGTH`, and comparison operators all work inside `rule` expressions. *** ## Timezone Without Resolver Users say "PST" or "Pacific Time". APIs need "America/Los\_Angeles". Without a resolver mapping display values to IANA identifiers, the raw user input flows straight to your API and breaks. ### What it looks like ```yaml title="Raw string for timezone" - name: timezone description: "User's timezone like PST, EST, or America/Los_Angeles" data_type: string ``` ### Why it breaks Users say "PST" or "Pacific Time". Calendar APIs (MS Graph, Google Calendar) need IANA format: `America/Los_Angeles`. Without a resolver: 1. User enters "PST" 2. Slot stores "PST" 3. API receives `timezone: "PST"` 4. API fails or interprets incorrectly ### Real consequence Meeting scheduled for "9 AM PST". MS Graph Calendar API receives `timeZone: "PST"`. Graph doesn't recognize "PST" — it needs IANA format. Meeting created with wrong timezone. Attendees in different timezones see the wrong time. Everyone shows up at different times. ### The fix ```yaml title="Static resolver for timezone mapping" - name: timezone description: "Timezone for the meeting in IANA format (e.g. America/Los_Angeles)" data_type: string resolver_strategy: type: static options: - display_value: "Pacific Time (PT)" value: "America/Los_Angeles" - display_value: "Eastern Time (ET)" value: "America/New_York" - display_value: "Central Time (CT)" value: "America/Chicago" - display_value: "Mountain Time (MT)" value: "America/Denver" ``` User sees "Pacific Time (PT)" in the selection. Slot stores `"America/Los_Angeles"`. API gets the right format. Meetings are correct. Note the description also specifies the expected format (IANA) — this helps the engine understand what shape the resolved value takes, even though the resolver handles the actual mapping. Yes, [static resolvers](/agent-studio/conversation-process/resolver-strategies) add an LLM call. For timezone mapping, it's worth it — the alternative is broken meetings. *** ## Static Resolver for Boolean Static resolvers add an LLM call to resolve the user's input to a value. For binary yes/no choices, this adds latency and cost with zero benefit over a native `boolean` slot. ### What it looks like ```yaml title="Resolver for a two-option choice" - name: meeting_type description: "Either virtual or in-person" data_type: string resolver_strategy: type: static options: - display_value: "Virtual (Teams)" value: "virtual" - display_value: "In-Person" value: "in-person" ``` ### Why it breaks Static resolvers add an LLM call to resolve the user's input to a matched value. For two options that map to yes/no, this is overhead: * Extra latency (\~200-500ms per resolution) * Extra cost (LLM tokens) * No benefit over simple `boolean` ### Real consequence Every meeting request takes 500ms longer than necessary. Multiply by thousands of requests. Your plugin is "slow" compared to others. Users notice. ### The fix ```yaml title="Boolean slot — no resolver needed" - name: is_virtual description: "Whether this is a virtual meeting with a Teams link" data_type: boolean ``` ### When to Use a Resolver vs Boolean | Scenario | Use | | ------------------------------------ | --------------- | | Yes/No choice | `boolean` | | Display value differs from API value | Static resolver | | More than 2 options | Static resolver | | Need fuzzy matching on input | Static resolver | Every resolver adds an LLM call. A `boolean` slot is resolved without one. If you're building a two-option choice (approve/reject, yes/no, enable/disable), always use `boolean` over a static resolver. See [Decision Frameworks — Slot Type Selection](/agent-studio/guides/decision-frameworks#slot-type-selection) for the full decision tree. *** ## Conditional Slot Collection You can't conditionally collect one slot based on another slot's value through slot configuration alone. Comments like "required if meeting\_type is in-person" in a description are ignored — the engine either always collects the slot or never does. There is one exception: **context passing** in resolver strategies. If a slot's resolver needs another slot's value to fetch its options (like fetching time-off types for a specific user, or subcategories based on a selected category), you can pass collected slot values into the resolver via [strategy mapping](/agent-studio/conversation-process/resolver-strategies#context-passing-into-dynamic-resolvers). But context passing is for resolver inputs, not for controlling whether a slot gets collected at all. ### What it looks like ```yaml title="Attempting conditional slot collection" - name: room_name description: "Conference room for in-person meetings. Required if meeting_type is in-person." data_type: string inference_policy: infer_if_available ``` ### Why it breaks **You can't conditionally skip slot collection based on another slot's value.** Putting "required if" logic in a description doesn't make the engine obey it. The reasoning engine will either: 1. Always collect the slot (ignoring your "required if" comment) 2. Never collect the slot 3. Randomly decide based on context ### Real consequence User schedules a virtual meeting. Engine asks "What conference room?" User confused — "It's virtual, I don't need a room." User types "none" or "N/A". Your API receives `room: "none"`. Room booking system tries to book a room named "none". Fails or books the wrong room. ### The fix Use a **decision policy** in your conversation process to control flow based on collected slot values: ```yaml title="Decision policy for conditional slot collection" decision_policies: - name: collect_room_if_in_person condition: data.is_virtual == false action: goto_activity:collect_room else_action: goto_activity:create_meeting activities: - id: collect_room action: action_name: collect_room_details required_slots: [room_name] output_key: room_collected next_activity: create_meeting - id: create_meeting action: action_name: create_meeting_ca output_key: meeting_result ``` The conversation process collects `is_virtual` first (as a `boolean` slot — no resolver needed). Then the [decision policy](/agent-studio/core-concepts/agentic-automation-engine/policies) routes to the right activity based on the value. Users scheduling virtual meetings never see a room prompt. **Context passing vs decision policies:** If your second slot's *resolver options* depend on the first slot's value (e.g. fetching subcategories based on a selected category), use [context passing](/agent-studio/conversation-process/resolver-strategies#context-passing-into-dynamic-resolvers) in your resolver strategy. If you need to *skip collecting a slot entirely* based on a condition, use a decision policy in the conversation process. They solve different problems. **Rule of thumb:** If you see "required if" or "only when" in a slot description, you need a decision policy in your conversation process. If one slot's *resolver options* depend on another slot's value, use context passing. *** ## Quick Reference | Pattern | Severity | Fix | | --------------------------- | -------- | ------------------------------------------------------------------------- | | Bloated descriptions | HIGH | Description = *what*. Validation policy = *how*. | | Wrong type for people | CRITICAL | Use `User` or `list[User]`. Always. | | Missing validation | HIGH | Add DSL validation policy. Show clear error messages. | | Timezone without resolver | CRITICAL | Static resolver mapping display names to IANA values. | | Static resolver for boolean | MEDIUM | Use `boolean` type. Save the LLM call. | | Conditional slot collection | CRITICAL | Decision policies to skip slots. Context passing for dependent resolvers. | Six decision trees including slot type selection and resolver vs boolean. Never chain action activities without a slot barrier. Slots are the breakpoints. How the platform converts natural language into business objects. Static and dynamic resolver configuration with output cardinality.