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

# 飞书

## 功能说明

`FeishuChannelExtension` 把飞书机器人的入站消息桥接到 VeADK `Runner`。

<Note>
  本页是把飞书**作为入站消息渠道**：飞书用户发来的消息会触发智能体运行。若需让智能体主动调用飞书能力，请参见[飞书工具](/productions/veadk/archives/1.0.7/zh/components/tools/lark)。
</Note>

它监听飞书机器人的消息事件，并把飞书的会话身份映射到 VeADK：以消息发送者作为 `Runner` 的用户标识，以消息所在的话题作为会话标识；话题不存在时，回退到当前群聊或单聊作为会话标识。1.0.5 支持提取文本消息、互动卡片与合并转发消息中的文本内容，并兼容同步 Channel SDK 生命周期。

<Warning>
  话题历史与引用消息默认开启。扩展会读取相关飞书消息并将内容发送给模型；应用配置了会话、长期记忆或日志时，这些内容还可能按对应策略保存。启用机器人前应确认消息读取范围、模型数据处理和保留策略符合业务要求；不需要上下文时关闭 `include_thread_history` 与 `include_parent_message`。
</Warning>

收到话题消息或回复消息时，扩展会自动获取话题历史或被引用的父消息，并将这些上下文拼接到用户消息前再转发给智能体，使智能体能够参考完整对话上下文。详见[话题历史与引用消息](#话题历史与引用消息)。

## 环境变量与前提

安装：

```bash lines theme={null}
pip install veadk-python[extensions]
```

如果只想要这一项能力，也可以单独安装：

```bash lines theme={null}
pip install lark-oapi
```

环境变量：

* `TOOL_FEISHU_CHANNEL_APP_ID`
* `TOOL_FEISHU_CHANNEL_APP_SECRET`
* `TOOL_FEISHU_CHANNEL_TRANSPORT`：默认 `ws`
* `TOOL_FEISHU_CHANNEL_STREAMING`：是否开启流式输出，默认 `false`
* `TOOL_FEISHU_CHANNEL_REACTIONS`：是否在收到消息时回复“收到”表情，默认 `false`

或在 `config.yaml` 中配置：

```yaml title="config.yaml" lines theme={null}
tool:
  feishu_channel:
    app_id: cli_xxx
    app_secret: xxx
    transport: ws
    streaming: true
    reactions: true
```

## 使用方法

```python title="examples/channel/feishu_bot.py" lines theme={null}
import asyncio

from veadk import Agent, Runner
from veadk.extensions import FeishuChannelExtension
from veadk.memory.short_term_memory import ShortTermMemory

agent = Agent(
    name="feishu_agent",
    model_name="doubao-seed-2-1-pro-260628",
    instruction="你是一个通过飞书机器人与用户沟通的助手。",
)

runner = Runner(
    agent=agent,
    app_name="veadk_feishu_demo",
    user_id="veadk_feishu_default_user",
    short_term_memory=ShortTermMemory(),
)

channel = FeishuChannelExtension(
    runner=runner,
    channel_kwargs={"transport": "ws"},
)


async def main():
    await channel.connect()


if __name__ == "__main__":
    asyncio.run(main())
```

## 参数

| 参数 | 类型 | 默认值 | 说明 |
| :- | :- | :- | :- |
| `runner` | `Runner` | 无 | 必填。处理飞书入站消息的 VeADK 执行器。 |
| `app_id` | `str \| None` | `TOOL_FEISHU_CHANNEL_APP_ID` | 飞书应用 ID；兼容回退到 `TOOL_LARK_ENDPOINT`。 |
| `app_secret` | `str \| None` | `TOOL_FEISHU_CHANNEL_APP_SECRET` | 飞书应用密钥；兼容回退到 `TOOL_LARK_API_KEY`。应通过环境变量或密钥服务提供。 |
| `channel` | `Any \| None` | `None` | 已创建的飞书 Channel 客户端；提供后不再自动创建客户端。 |
| `session_id_factory` | `Callable[[Any], str] \| None` | 话题、回复、会话或消息标识 | 自定义飞书消息到 VeADK `session_id` 的映射。 |
| `user_id_factory` | `Callable[[Any], str] \| None` | 发送者 `union_id`、`open_id` 或 `user_id` | 自定义飞书发送者到 VeADK `user_id` 的映射。 |
| `message_handler` | `Callable \| None` | `None` | 自定义消息处理函数；未设置时调用 `runner`。可返回字符串、异步字符串或 `None`。 |
| `response_formatter` | `Callable[[str], dict[str, str]] \| None` | `{"text": text}` | 把智能体文本转换为飞书发送载荷。 |
| `reply_in_thread` | `bool` | `True` | 是否回复原消息，使输出保持在当前消息线程中。 |
| `ignore_empty_messages` | `bool` | `True` | 是否忽略无法提取出文本的消息。 |
| `channel_kwargs` | `dict[str, Any] \| None` | `None` | 创建 Channel 客户端时传入的选项；`transport` 默认读取 `TOOL_FEISHU_CHANNEL_TRANSPORT`，未设置时为 `ws`。 |
| `streaming` | `bool` | `False` | 是否使用流式回复；`TOOL_FEISHU_CHANNEL_STREAMING=true` 也会启用。 |
| `reactions` | `bool` | `False` | 收到消息时是否添加“收到”表情；`TOOL_FEISHU_CHANNEL_REACTIONS=true` 也会启用。 |
| `include_parent_message` | `bool` | `True` | 收到回复消息时是否获取并附带被引用的父消息内容。 |
| `include_thread_history` | `bool` | `True` | 收到话题消息时是否获取并附带该话题的历史消息。 |
| `thread_history_limit` | `int` | `20` | 获取话题历史消息的最大条数；小于 `1` 的值按 `1` 处理。 |

## 话题历史与引用消息

当消息来自飞书话题（thread）或是一条回复消息时，扩展会在把消息转发给智能体之前，自动获取相关上下文并拼接到用户消息文本之前：

* **话题历史**：消息属于话题时，扩展获取该话题内按时间正序排列的历史消息（最多 `thread_history_limit` 条，不包含当前消息），将其作为话题上下文拼接到用户消息前。
* **引用消息**：消息是一条回复但未归属话题时，扩展获取被引用的父消息；若父消息无法获取，则回退获取该回复链的根消息，将其作为引用上下文拼接到用户消息前。

话题历史优先于引用消息：当消息同时存在话题标识时，仅附加话题历史，不再单独获取父消息。获取到的消息内容会被还原为可读文本，覆盖文本消息、富文本（post）、互动卡片与合并转发消息。

<Note>
  获取话题历史与引用消息需要通过飞书 OpenAPI 调用消息接口，因此必须配置 `app_id` 与 `app_secret`（构造参数，或 `TOOL_FEISHU_CHANNEL_APP_ID` / `TOOL_FEISHU_CHANNEL_APP_SECRET` 环境变量，回退到 `TOOL_LARK_ENDPOINT` / `TOOL_LARK_API_KEY`）。未配置凭证时该能力会被跳过，不会报错。
</Note>

### 使用示例

先保存并配置上方的 `feishu_bot.py`，再在同一目录创建以下文件：

```python title="examples/channel/feishu_bot_thread.py" lines theme={null}
from veadk.extensions import FeishuChannelExtension
from feishu_bot import runner

channel = FeishuChannelExtension(
    runner=runner,
    include_thread_history=True,
    thread_history_limit=30,
    include_parent_message=True,
)
```

关闭该能力，使扩展仅转发当前消息原文：

```python title="examples/channel/feishu_bot_plain.py" lines theme={null}
from veadk.extensions import FeishuChannelExtension
from feishu_bot import runner

channel = FeishuChannelExtension(
    runner=runner,
    include_parent_message=False,
    include_thread_history=False,
)
```

## 额外说明

* 默认使用飞书的 WebSocket 长连接模式，因此只要机器人已订阅消息事件，即可直接启动连接。
* 默认以回复原消息的形式发送，使 VeADK 的输出挂在当前飞书消息线程下。
* 可以通过 `session_id_factory` 与 `user_id_factory` 覆盖默认的身份映射逻辑。
* `connect()` 与 `disconnect()` 同时兼容异步客户端和提供 `start()` / `stop()` 的同步 Channel SDK。
