dsh-lark-bot
Verified@tigersheep/dsh-lark-bot · v0.1.4 · MIT
DeepSeek Harness 飞书机器人插件:每个飞书聊天对应一个真实 DSH agent 会话(工具/skill/多轮),收到消息贴表情确认并自动回复,工具授权走飞书交互卡片;支持白名单/触发规则/权限档位与连接开关。
Install
dsh plugin add @tigersheep/dsh-lark-bot Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
lark-bot —— 飞书机器人 DeepSeek Harness 插件(会话驱动版)
把飞书(Feishu/Lark)接入 DeepSeek Harness:每个飞书聊天对应一个真实的 DSH 会话,收到消息交给该会话的 agent(完整 agent loop:工具 / skill / 持久化), 回复发回飞书。工具需要授权时,发飞书交互卡片让用户点「允许 / 拒绝」。
两种引擎(config.engine)
| engine | 能力 | 说明 |
|---|---|---|
session(默认) |
完整 agent:工具 / skill / 多轮上下文 / 会话持久化 | 每个飞书聊天 ↔ 一个 DSH 会话,出现在 app 会话列表里;与 app 工具权限一致 |
llm(回退) |
仅大模型纯文本回复 | 直连 ctx.llm,无工具 / skill;排障或轻量问答用 |
工作原理
- 连接方式:复用你本机已登录的
lark-cli(无需再配 appId/appSecret)。- 收消息:
lark-cli event consume im.message.receive_v1 --as bot(NDJSON 流) - 收卡片回调:
lark-cli event consume card.action.trigger --as bot(session 引擎) - 发回复:
lark-cli im +messages-reply --message-id <om_...> ... - 发授权卡片:
lark-cli im +messages-send --chat-id <oc_...> --msg-type interactive ... - 主动推送(定时/提醒):
lark-cli im +messages-send --chat-id <oc_...> --markdown ...(无来消息的轮次直接发给聊天)
- 收消息:
- 会话引擎:注入
ctx.sessionController,create一个会话并prompt提交每一轮; 监听session/event的turn/end读取 agent 的最终回复发回飞书。 - 授权卡片:注入
approval/requestwaterfall——仅拦截本插件驱动的会话,发卡片、 等card.action.trigger回调,点「允许」→allowed-once,点「拒绝」/超时 →rejected; 其余会话(如 GUI)原样next()交回默认审批 UI,互不影响。 - 插件形态:纯 ESM 单文件(
lark-bot.plugin.mjs),只用ctx+ Node 内置模块, 不 import 任何@deepseek-ai/*(避免打包版 DSH 的模块单例解析问题)。以可安装 bundle(dsh.bundle)装进 profile,侧边栏 Plugins 页可见、可开关。
飞书消息 ─▶ event consume ─▶ 插件(筛选/去重) ─▶ ensureSession(chat↔session)
│ sessionController.prompt
▼
DSH 真实 agent(工具/skill)
│ 工具需授权?
┌───────────────┴───────────────┐
│ 是 │ 否
▼ ▼
发飞书授权卡片 ──点允许/拒绝──▶ 继续执行 ─▶ turn/end
(card.action.trigger) │
飞书用户 ◀──── +messages-reply ◀──────── 读 assistant/message 文本 ◀────┘
前置条件
- 已登录 lark-cli(bot 身份 ready):
lark-cli auth status确认identities.bot.available = true。若未登录:lark-cli auth login --domain im。 - 飞书开发者后台:
- 开启事件
im.message.receive_v1(收消息)。 - 开启回调配置(应用 → 事件与回调 → 回调配置),否则
card.action.trigger卡片按钮点击收不到——授权卡片会一直等到超时按拒绝处理。 - 添加机器人能力,把机器人拉进要服务的群或允许私聊。
- 开启事件
- DSH 桌面 App 已能正常聊天(
sessionController/llm可用)。
安装 / 启用
本插件以可安装 bundle 形态交付(package.json 带 dsh.bundle),装进桌面 profile 后
会出现在侧边栏 Plugins 页,可直接开关。
方式 A:从 npm 安装到桌面 App 的 profile(推荐)
桌面 App 的 profile 在 $DSH_HOME/profiles/<name>/(默认 ~/.dsh/profiles/desktop/)。
桌面没把 dsh CLI 放进 PATH,这里用 App 自带的 pnpm/node:
cd ~/.dsh/profiles/desktop
NODE=~/.dsh/dsh-runtimes/dsh-primary-runtime/dependencies/node/bin/node
PNPM=~/.dsh/dsh-runtimes/dsh-primary-runtime/dependencies/pnpm/bin/pnpm.mjs
"$NODE" "$PNPM" add @tigersheep/dsh-lark-bot
然后把 bundle 加入组合:编辑 ~/.dsh/profiles/desktop/package.json,在
dsh.profile.bundles 数组末尾追加 "@tigersheep/dsh-lark-bot"。
若
dshCLI 在 PATH 中(源码/CLI 用法),一步到位:dsh plugin --profile <name> add @tigersheep/dsh-lark-bot会自动装依赖并追加 bundle。
重启后:
- 侧边栏 Plugins 页能看到
@tigersheep/dsh-lark-bot(可开/关)。 - 设置 → 内置插件 → 插件列表 里显示
lark-bot,状态 active。 - 聊天里说「开启飞书机器人」即可调用
lark_bot_start;或把autoStart设为true。
⚠️
larkCliPath必须填绝对路径(在 profile 的cordis.patch.yml里按 idlark-bot覆盖config)。桌面 App 不继承终端 PATH,直接写lark-cli会找不到;用which lark-cli查到完整路径填入。
卸载:在侧边栏 Plugins 页移除;或从 dsh.profile.bundles 去掉 "@tigersheep/dsh-lark-bot"。
方式 B:本地源码 / 调试
# 从本地目录或 git 安装
dsh plugin --profile <name> add /path/to/dsh-lark-bot
dsh --profile <name> web
或直接叠加(不装 bundle,仅调试;此时不出现在侧边栏,只在插件列表里):
把 cordis.patch.yml 的插件行 name 改成插件文件的绝对路径后
dsh web --patch /path/to/dsh-lark-bot/cordis.patch.yml。
开 / 关连接
插件注册了 3 个模型可调用工具,你在 DSH 聊天里用自然语言即可触发:
| 工具 | 作用 |
|---|---|
lark_bot_start |
开启连接,开始监听飞书消息并自动回复 |
lark_bot_stop |
关闭连接,停止监听 |
lark_bot_status |
查看运行状态、引擎、已处理/已回复数、活跃会话数、待决授权数、最近错误 |
autoStart: true时,桌面启动加载插件即自动连接。autoStart: false(默认)时,需手动lark_bot_start。session引擎开启时会同时起两个lark-cli消费者:消息(im.message.receive_v1) 与卡片回调(card.action.trigger)。插件卸载 / 停止时都发SIGTERM干净退出。
配置项
全部写在 patch 条目的 config: 下,未写的用内置默认值。
| 键 | 默认 | 说明 |
|---|---|---|
larkCliPath |
lark-cli |
lark-cli 可执行文件路径;桌面 App 下务必填绝对路径 |
eventKey |
im.message.receive_v1 |
订阅的消息事件 |
identity |
bot |
consume / reply / send 的身份:bot 或 user |
autoStart |
false |
加载即连 / 手动开 |
engine |
session |
session=真实会话(工具/skill);llm=直连大模型(纯文本) |
cwd |
'' |
会话工作目录;workspacePath 非空时忽略本项 |
workspacePath |
'' |
工作区目录:会话落在此目录并归入对应分组(侧边栏项目);留空=不分组(落"未分组") |
agentPreset |
'' |
指定 agent preset;留空=默认 preset |
turnTimeoutMs |
180000 |
单轮等待 agent 完成的超时(ms) |
permissionPreset |
'' |
仅飞书会话生效的权限档:''=不改;auto=自动审查(实验);danger-full-access=全放行不弹卡;workspace-write/read-only=标准档 |
permissionPresetFallback |
danger-full-access |
permissionPreset=auto 但 Auto review 实验插件未启用时的回退档 |
allowList |
[] |
允许的 ou_...(用户) 或 oc_...(群) 白名单;留空=全部放行 |
respondInP2p |
true |
私聊是否回应 |
requireMentionInGroup |
true |
群聊是否必须 @机器人 才回应 |
triggerPrefix |
'' |
非空时消息须以此前缀开头才回应(前缀会被剥离) |
allowedMessageTypes |
['text','post'] |
只处理这些消息类型 |
approvalCardEnabled |
true |
session 引擎下,工具需授权时发飞书卡片让用户确认 |
approvalTimeoutMs |
120000 |
卡片等待超时(ms);超时按拒绝处理 |
questionCardEnabled |
true |
agent 用 ask_user_question 提多选时发飞书表单卡片让用户选,不弹 app 弹框 |
questionTimeoutMs |
600000 |
提问卡片等待超时(ms),默认 10 分钟 |
cardEventKey |
card.action.trigger |
卡片回调事件 |
provider |
'' |
留空=用 app 默认;需固定时填已注册的 provider 名 |
model |
'' |
留空=session 沿用 app 默认模型;需固定时填该 provider 支持的 model id |
systemPrompt |
见默认 | 仅 llm 引擎用 |
historyLimit |
12 |
仅 llm 引擎:每会话保留的消息条数 |
replyFormat |
markdown |
回复格式:text 或 markdown |
maxReplyChars |
4000 |
回复截断上限 |
dedupeCacheSize |
500 |
message_id 去重缓存容量 |
reactOnReceive |
true |
收到消息先在该消息上贴表情确认(附着在原消息,非单独发表情消息) |
reactEmoji |
OnIt |
确认表情的 emoji_type(如 OnIt/THUMBSUP/OK/SMILE) |
commandsEnabled |
true |
识别以 / 开头的斜杠命令;见下「斜杠命令」 |
sessionListEnabled |
true |
允许 /sessions 列出其他会话 |
approveOtherSessions |
true |
允许拉取式批准其他会话的工具审批(/approvals + /approve//deny);高权限,建议配合 allowList |
otherApprovalMode |
race |
其他会话审批的处理方式:race(默认)=飞书与桌面都弹、先到先得(飞书先答时桌面面板可能残留,需手动点掉);feishu=只走飞书、桌面不弹面板(无残留) |
otherApprovalTimeoutMs |
300000 |
feishu 模式下等待审批的超时(ms),超时按拒绝 |
sessionsFile |
'' |
chatId→sessionId 映射文件;留空=插件目录 sessions.json(测试/多实例时改它隔离状态) |
runtimeSettingsFile |
'' |
/set、/mode 的持久化文件;留空=插件目录 runtime-settings.json。该文件里的值覆盖 patch 配置 |
docMemoryInWorkspace |
true |
插件加载/更新/启动时把通用记忆幂等写入 workspacePath/AGENTS.md:身份性格、行为说明、斜杠命令三个托管块(各带 <!-- dsh-lark-bot:<key>:begin/end --> 标记,可自动更新;已有同主题手写章节则跳过该块) |
memoryDocFile |
AGENTS.md |
写入的文件名(相对 workspacePath) |
memoryPersona |
见默认 | 身份与性格文本。默认给一份通用人设(不暴露底层模型、幽默/简洁/俏皮);可覆盖成自己的角色,置空则不注入 |
memoryBehavior |
见默认 | 机器人行为说明文本;留空=不注入该块 |
debugLog |
false |
设 true 把每步诊断(含消息内容)写入插件目录 lark-bot.log,排查用;默认关闭 |
自定义身份性格(memoryPersona)
插件默认注入一份通用人设;想换成自己的角色(如给机器人起名/加职责),在 profile 的 cordis.patch.yml(或本包 cordis.patch.yml)里覆盖 memoryPersona,重启后自动写入工作区 AGENTS.md 的托管块:
- id: lark-bot
config:
workspacePath: /path/to/your/workspace
memoryPersona: |
## 身份与职责
- 你是一名**企业数字员工**,自称 **"铁宝"**,协助同事处理各种事情。
- 对外以铁宝的身份说话;被问到你是谁时,用铁宝的身份回答,而不是暴露底层模型或系统细节。
## 性格与说话风格
- **幽默风趣**:语气轻松,适度玩梗、带点俏皮,让同事觉得亲切。
- **回答简洁**:直给结论和要点,不啰嗦、不堆砌;能一句说清就不说三句。
- **俏皮但不越界**:玩笑点到为止,正事准确可靠;涉及数据、结论、操作时以严谨为先,不为搞笑牺牲准确性。
斜杠命令
在飞书里发以 / 开头的消息,机器人不经过模型直接执行(仍受 allowList 约束):
| 命令 | 作用 |
|---|---|
/model |
列出各 provider 的可用模型与当前模型 |
/model <model> |
切换到该模型(自动找广告了它的 provider) |
/model <provider> <model> |
显式按 provider+model 切换 |
/status |
查看飞书聊天↔DSH 会话、引擎、模型、上下文窗口、agent 冷/热、待决授权/提问、连接状态 |
/sessions |
列出其他会话(id 前缀、活/冷、目录、时间;标记本会话) |
/approvals |
查看其他会话里待你批准的工具操作(拉取式,不主动推送) |
/approve <#编号> |
批准某条其他会话的待审批操作 |
/deny <#编号> |
拒绝某条其他会话的待审批操作 |
/settings |
查看可运行时修改的设置项与当前值 |
/set <键> <值> |
修改并持久化设置(仅白名单键) |
/mode [feishu|race] |
查看/切换其他会话审批模式 |
/compact |
压缩上下文(DSH 内置,委托命令系统) |
/goal |
目标管理(DSH 内置) |
/help |
命令帮助 |
除
/model、/status、/help、/sessions、/approvals、/approve、/deny由插件原生实现外,其余命令委托 DSH 的命令系统执行——要求会话 agent 已激活(发过普通消息即激活;冷会话会提示先发一条消息)。群聊里 @机器人 后接命令也可识别。
运行时设置(可在飞书改,持久化)
部分设置无需重启、直接在飞书改,且持久化到 runtimeSettingsFile(默认插件目录 runtime-settings.json),优先级高于 patch 配置:
| 键 | 取值 | 说明 |
|---|---|---|
otherApprovalMode |
feishu / race |
其他会话审批模式(也可用 /mode) |
approveOtherSessions |
true / false | 是否允许拉取式跨会话审批 |
sessionListEnabled |
true / false | 是否允许 /sessions |
reactOnReceive |
true / false | 收到消息是否贴表情 |
reactEmoji |
表情名 | 如 OnIt、THUMBSUP |
replyFormat |
markdown / text |
回复格式 |
commandsEnabled |
true / false | 是否识别斜杠命令 |
用法:/settings 看当前值,/set <键> <值> 修改(如 /set reactEmoji THUMBSUP、/mode race)。改动立即生效并写入文件,重启后仍保留。要恢复 patch 配置,删掉该文件对应项或删文件后重启。
跨会话审批(拉取式)
默认不主动推送其他会话的审批,而是登记为待决,由你主动查询后发消息批准:
- 其他会话(如 app 里的普通会话)有工具需要审批时,插件把它登记为待决;
- 你在飞书发
/approvals看到#编号 · 工具 · 会话; - 发
/approve #编号或/deny #编号结算;超过otherApprovalTimeoutMs未处理按拒绝。
otherApprovalMode 两档:
race(默认):飞书与桌面同时弹,先到先得。注意:飞书先答时,桌面那块面板不会自动关(GUI 面板只能靠点击或请求 abort 关闭,Host 无法撤销已转发的请求)——需要手动点掉,这是竞速模式的固有副作用。feishu:桌面不再弹审批面板,只用飞书批。飞书一批就干净收尾,无残留(代价:桌面无法再批这些会话)。
随时可用 /mode feishu / /mode race 在运行时切换(立即生效并持久化)。
⚠️ 这意味着能给机器人发消息的人可以远程批准其他会话的工具执行(可能是命令执行等高危操作)。请务必用
allowList限定为可信用户。要关闭:approveOtherSessions: false。
行为细节
- 收到即确认:消息通过筛选后,先用飞书 reactions 在该条消息上贴一个表情
(
reactEmoji,默认OnIt;附着在原消息,不是单独发一条表情消息),再处理。 表情失败不影响正常回复。 - 卡片状态反馈:授权卡片/提问卡片在用户点击后,用回调
token调POST /open-apis/interactive/v1/card/update把原卡片更新为「已允许/已拒绝/已提交」, 交互控件随之消失(防止重复点击)。(token 30 分钟内有效、最多 2 次更新。) - 会话映射:
session引擎下每个chat_id对应一个 DSH 会话,映射持久化在插件目录sessions.json,重启后沿用(会话冷启动由prompt自动 resume)。 - 回复时机:
prompt提交后立即返回;真正的回复在该会话turn/end事件时读取 最后一条assistant/message文本发回飞书。一轮里 agent 可能先调用工具再回答——只取最终文本。 - 主动推送(定时/提醒):有些轮次没有对应的飞书消息(如 DSH 定时任务到点注入的 prompt)。
这类
turn/end若没有待回复的来消息,插件改用messages-send把结果主动推送到该会话 绑定的聊天(按replyFormat选 text/markdown),不再静默丢弃。详见下节「定时任务 / 提醒」。 - 多轮/多消息:同一聊天连发多条按 FIFO 排队,逐轮回复,顺序不乱。
- 去重:按
message_id去重(缓存最近dedupeCacheSize条)。 - 防自回环:
sender_type=bot的消息一律跳过。 - 群聊 @:
.content已被 lark-cli 预渲染为纯文本;requireMentionInGroup看mentions是否非空。
定时任务 / 提醒的主动推送
DSH 的定时任务(schedule)到点会把一段 prompt 注入对应会话跑一轮——这类轮次是
"无人发起"的,没有一条可回复的飞书消息。插件会把这类结果主动推送到该会话绑定的
聊天(lark-cli im +messages-send --chat-id <oc_...>,格式随 replyFormat)。
- 怎么用:直接在飞书里对机器人说需求即可,例如
「每天早上 8:30 给我发一份科技股早报」「20 分钟后提醒我开会」。agent 会调用
schedule_create把任务绑定到当前聊天对应的会话,到点自动生成并推送到本聊天。 - 重启不丢:插件启动时会把
sessions.json里已持久化的会话都纳入"归属", 因此重启后到点的任务仍能被认领并推送。 - 会话串行:同一会话一次只跑一轮。若到点时该会话正忙,任务会排在当前轮之后执行, 推送会相应晚一点(不是失败)。
- 务必同会话创建:定时任务绑定的是"创建它时的会话"。若之后 bot 重建了聊天↔会话
映射(
sessions.json变化、换了新会话),旧任务会指向旧会话而收不到推送——此时在 当前会话里重新说一遍需求重建任务即可。 - 排错:开
debugLog后,成功打pushed unsolicited turn,失败打pushMessage failed; 若看到turn/end no chat bound for session,说明该会话没有绑定聊天(多半是旧/孤儿会话)。
权限档位(permissionPreset)
飞书会话的工具权限可通过 permissionPreset 选档,只影响飞书会话,不动 GUI:
| 档位 | 行为 | 卡片 | 适用 |
|---|---|---|---|
''(默认不改) |
沿用 app 默认(通常 workspace-write) |
工具越界时弹 | 稳妥,人工把关 |
auto(实验) |
模型逐次评估每个工具调用:判安全→直接执行,判有风险→回退飞书卡片让你确认 | 仅风险操作弹 | 少打扰又保底,推荐试用 |
danger-full-access |
所有工具直接执行 | 永不弹 | 全自动无人值守(风险最高) |
workspace-write / read-only |
标准沙箱 + ask |
越界时弹 | 固定某档 |
关于 auto(自动审查 / Auto review):
- 这是 DSH 的实验特性,由
dsh-experimental-auto-review提供。桌面 profile 已带这个 bundle,但默认可能是关闭的——需在侧边栏 Plugins 页把「Auto review」开启,auto才会出现在可选档里。 - 若插件配了
permissionPreset: auto但该实验插件未启用,自动回退到permissionPresetFallback(默认danger-full-access),并记在日志里。 - 它是实验性的:可能放行不安全操作、误拒有用操作、额外消耗 token;被判拒的调用仍会
走飞书卡片让你定夺。重启后每个会话会重新进入 auto(插件在
ensureSession时重设)。
⚠️ 安全须知
- 工具权限与 app 一致:
session引擎下机器人就是一个真实 DSH agent,能跑 bash、 读写文件、调用 skill。谁能给机器人发消息,谁就能在你机器上驱动这些能力。 - 授权卡片是第二道闸:工具触发
ask审批时会发飞书卡片,必须有人点「允许」才执行; 点「拒绝」或approvalTimeoutMs超时都按拒绝。注意:danger-full-access全放行档不弹卡片;auto档只有被判有风险的调用才弹。 - 拦截顺序:本插件用
prepend把审批拦截器插到最前,所以飞书会话的审批走飞书卡片, GUI 会话仍走 app 面板,互不干扰。 - 务必收紧触发范围:上线前用
allowList(限定用户/群)、triggerPrefix(限定触发词)、requireMentionInGroup(群里需 @)把"谁能触发"缩到最小——尤其用danger-full-access时。 - 该连接复用你 lark-cli 的 bot 身份,没有额外鉴权边界;只在可信范围内开启。
- 回复/卡片经飞书发出会被该会话成员看到,注意不要泄露敏感信息。
- 想要"只问答、绝不动工具":把
engine设为llm。
排错
| 现象 | 排查 |
|---|---|
start 报 启动失败 / ENOENT |
larkCliPath 路径不对,填 which lark-cli 的绝对路径 |
| 连上但不回复 | 看 status:群聊需 @(或关 requireMentionInGroup);检查 allowList/triggerPrefix/allowedMessageTypes;开 debugLog 看 turn/end 有没有触发 |
| 回复失败 / 模型报错 | 开 debugLog:若 prompt FAILED 多为 model-unavailable,把 provider/model 设成你部署里实际可用的,或留空沿用 app 默认 |
| 点卡片按钮没反应 | 飞书后台回调配置没开;或卡片已超时(approvalTimeoutMs)。确认 card.action.trigger 消费者已就绪(status.running 且日志有"卡片回调 就绪") |
| 工具一直被拒 | 授权卡片超时=拒绝;或权限模式为 never。检查会话/preset 的权限模式 |
| 机器人回复出现在 app 会话列表 | 正常:session 引擎每个飞书聊天就是一个真实会话 |
| 改了配置/装了插件不生效 | 桌面 App 必须重启;bundle 与补丁只在启动时组合 |
| 侧边栏 Plugins 页看不到 | 确认 dsh.profile.bundles 含 @tigersheep/dsh-lark-bot 且 pnpm install 已链接 |
| 插件列表里状态不是 active | 多为加载失败;常见是 inject 写法(本版本 cordis 只认数组,不认 {required,optional}) |
| 看日志 | 开 debugLog 看插件目录 lark-bot.log;桌面 App 日志:~/Library/Logs/DeepSeek Harness/ |
文件清单
package.json—— bundle 清单(dsh.bundle,可被 pnpm 安装、侧边栏可见)lark-bot.plugin.mjs—— 插件主体(纯 ESM,inject: ['sessionController','tools','llm'])cordis.patch.yml—— bundle 配置层(按包名dsh-lark-bot引用插件行)sessions.json—— 运行时生成:chat_id → sessionId映射(可删,删后重建新会话)README.md—— 本文档