#定时任务
让 Agent 按计划自动执行(每天早上汇总新闻),并在界面上查看和管理这些任务。本页用业务卡片、插槽和会话列表自己的位置实现它。
recipe-schedules.tsx
import { useDemoSessions } from '../sessions';
import '@volcengine/chatkit/styles.css';
import { Chatkit, type ChatkitDataPartRenderer, SessionList, useChatkitActions } from '@volcengine/chatkit';
import { createMockAdapter } from '@volcengine/chatkit/adapters/mock';
import { createContext, useContext, useState } from 'react';
const SCHEDULE = { id: 'schedule-1', name: '每天查一次 chatkit 的更新', time: '每天 09:00' };
// 让 Mock 在回答的最后带上一块任务数据;真实接入时由适配器的 transformOutput 从后端的输出里取
const adapter = createMockAdapter({
transformOutput: (step, context) => {
const parts = (() => {
return step.kind === 'text' && step.text.includes('## 结论')
? [{ id: 'schedule', type: 'data' as const, kind: 'acme.schedule', data: SCHEDULE }]
: undefined;
})();
return parts && context.messageId
? [
...context.defaultChanges,
...parts.map(part => ({ type: 'set-part' as const, messageId: context.messageId!, part })),
]
: undefined;
},
});
// 业务方自己的状态:任务是否暂停。卡片和面板读同一份
const Paused = createContext({ paused: false, toggle: () => {} });
function PauseButton() {
const { paused, toggle } = useContext(Paused);
return (
<button type="button" className="demo-chip" onClick={toggle}>
{paused ? '恢复' : '暂停'}
</button>
);
}
// 回答里的任务卡片
function ScheduleCard({ schedule }: { schedule: typeof SCHEDULE }) {
const { paused } = useContext(Paused);
const { openPanel } = useChatkitActions();
return (
<div className="demo-card">
<header>
{schedule.name}
<span className="demo-badge">{paused ? '已暂停' : '运行中'}</span>
</header>
{schedule.time}
<footer>
<PauseButton />
<button type="button" className="demo-chip" onClick={() => openPanel('schedules')}>
全部任务
</button>
</footer>
</div>
);
}
const renderSchedule: ChatkitDataPartRenderer = ({ data }) => <ScheduleCard schedule={data as typeof SCHEDULE} />;
function SchedulesEntry() {
const { openPanel } = useChatkitActions();
return (
<div className="demo-line">
<button type="button" className="demo-chip" onClick={() => openPanel('schedules')}>
定时任务
</button>
</div>
);
}
// 定时任务:回答里的任务卡片用 renderers.parts,入口在 header,管理界面在 panel,
// 由定时任务产生的会话用 SessionList 的 itemBadge 标出来(真实接入时读 session.metadata)。
export default function RecipeSchedules() {
const { chat: sessions, listProps } = useDemoSessions({ adapter });
const [paused, setPaused] = useState(false);
return (
<Paused.Provider value={{ paused, toggle: () => setPaused(value => !value) }}>
<div className="demo-row">
<SessionList
{...listProps}
style={{ width: 190 }}
itemBadge={({ session }) =>
session.id === 'mock-session-weekly' ? <span className="demo-badge">定时</span> : null
}
/>
<div className="demo-main">
<Chatkit
sessions={sessions}
settings={{ placeholder: '比如:每天早上 9 点查一次 chatkit 的更新' }}
renderers={{ parts: { 'acme.schedule': renderSchedule } }}
slots={{
header: <SchedulesEntry />,
panel: ({ id, close }) =>
id === 'schedules' ? (
<div className="demo-panel">
<header>
定时任务
<button type="button" className="demo-chip" onClick={close}>
关闭
</button>
</header>
<div className="demo-card">
<header>
{SCHEDULE.name}
<span className="demo-badge">{paused ? '已暂停' : '运行中'}</span>
</header>
{SCHEDULE.time}
<footer>
<PauseButton />
</footer>
</div>
</div>
) : null,
}}
/>
</div>
</div>
</Paused.Provider>
);
}新建会话发一句话,回答的最后是任务卡片;点顶栏的「定时任务」在右侧面板管理新窗口打开
在线示例使用固定任务和示例数据。「暂停/恢复」只修改页面状态,会话标记来自固定示例 id;没有真正的调度、删除接口或后台执行。下方代码展示接入位置,pauseSchedule、removeSchedule、ScheduleManager 等需要业务应用实现。
#这个功能是什么
- 用户用自然语言让 Agent 创建定时任务,回答里出现一张任务卡片:名称、执行时间、状态,可以暂停或删除。
- 顶栏有「定时任务」入口,点开在右侧面板看全部任务。
- 由定时任务产生的会话在列表里有标记。
#用到的入口
| 入口 | 用来做什么 |
|---|---|
renderers.parts | 后端返回的任务数据显示成任务卡片 |
slots.header | 放「定时任务」入口 |
slots.panel 与 openPanel | 在右侧面板管理任务 |
SessionList 的 itemBadge | 标记由定时任务产生的会话 |
#代码
import {
Chatkit,
SessionList,
useChatkitActions,
useChatSessions,
type ChatkitDataPartRenderer,
} from '@volcengine/chatkit';
const renderSchedule: ChatkitDataPartRenderer = ({ data }) => {
const schedule = data as Schedule;
return (
<MyScheduleCard
name={schedule.name}
cron={schedule.cron}
onPause={() => pauseSchedule(schedule.id)}
onRemove={() => removeSchedule(schedule.id)}
/>
);
};
function SchedulesEntry() {
const { openPanel } = useChatkitActions();
return <button onClick={() => openPanel('schedules')}>定时任务</button>;
}
export function App() {
const sessions = useChatSessions({ adapter, activeSessionId });
return (
<div style={{ display: 'flex', height: '100vh' }}>
<SessionList
sessions={sessions}
itemBadge={({ session }) => (session.metadata?.scheduled ? <ClockIcon /> : null)}
/>
<div style={{ flex: 1, minWidth: 0 }}>
<Chatkit
sessions={sessions}
renderers={{ parts: { 'acme.schedule': renderSchedule } }}
slots={{
header: <SchedulesEntry />,
panel: ({ id, close }) => (id === 'schedules' ? <ScheduleManager onClose={close} /> : null),
}}
/>
</div>
</div>
);
}- 任务数据怎么从后端的输出变成
acme.schedule这块数据,见业务卡片的transformOutput。 - 会话上的
metadata由后端的会话接口返回,chatkit 原样交给itemBadge,见会话目录由应用管理。
#变化
- 后端以工具调用的形式返回:用
renderers.tools给创建任务的工具登记render。工具调用属于执行过程,这一轮完成后随执行过程收起;任务卡片要一直显示在回答里时,用本页的业务卡片。 - 从面板里发起对话:
useChatkitActions().send('把每天的新闻汇总改到早上 8 点')。 - 任务执行完通知用户:由业务后端推送;有列表能力时可调用
sessions.actions.refresh()更新列表。它不会刷新已打开会话的消息;消息更新还需适配器的会话订阅或历史恢复机制。
卡片操作成功后,需要同步业务状态或更新对应的业务数据片段,否则任务已暂停而旧卡片仍可能显示运行中。onPause / onRemove 只是组件回调名称,chatkit 不会根据名称自动执行任务操作。
#为什么不内置
任务存在哪里、由谁调度、执行结果怎么回到会话里,都需要业务方和自己的后端约定。chatkit 只负责把后端返回的内容显示出来。