# 免登录窗口发布

> 创建分享链接，在新会话核对发布配置、答案和引用。

Source: https://doc.fastgpt.cn/zh-CN/guide/build/publish/link

免登录窗口发布为已发布的 FastGPT 应用生成可独立设置有效期、QPM 限流、积分额度上限与引用可见性的公开访问链接，外部访客无需注册账号即可直接通过浏览器发起对话或以 `iframe` 嵌入业务网页。

## 介绍

免登录窗口可以创建一个临时可访问的地址，任何互联网上的用户可以通过该地址来使用应用。产生的费用产生在应用归属的团队下，所以请注意不要随意分享。

系统会为每个用户生成一个 localId，用于标识用户，从云端拉取对话记录。但是这种方式仅能保障用户在同一设备同一浏览器中使用，如果切换设备或者清空浏览器缓存则会丢失这些记录。这种方式存在一定的风险，因此我们仅允许用户拉取近 `30天` 的 `20条` 记录。

## 开始前

准备应用编辑和发布渠道管理权限、可用模型与额度，并先[发布已验证的配置](/zh-CN/guide/build/publish/versions)。链接使用量由所属团队承担；演示建议使用虚构资料并按可用能力设置有效期和用量限制。入口和选项以团队版本与权限为准。

## 使用流程

### 1. 创建链接

在 `应用详情` - `发布渠道` - `免登录窗口` 页面点击右上角 **创建新链接**，在弹出的 **创建链接** 弹窗中填写配置。

填写便于识别的名称，设置所需的过期时间、QPM 和积分上限。本任务需要核对原文，在可见度配置中开启 **查看引用片段**，然后确认创建。创建后核对列表显示的过期时间和积分上限。名称仅用于记录展示。

![创建分享链接并配置访问限制](/imgs/optimized/current/publish-link.png.webp)

### 2. 复制链接

在链接列表右侧操作栏点击 **开始使用**（旁边的 **……** 菜单支持 **编辑** 与 **删除**），在 **选择使用方式** 弹窗中选择使用方式，复制分享链接或获取 iframe / script 嵌入代码。

### 3. 验证新会话与引用

打开链接并创建新对话，重复调试时使用的已知问题。差旅示例应回答 15 个自然日（界面示例为 15 个日历日）、发票和已批准差旅申请；询问两份差旅资料均未涵盖的事项（如境外签证办理费用如何报销）时，应说明资料缺失。打开引用片段，核对来源和支持答案的原文。

如果引用入口不可见，检查该链接的引用展示设置以及当前访问权限。根据使用者需要开启允许的查看能力，再从链接新建对话复核。

### 4. 恢复访问或结果差异

链接无法打开时检查有效期、用量限制、可用额度和已配置的身份验证。能打开但回答有差异时核对链接所属应用、发布版本、知识库索引及会话历史。修复后从同一链接新建对话，重跑事实与引用检查，详见[知识库排障](/zh-CN/guide/dataset/faq)。

## 参数配置

部分参数仅商业版支持配置。

* **名称**：用于记录与展示该分享链接的标识名称。
* **过期时间**：超过该时间后，链接停止提供访问。
* **QPM**：每个 IP 每分钟最大提问次数。
* **积分上限**：该链接可消耗的最大 AI 积分额度。
* **身份验证**：接入第三方业务系统进行身份鉴权与对话回调。
* **实时运行状态**：控制对话过程中是否实时展示当前运行的节点状态。
* **查看技能引用**：控制访问者是否可查看技能调用引用详情。
* **查看引用片段**：控制访问者查看知识库检索引用片段的入口，用于核对答案依据。
* **查看引用全文**：允许访问者查看引用内容所属的完整文档正文。
* **下载/打开来源原文**：允许访问者下载知识库原始文件或跳转来源网站；开启前请核对资料的可分享范围。

## 身份验证说明

分享链接身份验证设计的目的在于，将 FastGPT 的对话框快速、安全的接入到你现有的系统中，仅需 2 个接口即可实现。该功能目前只在商业版中提供。

### 使用说明

