From 0d20cf66db6a31e497804a67b61f3430a70fa265 Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Fri, 14 Nov 2025 12:50:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(agent):=20=E6=B7=BB=E5=8A=A0=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E4=BD=93=E5=B7=A5=E4=BD=9C=E7=8A=B6=E6=80=81=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现智能体工作状态的实时显示,包括任务列表和文件管理功能 - 在 DeepAgent 中添加 todo 和 files 能力支持 - 新增 AgentPopover 组件展示工作状态 - 修改聊天界面添加状态显示按钮 - 更新服务端状态提取逻辑 - 更新文档和路线图 --- docs/latest/changelog/roadmap.md | 3 +- server/routers/chat_router.py | 44 +- src/agents/deep_agent/graph.py | 2 + web/src/components/AgentChatComponent.vue | 89 +++- web/src/components/AgentMessageComponent.vue | 5 - web/src/components/AgentPopover.vue | 499 +++++++++++++++++++ 6 files changed, 628 insertions(+), 14 deletions(-) create mode 100644 web/src/components/AgentPopover.vue diff --git a/docs/latest/changelog/roadmap.md b/docs/latest/changelog/roadmap.md index 8c2dc8e6..06acc03c 100644 --- a/docs/latest/changelog/roadmap.md +++ b/docs/latest/changelog/roadmap.md @@ -6,7 +6,6 @@ ### 看板 -- 新建 DeepAgents 智能体(暂时没有场景) - 统一图谱数据结构,优化可视化方式 [#298](https://github.com/xerrors/Yuxi-Know/issues/298) [#273](https://github.com/xerrors/Yuxi-Know/issues/273) - 集成智能体评估,首先使用命令行来实现,然后考虑放在 UI 里面展示 - 开发与生产环境隔离,构建生产镜像 @@ -17,12 +16,14 @@ ### Bugs - 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279) - DeepSeek 官方接口适配会出现问题 +- 当前版本如果调用结果为空的时候,工具调用状态会一直处于调用状态,尽管调用是成功的 ### 新增 - 优化知识库详情页面,更加简洁清晰 - 新增对于上传文件的智能体中间件 - 增强文件下载功能 - 新增多模态模型支持(当前仅支持图片,详见文档) +- 新建 DeepAgents 智能体(Demo) ### 修复 - 修复重排序模型实际未生效的问题 diff --git a/server/routers/chat_router.py b/server/routers/chat_router.py index 0c3f99e4..7759f51b 100644 --- a/server/routers/chat_router.py +++ b/server/routers/chat_router.py @@ -102,7 +102,6 @@ async def set_default_agent(request_data: dict = Body(...), current_user=Depends async def _get_langgraph_messages(agent_instance, config_dict): - """获取LangGraph中的消息""" graph = await agent_instance.get_graph() state = await graph.aget_state(config_dict) @@ -113,6 +112,24 @@ async def _get_langgraph_messages(agent_instance, config_dict): return state.values.get("messages", []) +def _extract_agent_state(values: dict) -> dict: + if not isinstance(values, dict): + return {} + + def _norm_list(v): + if v is None: + return [] + if isinstance(v, (list, tuple)): + return list(v) + return [v] + + result = {} + result["todos"] = _norm_list(values.get("todos"))[:20] + result["files"] = _norm_list(values.get("files"))[:50] + + return result + + def _get_existing_message_ids(conv_mgr, thread_id): """获取已保存的消息ID集合""" existing_messages = conv_mgr.get_messages_by_thread_id(thread_id) @@ -521,6 +538,7 @@ async def chat_agent( try: full_msg = None + langgraph_config = {"configurable": input_context} async for msg, metadata in agent.stream_messages(messages, input_context=input_context): if isinstance(msg, AIMessageChunk): full_msg = msg if not full_msg else full_msg + msg @@ -534,7 +552,18 @@ async def chat_agent( yield make_chunk(content=msg.content, msg=msg.model_dump(), metadata=metadata, status="loading") else: - yield make_chunk(msg=msg.model_dump(), metadata=metadata, status="loading") + msg_dict = msg.model_dump() + yield make_chunk(msg=msg_dict, metadata=metadata, status="loading") + + try: + if msg_dict.get("type") == "tool": + graph = await agent.get_graph() + state = await graph.aget_state(langgraph_config) + agent_state = _extract_agent_state(getattr(state, "values", {})) if state else {} + if agent_state: + yield make_chunk(status="agent_state", agent_state=agent_state, meta=meta) + except Exception: + pass if ( conf.enable_content_guard @@ -548,13 +577,22 @@ async def chat_agent( return # After streaming finished, check for interrupts and save messages - langgraph_config = {"configurable": input_context} # Check for human approval interrupts async for chunk in check_and_handle_interrupts(agent, langgraph_config, make_chunk, meta, thread_id): yield chunk meta["time_cost"] = asyncio.get_event_loop().time() - start_time + try: + graph = await agent.get_graph() + state = await graph.aget_state(langgraph_config) + agent_state = _extract_agent_state(getattr(state, "values", {})) if state else {} + except Exception: + agent_state = {} + + if agent_state: + yield make_chunk(status="agent_state", agent_state=agent_state, meta=meta) + yield make_chunk(status="finished", meta=meta) # Save all messages from LangGraph state diff --git a/src/agents/deep_agent/graph.py b/src/agents/deep_agent/graph.py index c6a23467..3afb6339 100644 --- a/src/agents/deep_agent/graph.py +++ b/src/agents/deep_agent/graph.py @@ -179,6 +179,8 @@ class DeepAgent(BaseAgent): context_schema = DeepContext capabilities = [ "file_upload", + "todo", + "files", ] def __init__(self, **kwargs): diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 8b21c78b..15c09a50 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -43,6 +43,20 @@
+ + +
+ + {{ totalAgentStateItems }} +
+
@@ -202,7 +216,8 @@
- + + + + \ No newline at end of file