# M9: 知识库与帮助中心(Portal / Category / Article / Help Center / Kbase Search) > 参照仓库:chatwoot-reference > 产出日期:2026-05-22 > 版本基准:Chatwoot v3.x --- ## 目录 1. [Portal CRUD + 设置 + Logo + 自定义域名](#1-portal-crud--设置--logo--自定义域名) 2. [Category / Folder 管理](#2-category--folder-管理) 3. [Article CRUD + 关联文章 + 排序 + 搜索](#3-article-crud--关联文章--排序--搜索) 4. [RelatedCategory 关联分类](#4-relatedcategory-关联分类) 5. [Help Center 前端展示(Portal布局/SEO/追踪像素)](#5-help-center-前端展示portal布局seo追踪像素) 6. [Kbase 文本搜索(PgSearch)](#6-kbase-文本搜索pgsearch) 7. [Article Embedding 向量搜索(企业版)](#7-article-embedding-向量搜索企业版) 8. [Article 批量操作(状态/删除/翻译)](#8-article-批量操作状态删除翻译) 9. [权限策略(PortalPolicy / ArticlePolicy / CategoryPolicy)](#9-权限策略portalpolicy--articlepolicy--categorypolicy) 10. [AI辅助:Help Center 策展 + Article Writer(企业版)](#10-ai辅助help-center-策展--article-writer企业版) --- ## 1. Portal CRUD + 设置 + Logo + 自定义域名 ### 功能描述 Portal 是帮助中心的顶层容器,每个 Portal 属于一个 Account,可绑定一个 WebWidget 渠道用于在线聊天。Portal 拥有完整的配置体系:名称、slug(唯一标识)、自定义域名、品牌颜色、header 文字、首页链接、布局模式(classic/documentation)、多语言设置、社交链接、SSL 配置等。 ### 用户操作流程 1. **创建 Portal**:管理员进入 Settings → Help Center → 点击 "Create Portal",填写名称、slug、颜色、布局、默认语言等;系统自动创建 Portal,可选绑定 WebWidget 渠道 2. **查看 Portal 列表**:按账户加载所有 Portal(含 logo、archived 状态等) 3. **更新 Portal 设置**:修改名称、header_text、homepage_link、custom_domain、颜色、布局、多语言配置(allowed_locales/default_locale/draft_locales)、社交链接等 4. **删除 Portal**:级联删除 Categories、Articles、Folders、Inbox 关联(nullify) 5. **归档 Portal**:将 Portal 标记为 archived(archived=true),归档后前端不展示 6. **上传/移除 Logo**:Portal 支持品牌 Logo(ActiveStorage attachment),创建/更新时传入 blob_id 7. **发送 DNS 配置邮件**:为自定义域名发送 DNS 设置指引邮件(send_instructions),接收方按邮件指引配置 CNAME 8. **查看 SSL 状态**:检查自定义域名的 SSL 证书状态 ### 涉及的API端点 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/accounts/{account_id}/portals` | 列出所有 Portal | | POST | `/api/v1/accounts/{account_id}/portals` | 创建 Portal | | GET | `/api/v1/accounts/{account_id}/portals/{id}` | 查看 Portal 详情(含 articles) | | PATCH | `/api/v1/accounts/{account_id}/portals/{id}` | 更新 Portal 设置 | | DELETE | `/api/v1/accounts/{account_id}/portals/{id}` | 删除 Portal | | PATCH | `/api/v1/accounts/{account_id}/portals/{id}/archive` | 归档 Portal | | DELETE | `/api/v1/accounts/{account_id}/portals/{id}/logo` | 移除 Logo | | POST | `/api/v1/accounts/{account_id}/portals/{id}/send_instructions` | 发送 DNS 配置邮件 | | GET | `/api/v1/accounts/{account_id}/portals/{id}/ssl_status` | 查看 SSL 状态 | ### 涉及的数据模型 + 关键字段 - **Portal** 模型(表 `portals`): - `id` (bigint, PK) - `name` (string, not null) — 帮助中心名称 - `slug` (string, not null, unique) — 唯一标识,用于 URL 路径 - `archived` (boolean, default false) — 归档标记 - `color` (string) — 品牌主题色,默认 `#1f93ff` - `header_text` (text) — 头部文案 - `homepage_link` (string) — 首页链接 - `page_title` (string) — SEO 页面标题 - `custom_domain` (string, unique) — 自定义域名 - `config` (jsonb) — 配置 JSON:`{ allowed_locales, default_locale, draft_locales, layout, social_profiles }` - `ssl_settings` (jsonb, not null) — SSL 证书配置 - `account_id` (integer, not null, FK) — 所属账户 - `channel_web_widget_id` (bigint, FK) — 关联的 WebWidget 渠道(可选,用于嵌入在线聊天) - `created_at`, `updated_at` - 关联: - `has_many :categories, dependent: :destroy_async` - `has_many :folders, through: :categories` - `has_many :articles, dependent: :destroy_async` - `has_one_attached :logo` — ActiveStorage Logo - `has_many :inboxes, dependent: :nullify` — Inbox 可关联 Portal - `belongs_to :channel_web_widget` — 可选绑定在线聊天 - 关键方法: - `default_locale` — 从 config 中读取默认语言 - `allowed_locale_codes` — 返回 Portal 支持的语言列表 - `public_locale_codes` — 已发布文章可见语言(排除 draft_locale) - `layout` — 返回布局模式(classic / documentation) - `social_profiles` — 社交链接配置 - `config_json_format` — before_validation 回调,将 config 规范化(确保 locale 列表有效) ### 涉及的业务逻辑 - `PortalsController#create` — 构建 Portal + 合并 live_chat_widget_params(可选创建 WebWidget 渠道) - `PortalsController#update` — 事务更新 Portal + 处理 Logo attachment - `PortalsController#archive` — 设置 `archived=true` - `PortalsController#send_instructions` — 验证邮箱格式后发送 DNS 配置邮件 - `PortalsController#show` — 加载 Portal 及其 articles(可按 locale 搜索) - `Portal#validate_slug_uniqueness` — slug 全局唯一校验 - `Portal#normalize_empty_string_to_nil` — 将 custom_domain/homepage_link 空字符串转为 nil --- ## 2. Category / Folder 管理 ### 功能描述 Category(分类)是 Portal 下的内容组织单元,支持层级结构(parent_category / sub_categories)和多语言。每个 Category 属于一个 Portal 和一种 locale。Folder(文件夹)是 Category 内的进一步分组,Article 可以归属于 Folder。 ### 用户操作流程 1. **创建 Category**:在 Portal 管理页面选择 "Add Category",填写名称、描述、图标、locale、父分类(可选);同时可指定关联分类(related_category_ids) 2. **查看 Category 列表**:按 Portal + locale 加载分类列表,按 position 排序 3. **更新 Category**:修改名称、描述、图标、关联分类等 4. **删除 Category**:级联删除 Folders,Articles 的 category_id 被设为 NULL 5. **排序 Category**:通过 reorder 接口批量更新 position ### 涉及的API端点 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories` | 列出分类(可按 locale 过滤) | | POST | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories` | 创建分类 | | GET | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories/{id}` | 查看分类详情 | | PATCH | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories/{id}` | 更新分类 | | DELETE | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories/{id}` | 删除分类 | | POST | `/api/v1/accounts/{account_id}/portals/{portal_id}/categories/reorder` | 批量排序 | ### 涉及的数据模型 + 关键字段 - **Category** 模型(表 `categories`): - `id` (bigint, PK) - `name` (string) — 分类名称 - `description` (text) — 分类描述 - `icon` (string, default "") — 图标标识 - `locale` (string, default "en") — 语言 - `slug` (string, not null) — URL slug - `position` (integer) — 排序位置 - `account_id` (integer, not null, FK) - `portal_id` (integer, not null, FK) - `parent_category_id` (bigint, FK) — 父分类 ID(层级结构) - `associated_category_id` (bigint, FK) — 关联分类 ID(跨语言关联) - 索引:`(slug, locale, portal_id)` 联合唯一索引 - 关联: - `has_many :folders, dependent: :destroy_async` - `has_many :articles, dependent: :nullify` - `has_many :category_related_categories (RelatedCategory), dependent: :destroy_async` - `has_many :related_categories (Category), through: :category_related_categories` - `has_many :sub_categories (Category), foreign_key: :parent_category_id` - `belongs_to :parent_category (Category), optional: true` - `belongs_to :associated_category (Category), optional: true` — 同内容的另一种语言版本 - `belongs_to :portal` - **Folder** 模型(表 `folders`): - `id` (bigint, PK) - `name` (string) — 文件夹名称 - `account_id` (integer, not null, FK) - `category_id` (integer, not null, FK) - 关联:`has_many :articles, dependent: :nullify` - 关键方法: - `Category.search(params)` — 按 locale 过滤 + 分页 + position 排序 - `Category.update_positions(portal, positions_hash)` — 批量更新 position - Category 创建时自动从 Portal 继承 `account_id` - Category 校验 `locale` 必须在 Portal 的 `allowed_locales` 范围内 - `(slug, locale, portal_id)` 联合唯一约束 --- ## 3. Article CRUD + 关联文章 + 排序 + 搜索 ### 功能描述 Article(文章)是帮助中心的核心内容单元,属于 Portal + Category(可选 Folder),支持草稿/发布/归档三种状态。文章可关联"根文章"(associated_article_id)实现多语言翻译链接,支持全文搜索(PgSearch)、排序、浏览量统计。 ### 用户操作流程 1. **创建 Article**:在 Portal 管理页面选择 "Add Article",填写标题、内容、描述、locale、作者、分类;默认状态为 draft;可选择关联已有文章(associated_article_id) 2. **查看 Article 列表**:按 Portal + 可选 category_slug/locale/author/status 过滤,支持全文搜索(query),有分类时按 position 排序,否则按 updated_at 排序 3. **查看 Article 详情**:获取单篇文章完整内容 4. **编辑 Article**:获取编辑数据(edit 接口) 5. **更新 Article**:修改标题、内容、状态、分类等 6. **删除 Article**:删除单篇文章 7. **排序 Articles**:通过 reorder 接口批量更新 position 8. **查看浏览量**:Article.views 字段,每次公开页面访问时通过 tracking pixel 增量 ### 涉及的API端点 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles` | 列出文章(支持搜索+过滤) | | POST | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles` | 创建文章 | | GET | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/{id}` | 查看文章详情 | | GET | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/{id}/edit` | 编辑数据 | | PATCH | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/{id}` | 更新文章 | | DELETE | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/{id}` | 删除文章 | | POST | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/reorder` | 批量排序 | ### 涉及的数据模型 + 关键字段 - **Article** 模型(表 `articles`): - `id` (bigint, PK) - `title` (string) — 标题 - `content` (text) — 正文内容 - `description` (text) — 简短描述(用于列表展示+搜索权重B) - `locale` (string, default "en", not null) — 语言 - `slug` (string, not null, unique) — URL slug - `status` (integer) — 状态枚举:draft(0), published(1), archived(2) - `position` (integer) — 排序位置 - `views` (integer) — 浏览次数 - `meta` (jsonb) — 元数据 JSON - `account_id` (integer, not null, FK) - `portal_id` (integer, not null, FK) - `category_id` (integer, FK) — 所属分类(可选) - `folder_id` (integer, FK) — 所属文件夹(可选) - `author_id` (bigint, FK) — 作者(User) - `associated_article_id` (bigint, FK) — 根文章 ID(多语言关联) - 关联: - `has_many :associated_articles (Article), foreign_key: :associated_article_id, dependent: :nullify` - `belongs_to :root_article (Article), foreign_key: :associated_article_id` - `belongs_to :category, optional: true` - `belongs_to :folder, optional: true` - `belongs_to :portal` - `belongs_to :author (User)` - 搜索 scope: - `search_by_category_slug`, `search_by_category_locale`, `search_by_locale`, `search_by_author`, `search_by_status` - `order_by_updated_at`, `order_by_position`, `order_by_views` - `pg_search_scope :text_search` — 全文搜索(title:A, description:B, content:C),tsearch prefix 模式 - 关键方法: - `Article.search(params)` — 组合过滤 + 全文搜索 - `Article.associate_root_article(associated_article_id)` — 建立多语言关联链 - `Article.find_root_article_id(article)` — 查找根文章 ID - `Article.update_positions(portal, positions_hash)` — 批量排序 - `Article#increment_view_count` — 浏览量 +1(跳过 validation) - `before_validation` 钩子:确保 account_id、slug、locale 自动填充 ### 涉及的业务逻辑 - `ArticlesController#index` — 先计算统计数(all_count、mine_count、published_count、draft_count),再执行搜索 + 分页 - `ArticlesController#create` — 默认 status=draft,创建后调用 `associate_root_article` - `ArticlesController#set_article_count` — 不含 status/author_id 的搜索计算各种状态文章数 --- ## 4. RelatedCategory 关联分类 ### 功能描述 RelatedCategory 是分类之间的关联关系表,用于在帮助中心展示"相关分类"推荐。这是一种多对多关系:一个 Category 可以关联多个其他 Category(通过 RelatedCategory 中间表)。 ### 用户操作流程 1. **创建/更新 Category 时指定关联分类**:在 category 参数中传入 `related_category_ids` 数组 2. **前端展示相关分类**:在分类详情页面展示关联的其他分类 ### 涉及的API端点 同 Category CRUD(通过 `related_category_ids` 参数) ### 涉及的数据模型 + 关键字段 - **RelatedCategory** 模型(表 `related_categories`): - `id` (bigint, PK) - `category_id` (bigint, FK) — 源分类 - `related_category_id` (bigint, FK) — 关联分类 - `created_at`, `updated_at` - 索引:`(category_id, related_category_id)` 和 `(related_category_id, category_id)` 双向唯一索引 - 关联: - `belongs_to :related_category (Category)` - `belongs_to :category (Category)` - Category 中: - `has_many :category_related_categories (RelatedCategory), dependent: :destroy_async` - `has_many :related_categories (Category), through: :category_related_categories` --- ## 5. Help Center 前端展示(Portal布局/SEO/追踪像素) ### 功能描述 帮助中心有公开的 Portal 前端,通过 `/hc/{slug}/{locale}` 系列路由对外展示。支持两种布局模式(classic / documentation),支持 SEO(OG 图片、sitemap)、自定义域名、暗/亮主题切换、iframe 嵌入、追踪像素(tracking pixel)统计浏览量。 ### 用户操作流程 1. **访问帮助中心首页**:`/hc/{slug}` 自动重定向到 `/hc/{slug}/{default_locale}`,加载 Portal 配置、分类、热门文章、精选文章 2. **浏览分类页**:`/hc/{slug}/{locale}/categories/{category_slug}`,展示分类下的已发布文章 3. **阅读文章**:`/hc/{slug}/articles/{article_slug}`,渲染文章内容、OG SEO 信息 4. **搜索文章**:`/hc/{slug}/{locale}/articles?query=xxx`,前端搜索+排序+分页 5. **Markdown 输出**:`/hc/{slug}/articles/{article_slug}.md`,纯 Markdown 格式(供 AI/工具调用) 6. **Tracking Pixel**:`/hc/{slug}/articles/{article_slug}.png`,1x1 像素追踪浏览量(24h private cache) 7. **Sitemap**:`/hc/{slug}/sitemap.xml`,SEO 爬虫用 8. **自定义域名**:Portal 配置 custom_domain 后,DNS CNAME 指向 Chatwoot,SSL 自动配置 ### 涉及的API端点(Public) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/hc/{slug}` | Portal 首页(重定向到 locale 版) | | GET | `/hc/{slug}/sitemap.xml` | Sitemap | | GET | `/hc/{slug}/{locale}` | Portal 首页(指定语言) | | GET | `/hc/{slug}/{locale}/articles` | 文章搜索列表 | | GET | `/hc/{slug}/{locale}/categories` | 分类列表(JSON) | | GET | `/hc/{slug}/{locale}/categories/{category_slug}` | 分类详情+文章 | | GET | `/hc/{slug}/{locale}/categories/{category_slug}/articles` | 分类下文章 | | GET | `/hc/{slug}/articles/{article_slug}` | 文章详情页 | | GET | `/hc/{slug}/articles/{article_slug}.md` | Markdown 输出 | | GET | `/hc/{slug}/articles/{article_slug}.png` | Tracking Pixel | ### 涉及的业务逻辑 - **Public::Api::V1::PortalsController**: - `show` — 加载 Portal + 文章 + 分类,按 locale 过滤 - `load_home_data`(documentation 布局)— 加载 visible_categories、popular_topics、featured articles、recently_articles - 自定义域名验证:`ensure_custom_domain_request` - 布局选择:`set_portal_layout`(classic/documentation) - 视图变体:`set_view_variant`(documentation 布局使用 :documentation variant) - **Public::Api::V1::Portals::CategoriesController**: - `index` — HTML 重定向到 locale 版,JSON 返回分类列表 - `show` — 加载分类详情 + 已发布文章(documentation 布局) - **Public::Api::V1::Portals::ArticlesController**: - `index` — 搜索 + 排序(sort: newest/popular) + 限制结果数 - `show` — 渲染文章内容(OG 图片) - `show_markdown` — 纯 Markdown 输出(仅已发布文章) - `tracking_pixel` — 增加浏览量 + 返回 1x1 PNG(24h private cache) - **Public::Api::V1::Portals::BaseController**: - `PORTAL_LAYOUTS = %w[classic documentation]` - Locale 切换:`switch_locale_with_portal` / `switch_locale_with_article` - 主题:dark/light 通过 params[:theme] - iframe 允许:`allow_iframe_requests` - 功能开关:`ensure_portal_feature_enabled` --- ## 6. Kbase 文本搜索(PgSearch) ### 功能描述 Article 模型使用 PgSearch(PostgreSQL 全文搜索)实现知识库文本搜索。搜索权重:标题(A)> 描述(B)> 正文(C),使用 tsearch prefix 模式(支持前缀匹配),normalization=2 防止长文堆叠关键词。 ### 用户操作流程 1. **管理端搜索**:在 Portal 管理页面的文章列表中输入搜索词(query 参数),结合 category_slug/locale/author/status 过滤 2. **公众端搜索**:在帮助中心前端搜索框输入关键词,搜索已发布文章 ### 搜索实现细节 - **pg_search_scope :text_search**: - `against: { title: 'A', description: 'B', content: 'C' }` — 权重分级 - `using: { tsearch: { prefix: true, normalization: 2 } }` — 前缀匹配 + log(document_length) 归一化 - `ranked_by: ':tsearch'` - **Article.search(params)**: - 先 left_outer_joins(:category) + 各种 scope 过滤 - 再 `text_search(params[:query])` 如果 query 存在 - 返回结果集 - **公众端搜索**: - `Public::Api::V1::Portals::ArticlesController#index` - 仅搜索已发布文章(`published` 状态) - 支持 sort 参数(newest/popular) - 企业版覆盖 `search_articles` → 使用向量搜索(见下节) --- ## 7. Article Embedding 向量搜索(企业版) ### 功能描述 企业版提供基于向量嵌入(Embedding)的语义搜索功能。当文章标题/描述/内容变更时,自动生成 embedding 向量存入 `article_embeddings` 表(使用 pgvector 扩展,1536维 cosine 距离)。公众端搜索时可使用向量搜索替代文本搜索,返回语义最相关的5篇文章。 ### 用户操作流程 1. **自动嵌入生成**:文章保存后自动触发 `Portal::ArticleIndexingJob` → 生成搜索词 → `Captain::Llm::EmbeddingService` 调用 LLM API 生成向量 → 存入 `article_embeddings` 2. **向量搜索**:公众端搜索时,若 Portal 账户开启了 `help_center_embedding_search` 功能开关,使用 `vector_search` 替代文本搜索 ### 涉及的数据模型 + 关键字段 - **ArticleEmbedding** 模型(表 `article_embeddings`): - `id` (bigint, PK) - `embedding` (vector(1536)) — 向量数据(pgvector 扩展) - `term` (text, not null) — 搜索词文本 - `article_id` (bigint, not null, FK) - `created_at`, `updated_at` - 索引:`ivfflat` 索引(向量近似搜索) - 关联: - `belongs_to :article` - `has_neighbors :embedding, normalize: true` — Neighbor 模式(cosine 距离) ### 涉及的业务逻辑 - **Enterprise::Concerns::Article**(prepend 到 Article 模型): - `after_save :add_article_embedding` — 当 title/description/content 变更时触发 - `self.vector_search(params)`: 1. 调用 `Captain::Llm::EmbeddingService` 将 query 生成向量 2. 在已过滤文章范围内,使用 `ArticleEmbedding.nearest_neighbors(:embedding, embedding, distance: 'cosine').limit(5)` 找最近的5篇 3. 返回匹配文章 - **Enterprise::Public::Api::V1::Portals::ArticlesController**(prepend): - `search_articles` 覆盖:若 `help_center_embedding_search` 开关开启且有 query,使用 `vector_search`;否则走原始文本搜索 - **Portal::ArticleIndexingJob**: - `perform(article)` → 调用 `article.generate_and_save_article_seach_terms` 生成搜索词 - **ArticleEmbedding#update_response_embedding**(after_commit): - 当 term 变更或 embedding 为 nil 时,调用 `Captain::Llm::UpdateEmbeddingJob` 重新生成向量 --- ## 8. Article 批量操作(状态/删除/翻译) ### 功能描述 提供文章的批量操作能力:批量修改状态(draft/published/archived)、批量删除、批量翻译(企业版,调用 Captain LLM 翻译服务)。 ### 用户操作流程 1. **批量修改状态**:在文章列表选中多篇文章,选择目标状态(draft/published/archived),调用 `update_status` 2. **批量删除**:选中多篇文章,调用 `delete_articles` 3. **批量翻译**(企业版):选中多篇文章 + 目标 locale + 目标分类,调用 `translate`;系统检查是否已存在翻译(若有则返回冲突提示,可 force 覆盖),然后异步执行 `Captain::Articles::TranslateJob` ### 涉及的API端点 | 方法 | 路径 | 说明 | |------|------|------| | PATCH | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/bulk_actions/update_status` | 批量修改状态 | | DELETE | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/bulk_actions/delete_articles` | 批量删除 | | POST | `/api/v1/accounts/{account_id}/portals/{portal_id}/articles/bulk_actions/translate` | 批量翻译(企业版) | ### 涉及的业务逻辑 - **BulkActionsController**(核心版): - `update_status` — 验证状态有效性,事务更新所有选中文章 - `delete_articles` — 批量 destroy_all - `translate` — 基础版返回 `not_implemented` - **Enterprise::BulkActionsController**(prepend): - `translate` 覆盖实现: 1. 验证 captain_tasks 功能开关 2. 验证 locale 在 Portal allowed_locales 中 3. 验证目标分类存在且 locale 匹配 4. 检查翻译是否已存在(`find_existing_translations`),若有返回 409 conflict(含重复文章列表),可 force=true 覆盖 5. 为每篇文章异步执行 `Captain::Articles::TranslateJob` - **Captain::Articles::TranslateJob**: - 调用 `Captain::Llm::ArticleTranslationService` 分别翻译标题和内容 - 若已有翻译则更新,否则创建新文章(关联到根文章) --- ## 9. 权限策略(PortalPolicy / ArticlePolicy / CategoryPolicy) ### 功能描述 知识库相关资源的权限策略:基础版仅管理员可操作(CRUD),普通账户成员可查看(index/show)。企业版扩展了自定义角色权限 `knowledge_base_manage`。 ### 涉及的权限策略 - **PortalPolicy**: - `index?` / `show?` / `ssl_status?` → 账户成员即可 - `create?` / `update?` / `destroy?` / `edit?` / `logo?` / `send_instructions?` → 仅管理员 - **CategoryPolicy**: - `index?` → 账户成员 - `create?` / `update?` / `destroy?` / `show?` / `edit?` / `reorder?` → 仅管理员 - **ArticlePolicy**: - `index?` → 账户成员 - `create?` / `update?` / `destroy?` / `show?` / `edit?` / `reorder?` → 仅管理员 - **Enterprise::ArticlePolicy**(prepend): - 所有 CRUD 操作增加 `knowledge_base_manage` 自定义权限检查 - 有 `knowledge_base_manage` 权限的自定义角色用户也可操作 --- ## 10. AI辅助:Help Center 策展 + Article Writer(企业版) ### 功能描述 企业版提供 AI 辅助的帮助中心内容生成能力: 1. **Help Center 策展(Curation)**:给定一组 URL,AI 自动筛选出适合作为帮助中心文章的页面,生成分类+文章结构 2. **Article Writer**:给定 URL,AI 抓取网页内容并重写为帮助中心文章格式 3. **Onboarding 自动生成**:新用户注册后,系统可自动从其网站抓取内容生成帮助中心 ### 涉及的业务逻辑 - **Captain::Llm::HelpCenterCurationService**: - 输入:account + links(URL列表) - 使用 GPT-4.1 模型,分析 URL 列表,筛选出适合做帮助中心文章的页面 - 输出:`{ categories: [...], articles: [...] }` 结构 - **Captain::Llm::ArticleWriterService**: - 输入:account + 原始内容/URL - 使用 LLM 将内容重写为帮助中心文章格式 - **Onboarding::HelpCenterArticleBuilder**(enterprise): - 初始化参数:account, portal, user, article(含 urls, title, category_id) - 流程:scrape → normalize → rewrite → 保存文章 - 抓取指定 URL 的页面内容,处理后生成帮助中心文章 - **Onboarding::HelpCenterArticleWriterJob** / **Onboarding::HelpCenterArticleGenerationJob**: - 异步执行文章生成,失败时记录错误上下文 - **Captain::Tools::FaqLookupTool**(AI Copilot 工具): - 输入:query(问题关键词) - 使用语义搜索在已审批的 FAQ Responses 中查找匹配答案 - 返回格式化的问答内容,包含外部链接(如果是帮助中心文章则显示 Source) --- ## 模块间依赖关系 ``` Account └── Portal (1:N) ├── Category (1:N) │ ├── Folder (1:N) │ ├── RelatedCategory (M:N via related_categories) │ ├── SubCategory (1:N, parent_category_id) │ └── Article (1:N, category_id, nullable) ├── Article (1:N) │ ├── ArticleEmbedding (1:N, enterprise) │ └── AssociatedArticle (多语言关联链) ├── Channel::WebWidget (1:1, 可选绑定在线聊天) └── Inbox (1:N, nullable) ``` ### 关键配置/功能开关 - `help_center_embedding_search` — 开启向量语义搜索(企业版) - `captain_tasks` — 开启 AI 翻译/写作能力(企业版) - Portal.config(jsonb):`allowed_locales`, `default_locale`, `draft_locales`, `layout`, `social_profiles` - Portal.ssl_settings(jsonb):自定义域名 SSL 配置 ### 技术要点 1. **PgSearch 全文搜索**:tsearch prefix 模式 + 权重分级(A/B/C) + normalization=2 2. **pgvector 向量搜索**:ivfflat 索引 + cosine 距离 + 1536维 OpenAI embedding 3. **多语言架构**:Portal.allowed_locales → Category.locale → Article.locale + associated_article_id 关联链 4. **Tracking Pixel**:1x1 PNG + 24h private cache + 浏览量计数 5. **Layout 模式**:classic(传统) / documentation(文档风格),通过 request.variant 区分视图模板 6. **自定义域名 + SSL**:DNS CNAME 指向 + 自动 SSL 证书管理 7. **AI Integration**:GPT-4.1 策展 + Article Writer + Embedding Service + Translation Service