免登录链接配置中，你可以选择填写 `身份验证` 栏。这是一个 `POST` 请求的根地址。在填写该地址后，分享链接的初始化、开始对话以及对话结束都会向该地址的特定接口发送一条请求。下面以 `host` 来表示 `身份验证根地址`。服务器接口仅需返回是否校验成功即可，不需要返回其他数据，格式如下：

#### 接口统一响应格式

```jsonc
{
  "success": true,
  "message": "错误提示",
  "msg": "同message, 错误提示",
  "data": {
    "uid": "用户唯一凭证" // 必须返回
  }
}
```

`FastGPT` 将会判断 `success` 是否为 `true` 决定是允许用户继续操作。`message` 与 `msg` 是等同的，你可以选择返回其中一个，当 `success` 不为 `true` 时，将会提示这个错误。

`uid` 是用户的唯一凭证，必须返回该 ID 且 ID 的格式为不包含 "|"、"/“、"\\" 字符的、小于等于 255 **字节长度**的字符串，否则会返回 `Invalid UID` 的错误。`uid` 将会用于拉取对话记录以及保存对话记录，可参考下方实践案例。

#### 触发流程

![配置身份验证后的分享链接流程](/imgs/diagrams/share-link-auth.zh-CN.light.svg)

初始化要求 `success=true` 且 `uid` 为有效字符串。商业版对话入口会在开始回调前校验到期时间、用量和 QPM；完成回调用于结果通知。受保护链接还会验证登录成员与应用读取权限。

### 配置教程

#### 1. 配置身份校验地址

配置校验地址后，在每次分享链接使用时，都会向对应的地址发起校验和上报请求。

这里仅需配置根地址，无需具体到完整请求路径。

#### 2. 分享链接中增加额外 query

在分享链接的地址中，增加一个额外的参数: authToken。例如：

原始的链接：`https://share.fastgpt.io/chat/share?shareId=648aaf5ae121349a16d62192`

完整链接: `https://share.fastgpt.io/chat/share?shareId=648aaf5ae121349a16d62192&authToken=userid12345`

这个 `authToken` 通常是你系统生成的用户唯一凭证（Token 之类的）。FastGPT 会在鉴权接口的 `body` 中携带 token=\[authToken] 的参数。

#### 3. 编写聊天初始化校验接口

#### 请求示例

```bash
curl --location --request POST '{{host}}/shareAuth/init' \
--header 'Content-Type: application/json' \
--data-raw '{
    "token": "[authToken]"
}'
```

#### 鉴权成功

```json
{
  "success": true,
  "data": {
    "uid": "用户唯一凭证"
  }
}
```

系统会拉取该分享链接下，uid 为接口返回值（示例中的“用户唯一凭证”）的对话记录。

#### 鉴权失败

```json
{
  "success": false,
  "message": "身份错误"
}
```

#### 4. 编写对话前校验接口

#### 请求示例

```bash
curl --location --request POST '{{host}}/shareAuth/start' \
--header 'Content-Type: application/json' \
--data-raw '{
    "token": "[authToken]",
    "question": "用户问题"
}'
```

#### 鉴权成功

```json
{
  "success": true,
  "data": {
    "uid": "用户唯一凭证"
  }
}
```

#### 鉴权失败

```json
{
  "success": false,
  "message": "身份验证失败"
}
```

```json
{
  "success": false,
  "message": "存在违规词"
}
```

#### 5. 编写对话结果上报接口（可选）

该接口无规定返回值。

