> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-claude-cool-pascal-5ay683.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 스킬 & 에이전트

> 앱용 AI 스킬 및 에이전트 정의.

<Warning>
  스킬과 에이전트는 현재 알파 단계입니다. 해당 기능은 작동하지만 아직 발전 중입니다.
</Warning>

앱은 워크스페이스 내에서 동작하는 AI 기능을 정의할 수 있습니다 — 재사용 가능한 스킬 지침과 사용자 지정 시스템 프롬프트를 갖춘 에이전트.

<AccordionGroup>
  <Accordion title="defineSkill" description="AI 에이전트 스킬 정의">
    스킬은 워크스페이스 내에서 AI 에이전트가 사용할 수 있는 재사용 가능한 지침과 기능을 정의합니다. `defineSkill()`을 사용해 내장 검증과 함께 스킬을 정의하세요:

    ```ts src/skills/example-skill.ts theme={null}
    import { defineSkill } from 'twenty-sdk/define';

    export default defineSkill({
      universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
      name: 'sales-outreach',
      label: 'Sales Outreach',
      description: 'Guides the AI agent through a structured sales outreach process',
      icon: 'IconBrain',
      content: `You are a sales outreach assistant. When reaching out to a prospect:
    1. Research the company and recent news
    2. Identify the prospect's role and likely pain points
    3. Draft a personalized message referencing specific details
    4. Keep the tone professional but conversational`,
    });
    ```

    핵심 요점:

    * `name`은 스킬의 고유 식별자 문자열입니다(케밥 케이스 권장).
    * `label`은 UI에 표시되는 사람이 읽기 쉬운 표시 이름입니다.
    * `content`에는 스킬 지침이 포함됩니다 — 이는 AI 에이전트가 사용하는 텍스트입니다.
    * `icon` (선택 사항)은 UI에 표시되는 아이콘을 설정합니다.
    * `description` (선택 사항)은 스킬의 목적에 대한 추가 컨텍스트를 제공합니다.
  </Accordion>

  <Accordion title="defineAgent" description="사용자 지정 프롬프트로 AI 에이전트를 정의하세요">
    에이전트는 워크스페이스 내에서 동작하는 AI 비서입니다. `defineAgent()`를 사용하여 사용자 지정 시스템 프롬프트로 에이전트를 생성하세요:

    ```ts src/agents/example-agent.ts theme={null}
    import { defineAgent } from 'twenty-sdk/define';

    export default defineAgent({
      universalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
      name: 'sales-assistant',
      label: 'Sales Assistant',
      description: 'Helps the sales team draft outreach emails and research prospects',
      icon: 'IconRobot',
      prompt: 'You are a helpful sales assistant. Help users with their questions and tasks.',
    });
    ```

    핵심 요점:

    * `name`은 에이전트의 고유 식별자 문자열입니다(케밥 케이스 권장).
    * `label`은 UI에 표시되는 표시 이름입니다.
    * `prompt`는 에이전트의 동작을 정의하는 시스템 프롬프트입니다.
    * `description` (선택 사항)은 에이전트가 수행하는 작업에 대한 컨텍스트를 제공합니다.
    * `icon` (선택 사항)은 UI에 표시되는 아이콘을 설정합니다.
    * `modelId` (선택 사항)은 에이전트가 사용하는 기본 AI 모델을 재정의합니다.
    * `responseFormat`(선택 사항)은 에이전트 출력의 형태를 제어합니다. 자유 형식 텍스트의 기본값은 `{ type: 'text' }`입니다. 구조화된 JSON 출력을 강제하려면 `{ type: 'json', schema }`를 사용하세요.

    기본적으로 에이전트는 자유 형식 텍스트를 반환합니다. 구조화된 출력을 받으려면, `responseFormat`을 `{ type: 'json' }`으로 설정하고 `schema`를 제공하세요:

    ```ts src/agents/structured-agent.ts theme={null}
    import { defineAgent } from 'twenty-sdk/define';

    export default defineAgent({
      universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345',
      name: 'lead-scorer',
      label: 'Lead Scorer',
      prompt: 'Score the lead and explain your reasoning.',
      responseFormat: {
        type: 'json',
        schema: {
          type: 'object',
          properties: {
            score: { type: 'number', description: 'Lead score from 0 to 100' },
            summary: { type: 'string', description: 'Short reasoning for the score' },
          },
          required: ['score', 'summary'],
          additionalProperties: false,
        },
      },
    });
    ```

    스키마 참고:

    * 스키마는 단일 수준의 객체입니다. 각 속성의 `type`은 반드시 기본형(`string`, `number`, 또는 `boolean`)이어야 합니다. 중첩 객체와 배열은 지원되지 않습니다.
    * 각 속성의 `description`(선택 사항)은 해당 위치에 무엇을 넣어야 하는지 모델을 안내합니다.
    * `required` (선택 사항)은 모델이 항상 반환해야 하는 속성을 나열합니다.
    * `additionalProperties: false` (선택 사항)은 `properties`에 선언되지 않은 모든 속성을 금지합니다.
  </Accordion>

  <Accordion title="runAgent" description="로직 함수에서 에이전트를 실행">
    `runAgent()`를 사용하면 로직 함수가 앱의 에이전트(해당 skills 및 tools 포함) 중 하나를 실행할 수 있습니다. `defineAgent()`에 전달한 `universalIdentifier`로 에이전트를 식별하세요: `prompt` 문자열 또는 `messages` 대화 기록 중 하나만 전달하고, 둘 다 전달하지는 마세요:

    ```ts src/logic-functions/run-enricher.ts theme={null}
    import { runAgent } from 'twenty-sdk/logic-function';

    const { result, error, success } = await runAgent({
      agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
      prompt: 'Enrich House Ad <recordId>: fill empty fields from its listing URL.',
    });
    ```

    멀티 턴 봇(Slack, Discord, Teams 등)의 경우, 단일 `prompt` 대신 스레드 기록을 `messages`로 전달하세요:

    ```ts src/logic-functions/reply-in-thread.ts theme={null}
    import { runAgent } from 'twenty-sdk/logic-function';

    const { result, error, success } = await runAgent({
      agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
      messages: [
        { role: 'user', content: 'Who owns Acme?' },
        { role: 'assistant', content: 'Sarah owns the Acme account.' },
        { role: 'user', content: 'What was the last touchpoint?' },
      ],
    });
    ```

    핵심 요점:

    * `prompt`(문자열) 또는 `messages`(1개에서 100개의 `{ role: 'user' | 'assistant', content: string }` 항목) 중 **정확히 하나만** 제공해야 합니다.
    * 에이전트는 **동기적으로** 실행되며, 자체 tools를 통해 레코드를 읽고/업데이트할 수 있습니다. 실행이 완료되면 `runAgent()`가 resolve됩니다.
    * 앱은 자신의 에이전트만 실행할 수 있습니다.
    * 앱의 [기본 역할](/l/ko/developers/extend/apps/config/roles)은 `AI` 권한 플래그를 부여해야 합니다. 이를 위해 `permissionFlagUniversalIdentifiers`에 `SystemPermissionFlag.AI`를 추가하거나, `canAccessAllTools: true`로 설정하세요.
      이 권한이 없으면 `runAgent()`는 권한 오류로 실패합니다.
    * 로직 함수에 충분히 큰 `timeoutSeconds`를 설정하세요. 에이전트 실행에는 여러 초가 걸릴 수 있습니다.
    * 실행이 완료되면 `success`는 `true`이고 `result`는 null이 아닙니다. 실패 시 `success`는 `false`, `result`는 `null`이며, `error`에 이유가 들어 있습니다(예: 실행 도중 워크스페이스의 AI 크레딧이 소진된 경우).

    ```ts src/roles/default-role.ts theme={null}
    import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define';

    export default defineApplicationRole({
      universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061',
      label: 'Default function role',
      // runAgent() requires the AI permission flag on the app's default role.
      permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI],
    });
    ```

    <Warning>
      **루프를 피하세요:** `*.updated` 데이터베이스 이벤트 트리거에서 `runAgent()`를 호출하고 에이전트가 동일한 레코드를 업데이트하는 경우, `updatedFields`를 에이전트가 절대 쓰지 않는 필드(예: 원본 URL)로 한정하거나, `runAgent()`를 호출하기 전에 대상 필드 중 어느 것이든 아직 비어 있는지 여부를 검사해 가드하세요.
    </Warning>

    ### 워크스페이스 멤버를 대신해 실행하기

    실행이 사람에 의해 트리거되는 경우(예: 메시지에 응답하는 챗봇) `runAsWorkspaceMemberId`를 전달하면, 에이전트가 앱이 아니라 해당 멤버로서 동작합니다:

    ```ts src/logic-functions/answer-question.ts theme={null}
    const { result } = await runAgent({
      agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
      prompt: 'How many open opportunities do we have?',
      runAsWorkspaceMemberId: '20202020-0687-4c41-b707-ed1bfca972a7',
    });
    ```

    실행은 이후 **구성원의 고유 역할**로 동작합니다. 해당 구성원이 할 수 있는 작업만 수행할 수 있으며, 그 이상은 할 수 없습니다. 실행이 생성하는 레코드는 해당 구성원에게 귀속되고, 구성원의 행(row) 수준 권한이 적용됩니다. 에이전트의 역할은 여기에 관여하지 않습니다. 이는 배후에 사람이 없는 실행에 대해 애플리케이션의 기본값으로 사용됩니다. 자율 실행(예약된 작업, 데이터베이스 이벤트 트리거)에는 이 필드를 생략하세요. 이러한 실행은 에이전트의 고유 역할을 유지합니다. 동작 중인 애플리케이션은 출처(provenance)를 위해 실행의 컨텍스트에 계속 연결되어 있지만, 구성원의 권한 범위를 축소하지는 않습니다.

    실행을 트리거한 사람을 워크스페이스 멤버와 매핑하는 책임은 앱에 있습니다. 이름을 지정하려면 앱 액세스 토큰이 필요하며, 토큰이 어떤 멤버를
    지정할 수 있는지는 그 토큰에 사용자가 포함되어 있는지 여부에 따라 달라집니다:

    * **사용자가 연결되지 않은** 토큰은 어떤 멤버든 이름을 지정할 수 있습니다. 하나를 사용해 로직 함수가 실행되며,
      `client_credentials`를 통해 발급된 토큰이나 API 키에서 발급된 토큰도 마찬가지입니다.
    * 프론트 컴포넌트가 받는 것처럼 **사용자를 대신해 발급된** 토큰은 해당 사용자의 멤버만 지정할 수 있습니다.

    그 밖의 것들 — 일반 사용자 세션, 앱 토큰이 없는 API 키 — 은 멤버를 전혀 지정할 수 없습니다.

    <Warning>
      `runAgent()`는 워크스페이스 멤버를 확인할 수 없을 때 — 알 수 없거나 제거되었거나, 역할이 없는 멤버인 경우 — 예외를 발생시킵니다. 호출자가 요청한 것보다 더 많은 액세스를 부여하게 되므로,
      에이전트 자신의 역할로는 절대 폴백하지 않습니다.
    </Warning>
  </Accordion>
</AccordionGroup>
