feat(agent): 添加智能体元数据支持和示例问题展示
- 新增智能体元数据配置文件及图标资源 - 在聊天界面中显示智能体图标和示例问题 - 更新 changelog 中已完成的任务项
This commit is contained in:
parent
2acbd4b93a
commit
04f49f48d5
@ -1,7 +1,7 @@
|
||||
目前已有的开发计划包括:
|
||||
|
||||
💭 **Features Todo**
|
||||
- [ ] LangGraph 升级到 0.6+ 版本,并适配新特性,如 context 等,添加 MCP 工具的支持。
|
||||
- [x] LangGraph 升级到 0.6+ 版本,并适配新特性,如 context 等,添加 MCP 工具的支持。
|
||||
- [x] 支持动态工具配置的同时,将 Configuration替换为 Context 后能够正常使用
|
||||
- [ ] 使用更好的知识图谱检索方法
|
||||
- [ ] 使用其他的聊天记录管理方法,解决两个问题,一个是上下文长度过长,一个是上下文的类型变得更加丰富,比如多模态等等。(现在是基于 LangGraph 的 Memory 实现的,v0.2.3 版本实现),暂定使用 [mem0](github.com/mem0ai/mem0) 来实现。但是目前了解下来,还不是我想要的那种方案。可能会基于这个实现一个 ThreadConvManager 这个类。
|
||||
@ -24,7 +24,7 @@
|
||||
下面的功能**可能**会放在后续版本实现,暂时未定
|
||||
|
||||
- [ ] 支持数据库查询功能
|
||||
- [ ] 消息内容支持图片显示
|
||||
- [x] 消息内容支持图片显示
|
||||
- [ ] 添加 SQL 读取工具
|
||||
- [ ] 添加绘图工具(这里的绘图是指绘制固定格式的图和表等,暂时没想好如何支持自定义绘图)
|
||||
- [ ] 添加测试脚本,覆盖最常见的功能
|
||||
|
||||
@ -2,6 +2,8 @@ import asyncio
|
||||
import json
|
||||
import traceback
|
||||
import uuid
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
@ -97,7 +99,11 @@ async def get_agent(current_user: User = Depends(get_required_user)):
|
||||
"""获取所有可用智能体(需要登录)"""
|
||||
agents = await agent_manager.get_agents_info()
|
||||
# logger.debug(f"agents: {agents}")
|
||||
return {"agents": agents}
|
||||
metadata = {}
|
||||
if Path("src/static/agents_meta.yaml").exists():
|
||||
with open("src/static/agents_meta.yaml") as f:
|
||||
metadata = yaml.safe_load(f)
|
||||
return {"agents": agents, "metadata": metadata}
|
||||
|
||||
|
||||
@chat.post("/agent/{agent_id}")
|
||||
|
||||
9
src/static/agents_meta.yaml
Normal file
9
src/static/agents_meta.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
ChatbotAgent:
|
||||
name: 食品安全知识助手
|
||||
icon: /agents-icon/chatbot.png
|
||||
examples:
|
||||
- 冰箱里不同食材应该怎么存放?
|
||||
- 有机食品和普通食品有什么区别?
|
||||
- 外卖食品安全需要注意什么?
|
||||
- 如何判断食品是否已经变质?
|
||||
- 食品添加剂对健康有害吗?
|
||||
BIN
web/public/agents-icon/chatbot.png
Normal file
BIN
web/public/agents-icon/chatbot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 626 KiB |
@ -60,8 +60,12 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="!conversations.length" class="chat-examples">
|
||||
<h1>{{ currentAgent ? currentAgent.name : '请选择一个智能体开始对话' }}</h1>
|
||||
<p>{{ currentAgent ? currentAgent.description : '不同的智能体有不同的专长和能力' }}</p>
|
||||
<img v-if="currentAgentMetadata.icon" class="agent-icons" :src="currentAgentMetadata.icon" alt="智能体图标" />
|
||||
<div v-else style="margin-bottom: 150px"></div>
|
||||
<h1>您好,我是{{ currentAgentName }}!有什么可以帮您?</h1>
|
||||
<!-- <h1>{{ currentAgent ? currentAgent.name : '请选择一个智能体开始对话' }}</h1>
|
||||
<p>{{ currentAgent ? currentAgent.description : '不同的智能体有不同的专长和能力' }}</p> -->
|
||||
|
||||
<div class="inputer-init">
|
||||
<MessageInputComponent
|
||||
v-model="userInput"
|
||||
@ -72,6 +76,21 @@
|
||||
@send="handleSendMessage"
|
||||
@keydown="handleKeyDown"
|
||||
/>
|
||||
|
||||
<!-- 示例问题 -->
|
||||
<div class="example-questions" v-if="exampleQuestions.length > 0">
|
||||
<div class="example-title">或试试这些问题:</div>
|
||||
<div class="example-chips">
|
||||
<div
|
||||
v-for="question in exampleQuestions"
|
||||
:key="question.id"
|
||||
class="example-chip"
|
||||
@click="handleExampleClick(question.text)"
|
||||
>
|
||||
{{ question.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-box" ref="messagesContainer">
|
||||
@ -164,6 +183,15 @@ const {
|
||||
// ==================== LOCAL CHAT & UI STATE ====================
|
||||
const userInput = ref('');
|
||||
|
||||
// 从智能体元数据获取示例问题
|
||||
const exampleQuestions = computed(() => {
|
||||
const examples = currentAgentMetadata.value?.examples || [];
|
||||
return examples.map((text, index) => ({
|
||||
id: index + 1,
|
||||
text: text
|
||||
}));
|
||||
});
|
||||
|
||||
const chatState = reactive({
|
||||
currentThreadId: null,
|
||||
isLoadingThreads: false,
|
||||
@ -195,6 +223,9 @@ const currentAgentId = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const currentAgentMetadata = computed(() => agentStore.metadata[currentAgentId.value] || {});
|
||||
const currentAgentName = computed(() => currentAgentMetadata?.value.name || currentAgent.name || '智能体');
|
||||
|
||||
const currentAgent = computed(() => agents.value[currentAgentId.value] || null);
|
||||
const chatsList = computed(() => threads.value || []);
|
||||
const currentChatId = computed(() => chatState.currentThreadId);
|
||||
@ -674,6 +705,14 @@ const handleKeyDown = (e) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理示例问题点击
|
||||
const handleExampleClick = (questionText) => {
|
||||
userInput.value = questionText;
|
||||
nextTick(() => {
|
||||
handleSendMessage();
|
||||
});
|
||||
};
|
||||
|
||||
const shareChat = async () => {
|
||||
if (!AgentValidator.validateShareOperation(currentChatId.value, currentAgent.value, handleValidationError)) return;
|
||||
try {
|
||||
@ -926,7 +965,7 @@ watch(conversations, () => {
|
||||
padding: 0 50px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
top: 15%;
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
animation: slideInUp 0.5s ease-out;
|
||||
@ -942,8 +981,58 @@ watch(conversations, () => {
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.agent-icons {
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.example-questions {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
|
||||
.example-title {
|
||||
font-size: 0.85rem;
|
||||
color: var(--gray-600);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.example-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.example-chip {
|
||||
padding: 6px 12px;
|
||||
background: var(--gray-100);
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
color: var(--gray-700);
|
||||
transition: all 0.15s ease;
|
||||
white-space: nowrap;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-50);
|
||||
border-color: var(--main-200);
|
||||
color: var(--main-700);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inputer-init {
|
||||
margin: 40px auto;
|
||||
margin: 20px auto;
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ export const useAgentStore = defineStore('agent', {
|
||||
state: () => ({
|
||||
// 智能体相关状态
|
||||
agents: {}, // 以ID为键的智能体对象
|
||||
metadata: {}, // 智能体元数据
|
||||
selectedAgentId: null, // 当前选中的智能体ID
|
||||
defaultAgentId: null, // 默认智能体ID
|
||||
|
||||
@ -102,6 +103,7 @@ export const useAgentStore = defineStore('agent', {
|
||||
acc[agent.id] = agent;
|
||||
return acc;
|
||||
}, {});
|
||||
this.metadata = response.metadata;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch agents:', error);
|
||||
handleChatError(error, 'fetch');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user