工具注册
所有 tool 在 createTools(resourceId, threadId, model?, taskId?)(src/ai/tools/index.ts)里注册,每个都用 withTracking 包一层 —— 自动绑定 OTel kira.tool.name、记 ai.tool.duration{tool,success,error_type}、发 PostHog tool_usage,并把不安全的 provider 报错替换成对用户友好的兜底文案(原始错误经 cause 仍进 Dash0)。
当前注册 25 个 tool:
统计口径以 createTools 实际返回的对象为准(src/ai/tools/index.ts)。没有任何 music / audio 工具 —— 音频特性已退役,无法生成、上传或读取。
图像生成 / 编辑(共 11)
| Tool | 用途 |
|---|
generateImageWithAI | 从文本 prompt 生成全新图(可带最多 9 张参考图) |
imageEdit | 编辑已有图(改内容 / 风格迁移 / 合图 / 改比例 / 保身份);对话里的 inpaint 也走它 |
removeBackground | 抠除背景 |
replaceBackgroundWithPrompt | 用 prompt 换背景 |
replaceBackgroundWithImage | 用参考图换背景 |
inpaintWithPrompt | 按 mask + prompt 局部重绘 |
inpaintWithImage | 按 mask + 参考图局部重绘 |
expand | 外扩画布(outpaint) |
eraser | 擦除指定区域(保持 FAL fal-ai/bria/eraser) |
upscale | 超分放大 |
redux | 图像变体 / 风格再创作 |
generateImageWithAI 的 NSFW provider 路由
文生图按”是否 NSFW + 是否带参考图”选 provider(src/ai/tools/generateImageWithAI.ts):
NSFW prompt 且无参考图 → BytePlus Seedream 4.5(NSFW t2i)
其余(SFW,或任意带参考图) → DashScope WAN 2.7 image
- NSFW 判定由 utility 模型(
gemini-3.1-flash-lite)做,fail-safe 到 NSFW(判定失败时全部走 Seedream)。
- 带参考图时跳过 NSFW 判定,直接走 WAN 2.7。
imageEdit 的多 provider 路由
imageEdit 一个工具按 “anime 与否 + 是否 ultra plan” fallback 到 3 个 provider(src/ai/tools/imageEdit.ts):
| 输入特征 | 路由 |
|---|
| anime + ultra | gpt-image-2 → 失败 fallback seedream-4.5 |
| anime + 非 ultra | seedream-4.5 |
| 写实 + ultra | gpt-image-2 → 失败 fallback wan2.7-image |
| 写实 + 非 ultra | wan2.7-image |
anime 判定同样由 utility 模型做(fail-soft 到非 anime)。credit:ultra 路径 gpt-image-2 扣 15,其余扣 5。
视频(共 5)
| Tool | 用途 |
|---|
generateVideo | 文生视频 / 图生视频(异步,worker 完成后通知) |
videoEdit | 视频局部编辑(WAN 2.7 VideoEdit) |
videoExtend | 视频续帧延长 |
motionControl | 运动控制(TencentCloud VOD + Kling) |
trimVideo | 裁剪 |
generateVideo 的 provider 路由
generateVideo 按 model tier 选 provider(src/ai/tools/generateVideo.ts),不是按 NSFW:
switch (model) {
case "nova":
case "ultra": provider = "dashscope-wan27"; break; // WAN 2.7(model: wan2.7-video)
case "lite":
default: provider = "seedance"; break; // Seedance(model: seedance-1.5-pro)
}
| tier | provider | 上报 model 名 |
|---|
lite(及默认) | seedance | seedance-1.5-pro |
nova / ultra | dashscope-wan27 | wan2.7-video |
视频工具不在 agent 进程里直接调 provider,而是返回一个 pending task,由 onFinish 通过 Trigger.dev 触发 worker 上的 task(见下)。status 可能是 pending / insufficient_credits / insufficient_plan,后两者要求 agent 停手并提示用户。
交互式工具(共 6)
这些 tool 不直接产出资产,而是向前端发出”需要用户操作”的信号(选 mask、选滤镜、确认裁剪等),由前端 UI 接管:
| Tool | 用途 |
|---|
initializeWithImage | 用一张图初始化画布 |
initializeWithVideo | 用一段视频初始化画布(也是上传视频的路由目标) |
getMaskImageFromUser | 请用户绘制 inpaint mask |
getExpandMaskImageFromUser | 请用户指定外扩区域 |
getFilterChoiceFromUser | 请用户选滤镜 / 调色预设 |
getAutoCropResultFromUser | 请用户确认自动裁剪结果 |
工具类(共 3)
| Tool | 用途 |
|---|
googleSearch | 实时检索(时事 / 事实核查 / 真实主体的视觉参考) |
readImage | 读取/理解一张图(用户上传图的路由目标) |
readVideo | 读取/理解一段视频 |
本类共 3 项(googleSearch / readImage / readVideo)。图像 11 + 视频 5 + 交互 6 + 工具 3 = 25 个注册 tool。
视频工具 → Trigger.dev
会产出视频的 tool(generateVideo / videoEdit / videoExtend / motionControl / trimVideo)不在 agent 进程里同步生成。run 的 onFinish 把 pending 视频从 responseMessage.parts 收集出来,落 thread_version,然后通过 kira-agent/src/lib/tasks.ts 的 publishVideoTask 触发 Trigger.dev Cloud 上的 task(由 kira-video-worker 执行):
// kira-agent/src/lib/tasks.ts —— 用 @trigger.dev/sdk/v3 触发任务
import { tasks } from "@trigger.dev/sdk/v3";
// SDK 从环境读 TRIGGER_SECRET_KEY(env-scoped:DEV key 路由到本地
// `trigger.dev dev` worker,PROD key 路由到已部署 worker)。
await tasks.trigger(
triggerTaskId, // toolName[:provider] -> task id(见下表)
{ ...task, priority: getPriority(userPlan) },
{
tags: [`videoTask:${task.taskId}`], // 供 purgeVideoTasks 按业务 taskId 找到本次 run 取消
priority: getPriority(userPlan), // Pro/Max plan = 100,free = 0,用于插队
},
);
task id 按 toolName[:provider] 路由(替代旧的 Inngest 事件名 video/{action}-{provider}.requested):
| toolName[:provider] | task id |
|---|
generateVideo:seedance | video-generation-seedance |
generateVideo:dashscope-wan27 | video-generation-dashscope-wan27 |
videoEdit:dashscope-wan27 | video-edit-dashscope-wan27 |
videoExtend:dashscope-wan27 | video-extend-dashscope-wan27 |
motionControl | video-motion-control-tencentcloud-vod-kling |
trimVideo | video-trim-kira |
upload(由 kira-cdn 触发,非 kira-agent) | video-upload-kira |
每次 trigger 都给 run 打 videoTask:<businessTaskId> 标签。kira-be 不是 publisher,它只负责取消在途的 run(kira-be/src/lib/tasks.ts 的 purgeVideoTasks):按 tag 列出 run(runs.list({ tag }))再对仍可取消的调 runs.cancel() —— 已经没有 video/task.cancelled 事件了。
鉴权只需一个 env:TRIGGER_SECRET_KEY(每个 publisher / canceller 服务各一份,environment-scoped)。它替代了旧的 INNGEST_EVENT_KEY + INNGEST_SIGNING_KEY(以及 INNGEST_BASE_URL / INNGEST_API_BASE_URL / INNGEST_SERVE_ORIGIN / INNGEST_DEV)。本地开发跑 Trigger.dev CLI dev 会话 bunx trigger.dev@latest dev(在 kira-video-worker 里);已没有 localhost:8288 本地事件服务器,也没有 /api/inngest webhook。
并发由 worker 侧的 per-provider 全局 queue 控制(kira-video-worker/src/trigger/_shared.ts 的 queue({ name, concurrencyLimit })):seedance 200、dashscope-wan27 200(gen + edit + extend 共享同一上游配额)、motion-control 50。publisher 不传 concurrencyKey,所以这些上限是真正的全局上限。Trigger.dev 用 CRIU 在 wait.for 处对 run 做 checkpoint,没有 replay(不像 Inngest 的 step replay):task 的 run() 就是一段普通 async 函数,无 step.run 包裹;配合 retry.maxAttempts=1,credit 恰好扣一次。
worker 侧处理详见 Kira Video Worker。