mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: 优化 Agent 实现,支持自动补全
This commit is contained in:
@@ -13,6 +13,7 @@ export default {
|
||||
'agentTagPlaceholder': '请输入标签',
|
||||
'archive': '归档',
|
||||
'autoGenerate': '自动补全',
|
||||
'autoGenerateTooltip': '基于提示词自动补全助手描述',
|
||||
'cancel': '取消',
|
||||
'close': '关闭',
|
||||
'confirmRemoveSessionItemAlert': '即将删除该助手,删除后该将无法找回,请确认你的操作',
|
||||
@@ -29,7 +30,7 @@ export default {
|
||||
'newAgent': '新建助手',
|
||||
'noDescription': '暂无描述',
|
||||
'ok': '确定',
|
||||
'profile': '身份卡',
|
||||
'profile': '助手身份',
|
||||
'reset': '重置',
|
||||
'searchAgentPlaceholder': '搜索助手和对话...',
|
||||
'sessionSetting': '会话设置',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ActionIcon, Avatar, Input } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import { ActionIcon, Avatar, Input, Tooltip } from '@lobehub/ui';
|
||||
import { Button, Collapse } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { LucideSparkles } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -18,8 +18,22 @@ const AgentMeta = () => {
|
||||
|
||||
const metaData = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
|
||||
const [autocompleteMeta, loading] = useSessionStore(
|
||||
(s) => [s.autocompleteMeta, s.autocompleteLoading],
|
||||
const [
|
||||
autocompleteMeta,
|
||||
autocompleteSessionAgentMeta,
|
||||
loading,
|
||||
updateAgentMeta,
|
||||
id,
|
||||
hasSystemRole,
|
||||
] = useSessionStore(
|
||||
(s) => [
|
||||
s.autocompleteMeta,
|
||||
s.autocompleteSessionAgentMeta,
|
||||
s.autocompleteLoading,
|
||||
s.updateAgentMeta,
|
||||
s.activeId,
|
||||
agentSelectors.hasSystemRole(s),
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
|
||||
@@ -34,50 +48,69 @@ const AgentMeta = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
paddingBlock={12}
|
||||
style={{
|
||||
borderBottom: `1px solid ${theme.colorBorder}`,
|
||||
}}
|
||||
>
|
||||
<Flexbox className={styles.profile}> {t('profile')}</Flexbox>
|
||||
<Button size={'large'}>{t('autoGenerate')}</Button>
|
||||
</Flexbox>
|
||||
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
|
||||
<Flexbox flex={1} gap={24}>
|
||||
{basic.map((item) => (
|
||||
<FormItem key={item.key} label={item.label}>
|
||||
<Input
|
||||
placeholder={item.placeholder}
|
||||
suffix={
|
||||
<ActionIcon
|
||||
icon={LucideSparkles}
|
||||
loading={loading[item.key as keyof typeof loading]}
|
||||
onClick={() => {
|
||||
autocompleteMeta(item.key as keyof typeof metaData);
|
||||
}}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.purple,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
}
|
||||
type={'block'}
|
||||
value={metaData[item.key as keyof typeof metaData]}
|
||||
/>
|
||||
</FormItem>
|
||||
))}
|
||||
</Flexbox>
|
||||
<FormItem label={t('agentAvatar')}>
|
||||
<Avatar avatar={metaData.avatar} size={200} />
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
</>
|
||||
<Collapse
|
||||
defaultActiveKey={hasSystemRole ? ['meta'] : []}
|
||||
items={[
|
||||
{
|
||||
children: (
|
||||
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
|
||||
<Flexbox flex={1} gap={24}>
|
||||
{basic.map((item) => (
|
||||
<FormItem key={item.key} label={item.label}>
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
updateAgentMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
suffix={
|
||||
<ActionIcon
|
||||
icon={LucideSparkles}
|
||||
loading={loading[item.key as keyof typeof loading]}
|
||||
onClick={() => {
|
||||
autocompleteMeta(item.key as keyof typeof metaData);
|
||||
}}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.purple,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
}
|
||||
type={'block'}
|
||||
value={metaData[item.key as keyof typeof metaData]}
|
||||
/>
|
||||
</FormItem>
|
||||
))}
|
||||
</Flexbox>
|
||||
<FormItem label={t('agentAvatar')}>
|
||||
<Avatar avatar={metaData.avatar} size={200} />
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
),
|
||||
className: styles.collapseHeader,
|
||||
extra: (
|
||||
<Tooltip title={t('autoGenerateTooltip')}>
|
||||
<Button
|
||||
disabled={!hasSystemRole}
|
||||
loading={Object.values(loading).some((i) => !!i)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
console.log(id);
|
||||
if (!id) return;
|
||||
|
||||
autocompleteSessionAgentMeta(id, true);
|
||||
}}
|
||||
size={'large'}
|
||||
>
|
||||
{t('autoGenerate')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
),
|
||||
key: 'meta',
|
||||
label: <Flexbox className={styles.profile}>{t('profile')}</Flexbox>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
collapseHeader: css`
|
||||
.ant-collapse-header {
|
||||
align-items: center !important;
|
||||
}
|
||||
`,
|
||||
footer: css`
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
|
||||
@@ -50,3 +50,16 @@ export const promptPickEmoji = (content: string): Partial<OpenAIStreamPayload> =
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const promptSummaryDescription = (content: string): Partial<OpenAIStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content: '你是一名擅长会话的助理,你需要将用户的提示词做一个3句话以内的总结。',
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `输入:${content}`,
|
||||
role: 'user',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { OpenAIChatMessage, OpenAIStreamPayload } from '@/types/openai';
|
||||
|
||||
export const promptSummaryTitle = (
|
||||
messages: OpenAIChatMessage[],
|
||||
): Partial<OpenAIStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content:
|
||||
'你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
||||
|
||||
请总结上述对话为10个字以内的标题,不需要包含标点符号`,
|
||||
role: 'user',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const promptSummaryDescription = (
|
||||
messages: OpenAIChatMessage[],
|
||||
): Partial<OpenAIStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content: '你是一名擅长会话的助理,你需要将用户的会话做一个3句话以内的总结。',
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
||||
|
||||
请总结上述对话内容,不超过 50 个字。`,
|
||||
role: 'user',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
import { StateCreator } from 'zustand/vanilla';
|
||||
|
||||
import { promptPickEmoji } from '@/prompts/agent';
|
||||
import { promptSummaryDescription, promptSummaryTitle } from '@/prompts/chat';
|
||||
import { SessionStore, chatSelectors, sessionSelectors } from '@/store/session';
|
||||
import { promptPickEmoji, promptSummaryAgentName, promptSummaryDescription } from '@/prompts/agent';
|
||||
import { SessionStore, sessionSelectors } from '@/store/session';
|
||||
import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
import { fetchPresetTaskResult } from '@/utils/fetch';
|
||||
@@ -36,7 +35,7 @@ export interface AgentAction {
|
||||
* 自动完成会话代理元数据
|
||||
* @param id - 代理的 ID
|
||||
*/
|
||||
autocompleteSessionAgentMeta: (id: string) => void;
|
||||
autocompleteSessionAgentMeta: (id: string, replace?: boolean) => void;
|
||||
|
||||
/**
|
||||
* 内部更新代理元数据
|
||||
@@ -55,6 +54,7 @@ export interface AgentAction {
|
||||
* @param config - 部分 LobeAgentConfig 的配置
|
||||
*/
|
||||
updateAgentConfig: (config: Partial<LobeAgentConfig>) => void;
|
||||
updateAgentMeta: (meta: Partial<MetaData>) => void;
|
||||
/**
|
||||
* 更新加载状态
|
||||
* @param key - SessionLoadingState 的键
|
||||
@@ -93,9 +93,9 @@ export const createAgentSlice: StateCreator<
|
||||
const session = sessionSelectors.getSessionById(id)(get());
|
||||
if (!session) return;
|
||||
|
||||
const chats = chatSelectors.currentChats(get());
|
||||
const systemRole = session.config.systemRole;
|
||||
|
||||
if (chats.length <= 0) return;
|
||||
if (!systemRole) return;
|
||||
|
||||
const preValue = session.meta.description;
|
||||
|
||||
@@ -115,7 +115,7 @@ export const createAgentSlice: StateCreator<
|
||||
updateLoadingState('description', loading);
|
||||
},
|
||||
onMessageHandle: internalUpdateAgentMeta(id)('description'),
|
||||
params: promptSummaryDescription(chats),
|
||||
params: promptSummaryDescription(systemRole),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -124,9 +124,9 @@ export const createAgentSlice: StateCreator<
|
||||
const session = sessionSelectors.getSessionById(id)(get());
|
||||
if (!session) return;
|
||||
|
||||
const chats = chatSelectors.currentChats(get());
|
||||
const systemRole = session.config.systemRole;
|
||||
|
||||
if (chats.length <= 0) return;
|
||||
if (!systemRole) return;
|
||||
|
||||
const previousTitle = session.meta.title;
|
||||
|
||||
@@ -141,7 +141,7 @@ export const createAgentSlice: StateCreator<
|
||||
updateLoadingState('title', loading);
|
||||
},
|
||||
onMessageHandle: internalUpdateAgentMeta(id)('title'),
|
||||
params: promptSummaryTitle(chats),
|
||||
params: promptSummaryAgentName(systemRole),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -166,19 +166,20 @@ export const createAgentSlice: StateCreator<
|
||||
}
|
||||
},
|
||||
|
||||
autocompleteSessionAgentMeta: (id) => {
|
||||
autocompleteSessionAgentMeta: (id, replace) => {
|
||||
const session = sessionSelectors.getSessionById(id)(get());
|
||||
|
||||
if (!session) return;
|
||||
if (!session.meta.title) {
|
||||
|
||||
if (!session.meta.title || replace) {
|
||||
get().autocompleteAgentTitle(id);
|
||||
}
|
||||
|
||||
if (!session.meta.description) {
|
||||
if (!session.meta.description || replace) {
|
||||
get().autocompleteAgentDescription(id);
|
||||
}
|
||||
|
||||
if (!session.meta.avatar) {
|
||||
if (!session.meta.avatar || replace) {
|
||||
get().autoPickEmoji(id);
|
||||
}
|
||||
},
|
||||
@@ -203,6 +204,22 @@ export const createAgentSlice: StateCreator<
|
||||
|
||||
get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
|
||||
},
|
||||
updateAgentMeta: (meta) => {
|
||||
const { activeId } = get();
|
||||
const session = sessionSelectors.currentSession(get());
|
||||
if (!activeId || !session) return;
|
||||
|
||||
for (const [key, value] of Object.entries(meta)) {
|
||||
if (value !== undefined) {
|
||||
get().dispatchSession({
|
||||
id: activeId,
|
||||
key: key as keyof MetaData,
|
||||
type: 'updateSessionMeta',
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
updateLoadingState: (key, value) => {
|
||||
set({ autocompleteLoading: { ...get().autocompleteLoading, [key]: value } });
|
||||
},
|
||||
|
||||
@@ -38,6 +38,11 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
|
||||
return config?.model || LanguageModel.GPT3_5;
|
||||
};
|
||||
|
||||
const hasSystemRole = (s: SessionStore) => {
|
||||
const config = currentAgentConfigSafe(s);
|
||||
|
||||
return !!config.systemRole;
|
||||
};
|
||||
export const agentSelectors = {
|
||||
currentAgentAvatar,
|
||||
currentAgentConfig,
|
||||
@@ -45,4 +50,5 @@ export const agentSelectors = {
|
||||
currentAgentMeta,
|
||||
currentAgentModel,
|
||||
currentAgentTitle,
|
||||
hasSystemRole,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user