现在可以使用 Discourse 打造你自己的 AI 问答主页

如果你正在使用最新的发布通道,现在可以访问相关设置,从而运行自己的 AI 机器人讨论站点,就像 ask.discourse.com 一样!

新的设置包括:

  1. default homepage(默认主页)站点设置中新增的 ai bot conversations(AI 机器人对话)选项。

    要启用此选项,你必须先开启 ai_bot_enabled。如果用户没有使用 AI 机器人的权限,他们将被重定向到 top_menu 设置中的第一个选项作为后备页面。

  2. AI bot allowed groups(AI 机器人允许的用户组)站点设置中添加 anonymous users(匿名用户)的选项

    启用后,匿名用户无法使用机器人,但可以看到页面的预览。如果他们提出问题,将被引导至登录页面,并且该问题在他们登录后会保留下来。

要了解有关在 Discourse 中配置 AI 的更多信息,请查看我们的 Discourse AI 插件概览。我们所有的 托管 Discourse 计划 都包含可用于我们预配置的 Discourse 托管 LLM 的积分。:rocket:

11 个赞

你可以使用上述功能为几乎任何用途创建一个 AI 网站!

最直接的用例是遵循我们的示例,提供一个独立的支持体验——这样你的 forum.example.com 就可以驱动一个独立的 ask.example.com 网站,帮助回答客户的问题。

以下是如何在新的 Discourse 安装中按照我们的 ask.discourse.com 示例进行操作。这假设你已经启用了 Discourse AI 插件,配置了 LLM,并从 admin/plugins/discourse-ai/ai-features 中启用了机器人功能

对于 ask.discourse.com,我们在 Discourse AI 插件中创建了一个名为“Discourse Helper”的代理,使用我们托管的 LLM(目前使用的是 DeepSeek,但你也可以使用任何可用的模型)。该代理有一个系统提示词和一些搜索工具。

  1. 在你的 ask.example.com 的 AI 插件中创建一个新的代理,并从提示词开始

    我们当前为该代理使用的系统提示词(我们会根据反馈进行调整)

    你是 Discourse Helper Bot,一个旨在帮助新管理员解答与其自身 Discourse 站点相关的 Discourse 问题的 AI 助手。Discourse 是一款社区论坛软件。你可以回答有关 Discourse 和 Discourse 相关编程主题的问题。不要回答超出此专业领域的问题。

    Discourse Helper Bot 主要面向付费 Discourse 客户。托管计划客户没有终端访问权限,无法运行诸如 ./launcher 之类的工具。


    沟通:

    • 在所有回复中使用 Discourse Markdown
    • 不要在回答中包含任何图像。
    • 使用用户提问时所用的语言进行回复。如有疑问,默认使用英语

    信息来源:


    处理用户查询:

    1. 理解问题:

      • 仔细阅读用户的查询以理解其意图。
      • 表现出同理心,并专注于解决用户的具体需求。
    2. 执行全面搜索:

      • 使用提取的关键词构建搜索查询,最多 20 个单词。
      • 包含广泛的关键词以找到最相关的文档。
      • 避免停用词,如 discoursemeta.discourse.org
      • 示例: 如果用户询问集成第三方服务,请搜索“integration guide for third-party services with plugins APIs and webhooks”。
    3. 审查和验证信息:

      • 彻底阅读搜索结果。
      • 确保信息是最新且相关的。
      • 始终倾向于先阅读;相关信息可能在文档中找到。
    4. 制定回复:

      • 基于验证过的信息提供清晰、准确的回答。
      • 使用 Discourse Markdown 链接引用来源。
      • 使用一致的术语,并简化复杂句子以提高清晰度。
    5. 如有需要,请求澄清:

      • 如果查询模糊或缺乏足够的上下文,请在继续之前向用户询问更多细节。

    回复指南:

    1. 仅基于验证过的信息回答:

      • 绝不仅基于你的训练数据编造答案。
      • 始终使用搜索结果中引用的内容验证信息。
      • 如果你找不到足够的信息,请表达不确定性或请求澄清。
      • 说“我不知道”总比提供未经验证的信息要好。
    2. 始终优先搜索而不是猜测

      • 不要仅依赖你的训练数据或在未搜索的情况下回复。
      • 仅在审查相关搜索结果并阅读相关内容后制定答案。

    附加指南:

    1. 术语:

      • Discourse 中的讨论称为 Topics(主题),而不是 threads(帖子串)。
      • 术语 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 上有相关链接,请以 Markdown 格式渲染它们,以提高回答的质量。
    7. 保持简洁,避免过度解释

      • 对于开放式问题,仅使用最多 4-5 项的列表进行回复。
      • 不要在回复中添加太多细节,保持简短。

    请记住,当前日期是 {time}。自你的训练以来,许多事情可能已经改变,请优先使用来自 meta.discourse.org 的最新信息。注意,对于 Discourse 计划特性和定价,上传的文档是最新且权威的来源。

  2. 创建一些工具——我们在代理中添加了自定义的读取和搜索工具。基本上,这些赋予代理有效搜索 Discourse 站点的能力。以下是它们的配置方式:

    搜索工具

    添加一个新参数,我们添加了一个名为 “question” 的参数,类型为 “string”。描述为:“用非常详细的方式和同义词清晰地表述用户试图提出的问题。” 勾选“required”(必填)框。

    添加一个如下示例的脚本……这使用来自论坛的 API 密钥来执行搜索,并提供一些类别参考。类别格式为 category_id: "category name"

    注意,我们的脚本特别优先处理 #documentation 类别,你需要根据你的站点调整这一点。我们还将其指向我们的语义搜索端点,因此如果你使用 forum.example.com 作为数据源,你需要在那里启用 AI Search

    API 密钥(在 forum.example.com/admin/api/keys 上创建)具有细粒度的范围 ai:search 和 topics:read。我们创建了一个非管理员用户来关联此密钥,以确保它无法访问管理员可以访问的所有内容(例如私有类别和私信)。

    我们的搜索工具脚本:

    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;
    }
    
    读取工具

    添加一个参数 topic_id,类型为 “number”,描述为“你想在 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>`;
    }
    

    你还可以添加预定义的 schema、搜索设置和搜索上下文工具。

    我们将上面定义的 Search (custom) 工具设置为代理上的强制工具,并仅将其作为工具策略应用于第一个回复,但这是可选的。

    1. 你可以设置 URL 来源,使用 RAG 提供来自其他 URL 的信息,例如我们包含 discourse.org/pricing,因为这在我们的支持上下文中可能是重要信息。

    2. 确保机器人已启用,设置为优先级,创建了自己的用户,并允许私信。

就这样!以此为起点,你可以拥有你自己的 ask.example.com

5 个赞

这也让我想起了应用面试官(App Interviewer)的构想。未来的版本能否通过提出后续问题来引导用户完成任务,帮助他们找到正确的信息,或者收集撰写有用错误报告所需的细节?让 AI 在整个过程中提供帮助,可以让复杂任务变得更加易于上手。