Discourse AI - 웹 아티팩트

AI 아티팩트란?

AI 아티팩트는 사용자가 Discourse 게시글 내에서 동적인 웹 기반 컴포넌트를 생성, 임베드 및 상호작용할 수 있도록 해주는 강력한 도구입니다. 이러한 컴포넌트는 커스텀 HTML, CSS, JavaScript를 포함할 수 있으며, 다음과 같은 다양한 사용 사례를 지원합니다:

  • 인터랙티브 퀴즈 또는 양식 임베드.
  • 풍부한 그래픽과 애니메이션을 통한 데이터 시각화.
  • 경량 웹 애플리케이션 또는 도구 통합.
  • 웹 프레임워크, JavaScript 라이브러리 등에 대한 학습 도구.

AI 아티팩트는 상호작용성을 추가하면서 안전한 브라우징 경험을 보장함으로써 Discourse 게시글을 자연스럽게 향상시킵니다.


사이트 설정

관리자는 여러 설정을 구성할 수 있습니다:

1. 기능 활성화

  • 설정: discourse_ai_enabled
    • AI 아티팩트가 작동하려면 이 전역 설정이 활성화되어 있는지 확인하십시오.

2. 보안 모드

  • 설정: ai_artifact_security
    • 옵션:
      • disabled: 아티팩트 시스템 비활성화
      • lax: 사용자 상호작용 없이 아티팩트가 게시글에 자동으로 표시됩니다.
      • hybrid: 기본적으로 실행을 위해 클릭이 필요하지만, 게시글 작성자는 임베드 마크업에 data-ai-artifact-autorun을 추가하여 특정 아티팩트가 즉시 실행되도록 할 수 있습니다.
      • strict: 사용자가 브라우저에서 “보기” 또는 “실행” 버튼을 클릭하여 아티팩트를 명시적으로 활성화해야 합니다. 보안에 민감한 환경에서 이 설정을 권장합니다. (기본값)

3. 아티팩트 생성자 접근 권한

  • 기본적으로 아티팩트 생성자 에이전트스태프 사용자만 사용할 수 있습니다. 이 제한은 신뢰할 수 있는 개인만 아티팩트를 생성할 수 있도록 하여 부적절하거나 악의적인 사용 위험을 최소화합니다.
  • 더 넓은 접근이 필요하면 사이트 관리자가 수동으로 권한을 구성해야 합니다.

게시글에서 AI 아티팩트 사용

아티팩트는 요청 시 아티팩트 생성자 에이전트에 의해 자동으로 생성됩니다.

위 아티팩트는 GPT-4o와 anime.js 라이브러리를 사용하여 생성되었습니다

생성된 아티팩트는 기본적으로 작성자와 아티팩트 생성자 에이전트에게만 비공개입니다. AI 대화를 공유할 수 있는 권한이 있는 사용자(ai bot public sharing allowed groups에 속한 사용자)는 대화를 공유하여 아티팩트를 공개로 만들 수도 있습니다.

공유가 완료되면 다음 HTML 마크업을 사용하여 게시글에 렌더링할 수 있습니다:

<div class="ai-artifact" data-ai-artifact-version="3" data-ai-artifact-id="71"></div>

(여기서 버전과 아티팩트 ID는 사용자의 버전화된 아티팩트 정보입니다)

보안 고려 사항

AI 아티팩트는 커스텀으로 작성된 HTML, CSS, JavaScript를 실행할 수 있으므로 Discourse는 견고한 안전 장치를 구현했습니다:

1. iFrame 샌드박스 처리

  • 아티팩트는 sandbox 속성을 가진 격리된 iframe 컨테이너 내에서 렌더링되어 잠재적으로 위험한 작업을 제한합니다:
    • 크로스 사이트 스크립팅.
    • 외부 사이트 또는 API에 대한 접근.

2. 콘텐츠 보안 정책 (CSP)