FastGPT 发送的请求 body 为 `{ token, appName, responseData, chatId }`：`responseData` 与 [chat 接口](/zh-CN/openapi/intro#响应) 返回的响应详情格式相同，`appName` 为应用名称，`chatId` 为对话 ID。

重点关注 `responseData` 中的 `totalPoints`（AI 积分）与 `tokens`（模型上下文 token 数）。顶层 `token` 是业务身份凭证，按鉴权约定处理。以下示例中的模型名称与模块名称仅作字段结构示意：

```bash
curl --location --request POST '{{host}}/shareAuth/finish' \
--header 'Content-Type: application/json' \
--data-raw '{
    "token": "[authToken]",
    "appName": "[应用名称]",
    "responseData": [
        {
            "moduleName": "知识库搜索",
            "moduleType": "datasetSearchNode",
            "totalPoints": 1.5278,
            "query": "导演是谁\n《铃芽之旅》的导演是谁？\n这部电影的导演是谁？\n谁是《铃芽之旅》的导演？",
            "model": "text-embedding-v4",
            "tokens": 1524,
            "similarity": 0.83,
            "limit": 400,
            "searchMode": "embedding",
            "searchUsingReRank": false,
            "extensionModel": "qwen3.8-flash",
            "extensionResult": "《铃芽之旅》的导演是谁？\n这部电影的导演是谁？\n谁是《铃芽之旅》的导演？",
            "runningTime": 2.15
        },
        {
            "moduleName": "AI 对话",
            "moduleType": "chatNode",
            "totalPoints": 0.593,
            "model": "qwen3.8-flash",
            "tokens": 593,
            "query": "导演是谁",
            "maxToken": 2000,
            "quoteList": [
                {
                    "id": "65bb346a53698398479a8854",
                    "q": "导演是谁？",
                    "a": "电影《铃芽之旅》的导演是新海诚。",
                    "chunkIndex": 0,
                    "datasetId": "65af9b947916ae0e47c834d2",
                    "collectionId": "65bb345c53698398479a868f",
                    "sourceName": "dataset - 2024-01-23T151114.198.csv",
                    "sourceId": "65bb345b53698398479a868d",
                    "score": [
                        {
                            "type": "embedding",
                            "value": 0.9377183318138123,
                            "index": 0
                        },
                        {
                            "type": "rrf",
                            "value": 0.06557377049180328,
                            "index": 0
                        }
                    ]
                }
            ],
            "historyPreview": [
                {
                    "obj": "Human",
                    "value": "使用 <Data></Data> 标记中的内容作为本次对话的参考:\n\n<Data>\n导演是谁？\n电影《铃芽之旅》的导演是新海诚。\n------\n电影《铃芽之旅》的编剧是谁？22\n新海诚是本片的编剧。\n------\n电影《铃芽之旅》的女主角是谁？\n电影的女主角是铃芽。\n------\n电影《铃芽之旅》的制作团队中有哪位著名人士？2\n川村元气是本片的制作团队成员之一。\n------\n你是谁？\n我是电影《铃芽之旅》助手\n------\n电影《铃芽之旅》男主角是谁？\n电影《铃芽之旅》男主角是宗像草太，由松村北斗配音。\n------\n电影《铃芽之旅》的作者新海诚写了一本小说，叫什么名字？\n小说名字叫《铃芽之旅》。\n------\n电影《铃芽之旅》的女主角是谁？\n电影《铃芽之旅》的女主角是岩户铃芽，由原菜乃华配音。\n------\n电影《铃芽之旅》的故事背景是什么？\n日本\n------\n谁担任电影《铃芽之旅》中岩户环的配音？\n深津绘里担任电影《铃芽之旅》中岩户环的配音。\n</Data>\n\n回答要求：\n- 如果你不清楚答案，你需要澄清。\n- 避免提及你是从 <Data></Data> 获取的知识。\n- 保持答案与 <Data></Data> 中描述的一致。\n- 使用 Markdown 语法优化回答格式。\n- 使用与问题相同的语言回答。\n\n问题:\"\"\"导演是谁\"\"\""
                },
                {
                    "obj": "AI",
                    "value": "电影《铃芽之旅》的导演是新海诚。"
                }
            ],
            "contextTotalLen": 2,
            "runningTime": 1.32
        }
    ],
    "chatId": "[chatId]"
}'
```

**responseData 完整字段说明：**

```ts
type ResponseType = {
  moduleType: FlowNodeTypeEnum; // 模块类型
  moduleName: string; // 模块名
  moduleLogo?: string; // logo
  runningTime?: number; // 运行时间
  query?: string; // 用户问题/检索词
  textOutput?: string; // 文本输出

  tokens?: number; // 上下文总Tokens
  model?: string; // 使用到的模型
  contextTotalLen?: number; // 上下文总长度
  totalPoints?: number; // 总消耗AI积分

  temperature?: number; // 温度
  maxToken?: number; // 模型的最大token
  quoteList?: SearchDataResponseItemType[]; // 引用列表
  historyPreview?: ChatItemMiniType[]; // 上下文预览（历史记录会被裁剪）

  similarity?: number; // 最低相关度
  limit?: number; // 引用上限token
  searchMode?: `${DatasetSearchModeEnum}`; // 搜索模式
  searchUsingReRank?: boolean; // 是否使用rerank
  extensionModel?: string; // 问题扩展模型
  extensionResult?: string; // 问题扩展结果
  extensionTokens?: number; // 问题扩展总字符长度

  cqList?: ClassifyQuestionAgentItemType[]; // 分类问题列表
  cqResult?: string; // 分类问题结果

  extractDescription?: string; // 内容提取描述
  extractResult?: Record<string, any>; // 内容提取结果

  params?: Record<string, any>; // HTTP模块params
  body?: Record<string, any>; // HTTP模块body
  headers?: Record<string, any>; // HTTP模块headers
  httpResult?: Record<string, any>; // HTTP模块结果

  pluginOutput?: Record<string, any>; // 插件输出
  pluginDetail?: ChatHistoryItemResType[]; // 插件详情

  isElseResult?: boolean; // 判断器结果
};
```

### 实践案例

我们以 [Laf 作为服务器为例](https://laf.dev/)，简单展示这 3 个接口的使用方式。

#### 1. 创建 3 个 Laf 接口

#### /shareAuth/init

这个接口中，我们设置了 `token` 必须等于 `fastgpt` 才能通过校验。（实际生产中不建议固定写死）

```ts
import cloud from '@lafjs/cloud';

export default async function (ctx: FunctionContext) {
  const { token } = ctx.body;

  // 此处省略 token 解码过程
  if (token === 'fastgpt') {
    return { success: true, data: { uid: 'user1' } };
  }

  return { success: false, message: '身份错误' };
}
```

#### /shareAuth/start

这个接口中，我们设置了 `token` 必须等于 `fastgpt` 才能通过校验。并且如果问题中包含了 `你` 字，则会报错，用于模拟敏感校验。

```ts
import cloud from '@lafjs/cloud';

export default async function (ctx: FunctionContext) {
  const { token, question } = ctx.body;

  // 此处省略 token 解码过程
  if (token !== 'fastgpt') {
    return { success: false, message: '身份错误' };
  }

  if (question.includes('你')) {
    return { success: false, message: '内容不合规' };
  }

  return { success: true, data: { uid: 'user1' } };
}
```

#### /shareAuth/finish

结果上报接口可自行进行逻辑处理。

```ts
import cloud from '@lafjs/cloud';

export default async function (ctx: FunctionContext) {
  const { token, responseData } = ctx.body;

  const totalPoints = responseData.reduce((sum, item) => sum + (item.totalPoints ?? 0), 0);

  // Authenticate the callback and record points with an idempotency key.
  // Currency conversion follows your own billing contract.
  return { totalPoints };
}
```

#### 2. 配置校验地址

复制任一接口地址（如 `https://d8dns0.laf.dev/shareAuth/finish`），去掉路径部分 `/shareAuth/finish` 后，将根地址 `https://d8dns0.laf.dev` 填入 **身份验证**。

#### 3. 修改分享链接参数

源分享链接：`https://share.fastgpt.io/chat/share?shareId=64be36376a438af0311e599c`

修改后：`https://share.fastgpt.io/chat/share?shareId=64be36376a438af0311e599c&authToken=fastgpt`

#### 4. 测试效果

1. 打开源链接或者 `authToken` 不等于 `fastgpt` 的链接会提示身份错误。
2. 发送内容中包含你字，会提示内容不合规。

### 使用场景

这个鉴权方式通常是帮助你直接嵌入 `分享链接` 到你的应用中，在你的应用打开分享链接前，应做 `authToken` 的拼接后再打开。

除了对接已有系统的用户外，你还可以对接 `余额` 功能，通过 `结果上报` 接口扣除用户余额，通过 `对话前校验` 接口检查用户的余额。
