이제 Discourse를 사용하여 자체 AI Q&A 홈페이를 구축할 수 있습니다

위에서 설명한 기능을 활용하면 거의 모든 용도로 AI 사이트를 구축할 수 있습니다!

가장 명백한 사례는 독립적인 지원 경험을 제공하는 것입니다. 예를 들어, forum.example.com을 기반으로 별도의 ask.example.com 사이트를 운영하여 고객들의 질문에 답변을 도울 수 있습니다.

ask.discourse.com 예시를 참고하여 새 Discourse 설치 환경에서 이를 설정하는 방법은 다음과 같습니다. 이 가이드는 이미 Discourse AI 플러그인을 활성화하고, LLM을 구성했으며, admin/plugins/discourse-ai/ai-features에서 봇 기능을 활성화했다고 가정합니다.

ask.discourse.com의 경우, Discourse AI 플러그인에서 호스팅된 LLM(현재는 DeepSeek를 사용 중이지만, 사용 가능한 어떤 모델이든 사용할 수 있습니다)을 사용하여 “Discourse Helper” 에이전트를 생성했습니다. 이 에이전트는 시스템 프롬프트와 몇 가지 검색 도구를 갖추고 있습니다.

  1. ask.example.com의 AI 플러그인에서 새 에이전트를 생성하고, 아래 프롬프트로 시작하세요.

    에이전트를 위한 현재 시스템 프롬프트 (피드백에 따라 필요 시 조정함)

    당신은 Discourse Helper Bot입니다. Discourse 관련 질문에 대해 새로운 관리자(Admin)들을 돕기 위해 설계된 AI 어시스턴트입니다. Discourse는 커뮤니티 포럼 소프트웨어입니다. Discourse 및 Discourse 관련 프로그래밍 주제에 대한 질문에 답변할 수 있습니다. 이 전문 분야 밖의 질문에 대해서는 답변하지 마십시오.

    Discourse Helper Bot은 주로 유료 Discourse 고객들을 대상으로 합니다. 호스팅 플랜을 사용하는 고객은 터미널 접근 권한이 없으며 ./launcher와 같은 도구를 실행할 수 없습니다.


    소통:

    • 모든 응답에 Discourse 마크다운을 사용하세요.
    • 답변에 이미지를 포함하지 마십시오.
    • 사용자가 질문한 언어로 답변하세요. 불확실한 경우 영어를 기본으로 사용하세요.

    정보 출처:

    • Discourse 플랜 및 가격에 대한 질문의 경우, 업로드된 문서를 주요 출처로 사용하세요. 이 문서는 discourse.org/pricing의 공식 가격 페이지를 기반으로 합니다.
    • 기타 모든 질문의 경우, meta.discourse.org를 검색하여 주요 출처로 취급하세요.

    사용자 쿼리 처리:

    1. 질문 이해:

      • 사용자의 의도를 파악하기 위해 쿼리를 주의 깊게 읽으세요.
      • 공감하는 태도를 취하고 사용자의 특정 요구사항에 집중하세요.
    2. 포괄적 검색 수행:

      • 추출된 키워드를 사용하여 최대 20단어 이내의 검색 쿼리를 구성하세요.
      • 가장 관련성 높은 문서를 찾기 위해 광범위한 키워드를 포함하세요.
      • discoursemeta.discourse.org와 같은 스톱 워드(stop words)는 피하세요.
      • 예시: 사용자가 서드파티 서비스 통합에 대해 질문할 경우, "서드파티 서비스와 플러그인 API 및 웹훅 통합 가이드"로 검색하세요.
    3. 정보 검토 및 검증:

      • 검색 결과를 면밀히 읽으세요.
      • 정보가 최신이고 관련성이 있는지 확인하세요.
      • 항상 먼저 읽는 것을 우선시하세요. 관련 정보가 문서에 있을 수 있습니다.
    4. 응답 구성:

      • 검증된 정보를 바탕으로 명확하고 정확한 답변을 제공하세요.
      • Discourse 마크다운 링크를 사용하여 출처를 인용하세요.
      • 일관된 용어를 사용하고 복잡한 문장은 명확성을 위해 단순화하세요.
    5. 필요 시 명확히 요청:

      • 쿼리가 모호하거나 충분한 맥락이 부족하면 진행하기 전에 사용자에게 추가 세부 정보를 요청하세요.

    응답 가이드라인:

    1. 검증된 정보에만 기반하여 답변:

      • 훈련 데이터에만 의존하여 답변을 발명하지 마십시오.
      • 항상 검색 결과에서 인용된 내용으로 정보를 검증하세요.
      • 충분한 정보를 찾을 수 없다면 불확실성을 표현하거나 명확히 요청하세요.
      • 검증되지 않은 정보를 제공하는 것보다 "모릅니다"라고 말하는 것이 낫습니다.
    2. 추측보다 검색을 항상 우선시하세요

      • 훈련 데이터에만 의존하거나 검색 없이 응답하지 마세요.
      • 관련 검색 결과를 검토하고 관련 내용을 읽은 후에만 답변을 구성하세요.

    추가 가이드라인:

    1. 용어:

      • Discourse 토론은 Threads가 아닌 Topics라고 부릅니다.
      • Threads라는 용어는 오직 Discourse Chat Threads에만 사용됩니다.
    2. 지원 연락처:

      • 모든 플랜의 호스팅 고객은 현재 답변이 도움이 되지 않을 경우 언제든지 team@discourse.org으로 지원을 문의할 수 있습니다.
    3. 검색에 대한 헌신:

      • Discourse 용어에 익숙하더라도 항상 검색을 수행하세요.
      • 광범위한 답변에 도움이 될 정보가 meta.discourse.org에 있을 수 있습니다.
      • 사용자가 제품 X를 Discourse와 비교하려 할 경우, meta.discourse.org에 더 많은 정보가 있는지 확인하기 위해 항상 검색하세요. 비교를 조사할 때는 최소 5회 검색하고 더 깊이 조사해야 합니다. 더 넓은 범위를 커버하기 위해 다른 용어를 사용하세요.
      • 사용자가 매우 익숙한 기능 X에 대해 알고 싶어 할 경우에도 항상 검색하세요.
      • 모른다고 말하기 전에 신중하게 검색하세요.
    4. 최신 정보 우선:

      • 훈련 종료 시점 이후 새로운 기능이나 업데이트가 추가되었을 수 있음을 기억하세요.
      • 항상 meta.discourse.org의 가장 최신 정보를 우선시하세요.
    5. 사용자의 언어 유지 시도:

      • 도구 호출은 항상 영어로 수행해야 합니다.
      • 응답은 사용자의 언어를 따르세요.
    6. 항상 출처 자료에 대한 링크 추가

      • meta.discourse.org에 관련 링크가 있다면 마크다운으로 렌더링하여 답변의 품질을 높여주세요.
    7. 간결하게, 과도한 설명 피하기

      • 개방형 질문에는 최대 4~5개 항목의 목록으로만 응답하세요.
      • 응답에 너무 많은 세부 정보를 추가하지 말고 간결하게 유지하세요.

    기억하세요. 현재 날짜는 {time}입니다. 훈련 이후 많은 것이 변경되었을 수 있으므로, meta.discourse.org의 가장 최신 정보를 우선시하세요. Discourse 플랜 기능과 가격의 경우, 업로드된 문서가 가장 최신이고 권위 있는 출처입니다.

  2. 몇 가지 도구를 생성하세요 — 우리는 에이전트에 커스텀 읽기(read) 및 검색(search) 도구를 추가했습니다. 기본적으로 이 도구들은 에이전트가 Discourse 사이트를 효과적으로 검색할 수 있는 능력을 부여합니다. 구성 방법은 다음과 같습니다:

    검색 도구

    새 매개변수를 추가합니다. 우리는 “string” 타입의 "question"이라는 이름의 매개변수를 추가했습니다. 설명은 다음과 같습니다: “사용자가 묻고자 하는 질문을 동의어를 사용하여 매우 명확하고 상세하게 표현하세요.” “required”(필수) 상자를 체크하세요.

    아래 예시와 같은 스크립트를 추가하세요… 이 스크립트는 포럼의 API 키를 사용하여 검색을 수행하고, 카테고리에 대한 참조를 제공합니다. 카테고리는 category_id: "category name" 형식으로 포맷됩니다.

    참고: 우리의 스크립트는 #documentation 카테고리를 특별히 우선시하므로, 사이트 상황에 맞게 조정해야 합니다. 또한 시맨틱 검색 엔드포인트를 지정하므로, forum.example.com을 데이터 소스로 사용하는 경우 해당 사이트에서 AI Search를 활성화해야 합니다.

    API 키(forum.example.com/admin/api/keys에서 생성)에는 ai:searchtopics:read라는 세분화된 스코프가 있습니다. 이 키를 비관리자(Non-admin) 사용자와 연관시켜 생성함으로써, 관리자(Admin)가 접근할 수 있는 모든 것(예: 비공개 카테고리 및 개인 메시지)에 접근할 수 없도록 보장했습니다.

    검색 도구 스크립트:

    let terms;
    const API_KEY = "your api key here";
    
    const categories = {
      1: "example",
      2: "documentation",
      164: "documentation > customers",
    };
    
    function search(terms) {
      const encoded = encodeURIComponent(terms);
      const searchUrl =
        "https://forum.example.com/discourse-ai/embeddings/semantic-search.json?hyde=false&q=";
      const result = http.get(`${searchUrl}${encoded}`, { "Api-Key": API_KEY });
      return processResults(JSON.parse(result.body));
    }
    
    function invoke(p) {
      if (!p.question) {
        terms = "No search performed!";
        return "You must supply the question parameter";
      }
      terms = p.question;
    
      let results = [];
      let otherResults = [];
      const errors = [];
    
      try {
        results = search(terms + " #documentation");
      } catch {
        errors.push("Failed to search for documentation! (rate limiting maybe)");
      }
    
      try {
        otherResults = search(terms);
      } catch {
        errors.push("Failed global search! (rate limiting maybe)");
      }
    
      const topicIds = {};
      results.forEach((topic) => {
        topicIds[topic.topic_id] = topic;
      });
      otherResults.forEach((topic) => {
        if (topicIds[topic.topic_id] || results.length > 15) {
          return;
        }
        results.push(topic);
      });
    
      if (results.length === 0) {
        results = "No results found!";
      }
    
      if (errors.length) {
        results = { results, errors };
      }
    
      return results;
    }
    
    function processResults(json) {
      if (!json.topics) {
        return [];
      }
    
      const postData = {};
      json.posts.forEach((p) => {
        postData[p.topic_id] = p;
      });
    
      return json.topics.map((t) => ({
        url: `/t/${t.slug}/${t.id}`,
        title: t.title,
        blurb: postData[t.id]?.blurb,
        tags: t.tags,
        topic_id: t.id,
        category: categories[t.category_id],
      }));
    }
    
    function details() {
      return "Searching for: " + terms;
    }
    
    읽기 도구

    “number” 타입의 topic_id 매개변수를 추가하고, 설명에 "읽고자 하는 forum.example.com의 토픽 ID"라고 입력한 후 “required”(필수) 상자를 체크하세요.

    검색 도구에 사용했던 것과 동일한 API 키를 사용하여 스크립트를 채울 수 있습니다:

    const API_KEY = "your api key here";
    let url = "https://forum.example.com";
    let title = "Unknown topic";
    
    function invoke(p) {
      const jsonUrl = `https://forum.example.com/t/${p.topic_id}.json?include_raw=true`;
    
      try {
        const result = http.get(jsonUrl, { "Api-Key": API_KEY });
        const parsed = JSON.parse(result.body);
        url = `https://forum.example.com/t/${parsed.slug}/${parsed.id}`;
        title = parsed.title;
    
        const raw = parsed.post_stream.posts
          .map((post) => {
            const solution = post.is_solution ? "(solution)" : "";
            return `post #${post.post_number}:${solution}\n${post.raw}`;
          })
          .join("\n\n");
    
        return llm.truncate(raw, 10000);
      } catch {
        return "Something went wrong, topic not found!";
      }
    }
    
    function details() {
      return `Read: <a href='${url}'>${title}</a>`;
    }
    

    사전 정의된 스키마, 검색 설정 및 검색 컨텍스트 도구도 추가할 수 있습니다.

    우리는 위에서 정의한 Search (custom) 도구를 에이전트의 강제(forced) 도구로 설정하고, 도구 전략으로 첫 번째 응답에만 적용했지만, 이는 선택 사항입니다.

    1. RAG를 사용하여 URL 소스를 설정하여 다른 유용한 URL에서 정보를 제공할 수 있습니다. 예를 들어, 지원 컨텍스트에서 중요한 정보가 될 수 있으므로 discourse.org/pricing을 포함합니다.

    2. 봇이 활성화되어 있는지, 우선 순위(priority)로 설정되어 있는지, 전용 사용자가 생성되어 있는지, 개인 메시지가 허용되는지 확인하세요.

모든 설정이 완료되었습니다! 이 내용을 시작점으로 사용하면 나만의 ask.example.com을 가질 수 있습니다.

5개의 좋아요