3. 보안 모드

  • Strict 모드: 아티팩트를 완전히 신뢰할 수 없는 환경에서 권장됩니다. 사용자는 렌더링 전에 브라우저에서 아티팩트를 수동으로 활성화해야 합니다.

4. 접근 및 권한

  • 아티팩트는 다음 사용자만 볼 수 있습니다:
    • 생성자.
    • 관련 게시글(예: 개인 메시지)을 볼 권한이 있는 사용자.
  • 공개 아티팩트는 AI 대화를 공유하여 명시적으로 공개로 표시되어야 합니다.

5. 길이 제한

  • 아티팩트의 HTML, CSS, JavaScript 크기는 각각 64KB로 제한됩니다. 이를 통해 컴포넌트가 경량을 유지하고 사용자나 시스템에 부담을 주지 않도록 합니다.

아티팩트 저장소

웹 아티팩트는 선택적으로 사용자별 데이터를 저장할 수 있습니다. 이렇게 하려면 아티팩트 생성자에게 “사용자 저장소 사용” 또는 유사한 지시를 하십시오.

이 시스템은 키, 값 쌍을 저장할 수 있습니다:

  • 비공개 (관리자와 특정 사용자만 볼 수 있음)
  • 공개 (익명 사용자를 포함한 모든 사용자에게 표시됨)

키 값 쌍은 아티팩트가 유래한 게시글에 대해 보안 처리되지만, 아티팩트를 공개적으로 공유하면 모든 사용자에게 키 추가가 허용됩니다.

저장을 제어하려면 숨겨진 설정을 사용할 수 있습니다:

  • ai_artifact_kv_value_max_length (기본적으로 항목은 5000자 이하만 허용)
  • ai_artifact_max_keys_per_user_per_artifact (기본값 100)

FAQ

누가 AI 아티팩트를 생성할 수 있나요?

기본적으로 스태프 사용자(예: 관리자 또는 모더레이터)만 아티팩트 생성자 에이전트를 통해 아티팩트를 생성할 수 있습니다. 이 에이전트는 HTML, CSS, JavaScript를 사용하여 인터랙티브 웹 위젯을 설계하는 과정을 단순화합니다.

아티팩트를 클릭하면 어떻게 되나요?

  • lax 모드에서는 아티팩트가 자동으로 표시됩니다.
  • hybrid 모드에서는 기본적으로 클릭이 필요하지만, 임베드에 data-ai-artifact-autorun이 포함되어 있으면 자동 실행됩니다.
  • strict 모드에서는 “실행” 버튼을 클릭하면 아티팩트가 활성화되어 브라우저에서 로드됩니다.

AI 아티팩트는 안전한가요?

네. AI 아티팩트는 엄격하게 제어된 환경에서 실행됩니다:

  • 샌드박스에 격리되어 Discourse 애플리케이션이나 사용자 컨텍스트와 직접 상호작용할 수 없으며, iframe 메시징을 통해서만 가능합니다.
  • strict 모드는 활성화에 대한 제어권을 제공합니다.
  • 아티팩트는 기본적으로 비공개이며, 전역 접근을 허용하려면 능동적으로 공유해야 합니다.

아티팩트의 소스 코드를 볼 수 있나요?

네. Discourse AI가 아티팩트를 생성할 때 전체 마크업, CSS 및 JavaScript를 포함합니다.

어떤 LLM이 지원되나요?

아티팩트는 구성한 모든 LLM을 사용하여 생성할 수 있지만, 다양한 LLM은 아티팩트 생성에 특화되어 더 세밀하게 조정되어 있습니다.

o3, Anthropic Claude Sonnet 3.7 - 4.0, GPT-4.1, Gemini Pro 2.5 등에서 좋은 결과를 확인했습니다. 일반적으로 더 고급 모델일수록 더 잘 작동합니다.

아티팩트 생성에는 시행착오의 측면이 크므로, 실험이 핵심입니다.

23개의 좋아요

