diff --git a/docs/changelog/update.md b/docs/changelog/update.md index e2c213ed..fc34bf9a 100644 --- a/docs/changelog/update.md +++ b/docs/changelog/update.md @@ -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 读取工具 - [ ] 添加绘图工具(这里的绘图是指绘制固定格式的图和表等,暂时没想好如何支持自定义绘图) - [ ] 添加测试脚本,覆盖最常见的功能 diff --git a/server/routers/chat_router.py b/server/routers/chat_router.py index 0b8d16b0..eb17a680 100644 --- a/server/routers/chat_router.py +++ b/server/routers/chat_router.py @@ -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}") diff --git a/src/static/agents_meta.yaml b/src/static/agents_meta.yaml new file mode 100644 index 00000000..2d03a4be --- /dev/null +++ b/src/static/agents_meta.yaml @@ -0,0 +1,9 @@ +ChatbotAgent: + name: 食品安全知识助手 + icon: /agents-icon/chatbot.png + examples: + - 冰箱里不同食材应该怎么存放? + - 有机食品和普通食品有什么区别? + - 外卖食品安全需要注意什么? + - 如何判断食品是否已经变质? + - 食品添加剂对健康有害吗? \ No newline at end of file diff --git a/web/public/agents-icon/chatbot.png b/web/public/agents-icon/chatbot.png new file mode 100644 index 00000000..322ca76e Binary files /dev/null and b/web/public/agents-icon/chatbot.png differ diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index afa1b1f8..17c825fe 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -60,8 +60,12 @@
-

{{ currentAgent ? currentAgent.name : '请选择一个智能体开始对话' }}

-

{{ currentAgent ? currentAgent.description : '不同的智能体有不同的专长和能力' }}

+ 智能体图标 +
+

您好,我是{{ currentAgentName }}!有什么可以帮您?

+ +
+ + +
+
或试试这些问题:
+
+
+ {{ question.text }} +
+
+
@@ -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; } diff --git a/web/src/stores/agent.js b/web/src/stores/agent.js index 806ebfa6..6f882155 100644 --- a/web/src/stores/agent.js +++ b/web/src/stores/agent.js @@ -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');