From 1224802ac6dfdfab5c638851411eb8ddd26d4694 Mon Sep 17 00:00:00 2001 From: Timebomb2018 <18868801967@163.com> Date: Mon, 2 Feb 2026 19:01:11 +0800 Subject: [PATCH 1/5] feat(app and model): token consumption statistics of the cluster --- api/app/services/app_chat_service.py | 44 +++++--- api/app/services/draft_run_service.py | 5 + api/app/services/handoffs_service.py | 18 +++- api/app/services/multi_agent_orchestrator.py | 20 +++- api/app/services/multi_agent_service.py | 106 ++++++++++++++++++- 5 files changed, 175 insertions(+), 18 deletions(-) diff --git a/api/app/services/app_chat_service.py b/api/app/services/app_chat_service.py index 26abd0f9..bd9106e5 100644 --- a/api/app/services/app_chat_service.py +++ b/api/app/services/app_chat_service.py @@ -427,7 +427,11 @@ class AppChatService: meta_data={ "mode": result.get("mode"), "elapsed_time": result.get("elapsed_time"), - "sub_results": result.get("sub_results") + "usage": result.get("usage", { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0 + }) } ) @@ -469,6 +473,7 @@ class AppChatService: yield f"event: start\ndata: {json.dumps({'conversation_id': str(conversation_id)}, ensure_ascii=False)}\n\n" full_content = "" + total_tokens = 0 # 2. 创建编排器 orchestrator = MultiAgentOrchestrator(self.db, config) @@ -485,16 +490,26 @@ class AppChatService: storage_type=storage_type, user_rag_memory_id=user_rag_memory_id ): - yield event - # 尝试提取内容(用于保存) - if "data:" in event: - try: - data_line = event.split("data: ", 1)[1].strip() - data = json.loads(data_line) - if "content" in data: - full_content += data["content"] - except: - pass + if "sub_usage" in event: + if "data:" in event: + try: + data_line = event.split("data: ", 1)[1].strip() + data = json.loads(data_line) + if "total_tokens" in data: + total_tokens += data["total_tokens"] + except: + pass + else: + yield event + # 尝试提取内容(用于保存) + if "data:" in event: + try: + data_line = event.split("data: ", 1)[1].strip() + data = json.loads(data_line) + if "content" in data: + full_content += data["content"] + except: + pass elapsed_time = time.time() - start_time @@ -510,7 +525,12 @@ class AppChatService: role="assistant", content=full_content, meta_data={ - "elapsed_time": elapsed_time + "elapsed_time": elapsed_time, + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": total_tokens + } } ) diff --git a/api/app/services/draft_run_service.py b/api/app/services/draft_run_service.py index dc01e541..9a3e1d37 100644 --- a/api/app/services/draft_run_service.py +++ b/api/app/services/draft_run_service.py @@ -678,6 +678,11 @@ class DraftRunService: elapsed_time = time.time() - start_time + if sub_agent: + yield self._format_sse_event("sub_usage", { + "total_tokens": total_tokens + }) + # 10. 保存会话消息 if not sub_agent and agent_config.memory and agent_config.memory.get("enabled"): await self._save_conversation_message( diff --git a/api/app/services/handoffs_service.py b/api/app/services/handoffs_service.py index 114e9945..10e4d646 100644 --- a/api/app/services/handoffs_service.py +++ b/api/app/services/handoffs_service.py @@ -4,7 +4,7 @@ import uuid from typing import List, Dict, Any, Optional, AsyncGenerator, Annotated from typing_extensions import TypedDict -from langchain_core.messages import HumanMessage, AIMessage, BaseMessage +from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, AIMessageChunk from langgraph.graph import StateGraph, START, END from langgraph.types import Command from langgraph.checkpoint.memory import MemorySaver @@ -727,9 +727,12 @@ class HandoffsService: # 提取响应 response_content = "" + total_tokens = 0 for msg in result.get("messages", []): if isinstance(msg, AIMessage): response_content = msg.content + response_meta = msg.response_metadata if hasattr(msg, 'response_metadata') else None + total_tokens = response_meta.get("token_usage", {}).get("total_tokens", 0) if response_meta else 0 break return { @@ -737,7 +740,12 @@ class HandoffsService: "active_agent": result.get("active_agent"), "response": response_content, "message_count": len(result.get("messages", [])), - "handoff_count": result.get("handoff_count", 0) + "handoff_count": result.get("handoff_count", 0), + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": total_tokens + } } async def chat_stream( @@ -830,6 +838,12 @@ class HandoffsService: # 捕获 LLM 结束事件,输出收集到的工具调用 elif kind == "on_chat_model_end": + output_message = event.get("data", {}).get("output", {}) + if isinstance(output_message, AIMessageChunk): + response_meta = output_message.response_metadata if hasattr(output_message, 'response_metadata') else None + total_tokens = response_meta.get("token_usage", {}).get("total_tokens", + 0) if response_meta else 0 + yield f"event: sub_usage\ndata: {json.dumps({"total_tokens": total_tokens}, ensure_ascii=False)}\n\n" if collected_tool_calls: # 找到参数最完整的 transfer 工具调用 best_tc = None diff --git a/api/app/services/multi_agent_orchestrator.py b/api/app/services/multi_agent_orchestrator.py index d9062eaf..b28bafbf 100644 --- a/api/app/services/multi_agent_orchestrator.py +++ b/api/app/services/multi_agent_orchestrator.py @@ -280,14 +280,22 @@ class MultiAgentOrchestrator: # 4. 提取子 Agent 的 conversation_id(用于多轮对话) sub_conversation_id = None + total_tokens = 0 + if isinstance(results, dict): sub_conversation_id = results.get("conversation_id") or results.get("result", {}).get("conversation_id") + # 提取 token 信息 + usage = results.get("usage", {}) or results.get("result", {}).get("usage", {}) + total_tokens += usage.get("total_tokens", 0) elif isinstance(results, list) and results: for item in results: if "result" in item: sub_conversation_id = item["result"].get("conversation_id") if sub_conversation_id: break + # 累加每个子 Agent 的 token + usage = item.get("usage", {}) or item.get("result", {}).get("usage", {}) + total_tokens += usage.get("total_tokens", 0) logger.info( "多 Agent 任务完成", @@ -301,9 +309,15 @@ class MultiAgentOrchestrator: return { "message": final_result, "conversation_id": sub_conversation_id, + "mode": OrchestrationMode.SUPERVISOR, "elapsed_time": elapsed_time, "strategy": routing_decision.get("collaboration_strategy", "single"), - "sub_results": results + "sub_results": results, + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": total_tokens + } } except Exception as e: @@ -1552,10 +1566,12 @@ class MultiAgentOrchestrator: return { "message": result.get("response", ""), "conversation_id": result.get("conversation_id"), + "mode": OrchestrationMode.COLLABORATION, "elapsed_time": elapsed_time, "strategy": "collaboration", "active_agent": result.get("active_agent"), - "sub_results": result + "sub_results": result, + "usage": result.get("usage") } except Exception as e: diff --git a/api/app/services/multi_agent_service.py b/api/app/services/multi_agent_service.py index da984d16..c52814ed 100644 --- a/api/app/services/multi_agent_service.py +++ b/api/app/services/multi_agent_service.py @@ -1,5 +1,6 @@ """多 Agent 配置管理服务""" import uuid +import json from typing import Optional, List, Tuple, Any, Annotated from fastapi import Depends @@ -427,6 +428,23 @@ class MultiAgentService: memory=getattr(request, 'memory', True) # 记忆功能参数 ) + await self._save_conversation_message( + conversation_id=request.conversation_id, + user_message=request.message, + assistant_message=result.get("message", ""), + app_id=app_id, + user_id=request.user_id, + meta_data={ + "mode": result.get("mode"), + "elapsed_time": result.get("elapsed_time"), + "usage": result.get("usage", { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0 + }) + } + ) + return result async def run_stream( @@ -451,11 +469,14 @@ class MultiAgentService: raise ResourceNotFoundException("多 Agent 配置", str(app_id)) if not config.is_active: - raise BusinessException("多 Agent 配置已禁用", BizCode.RESOURCE_DISABLED) + raise BusinessException("多 Agent 配置已禁用", BizCode.NOT_FOUND) # 2. 创建编排器 orchestrator = MultiAgentOrchestrator(self.db, config) + full_content = "" + total_tokens = 0 + # 3. 流式执行任务 async for event in orchestrator.execute_stream( message=request.message, @@ -468,7 +489,88 @@ class MultiAgentService: storage_type=storage_type, user_rag_memory_id=user_rag_memory_id ): - yield event + if "sub_usage" in event: + if "data:" in event: + try: + data_line = event.split("data: ", 1)[1].strip() + data = json.loads(data_line) + if "total_tokens" in data: + total_tokens += data["total_tokens"] + except: + pass + else: + yield event + if "data:" in event: + try: + data_line = event.split("data: ", 1)[1].strip() + data = json.loads(data_line) + if "content" in data: + full_content += data["content"] + except: + pass + + await self._save_conversation_message( + conversation_id=request.conversation_id, + user_message=request.message, + assistant_message=full_content, + app_id=app_id, + user_id=request.user_id, + meta_data={ + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": total_tokens + } + } + ) + + async def _save_conversation_message( + self, + conversation_id: uuid.UUID, + user_message: str, + assistant_message: str, + meta_data: dict, + app_id: Optional[uuid.UUID] = None, + user_id: Optional[str] = None + ) -> None: + """保存会话消息 + + Args: + conversation_id: 会话ID + user_message: 用户消息 + assistant_message: AI 回复消息 + meta_data: 元数据(包括 token 消耗) + app_id: 应用ID + user_id: 用户ID + """ + try: + from app.services.conversation_service import ConversationService + + conversation_service = ConversationService(self.db) + + conversation_service.add_message( + conversation_id=conversation_id, + role="user", + content=user_message + ) + conversation_service.add_message( + conversation_id=conversation_id, + role="assistant", + content=assistant_message, + meta_data=meta_data + ) + + logger.debug( + "保存多 Agent 会话消息", + extra={ + "conversation_id": conversation_id, + "user_message_length": len(user_message), + "assistant_message_length": len(assistant_message) + } + ) + + except Exception as e: + logger.warning("保存会话消息失败", extra={"error": str(e)}) # def add_sub_agent( # self, From 61f802920592a29ee9873a8f8c8d89acdc160332 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 3 Feb 2026 10:27:43 +0800 Subject: [PATCH 2/5] fix(web): prompt history remove pageLoading --- web/src/components/PageScrollList/index.tsx | 5 ++++- web/src/styles/index.css | 3 +++ web/src/views/Prompt/History.tsx | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/web/src/components/PageScrollList/index.tsx b/web/src/components/PageScrollList/index.tsx index ef413d9f..bea97b04 100644 --- a/web/src/components/PageScrollList/index.tsx +++ b/web/src/components/PageScrollList/index.tsx @@ -26,6 +26,7 @@ interface PageScrollListProps> { query?: Q; column?: number; className?: string; + needLoading?: boolean; } const PageScrollList = forwardRef(>({ renderItem, @@ -33,6 +34,7 @@ const PageScrollList = forwardRef(>({ url, column = 4, className = '', + needLoading = true, }: PageScrollListProps, ref: React.Ref) => { useImperativeHandle(ref, () => ({ refresh, @@ -104,9 +106,10 @@ const PageScrollList = forwardRef(>({ dataLength={data.length} next={loadMoreData} hasMore={hasMore} - loader={} + loader={needLoading ? : undefined} // endMessage={It is all, nothing more 🤐} scrollableTarget="scrollableDiv" + className='rb:h-full!' > {data.length > 0 ? ( body { min-height: 100%; max-height: 100%; +} +#scrollableDiv .infinite-scroll-component__outerdiv { + height: 100%; } \ No newline at end of file diff --git a/web/src/views/Prompt/History.tsx b/web/src/views/Prompt/History.tsx index 712cbf82..fda662b4 100644 --- a/web/src/views/Prompt/History.tsx +++ b/web/src/views/Prompt/History.tsx @@ -50,6 +50,7 @@ const History: React.FC<{ query: HistoryQuery; edit: (item: HistoryItem) => void url={getPromptReleaseListUrl} query={query} column={3} + needLoading={false} renderItem={(item) => { const historyItem = item as unknown as HistoryItem; return ( From b471d56a86a8bbfffc3a9d353758a730ae6d71f3 Mon Sep 17 00:00:00 2001 From: Eternity <61316157+myhMARS@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:29:51 +0800 Subject: [PATCH 3/5] fix(prompt): remove hard-coded import of prompt file paths (#279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix/develop memory bug (#274) * 遗漏的历史映射 * 遗漏的历史映射 * fix_timeline_memories * fix(web): update retrieve_type key * Fix/develop memory bug (#276) * 遗漏的历史映射 * 遗漏的历史映射 * fix_timeline_memories * fix_timeline_memories * write_gragp/bug_fix * write_gragp/bug_fix * write_gragp/bug_fix * chore(celery): disable periodic task scheduling * fix(prompt): remove hard-coded import of prompt file paths --------- Co-authored-by: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Co-authored-by: zhaoying Co-authored-by: yingzhao Co-authored-by: Ke Sun --- api/app/celery_app.py | 67 ++++++++++--------- .../agent/langgraph_graph/write_graph.py | 14 +--- api/app/repositories/neo4j/cypher_queries.py | 9 ++- api/app/services/conversation_service.py | 7 +- api/app/services/memory_reflection_service.py | 17 +++-- api/app/services/prompt_optimizer_service.py | 6 +- .../Knowledge/KnowledgeListModal.tsx | 2 +- .../Knowledge/KnowledgeListModal.tsx | 2 +- .../views/Workflow/hooks/useWorkflowGraph.ts | 2 +- 9 files changed, 62 insertions(+), 64 deletions(-) diff --git a/api/app/celery_app.py b/api/app/celery_app.py index e880e7d0..3e7db8cb 100644 --- a/api/app/celery_app.py +++ b/api/app/celery_app.py @@ -3,9 +3,10 @@ import platform from datetime import timedelta from urllib.parse import quote -from app.core.config import settings from celery import Celery +from app.core.config import settings + # 创建 Celery 应用实例 # broker: 任务队列(使用 Redis DB 0) # backend: 结果存储(使用 Redis DB 10) @@ -79,40 +80,40 @@ celery_app.conf.update( celery_app.autodiscover_tasks(['app']) # Celery Beat schedule for periodic tasks -memory_increment_schedule = timedelta(hours=settings.MEMORY_INCREMENT_INTERVAL_HOURS) -memory_cache_regeneration_schedule = timedelta(hours=settings.MEMORY_CACHE_REGENERATION_HOURS) -workspace_reflection_schedule = timedelta(seconds=30) # 每30秒运行一次settings.REFLECTION_INTERVAL_TIME -forgetting_cycle_schedule = timedelta(hours=24) # 每24小时运行一次遗忘周期 +# memory_increment_schedule = timedelta(hours=settings.MEMORY_INCREMENT_INTERVAL_HOURS) +# memory_cache_regeneration_schedule = timedelta(hours=settings.MEMORY_CACHE_REGENERATION_HOURS) +# workspace_reflection_schedule = timedelta(seconds=30) # 每30秒运行一次settings.REFLECTION_INTERVAL_TIME +# forgetting_cycle_schedule = timedelta(hours=24) # 每24小时运行一次遗忘周期 # 构建定时任务配置 -beat_schedule_config = { - "run-workspace-reflection": { - "task": "app.tasks.workspace_reflection_task", - "schedule": workspace_reflection_schedule, - "args": (), - }, - "regenerate-memory-cache": { - "task": "app.tasks.regenerate_memory_cache", - "schedule": memory_cache_regeneration_schedule, - "args": (), - }, - "run-forgetting-cycle": { - "task": "app.tasks.run_forgetting_cycle_task", - "schedule": forgetting_cycle_schedule, - "kwargs": { - "config_id": None, # 使用默认配置,可以通过环境变量配置 - }, - }, -} +# beat_schedule_config = { +# "run-workspace-reflection": { +# "task": "app.tasks.workspace_reflection_task", +# "schedule": workspace_reflection_schedule, +# "args": (), +# }, +# "regenerate-memory-cache": { +# "task": "app.tasks.regenerate_memory_cache", +# "schedule": memory_cache_regeneration_schedule, +# "args": (), +# }, +# "run-forgetting-cycle": { +# "task": "app.tasks.run_forgetting_cycle_task", +# "schedule": forgetting_cycle_schedule, +# "kwargs": { +# "config_id": None, # 使用默认配置,可以通过环境变量配置 +# }, +# }, +# } # 如果配置了默认工作空间ID,则添加记忆总量统计任务 -if settings.DEFAULT_WORKSPACE_ID: - beat_schedule_config["write-total-memory"] = { - "task": "app.controllers.memory_storage_controller.search_all", - "schedule": memory_increment_schedule, - "kwargs": { - "workspace_id": settings.DEFAULT_WORKSPACE_ID, - }, - } +# if settings.DEFAULT_WORKSPACE_ID: +# beat_schedule_config["write-total-memory"] = { +# "task": "app.controllers.memory_storage_controller.search_all", +# "schedule": memory_increment_schedule, +# "kwargs": { +# "workspace_id": settings.DEFAULT_WORKSPACE_ID, +# }, +# } -celery_app.conf.beat_schedule = beat_schedule_config +# celery_app.conf.beat_schedule = beat_schedule_config diff --git a/api/app/core/memory/agent/langgraph_graph/write_graph.py b/api/app/core/memory/agent/langgraph_graph/write_graph.py index 5101fa29..d0e8a45d 100644 --- a/api/app/core/memory/agent/langgraph_graph/write_graph.py +++ b/api/app/core/memory/agent/langgraph_graph/write_graph.py @@ -39,7 +39,6 @@ async def make_write_graph(): graph = workflow.compile() yield graph - async def long_term_storage(long_term_type:str="chunk",langchain_messages:list=[],memory_config:str='',end_user_id:str='',scope:int=6): from app.core.memory.agent.langgraph_graph.routing.write_router import memory_long_term_storage, window_dialogue,aggregate_judgment from app.core.memory.agent.langgraph_graph.tools.write_tool import chat_data_format @@ -49,7 +48,7 @@ async def long_term_storage(long_term_type:str="chunk",langchain_messages:list=[ db_session = next(get_db()) config_service = MemoryConfigService(db_session) memory_config = config_service.load_memory_config( - config_id="08ed205c-0f05-49c3-8e0c-a580d28f5fd4", # 改为整数 + config_id=memory_config, # 改为整数 service_name="MemoryAgentService" ) if long_term_type=='chunk': @@ -63,7 +62,7 @@ async def long_term_storage(long_term_type:str="chunk",langchain_messages:list=[ """方案三:聚合判断""" await aggregate_judgment(end_user_id, langchain_messages, memory_config) -# + # async def main(): # """主函数 - 运行工作流""" # langchain_messages = [ @@ -80,14 +79,7 @@ async def long_term_storage(long_term_type:str="chunk",langchain_messages:list=[ # end_user_id = '837fee1b-04a2-48ee-94d7-211488908940' # 组ID # memory_config="08ed205c-0f05-49c3-8e0c-a580d28f5fd4" # # await long_term_storage(long_term_type="chunk",langchain_messages=langchain_messages,memory_config=memory_config,end_user_id=end_user_id,scope=2) -# from app.core.memory.agent.utils.redis_tool import write_store -# result=write_store.get_session_by_userid(end_user_id) -# data=await format_parsing(result,"dict") -# chunk_data=data[:6] -# -# long_time_data = write_store.find_user_recent_sessions(end_user_id, 240) -# long_=await messages_parse(long_time_data) -# print(long_) +# result=await long_term_storage(long_term_type="chunk",langchain_messages=langchain_messages,memory_config=memory_config,end_user_id=end_user_id,scope=2) # # # if __name__ == "__main__": diff --git a/api/app/repositories/neo4j/cypher_queries.py b/api/app/repositories/neo4j/cypher_queries.py index c93e75b3..cf1732fd 100644 --- a/api/app/repositories/neo4j/cypher_queries.py +++ b/api/app/repositories/neo4j/cypher_queries.py @@ -877,7 +877,8 @@ RETURN CASE WHEN ms:ExtractedEntity THEN { text: ms.name, - created_at: ms.created_at + created_at: ms.created_at, + type: "情景记忆" } END ) AS ExtractedEntity, @@ -887,7 +888,8 @@ RETURN CASE WHEN n:MemorySummary THEN { text: n.content, - created_at: n.created_at + created_at: n.created_at, + type: "长期沉淀" } END ) AS MemorySummary, @@ -895,7 +897,8 @@ RETURN collect( DISTINCT { text: e.statement, - created_at: e.created_at + created_at: e.created_at, + type: "情绪记忆" } ) AS statement; """ diff --git a/api/app/services/conversation_service.py b/api/app/services/conversation_service.py index 526e0fe2..553aefc4 100644 --- a/api/app/services/conversation_service.py +++ b/api/app/services/conversation_service.py @@ -1,4 +1,5 @@ """会话服务""" +import os import uuid from datetime import datetime, timedelta from typing import Annotated @@ -529,12 +530,12 @@ class ConversationService: takeaways=[], info_score=0, ) - - with open('app/services/prompt/conversation_summary_system.jinja2', 'r', encoding='utf-8') as f: + prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prompt') + with open(os.path.join(prompt_path, 'conversation_summary_system.jinja2'), 'r', encoding='utf-8') as f: system_prompt = f.read() rendered_system_message = Template(system_prompt).render() - with open('app/services/prompt/conversation_summary_user.jinja2', 'r', encoding='utf-8') as f: + with open(os.path.join(prompt_path, 'conversation_summary_user.jinja2'), 'r', encoding='utf-8') as f: user_prompt = f.read() rendered_user_message = Template(user_prompt).render( language=language, diff --git a/api/app/services/memory_reflection_service.py b/api/app/services/memory_reflection_service.py index 3287366e..e025c1b3 100644 --- a/api/app/services/memory_reflection_service.py +++ b/api/app/services/memory_reflection_service.py @@ -286,7 +286,7 @@ class MemoryReflectionService: # 检查是否需要执行反思 should_execute = False hours_diff = 0 - + if current_reflection_time is None: # 首次执行反思 should_execute = True @@ -298,11 +298,11 @@ class MemoryReflectionService: reflection_time = datetime.fromisoformat(current_reflection_time) else: reflection_time = current_reflection_time - + current_time = datetime.now() time_diff = current_time - reflection_time hours_diff = int(time_diff.total_seconds() / 3600) - + # 检查是否达到反思周期 if hours_diff >= iteration_period: should_execute = True @@ -312,7 +312,7 @@ class MemoryReflectionService: except (ValueError, TypeError) as e: api_logger.warning(f"解析反思时间失败: {e},将执行反思") should_execute = True - + if should_execute: api_logger.info(f"与上次的反思时间间隔为: {hours_diff} 小时") # 3. 执行反思引擎 @@ -345,7 +345,7 @@ class MemoryReflectionService: "next_reflection_in_hours": iteration_period - hours_diff } - + except Exception as e: config_id = config_data.get("config_id", "unknown") api_logger.error(f"启动反思失败,config_id: {config_id}, end_user_id: {end_user_id}, 错误: {str(e)}") @@ -356,7 +356,7 @@ class MemoryReflectionService: "end_user_id": end_user_id, "config_data": config_data } - + def _create_reflection_config_from_data(self, config_data: Dict[str, Any]) -> ReflectionConfig: """Create reflective configuration objects from configuration data""" @@ -364,12 +364,12 @@ class MemoryReflectionService: if reflexion_range_value is None or reflexion_range_value == "": reflexion_range_value = "partial" reflexion_range = ReflectionRange(reflexion_range_value) - + baseline_value = config_data.get("baseline") if baseline_value is None or baseline_value == "": baseline_value = "TIME" baseline = ReflectionBaseline(baseline_value) - + # iteration_period = iteration_period = config_data.get("iteration_period", 24) if isinstance(iteration_period, str): @@ -377,7 +377,6 @@ class MemoryReflectionService: iteration_period = int(iteration_period) except (ValueError, TypeError): iteration_period = 24 # 默认24小时 - return ReflectionConfig( enabled=config_data.get("enable_self_reflexion", False), iteration_period=str(iteration_period), # ReflectionConfig期望字符串 diff --git a/api/app/services/prompt_optimizer_service.py b/api/app/services/prompt_optimizer_service.py index 81bf899d..2c0b57ac 100644 --- a/api/app/services/prompt_optimizer_service.py +++ b/api/app/services/prompt_optimizer_service.py @@ -1,3 +1,4 @@ +import os import re import uuid from typing import Any, AsyncGenerator @@ -182,11 +183,12 @@ class PromptOptimizerService: base_url=api_config.api_base ), type=ModelType(model_config.type)) try: - with open('app/services/prompt/prompt_optimizer_system.jinja2', 'r', encoding='utf-8') as f: + prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prompt') + with open(os.path.join(prompt_path, 'prompt_optimizer_system.jinja2'), 'r', encoding='utf-8') as f: opt_system_prompt = f.read() rendered_system_message = Template(opt_system_prompt).render() - with open('app/services/prompt/prompt_optimizer_user.jinja2', 'r', encoding='utf-8') as f: + with open(os.path.join(prompt_path, 'prompt_optimizer_user.jinja2'), 'r', encoding='utf-8') as f: opt_user_prompt = f.read() except FileNotFoundError: raise BusinessException(message="System prompt template not found", code=BizCode.NOT_FOUND) diff --git a/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx index f1ebd516..13084701 100644 --- a/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx +++ b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx @@ -64,7 +64,7 @@ const KnowledgeListModal = forwardRef(({ ...item, config: { similarity_threshold: 0.7, - strategy: "hybrid", + retrieve_type: "hybrid", top_k: 3, weight: 1, } diff --git a/web/src/views/Workflow/components/Properties/Knowledge/KnowledgeListModal.tsx b/web/src/views/Workflow/components/Properties/Knowledge/KnowledgeListModal.tsx index 6bd3bbf4..c74db398 100644 --- a/web/src/views/Workflow/components/Properties/Knowledge/KnowledgeListModal.tsx +++ b/web/src/views/Workflow/components/Properties/Knowledge/KnowledgeListModal.tsx @@ -64,7 +64,7 @@ const KnowledgeListModal = forwardRef(({ ...item, config: { similarity_threshold: 0.7, - strategy: "hybrid", + retrieve_type: "hybrid", top_k: 3, weight: 1, } diff --git a/web/src/views/Workflow/hooks/useWorkflowGraph.ts b/web/src/views/Workflow/hooks/useWorkflowGraph.ts index 4c010de0..774b3f7b 100644 --- a/web/src/views/Workflow/hooks/useWorkflowGraph.ts +++ b/web/src/views/Workflow/hooks/useWorkflowGraph.ts @@ -885,7 +885,7 @@ export const useWorkflowGraph = ({ ...itemConfig, ...(data.config[key].defaultValue || {}), knowledge_bases: knowledge_bases?.map((vo: any) => { - const kb_config = vo.config || { similarity_threshold: vo.similarity_threshold, strategy: vo.strategy, top_k: vo.top_k, weight: vo.weight } + const kb_config = vo.config || { similarity_threshold: vo.similarity_threshold, retrieve_type: vo.retrieve_type, top_k: vo.top_k, weight: vo.weight } return { kb_id: vo.kb_id || vo.id, ...kb_config, } }) } From 567d1ba18b6144060ad83ab7ad6460fe1fafd74e Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 3 Feb 2026 15:20:31 +0800 Subject: [PATCH 4/5] fix(web): remove delete confirm content --- .../views/ModelManagement/components/ModelImplement/index.tsx | 1 - web/src/views/Prompt/History.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/web/src/views/ModelManagement/components/ModelImplement/index.tsx b/web/src/views/ModelManagement/components/ModelImplement/index.tsx index 2720009c..4acb4d48 100644 --- a/web/src/views/ModelManagement/components/ModelImplement/index.tsx +++ b/web/src/views/ModelManagement/components/ModelImplement/index.tsx @@ -27,7 +27,6 @@ const ModelImplement: FC = ({ type, value, onChange }) => { const handleDelete = (vo: any) => { modal.confirm({ title: t('common.confirmDeleteDesc', { name: [vo.model_name, vo.api_key].join(' / ') }), - content: t('application.apiKeyDeleteContent'), okText: t('common.delete'), cancelText: t('common.cancel'), okType: 'danger', diff --git a/web/src/views/Prompt/History.tsx b/web/src/views/Prompt/History.tsx index fda662b4..ab07e4bc 100644 --- a/web/src/views/Prompt/History.tsx +++ b/web/src/views/Prompt/History.tsx @@ -25,7 +25,6 @@ const History: React.FC<{ query: HistoryQuery; edit: (item: HistoryItem) => void e?.stopPropagation(); modal.confirm({ title: t('common.confirmDeleteDesc', { name: item.title }), - content: t('application.apiKeyDeleteContent'), okText: t('common.delete'), cancelText: t('common.cancel'), okType: 'danger', From e196f86e30d75d2e7ac0ecf8c5c298df08807279 Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Tue, 3 Feb 2026 15:24:16 +0800 Subject: [PATCH 5/5] refactor(workflow): relocate template directory into workflow --- api/app/core/workflow/template_loader.py | 33 ++++++++++--------- .../templates}/customer_service/template.yml | 0 .../templates}/data_processing/template.yml | 0 .../templates}/multi_step_qa/template.yml | 0 .../templates}/simple_qa/template.yml | 0 5 files changed, 18 insertions(+), 15 deletions(-) rename api/app/{templates/workflows => core/workflow/templates}/customer_service/template.yml (100%) rename api/app/{templates/workflows => core/workflow/templates}/data_processing/template.yml (100%) rename api/app/{templates/workflows => core/workflow/templates}/multi_step_qa/template.yml (100%) rename api/app/{templates/workflows => core/workflow/templates}/simple_qa/template.yml (100%) diff --git a/api/app/core/workflow/template_loader.py b/api/app/core/workflow/template_loader.py index 4ef49ba5..ef16cf74 100644 --- a/api/app/core/workflow/template_loader.py +++ b/api/app/core/workflow/template_loader.py @@ -4,16 +4,19 @@ 从文件系统加载预定义的工作流模板 """ +import os from pathlib import Path from typing import Optional import yaml +TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') + class TemplateLoader: """工作流模板加载器""" - - def __init__(self, templates_dir: str = "app/templates/workflows"): + + def __init__(self, templates_dir: str = TEMPLATE_DIR): """初始化模板加载器 Args: @@ -22,7 +25,7 @@ class TemplateLoader: self.templates_dir = Path(templates_dir) if not self.templates_dir.exists(): raise ValueError(f"模板目录不存在: {templates_dir}") - + def list_templates(self) -> list[dict]: """列出所有可用的模板 @@ -30,22 +33,22 @@ class TemplateLoader: 模板列表,每个模板包含 id, name, description 等信息 """ templates = [] - + # 遍历模板目录 for template_dir in self.templates_dir.iterdir(): if not template_dir.is_dir(): continue - + # 检查是否有 template.yml 文件 template_file = template_dir / "template.yml" if not template_file.exists(): continue - + try: # 读取模板配置 with open(template_file, 'r', encoding='utf-8') as f: template_data = yaml.safe_load(f) - + # 提取模板信息 templates.append({ "id": template_dir.name, @@ -59,9 +62,9 @@ class TemplateLoader: except Exception as e: print(f"加载模板 {template_dir.name} 失败: {e}") continue - + return templates - + def load_template(self, template_id: str) -> Optional[dict]: """加载指定的模板 @@ -73,14 +76,14 @@ class TemplateLoader: """ template_dir = self.templates_dir / template_id template_file = template_dir / "template.yml" - + if not template_file.exists(): return None - + try: with open(template_file, 'r', encoding='utf-8') as f: template_data = yaml.safe_load(f) - + # 返回工作流配置部分 return { "name": template_data.get("name", template_id), @@ -94,7 +97,7 @@ class TemplateLoader: except Exception as e: print(f"加载模板 {template_id} 失败: {e}") return None - + def get_template_readme(self, template_id: str) -> Optional[str]: """获取模板的 README 文档 @@ -106,10 +109,10 @@ class TemplateLoader: """ template_dir = self.templates_dir / template_id readme_file = template_dir / "README.md" - + if not readme_file.exists(): return None - + try: with open(readme_file, 'r', encoding='utf-8') as f: return f.read() diff --git a/api/app/templates/workflows/customer_service/template.yml b/api/app/core/workflow/templates/customer_service/template.yml similarity index 100% rename from api/app/templates/workflows/customer_service/template.yml rename to api/app/core/workflow/templates/customer_service/template.yml diff --git a/api/app/templates/workflows/data_processing/template.yml b/api/app/core/workflow/templates/data_processing/template.yml similarity index 100% rename from api/app/templates/workflows/data_processing/template.yml rename to api/app/core/workflow/templates/data_processing/template.yml diff --git a/api/app/templates/workflows/multi_step_qa/template.yml b/api/app/core/workflow/templates/multi_step_qa/template.yml similarity index 100% rename from api/app/templates/workflows/multi_step_qa/template.yml rename to api/app/core/workflow/templates/multi_step_qa/template.yml diff --git a/api/app/templates/workflows/simple_qa/template.yml b/api/app/core/workflow/templates/simple_qa/template.yml similarity index 100% rename from api/app/templates/workflows/simple_qa/template.yml rename to api/app/core/workflow/templates/simple_qa/template.yml