아티팩트(artifact)의 프라이버시를 좀 더 세밀하게 제어할 수 있으면 좋겠습니다. 이렇게 하면 특정 그룹과만 공유할 수 있겠죠. 현재 카테고리 권한이 작동하는 방식과 매우 유사합니다 :smiley:

그룹을 봇의 개인 채팅에 추가하면 작동할 것 같아요.

1개의 좋아요

테스트를 해보고 여기서 결과를 보고할게요. 특정 비공개 카테고리 내에서 하나의 그룹만 접근할 수 있는 아티팩트가 필요한 특수한 사용 사례가 있거든요.

1개의 좋아요

그룹이 접근할 수 있는 비공개 카테고리에서 새 주제를 만들고 Web Artifact Creator 봇을 태그하여, 나와 해당 그룹만 볼 수 있는 아티팩트를 생성하도록 했습니다. 그러나 아티팩트 iframe 창에는 특정 페이지에 접근 권한이 없는 사용자가 볼 때 나타나는 기본 오류 메시지인 "Oops! That page doesn’t exist or is private."만 표시됩니다.

그래서 해당 주제에 대한 데이터 탐색기 쿼리를 작성하여 누가 그 주제를 볼 수 있는지 확인해 보았습니다. 데이터베이스에 따르면, 아티팩트를 볼 수 있어야 할 그룹의 사용자들은 실제로도 볼 수 있는 것으로 나타났습니다.

그러므로 이것이 버그일까요?

제 SQL 쿼리
-- [params]
-- int :artifact_id = 22

WITH artifact_info AS (
  SELECT 
    a.id,
    a.user_id as creator_id,
    a.post_id,
    p.topic_id,
    t.category_id,
    t.archetype,
    c.read_restricted,
    t.title as topic_title
  FROM ai_artifacts a
  LEFT JOIN posts p ON a.post_id = p.id
  LEFT JOIN topics t ON p.topic_id = t.id
  LEFT JOIN categories c ON t.category_id = c.id
  WHERE a.id = :artifact_id
),
users_with_access AS (
  -- Creator always has access
  SELECT 
    ai.creator_id as user_id,
    'Creator' as access_reason
  FROM artifact_info ai
  
  UNION
  
  -- Users with access to private messages
  SELECT 
    tau.user_id,
    'Private Message Access' as access_reason
  FROM artifact_info ai
  JOIN topic_allowed_users tau ON ai.topic_id = tau.topic_id
  WHERE ai.archetype = 'private_message'
  
  UNION
  
  -- Group members with access to private messages
  SELECT 
    gu.user_id,
    'Private Message Group Access' as access_reason
  FROM artifact_info ai
  JOIN topic_allowed_groups tag ON ai.topic_id = tag.topic_id
  JOIN group_users gu ON tag.group_id = gu.group_id
  WHERE ai.archetype = 'private_message'
  
  UNION
  
  -- Users with access to restricted categories
  SELECT 
    gu.user_id,
    'Category Group Access' as access_reason
  FROM artifact_info ai
  JOIN category_groups cg ON ai.category_id = cg.category_id
  JOIN group_users gu ON cg.group_id = gu.group_id
  WHERE ai.read_restricted = true
    AND ai.archetype != 'private_message'
    AND cg.permission_type IN (1, 2) -- full access or create/reply/see
  
  UNION
  
  -- All users if topic is public (not restricted and not private message)
  SELECT 
    u.id as user_id,
    'Public Access' as access_reason
  FROM artifact_info ai
  CROSS JOIN users u
  WHERE (ai.read_restricted = false OR ai.read_restricted IS NULL)
    AND (ai.archetype != 'private_message' OR ai.archetype IS NULL)
    AND u.active = true
)

SELECT 
  u.id as user_id,
  u.username,
  u.name,
  u.trust_level,
  uwa.access_reason,
  ai.topic_title
FROM users_with_access uwa
JOIN users u ON uwa.user_id = u.id
CROSS JOIN artifact_info ai
WHERE u.active = true
ORDER BY u.username