From dc9dd3cbd7e2b6d24a6c92577976265c4ef99049 Mon Sep 17 00:00:00 2001 From: puzhen <1303385763@qq.com> Date: Tue, 21 Oct 2025 17:22:24 +0100 Subject: [PATCH 1/2] fix: question confirm --- backend/app/service/chat_service.py | 110 ++++++++++++++-------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/backend/app/service/chat_service.py b/backend/app/service/chat_service.py index 973a7c43c..a75e0d295 100644 --- a/backend/app/service/chat_service.py +++ b/backend/app/service/chat_service.py @@ -21,7 +21,7 @@ from camel.toolkits import AgentCommunicationToolkit, ToolkitMessageIntegration from app.utils.toolkit.human_toolkit import HumanToolkit from app.utils.toolkit.note_taking_toolkit import NoteTakingToolkit from app.utils.workforce import Workforce -from app.model.chat import Chat, NewAgent, QuestionAnalysisResult, Status, sse_json, TaskContent +from app.model.chat import Chat, NewAgent, Status, sse_json, TaskContent from camel.tasks import Task from app.utils.agent import ( ListenChatAgent, @@ -318,23 +318,26 @@ async def step_solve(options: Chat, request: Request, task_lock: TaskLock): continue # Simplified logic: attachments mean workforce, otherwise let agent decide - confirm: str | Literal[True] + is_complex_task: bool if len(options.attaches) > 0: # Questions with attachments always need workforce - confirm = True + is_complex_task = True else: - # No attachments - let agent decide based on question content and context - confirm = await question_confirm(question_agent, question, task_lock) + is_complex_task = await question_confirm(question_agent, question, task_lock) + + if not is_complex_task: + simple_answer_prompt = f"{build_conversation_context(task_lock, header='=== Previous Conversation ===')}User Query: {question}\n\nProvide a direct, helpful answer to this simple question." - if confirm is not True: - # confirm is str here (simple answer from agent) try: - import json - response_data = json.loads(confirm.split("data: ")[1].strip()) - response_content = response_data['data']['content'] - task_lock.add_conversation('assistant', response_content) + simple_resp = question_agent.step(simple_answer_prompt) + answer_content = simple_resp.msgs[0].content if simple_resp and simple_resp.msgs else "I understand your question, but I'm having trouble generating a response right now." + + task_lock.add_conversation('assistant', answer_content) + + yield sse_json("wait_confirm", {"content": answer_content, "question": question}) except Exception as e: - logger.error(f"[CONTEXT] Failed to save response to history: {e}") + logger.error(f"Error generating simple answer: {e}") + yield sse_json("wait_confirm", {"content": "I encountered an error while processing your question.", "question": question}) # Clean up empty folder if it was created for this task if hasattr(task_lock, 'new_folder_path') and task_lock.new_folder_path: @@ -356,8 +359,6 @@ async def step_solve(options: Chat, request: Request, task_lock: TaskLock): task_lock.new_folder_path = None except Exception as e: logger.error(f"Error cleaning up folder: {e}") - - yield confirm else: yield sse_json("confirmed", {"question": question}) @@ -548,12 +549,24 @@ async def step_solve(options: Chat, request: Request, task_lock: TaskLock): workforce.pause() try: - multi_turn_confirm = await question_confirm(question_agent, new_task_content, task_lock) + is_multi_turn_complex = await question_confirm(question_agent, new_task_content, task_lock) + + if not is_multi_turn_complex: + simple_answer_prompt = f"{build_conversation_context(task_lock, header='=== Previous Conversation ===')}User Query: {new_task_content}\n\nProvide a direct, helpful answer to this simple question." + + try: + simple_resp = question_agent.step(simple_answer_prompt) + answer_content = simple_resp.msgs[0].content if simple_resp and simple_resp.msgs else "I understand your question, but I'm having trouble generating a response right now." + + task_lock.add_conversation('assistant', answer_content) + + # Send response to user + yield sse_json("confirmed", {"question": new_task_content}) + yield sse_json("wait_confirm", {"content": answer_content, "question": new_task_content}) + except Exception as e: + logger.error(f"Error generating simple answer in multi-turn: {e}") + yield sse_json("wait_confirm", {"content": "I encountered an error while processing your question.", "question": new_task_content}) - if multi_turn_confirm is not True: - # Still need to send appropriate responses - yield sse_json("confirmed", {"question": new_task_content}) - yield multi_turn_confirm workforce.resume() continue # This continues the main while loop, waiting for next action @@ -797,8 +810,8 @@ def add_sub_tasks(camel_task: Task, update_tasks: list[TaskContent]): ) -async def question_confirm(agent: ListenChatAgent, prompt: str, task_lock: TaskLock | None = None) -> str | Literal[True]: - """Unified question confirmation using structured output.""" +async def question_confirm(agent: ListenChatAgent, prompt: str, task_lock: TaskLock | None = None) -> bool: + """Simple question confirmation - returns True for complex tasks, False for simple questions.""" context_prompt = "" if task_lock: @@ -806,51 +819,38 @@ async def question_confirm(agent: ListenChatAgent, prompt: str, task_lock: TaskL full_prompt = f"""{context_prompt}User Query: {prompt} -Analyze if this is a simple question or complex task: +Determine if this user query is a complex task or a simple question. -**Simple question**: Can be answered directly using knowledge or conversation history -- Examples: greetings, fact queries, clarifications about previous results -- Response: Provide a direct, helpful answer +**Complex task** (answer "yes"): Requires tools, code execution, file operations, multi-step planning, or creating/modifying content +- Examples: "create a file", "search for X", "implement feature Y", "write code", "analyze data", "build something" -**Complex task**: Requires tools, code execution, file operations, or multi-step planning -- Examples: "create a file", "search for", "implement feature X" -- Response: Indicate this is complex +**Simple question** (answer "no"): Can be answered directly with knowledge or conversation history, no action needed +- Examples: greetings ("hello", "hi"), fact queries ("what is X?"), clarifications ("what did you mean?"), status checks ("how are you?") -Based on the user query, determine the type and provide appropriate response.""" +Answer only "yes" or "no". Do not provide any explanation. + +Is this a complex task? (yes/no):""" try: - resp = agent.step(full_prompt, response_format=QuestionAnalysisResult) + resp = agent.step(full_prompt) if not resp or not resp.msgs or len(resp.msgs) == 0: + logger.warning("No response from agent, defaulting to complex task") return True - result = resp.msgs[0].parsed - - if not result: - content = resp.msgs[0].content - if not content: - return True - content_stripped = content.strip() - if content_stripped.startswith('{') and content_stripped.endswith('}'): - try: - parsed_json = json.loads(content_stripped) - result = QuestionAnalysisResult(**parsed_json) - except (json.JSONDecodeError, ValueError) as e: - logger.warning(f"Failed to parse JSON from content: {e}") - normalized = content.strip().lower() - if normalized in ["yes", "complex"]: - return True - return sse_json("wait_confirm", {"content": content, "question": prompt}) - else: - normalized = content.strip().lower() - if normalized in ["yes", "complex"]: - return True - return sse_json("wait_confirm", {"content": content, "question": prompt}) - if result.type == "simple" and result.answer: - return sse_json("wait_confirm", {"content": result.answer, "question": prompt}) - else: + content = resp.msgs[0].content + if not content: + logger.warning("Empty content from agent, defaulting to complex task") return True + normalized = content.strip().lower() + is_complex = "yes" in normalized + + logger.info(f"Question confirm result: {'complex task' if is_complex else 'simple question'}", + extra={"response": content, "is_complex": is_complex}) + + return is_complex + except Exception as e: logger.error(f"Error in question_confirm: {e}") return True From 614b90cfa303550c49e9cb1d70d34cd7426346ff Mon Sep 17 00:00:00 2001 From: Wendong-Fan Date: Wed, 22 Oct 2025 00:47:41 +0800 Subject: [PATCH 2/2] update --- utils/__pycache__/__init__.cpython-310.pyc | Bin 155 -> 155 bytes .../traceroot_wrapper.cpython-310.pyc | Bin 2359 -> 2359 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/utils/__pycache__/__init__.cpython-310.pyc b/utils/__pycache__/__init__.cpython-310.pyc index 9b563d940e9b0cbef526e4e7b35dc01daf8bc128..664fc47bd9826a3e449c88dcd75e10bb0081579c 100644 GIT binary patch delta 27 hcmbQuIGd3>pO=@50SIEYf1k)5$6{z;WHd2X4**yd2G9Ti delta 27 hcmbQuIGd3>pO=@50SGu!zD?wgV=*!?G?