From 8592af6c69666f9f7bebb39428234cddf12d700c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Wed, 10 Dec 2025 18:40:07 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E5=A4=84=E7=90=86=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/agent/conference_research/supervisor.py | 13 +++++++------ deepinsight/core/agent/deep_research/researcher.py | 7 ++++--- deepinsight/core/agent/deep_research/supervisor.py | 9 +++++---- deepinsight/service/schemas/streaming.py | 3 +++ deepinsight/service/streaming/stream_adapter.py | 13 +++++++++++++ 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/deepinsight/core/agent/conference_research/supervisor.py b/deepinsight/core/agent/conference_research/supervisor.py index 2961d29..d66c8e8 100644 --- a/deepinsight/core/agent/conference_research/supervisor.py +++ b/deepinsight/core/agent/conference_research/supervisor.py @@ -15,6 +15,7 @@ from langgraph.config import get_stream_writer from langgraph.constants import END from langgraph.graph import StateGraph, add_messages from langgraph.types import Command, interrupt +from deepinsight.core.utils.progress_utils import progress_stage from deepinsight.core.tools.best_paper_analysis import batch_analyze_papers from deepinsight.core.tools.paper_statistic import ( @@ -184,7 +185,7 @@ async def construct_sub_config(config, prompt_group: ConferenceGraphNodeType): "tools": tools, } - +@progress_stage("会议概览信息收集") async def conference_overview_node(state: ConferenceState, config: RunnableConfig): result = await deep_research_graph.with_config( configurable=await construct_sub_config(config, ConferenceGraphNodeType.CONFERENCE_OVERVIEW) @@ -195,7 +196,7 @@ async def conference_overview_node(state: ConferenceState, config: RunnableConfi "conference_overview": result["final_report"] } - +@progress_stage("会议统计分析") async def conference_submission_node(state: ConferenceState, config: RunnableConfig): result = await conf_stat_graph.with_config( configurable=await construct_sub_config(config, ConferenceGraphNodeType.CONFERENCE_SUBMISSION) @@ -206,7 +207,7 @@ async def conference_submission_node(state: ConferenceState, config: RunnableCon "conference_submission": result["static_summary"] } - +@progress_stage("会议keynotes分析") async def conference_keynotes_node(state: ConferenceState, config: RunnableConfig): result = await deep_research_graph.with_config( configurable=await construct_sub_config(config, ConferenceGraphNodeType.CONFERENCE_KEYNOTE) @@ -217,7 +218,7 @@ async def conference_keynotes_node(state: ConferenceState, config: RunnableConfi "conference_keynotes": result["final_report"] } - +@progress_stage("会议主题分析") async def conference_topic_node(state: ConferenceState, config: RunnableConfig): result = await deep_research_graph.with_config( configurable=await construct_sub_config(config, ConferenceGraphNodeType.CONFERENCE_TOPIC) @@ -228,7 +229,7 @@ async def conference_topic_node(state: ConferenceState, config: RunnableConfig): "conference_topic": result["final_report"] } - +@progress_stage("会议最佳论文分析") async def conference_best_paper_node(state: ConferenceState, config: RunnableConfig): result = await deep_research_graph.with_config( configurable=await construct_sub_config(config, ConferenceGraphNodeType.CONFERENCE_BEST_PAPER) @@ -244,7 +245,7 @@ async def conference_best_paper_node(state: ConferenceState, config: RunnableCon "conference_best_papers": paper_file_content, } - +@progress_stage("洞察总结") async def insight_summary_node(state: ConferenceState, config: RunnableConfig): rc = parse_research_config(config) model = rc.get_model() diff --git a/deepinsight/core/agent/deep_research/researcher.py b/deepinsight/core/agent/deep_research/researcher.py index f743e4d..3926173 100644 --- a/deepinsight/core/agent/deep_research/researcher.py +++ b/deepinsight/core/agent/deep_research/researcher.py @@ -11,6 +11,7 @@ from langgraph.constants import START, END from langgraph.config import get_stream_writer from langgraph.graph import StateGraph from langgraph.types import Command +from deepinsight.core.utils.progress_utils import progress_stage from deepinsight.core.types.research import ( ErrorResult, @@ -105,7 +106,7 @@ async def execute_tool_safely(tool, args, config, name): writer(error_response) return f"Error executing tool: {str(e)}" - +@progress_stage("规划主题研究") async def topic_researcher(state: ResearcherState, config: RunnableConfig) -> Command[Literal["researcher_tools"]]: """Individual researcher that conducts focused research on specific topics. @@ -173,7 +174,7 @@ async def topic_researcher(state: ResearcherState, config: RunnableConfig) -> Co } ) - +@progress_stage("执行主题研究") async def topic_tools_exec(state: ResearcherState, config: RunnableConfig) -> Command[ Literal["researcher", "compress_research"]]: """Execute tools called by the researcher, including search tools and strategic thinking. @@ -304,7 +305,7 @@ async def topic_tools_exec(state: ResearcherState, config: RunnableConfig) -> Co logging.error(f"Exception traceback: {traceback.format_exc()}") raise - +@progress_stage("搜索结果压缩") async def topic_results_compress(state: ResearcherState, config: RunnableConfig): """Compress and synthesize research findings into a concise, structured summary. diff --git a/deepinsight/core/agent/deep_research/supervisor.py b/deepinsight/core/agent/deep_research/supervisor.py index 1359df8..6e79e23 100644 --- a/deepinsight/core/agent/deep_research/supervisor.py +++ b/deepinsight/core/agent/deep_research/supervisor.py @@ -39,6 +39,7 @@ from deepinsight.core.utils.llm_token_utils import ( is_token_limit_exceeded, get_model_token_limit, ) +from deepinsight.core.utils.progress_utils import progress_stage class ConductResearch(BaseModel): @@ -150,7 +151,7 @@ async def wait_user_clarification(state: AgentState): "messages": [HumanMessage(content=user_reply)] } - +@progress_stage("制定研究概要") async def write_research_brief(state: AgentState, config: RunnableConfig): """Transform user messages into a structured research brief and initialize supervisor.""" # Step 1: Set up the research model (without structured output) @@ -230,7 +231,7 @@ async def wait_user_confirm_research_brief(state: AgentState, config: RunnableCo } } - +@progress_stage("生成研究大纲") async def generate_report_outline(state: AgentState, config: RunnableConfig): # Step 1: Extract research findings and prepare state cleanup notes = state.get("notes", []) @@ -512,7 +513,7 @@ async def publish_result(state: AgentState, config: RunnableConfig): )) return state - +@progress_stage("生成研究主题指令") async def supervisor(state: SupervisorState, config: RunnableConfig) -> Command[Literal["supervisor_tools"]]: """Lead research supervisor that plans research strategy and delegates to researchers. @@ -555,7 +556,7 @@ async def supervisor(state: SupervisorState, config: RunnableConfig) -> Command[ } ) - +@progress_stage("执行研究主题") async def supervisor_tools(state: SupervisorState, config: RunnableConfig) -> Command[Literal["supervisor", "__end__"]]: """Execute tools called by the supervisor, including research delegation and strategic thinking. diff --git a/deepinsight/service/schemas/streaming.py b/deepinsight/service/schemas/streaming.py index 310b855..ef69b64 100644 --- a/deepinsight/service/schemas/streaming.py +++ b/deepinsight/service/schemas/streaming.py @@ -34,6 +34,9 @@ class EventType(str, Enum): thinking_step_topic = "thinking_step_topic" thinking_step_report_generating = "thinking_step_report_generating" + # progress + progress = "progress" + class MessageToolCallContent(BaseModel): index: Optional[int] = Field(None, description="Tool call index") diff --git a/deepinsight/service/streaming/stream_adapter.py b/deepinsight/service/streaming/stream_adapter.py index 93a4acc..636cf95 100644 --- a/deepinsight/service/streaming/stream_adapter.py +++ b/deepinsight/service/streaming/stream_adapter.py @@ -11,6 +11,7 @@ from langchain_core.runnables import RunnableConfig from langchain_core.messages import HumanMessage, AIMessage, AIMessageChunk, ToolMessage, ToolMessageChunk from langgraph.types import StateSnapshot, Interrupt, Command from langgraph.graph.state import CompiledStateGraph +from deepinsight.core.utils.progress_utils import ProgressEvent from deepinsight.service.schemas.streaming import ( EventType, @@ -260,6 +261,18 @@ class StreamEventAdapter: ) ], ) + elif isinstance(message_chunk, ProgressEvent): + yield StreamEvent( + event=EventType.progress, + run_id=run_id, + conversation_id=conversation_id, + messages=[ + ResponseMessage( + content=ResponseMessageContent(text=message_chunk.description), + content_type=ResponseMessageContentType.plain_text, + ) + ] + ) elif mode == "updates": if isinstance(data, dict) and "__interrupt__" in data: -- Gitee From fe7f6af05cf9804c7a8e86ceb7ecb347c8f7346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Wed, 10 Dec 2025 19:10:06 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A1=B6=E4=BC=9A?= =?UTF-8?q?=E6=B4=9E=E5=AF=9F=E5=B7=A5=E5=85=B7=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deepinsight/core/agent/conference_research/supervisor.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/deepinsight/core/agent/conference_research/supervisor.py b/deepinsight/core/agent/conference_research/supervisor.py index d66c8e8..67fddfc 100644 --- a/deepinsight/core/agent/conference_research/supervisor.py +++ b/deepinsight/core/agent/conference_research/supervisor.py @@ -28,7 +28,7 @@ from deepinsight.core.tools.paper_statistic import ( ) from deepinsight.core.utils.mcp_utils import MCPClientUtils from deepinsight.core.utils.research_utils import parse_research_config -from deepinsight.core.types.graph_config import ResearchConfig +from deepinsight.core.types.graph_config import ResearchConfig, SearchAPI from deepinsight.core.types.research import FinalResult from deepinsight.core.agent.deep_research.supervisor import graph as deep_research_graph @@ -153,10 +153,7 @@ async def wait_user_clarify_node(state: ConferenceState): async def construct_sub_config(config, prompt_group: ConferenceGraphNodeType): - parent_configurable = config.get("configurable", {}) tools = [] - if parent_configurable.get("tools"): - tools.extend(parent_configurable["tools"]) if prompt_group == ConferenceGraphNodeType.CONFERENCE_BEST_PAPER: tools.append(batch_analyze_papers) elif prompt_group == ConferenceGraphNodeType.CONFERENCE_SUBMISSION: @@ -183,6 +180,7 @@ async def construct_sub_config(config, prompt_group: ConferenceGraphNodeType): "allow_edit_report_outline": False, "allow_publish_result": False, "tools": tools, + "search_api": [SearchAPI.TAVILY], } @progress_stage("会议概览信息收集") -- Gitee From feed0c1b9f7aef78243c75a05349efb75786789c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Wed, 10 Dec 2025 20:36:58 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E5=88=A0=E9=99=A4deepagent=E7=9A=84?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E4=B8=AD=E9=97=B4=E4=BB=B6=EF=BC=8C=E9=80=82?= =?UTF-8?q?=E9=85=8D=E6=9C=80=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/agent/conference_research/conf_stat_value_mining.py | 3 +-- deepinsight/core/tools/best_paper_analysis.py | 3 --- deepinsight/core/tools/keynote_analysis.py | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/deepinsight/core/agent/conference_research/conf_stat_value_mining.py b/deepinsight/core/agent/conference_research/conf_stat_value_mining.py index cf24b00..7d5b36d 100644 --- a/deepinsight/core/agent/conference_research/conf_stat_value_mining.py +++ b/deepinsight/core/agent/conference_research/conf_stat_value_mining.py @@ -20,7 +20,6 @@ from deepinsight.core.tools.wordcloud_tool import generate_wordcloud from deepinsight.core.utils.progress_utils import progress_stage from deepinsight.core.utils.research_utils import parse_research_config from deepinsight.core.tools.tavily_search import tavily_search -from deepinsight.core.utils.context_utils import DefaultSummarizationMiddleware from deepinsight.utils.db_schema_utils import get_db_models_source_markdown from integrations.mcps.generate_chart import generate_column_chart, generate_bar_chart, generate_pie_chart @@ -61,7 +60,7 @@ async def get_deep_agents(config: RunnableConfig, prompt_template_name, extent_t tools=tools, system_prompt=system_prompt, backend=mem_file_system_instance, - middleware=[ModelFallbackMiddleware(llm_model, llm_model), DefaultSummarizationMiddleware(model=llm_model)] + middleware=[ModelFallbackMiddleware(llm_model, llm_model)] ) return agent diff --git a/deepinsight/core/tools/best_paper_analysis.py b/deepinsight/core/tools/best_paper_analysis.py index 92329a5..bab0b0f 100644 --- a/deepinsight/core/tools/best_paper_analysis.py +++ b/deepinsight/core/tools/best_paper_analysis.py @@ -11,11 +11,9 @@ from langchain_tavily import TavilySearch from deepinsight.core.tools.file_system import register_fs_tools, MemoryMCPFilesystem from deepinsight.core.utils.tool_utils import create_retrieval_tool, CoerceToolOutput -from deepinsight.core.utils.context_utils import SummarizationMiddleware from deepinsight.core.types.graph_config import RetrievalType from deepinsight.core.utils.research_utils import parse_research_config from deepinsight.utils.db_schema_utils import get_db_models_source_markdown -from deepinsight.core.utils.context_utils import DefaultSummarizationMiddleware # ----------------- 单篇论文解析函数 ----------------- @@ -83,7 +81,6 @@ async def analyze_single_paper(paper_info: str, output_dir: str, config: Runnabl } middleware = [ CoerceToolOutput(), - SummarizationMiddleware(model=rc.default_model), ModelFallbackMiddleware( rc.default_model, # Try first on error rc.default_model, # Then this diff --git a/deepinsight/core/tools/keynote_analysis.py b/deepinsight/core/tools/keynote_analysis.py index 4844394..24df184 100644 --- a/deepinsight/core/tools/keynote_analysis.py +++ b/deepinsight/core/tools/keynote_analysis.py @@ -13,7 +13,6 @@ from langchain_tavily import TavilySearch from deepinsight.core.utils.research_utils import parse_research_config from deepinsight.core.utils.tool_utils import CoerceToolOutput -from deepinsight.core.utils.context_utils import SummarizationMiddleware from deepinsight.core.tools.file_system import register_fs_tools, fs_instance @@ -100,7 +99,6 @@ async def analyze_single_keynote(keynote_info: str, output_dir: str, config: Run from deepagents import create_deep_agent middleware = [ CoerceToolOutput(), - SummarizationMiddleware(model=rc.default_model), ModelFallbackMiddleware( rc.default_model, rc.default_model, -- Gitee From e395f08c0419ee694a7d149f3d91d38b8276fd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Thu, 11 Dec 2025 11:13:45 +0800 Subject: [PATCH 4/6] =?UTF-8?q?CoerceToolOutput=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deepinsight/core/utils/tool_utils.py | 50 ++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/deepinsight/core/utils/tool_utils.py b/deepinsight/core/utils/tool_utils.py index 4c49ed1..1f31b96 100644 --- a/deepinsight/core/utils/tool_utils.py +++ b/deepinsight/core/utils/tool_utils.py @@ -1,6 +1,6 @@ import logging import json -from typing import Callable +from typing import Callable, Awaitable from google.ai.generativelanguage_v1beta.types import Tool as GenAITool @@ -249,4 +249,50 @@ class CoerceToolOutput(AgentMiddleware): # 如果无法序列化,则转为通用的字符串表示 result.content = str(result.content) - return result \ No newline at end of file + return result + + async def wrap_tool_call( + self, + request: ToolCallRequest, + handler: Callable[[ToolCallRequest], Awaitable[ToolMessage]], + ) -> ToolMessage: + # 1. 先执行实际的工具调用,获取结果 + result = await handler(request) + + # 2. 确保 tool_call['args']['messages'] 每项的 content 都是字符串 + if isinstance(request.tool_call.get("args", {}).get("messages"), list): + for message in request.tool_call["args"]["messages"]: + if not isinstance(message.get("content"), str): + # 添加更细致的类型转换,确保每个 message 的内容都被处理为字符串 + if isinstance(message["content"], (dict, list)): + message["content"] = json.dumps(message["content"], ensure_ascii=False) + else: + message["content"] = str(message["content"]) + + # 3. 处理 ToolMessage 中的消息 + if isinstance(result, ToolMessage): + if isinstance(result.content, dict): + # 提取消息列表,如果 content 是字典 + messages = result.content.get("messages", []) + else: + messages = [] + + # 遍历 messages,确保每个 message 的 content 都是字符串 + for message in messages: + if not isinstance(message.content, str): + if isinstance(message.content, (dict, list)): + message.content = json.dumps(message.content, ensure_ascii=False) + else: + message.content = str(message.content) + + # 4. 最终处理 ToolMessage 的顶层 content + if not isinstance(result.content, str): + try: + # 尝试将整个内容对象序列化为 JSON 字符串 + result.content = json.dumps(result.content, ensure_ascii=False) + except TypeError: + # 如果无法序列化,则转为通用的字符串表示 + result.content = str(result.content) + + return result + -- Gitee From a77e5821113ad49dfcf5193656586f8622de0a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Thu, 11 Dec 2025 12:45:26 +0800 Subject: [PATCH 5/6] =?UTF-8?q?tavily=E8=B0=83=E7=94=A8=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E4=B9=9F=E5=8F=91=E9=80=81=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E5=88=B0=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deepinsight/core/tools/tavily_search.py | 11 +++++++++++ deepinsight/core/utils/tool_utils.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/deepinsight/core/tools/tavily_search.py b/deepinsight/core/tools/tavily_search.py index 6af315a..5576427 100644 --- a/deepinsight/core/tools/tavily_search.py +++ b/deepinsight/core/tools/tavily_search.py @@ -15,6 +15,7 @@ from langchain_core.messages import HumanMessage from deepinsight.core.types.graph_config import ResearchConfig from deepinsight.core.utils.research_utils import parse_research_config from deepinsight.core.types.research import ( + ErrorResult, WebSearchResult, ToolType, ToolUnifiedResponse, @@ -160,6 +161,16 @@ async def tavily_search( except Exception as e: error_message = f"Tavily search failed with error: {type(e).__name__}: {e}" logging.error(error_message) + writer = get_stream_writer() + writer(ToolUnifiedResponse( + parent_message_id=config.get("metadata", {}).get("parent_message_id", None), + type=ToolType.web_search, + name="tavily_search", + args={"queries": queries}, + result=ErrorResult( + error=error_message + ) + )) return error_message # Step 2: Deduplicate results by URL to avoid processing the same content multiple times diff --git a/deepinsight/core/utils/tool_utils.py b/deepinsight/core/utils/tool_utils.py index 1f31b96..7a4f198 100644 --- a/deepinsight/core/utils/tool_utils.py +++ b/deepinsight/core/utils/tool_utils.py @@ -251,7 +251,7 @@ class CoerceToolOutput(AgentMiddleware): return result - async def wrap_tool_call( + async def awrap_tool_call( self, request: ToolCallRequest, handler: Callable[[ToolCallRequest], Awaitable[ToolMessage]], -- Gitee From aeea55c69bd1c97e391515dd9d4e178c08cdb311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A9=E6=97=AD=E7=BA=A2?= <2250244225@qq.com> Date: Fri, 12 Dec 2025 09:06:32 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E9=A1=B6=E4=BC=9A=E7=A6=81=E7=94=A8?= =?UTF-8?q?=E5=A4=A7=E7=BA=B2=E5=92=8C=E6=8A=A5=E5=91=8A=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yaml | 2 -- deepinsight/core/agent/conference_qa/supervisor.py | 4 +++- deepinsight/service/research/research.py | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config.yaml b/config.yaml index 52369c0..6cb9a52 100644 --- a/config.yaml +++ b/config.yaml @@ -106,9 +106,7 @@ scenarios: clarify_with_user: true write_research_brief: true compress_research: true - generate_report_outline: true tool_call: clarify_with_user: true write_research_brief: true compress_research: true - generate_report_outline: true diff --git a/deepinsight/core/agent/conference_qa/supervisor.py b/deepinsight/core/agent/conference_qa/supervisor.py index 4ea24bb..32a4976 100644 --- a/deepinsight/core/agent/conference_qa/supervisor.py +++ b/deepinsight/core/agent/conference_qa/supervisor.py @@ -372,7 +372,9 @@ async def deep_research_team_node(state: SupervisorState, config: RunnableConfig for key, usage in all_keys_usage.items(): logging.error(f"API Key: {key} - Plan Limit: {usage['plan_limit']}, Plan Usage: {usage['plan_usage']}") writer = get_stream_writer() - writer({"result": "no tavily key can be used, please set first."}) + writer(FinalResult( + final_report="no tavily key can be used, please set first." + )) return Command(goto=END) parent_configurable = config.get("configurable", {}) diff --git a/deepinsight/service/research/research.py b/deepinsight/service/research/research.py index f398642..1843809 100644 --- a/deepinsight/service/research/research.py +++ b/deepinsight/service/research/research.py @@ -99,9 +99,11 @@ class ResearchService: # Additional filters for conference scenes (only new ones not in config.yaml) conference_additional_filters = { "researcher_tools": True, + "researcher": True, "publish_result": True, + "generate_report": True, + "generate_report_outline": True, "tools": True, - "researcher": True, "model": True, "agent": True, } -- Gitee