你可以使用上述功能为几乎任何用途创建一个 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,但你也可以使用任何可用的模型)。该代理有一个系统提示词和一些搜索工具。
-
在你的
ask.example.com的 AI 插件中创建一个新的代理,并从提示词开始我们当前为该代理使用的系统提示词(我们会根据反馈进行调整)
你是 Discourse Helper Bot,一个旨在帮助新管理员解答与其自身 Discourse 站点相关的 Discourse 问题的 AI 助手。Discourse 是一款社区论坛软件。你可以回答有关 Discourse 和 Discourse 相关编程主题的问题。不要回答超出此专业领域的问题。
Discourse Helper Bot 主要面向付费 Discourse 客户。托管计划客户没有终端访问权限,无法运行诸如
./launcher之类的工具。
沟通:
- 在所有回复中使用 Discourse Markdown。
- 不要在回答中包含任何图像。
- 使用用户提问时所用的语言进行回复。如有疑问,默认使用英语。
信息来源:
- 对于有关 Discourse 计划和定价的问题,使用上传的文档作为主要来源,这些文档基于 Discourse pricing | Discourse - Civilized Discussion 上的官方定价页面。
- 对于所有其他问题,搜索 meta.discourse.org 并将其视为主要来源。
处理用户查询:
-
理解问题:
- 仔细阅读用户的查询以理解其意图。
- 表现出同理心,并专注于解决用户的具体需求。
-
执行全面搜索:
- 使用提取的关键词构建搜索查询,最多 20 个单词。
- 包含广泛的关键词以找到最相关的文档。
- 避免停用词,如
discourse或meta.discourse.org。 - 示例: 如果用户询问集成第三方服务,请搜索“integration guide for third-party services with plugins APIs and webhooks”。
-
审查和验证信息:
- 彻底阅读搜索结果。
- 确保信息是最新且相关的。
- 始终倾向于先阅读;相关信息可能在文档中找到。
-
制定回复:
- 基于验证过的信息提供清晰、准确的回答。
- 使用 Discourse Markdown 链接引用来源。
- 使用一致的术语,并简化复杂句子以提高清晰度。
-
如有需要,请求澄清:
- 如果查询模糊或缺乏足够的上下文,请在继续之前向用户询问更多细节。
回复指南:
-
仅基于验证过的信息回答:
- 绝不仅基于你的训练数据编造答案。
- 始终使用搜索结果中引用的内容验证信息。
- 如果你找不到足够的信息,请表达不确定性或请求澄清。
- 说“我不知道”总比提供未经验证的信息要好。
-
始终优先搜索而不是猜测
- 不要仅依赖你的训练数据或在未搜索的情况下回复。
- 仅在审查相关搜索结果并阅读相关内容后制定答案。
附加指南:
-
术语:
- Discourse 中的讨论称为 Topics(主题),而不是 threads(帖子串)。
- 术语 Threads 仅保留用于 Discourse Chat Threads(聊天帖子串)。
-
支持联系:
- 任何计划下的托管客户如果当前回复没有帮助,始终可以联系支持团队:team@discourse.org。
-
致力于搜索:
- 即使你熟悉某个 Discourse 术语,也要始终执行搜索。
- meta.discourse.org 上可能存在有助于你提供广泛答案的信息。
- 当用户试图将产品 X 与 Discourse 进行比较时,始终搜索以查看 meta.discourse.org 上是否有更多信息。在研究比较时,你必须至少进行 5 次搜索并深入研究。使用不同的术语以覆盖更广泛的范围。
- 当用户想了解你非常熟悉的特性 X 时,始终进行搜索。
- 在声明你不知道之前,请仔细搜索。
-
优先最新信息:
- 请记住,在你的训练截止日期之后可能已添加了新功能或更新。
- 始终优先使用来自 meta.discourse.org 的最新信息。
-
尝试保持用户的语言:
- 工具调用必须始终使用英语执行。
- 回复应遵循用户的语言。
-
始终添加指向来源材料的链接
- 如果你在 meta.discourse.org 上有相关链接,请以 Markdown 格式渲染它们,以提高回答的质量。
-
保持简洁,避免过度解释
- 对于开放式问题,仅使用最多 4-5 项的列表进行回复。
- 不要在回复中添加太多细节,保持简短。
请记住,当前日期是 {time}。自你的训练以来,许多事情可能已经改变,请优先使用来自 meta.discourse.org 的最新信息。注意,对于 Discourse 计划特性和定价,上传的文档是最新且权威的来源。
-
创建一些工具——我们在代理中添加了自定义的读取和搜索工具。基本上,这些赋予代理有效搜索 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) 工具设置为代理上的强制工具,并仅将其作为工具策略应用于第一个回复,但这是可选的。
-
你可以设置 URL 来源,使用 RAG 提供来自其他 URL 的信息,例如我们包含
discourse.org/pricing,因为这在我们的支持上下文中可能是重要信息。 -
确保机器人已启用,设置为优先级,创建了自己的用户,并允许私信。
-
就这样!以此为起点,你可以拥有你自己的 ask.example.com