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
21 changes: 21 additions & 0 deletions apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5462,3 +5462,24 @@ export function EnrichSoIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}

export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
xmlns='http://www.w3.org/2000/svg'
width='16'
height='16'
viewBox='0 0 16 16'
fill='none'
>
<path
d='M8 1L14.0622 4.5V11.5L8 15L1.93782 11.5V4.5L8 1Z'
stroke='currentColor'
strokeWidth='1.5'
fill='none'
/>
<path d='M8 4.5L11 6.25V9.75L8 11.5L5 9.75V6.25L8 4.5Z' fill='currentColor' />
</svg>
)
}
63 changes: 57 additions & 6 deletions apps/docs/content/docs/en/skills/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ This means you can attach many skills to an agent without bloating its context w

## Creating Skills

Go to **Settings** (gear icon) and select **Skills** under the Tools section.
Go to **Settings** and select **Skills** under the Tools section.

![Manage Skills](/static/skills/manage-skills.png)

Click **Add** to create a new skill with three fields:

Expand Down Expand Up @@ -52,11 +54,22 @@ Use when the user asks you to write, optimize, or debug SQL queries.
...
```

**Recommended structure:**
- **When to use** — Specific triggers and scenarios
- **Instructions** — Step-by-step guidance with numbered lists
- **Examples** — Input/output samples showing expected behavior
- **Common Patterns** — Reusable approaches for frequent tasks
- **Edge Cases** — Gotchas and special considerations

Keep skills focused and under 500 lines. If a skill grows too large, split it into multiple specialized skills.

## Adding Skills to an Agent

Open any **Agent** block and find the **Skills** dropdown below the tools section. Select the skills you want the agent to have access to.

Selected skills appear as chips that you can click to edit or remove.
![Add Skill](/static/skills/add-skill.png)

Selected skills appear as cards that you can click to edit or remove.

### What Happens at Runtime

Expand All @@ -69,12 +82,50 @@ When the workflow runs:

This works across all supported LLM providers — the `load_skill` tool uses standard tool-calling, so no provider-specific configuration is needed.

## Tips
## Common Use Cases

Skills are most valuable when agents need specialized knowledge or multi-step workflows:

**Domain Expertise**
- `api-integration-expert` — Best practices for calling specific APIs (authentication, rate limiting, error handling)
- `data-transformation` — ETL patterns, data cleaning, and validation rules
- `code-reviewer` — Code review guidelines specific to your team's standards

**Workflow Templates**
- `bug-investigation` — Step-by-step debugging methodology (reproduce → isolate → test → fix)
- `feature-implementation` — Development workflow from requirements to deployment
- `document-generator` — Templates and formatting rules for technical documentation

**Company-Specific Knowledge**
- `our-architecture` — System architecture diagrams, service dependencies, and deployment processes
- `style-guide` — Brand guidelines, writing tone, UI/UX patterns
- `customer-onboarding` — Standard procedures and common customer questions

**When to use skills vs. agent instructions:**
- Use **skills** for knowledge that applies across multiple workflows or changes frequently
- Use **agent instructions** for task-specific context that's unique to a single agent

## Best Practices

**Writing Effective Descriptions**
- **Be specific and keyword-rich** — Instead of "Helps with SQL", write "Write optimized SQL queries for PostgreSQL, MySQL, and SQLite, including index recommendations and query plan analysis"
- **Include activation triggers** — Mention specific words or phrases that should prompt the skill (e.g., "Use when the user mentions PDFs, forms, or document extraction")
- **Keep it under 200 words** — Agents scan descriptions quickly; make every word count

- **Keep descriptions actionable** — Instead of "Helps with SQL", write "Write optimized SQL queries for PostgreSQL, MySQL, and SQLite, including index recommendations and query plan analysis"
**Skill Scope and Organization**
- **One skill per domain** — A focused `sql-expert` skill works better than a broad `database-everything` skill
- **Use markdown structure** — Headers, lists, and code blocks help the agent parse and follow instructions
- **Test iteratively** — Run your workflow and check if the agent activates the skill when expected
- **Limit to 5-10 skills per agent** — More skills = more decision overhead; start small and add as needed
- **Split large skills** — If a skill exceeds 500 lines, break it into focused sub-skills

**Content Structure**
- **Use markdown formatting** — Headers, lists, and code blocks help agents parse and follow instructions
- **Provide examples** — Show input/output pairs so agents understand expected behavior
- **Be explicit about edge cases** — Don't assume agents will infer special handling

**Testing and Iteration**
- **Test activation** — Run your workflow and verify the agent loads the skill when expected
- **Check for false positives** — Make sure skills aren't activating when they shouldn't
- **Refine descriptions** — If a skill isn't loading when needed, add more keywords to the description

## Learn More

Expand Down
15 changes: 15 additions & 0 deletions apps/docs/content/docs/en/tools/airweave.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
color="#6366F1"
/>

{/* MANUAL-CONTENT-START:intro */}
[Airweave](https://airweave.ai/) is an AI-powered semantic search platform that helps you discover and retrieve knowledge across all your synced data sources. Built for modern teams, Airweave enables fast, relevant search results using neural, hybrid, or keyword-based strategies tailored to your needs.

With Airweave, you can:

- **Search smarter**: Use natural language queries to uncover information stored across your connected tools and databases
- **Unify your data**: Seamlessly access content from sources like code, docs, chat, emails, cloud files, and more
- **Customize retrieval**: Select between hybrid (semantic + keyword), neural, or keyword search strategies for optimal results
- **Boost recall**: Expand search queries with AI to find more comprehensive answers
- **Rerank results using AI**: Prioritize the most relevant answers with powerful language models
- **Get instant answers**: Generate clear, AI-powered responses synthesized from your data

In Sim, the Airweave integration empowers your agents to search, summarize, and extract insights from all your organization’s data via a single tool. Use Airweave to drive rich, contextual knowledge retrieval within your workflows—whether answering questions, generating summaries, or supporting dynamic decision-making.
{/* MANUAL-CONTENT-END */}

## Usage Instructions

Search across your synced data sources using Airweave. Supports semantic search with hybrid, neural, or keyword retrieval strategies. Optionally generate AI-powered answers from search results.
Expand Down
Binary file added apps/docs/public/static/skills/add-skill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docs/public/static/skills/manage-skills.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -130,39 +130,52 @@ export function SkillInput({
onOpenChange={setOpen}
/>

{selectedSkills.length > 0 && (
<div className='flex flex-wrap gap-[4px]'>
{selectedSkills.map((stored) => {
const fullSkill = workspaceSkills.find((s) => s.id === stored.skillId)
return (
{selectedSkills.length > 0 &&
selectedSkills.map((stored) => {
const fullSkill = workspaceSkills.find((s) => s.id === stored.skillId)
return (
<div
key={stored.skillId}
className='group relative flex flex-col overflow-hidden rounded-[4px] border border-[var(--border-1)] transition-all duration-200 ease-in-out'
>
<div
key={stored.skillId}
className='flex cursor-pointer items-center gap-[4px] rounded-[4px] border border-[var(--border-1)] bg-[var(--surface-5)] px-[6px] py-[2px] font-medium text-[12px] text-[var(--text-secondary)] hover:bg-[var(--surface-6)]'
className='flex cursor-pointer items-center justify-between gap-[8px] rounded-t-[4px] bg-[var(--surface-4)] px-[8px] py-[6.5px]'
onClick={() => {
if (fullSkill && !disabled && !isPreview) {
setEditingSkill(fullSkill)
}
}}
>
<AgentSkillsIcon className='h-[10px] w-[10px] text-[var(--text-tertiary)]' />
<span className='max-w-[140px] truncate'>{resolveSkillName(stored)}</span>
{!disabled && !isPreview && (
<button
type='button'
onClick={(e) => {
e.stopPropagation()
handleRemove(stored.skillId)
}}
className='ml-[2px] rounded-[2px] p-[1px] text-[var(--text-tertiary)] hover:bg-[var(--surface-7)] hover:text-[var(--text-secondary)]'
<div className='flex min-w-0 flex-1 items-center gap-[8px]'>
<div
className='flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[4px]'
style={{ backgroundColor: '#e0e0e0' }}
>
<XIcon className='h-[10px] w-[10px]' />
</button>
)}
<AgentSkillsIcon className='h-[10px] w-[10px] text-[#333]' />
</div>
<span className='truncate font-medium text-[13px] text-[var(--text-primary)]'>
{resolveSkillName(stored)}
</span>
</div>
<div className='flex flex-shrink-0 items-center gap-[8px]'>
{!disabled && !isPreview && (
<button
type='button'
onClick={(e) => {
e.stopPropagation()
handleRemove(stored.skillId)
}}
className='flex items-center justify-center text-[var(--text-tertiary)] transition-colors hover:text-[var(--text-primary)]'
aria-label='Remove skill'
>
<XIcon className='h-[13px] w-[13px]' />
</button>
)}
</div>
</div>
)
})}
</div>
)}
</div>
)
})}
</div>

<SkillModal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
isSubBlockVisibleForMode,
} from '@/lib/workflows/subblocks/visibility'
import type { BlockConfig, SubBlockConfig, SubBlockType } from '@/blocks/types'
import { usePermissionConfig } from '@/hooks/use-permission-config'
import { useWorkflowDiffStore } from '@/stores/workflow-diff'
import { mergeSubblockState } from '@/stores/workflows/utils'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
Expand Down Expand Up @@ -35,6 +36,7 @@ export function useEditorSubblockLayout(
const blockDataFromStore = useWorkflowStore(
useCallback((state) => state.blocks?.[blockId]?.data, [blockId])
)
const { config: permissionConfig } = usePermissionConfig()

return useMemo(() => {
// Guard against missing config or block selection
Expand Down Expand Up @@ -100,6 +102,9 @@ export function useEditorSubblockLayout(
const visibleSubBlocks = (config.subBlocks || []).filter((block) => {
if (block.hidden) return false

// Hide skill-input subblock when skills are disabled via permissions
if (block.type === 'skill-input' && permissionConfig.disableSkills) return false

// Check required feature if specified - declarative feature gating
if (!isSubBlockFeatureEnabled(block)) return false

Expand Down Expand Up @@ -149,5 +154,6 @@ export function useEditorSubblockLayout(
activeWorkflowId,
isSnapshotView,
blockDataFromStore,
permissionConfig.disableSkills,
])
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { useCustomTools } from '@/hooks/queries/custom-tools'
import { useMcpServers, useMcpToolsQuery } from '@/hooks/queries/mcp'
import { useCredentialName } from '@/hooks/queries/oauth-credentials'
import { useReactivateSchedule, useScheduleInfo } from '@/hooks/queries/schedules'
import { useSkills } from '@/hooks/queries/skills'
import { useDeployChildWorkflow } from '@/hooks/queries/workflows'
import { useSelectorDisplayName } from '@/hooks/use-selector-display-name'
import { useVariablesStore } from '@/stores/panel'
Expand Down Expand Up @@ -618,6 +619,48 @@ const SubBlockRow = memo(function SubBlockRow({
return `${toolNames[0]}, ${toolNames[1]} +${toolNames.length - 2}`
}, [subBlock?.type, rawValue, customTools, workspaceId])

/**
* Hydrates skill references to display names.
* Resolves skill IDs to their current names from the skills query.
*/
const { data: workspaceSkills = [] } = useSkills(workspaceId || '')

const skillsDisplayValue = useMemo(() => {
if (subBlock?.type !== 'skill-input' || !Array.isArray(rawValue) || rawValue.length === 0) {
return null
}

interface StoredSkill {
skillId: string
name?: string
}

const skillNames = rawValue
.map((skill: StoredSkill) => {
if (!skill || typeof skill !== 'object') return null

// Priority 1: Resolve skill name from the skills query (fresh data)
if (skill.skillId) {
const foundSkill = workspaceSkills.find((s) => s.id === skill.skillId)
if (foundSkill?.name) return foundSkill.name
}

// Priority 2: Fall back to stored name (for deleted skills)
if (skill.name && typeof skill.name === 'string') return skill.name

// Priority 3: Use skillId as last resort
if (skill.skillId) return skill.skillId

return null
})
.filter((name): name is string => !!name)

if (skillNames.length === 0) return null
if (skillNames.length === 1) return skillNames[0]
if (skillNames.length === 2) return `${skillNames[0]}, ${skillNames[1]}`
return `${skillNames[0]}, ${skillNames[1]} +${skillNames.length - 2}`
}, [subBlock?.type, rawValue, workspaceSkills])

const isPasswordField = subBlock?.password === true
const maskedValue = isPasswordField && value && value !== '-' ? '•••' : null

Expand All @@ -627,6 +670,7 @@ const SubBlockRow = memo(function SubBlockRow({
dropdownLabel ||
variablesDisplayValue ||
toolsDisplayValue ||
skillsDisplayValue ||
knowledgeBaseDisplayName ||
workflowSelectionName ||
mcpServerDisplayName ||
Expand Down
Loading