> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.so/llms.txt
> Use this file to discover all available pages before exploring further.

# 展示第一个广告

> 创建会话、喂入消息、挂载广告、订阅事件。

这是 5 分钟接入的完整流程。开始之前，请确认你已经完成 [安装](/sdk/js/installation)。

## 1. 创建会话

引入 `createSession()`，为某个用户的某次对话配置会话。返回的 `Session` 是后续一切操作的入口——喂消息、创建广告、监听事件。

```ts theme={null}
import { createSession } from '@kontextso/sdk-js'

const session = createSession({
  publisherToken: '<your-publisher-token>',
  userId: 'user-1234',
  conversationId: 'conv-5678',
  character: {
    id: 'character-1234',
    name: 'John Doe',
    avatarUrl: 'https://example.com/avatar.png',
    greeting: 'Hello, how can I help you today?',
  },
  onEvent: ({ name, code, payload }) => {
    // 处理事件
  },
})
```

如果你使用 CDN 全局版本：

```js theme={null}
const { createSession } = window.KontextSdk
const session = createSession({ /* ... */ })
```

**说明：**

* `userId` 应在用户的整个生命周期内、跨会话与设备保持不变——它驱动个性化、频次封顶以及奖励广告。
* `conversationId` 应在每个聊天线程（一个聊天室、一个工单、应用中的一次对话）内唯一。

## 2. 向 SDK 喂消息

SDK 会根据新的聊天消息预加载广告。无论是用户消息还是助手消息，每出现一条就调用一次 `addMessage()`——两种角色都需要喂入，Kontext 才能完整理解对话上下文。

```ts theme={null}
// 用户消息
session.addMessage({
  id: 'msg-1',
  role: 'user',
  content: 'Hello, how are you?',
  createdAt: new Date(),
})

// 助手回复
session.addMessage({
  id: 'msg-2',
  role: 'assistant',
  content: 'I am good, thank you!',
  createdAt: new Date(),
})
```

**说明：**

* `id` 必须每条消息唯一且稳定——后续创建广告时会用同一个 id 引用。

## 3. 挂载广告

要展示广告，先为目标助手消息创建一个 `Ad` 实例，再将它挂载到一个 DOM 元素中。SDK 负责管理 iframe 及其生命周期。

```html theme={null}
<div id="ad-msg-2"></div>

<script type="module">
  // ... 上一节中的 session 与消息

  const ad = session.createAd('msg-2') // 助手消息的 messageId
  ad.mount(document.getElementById('ad-msg-2'))
</script>
```

在典型的聊天 UI 中，你会这样做：

1. 渲染消息列表。
2. 为每条助手消息渲染一个占位的 `<div>` 作为广告容器。
3. 当占位 DOM 存在后调用 `session.createAd(messageId)`，再调用 `ad.mount(element)`。
4. 当消息被滚动出视野或对话结束时，调用 `ad.destroy()` 释放 iframe。

```ts theme={null}
const ad = session.createAd('msg-2', { theme: 'dark' })
ad.mount(document.getElementById('ad-msg-2'))
// 之后，当消息被移除时：
ad.destroy()
```

如果你想一次完成，`session.render({ messageId, element, theme })` 是 `createAd + mount` 的便捷封装。

## 4. 释放资源

对话结束或页面卸载时调用 `session.destroy()`。该方法是幂等的，且是取消进行中预加载、释放 iframe 资源的必要操作。

```ts theme={null}
session.destroy()
```

## 订阅事件

通过 `onEvent` 回调订阅每个广告的生命周期。每个事件都有稳定的字符串 `name` 和类型化的 `payload`：

```ts theme={null}
const session = createSession({
  // ...
  onEvent: (event) => {
    switch (event.name) {
      case 'ad.filled':
        console.log('filled:', event.payload.id, 'revenue=', event.payload.revenue)
        break
      case 'ad.clicked':
        console.log('clicked:', event.payload.url)
        break
      case 'ad.no-fill':
        console.log('no fill, skipCode=', event.payload.skipCode)
        break
    }
  },
})
```

每个与广告位相关的事件（`ad.filled`、`ad.viewed`、`ad.clicked`、`ad.render-*`、`video.*`、`reward.granted`）的顶层还携带一个 `code` 字段，标识匹配到的广告位——配置了多个 `enabledPlacementCodes` 的发布方可以据此区分。会话级事件（`ad.no-fill`、`ad.error`）则不带这个字段。

接入期间若需查看 SDK 内部诊断信息，可以再传入 `onDebugEvent` 回调——它会对 SDK 的每一个内部步骤回调 `(name, data?)`。
