fix: handle greetings without triggering research workflow (#755)

* fix: handle greetings without triggering research workflow (#733)

* test: update tests for direct_response tool behavior

* fix: address Copilot review comments for coordinator_node - Extract locale from direct_response tool_args - Fix import sorting (ruff I001)

* fix: remove locale extraction from tool_args in direct_response

Use locale from state instead of tool_args to avoid potential side effects. The locale is already properly passed from frontend via state.

* fix: only fallback to planner when clarification is enabled

In legacy mode (BRANCH 1), no tool calls should end the workflow gracefully instead of falling back to planner. This fixes the test_coordinator_node_no_tool_calls integration test.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
Jiahe Wu
2025-12-13 20:25:46 +08:00
committed by GitHub
parent a6d8deee8b
commit c686ab7016
4 changed files with 64 additions and 36 deletions

View File

@@ -5,7 +5,7 @@ import json
import logging
import os
from functools import partial
from typing import Any, Annotated, Literal
from typing import Annotated, Any, Literal
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
from langchain_core.runnables import RunnableConfig
@@ -63,6 +63,15 @@ def handoff_after_clarification(
return
@tool
def direct_response(
message: Annotated[str, "The response message to send directly to user."],
locale: Annotated[str, "The user's detected language locale (e.g., en-US, zh-CN)."],
):
"""Respond directly to user for greetings, small talk, or polite rejections. Do NOT use this for research questions - use handoff_to_planner instead."""
return
def needs_clarification(state: dict) -> bool:
"""
Check if clarification is needed based on current state.
@@ -524,12 +533,12 @@ def coordinator_node(
messages.append(
{
"role": "system",
"content": "CRITICAL: Clarification is DISABLED. You MUST immediately call handoff_to_planner tool with the user's query as-is. Do NOT ask questions or mention needing more information.",
"content": "Clarification is DISABLED. For research questions, use handoff_to_planner. For greetings or small talk, use direct_response. Do NOT ask clarifying questions.",
}
)
# Only bind handoff_to_planner tool
tools = [handoff_to_planner]
# Bind both handoff_to_planner and direct_response tools
tools = [handoff_to_planner, direct_response]
response = (
get_llm_by_type(AGENT_LLM_MAP["coordinator"])
.bind_tools(tools)
@@ -556,11 +565,24 @@ def coordinator_node(
if tool_args.get("research_topic"):
research_topic = tool_args.get("research_topic")
break
elif tool_name == "direct_response":
logger.info("Direct response to user (greeting/small talk)")
goto = "__end__"
# Append direct message to messages list instead of overwriting response
if tool_args.get("message"):
messages.append(AIMessage(content=tool_args.get("message"), name="coordinator"))
break
except Exception as e:
logger.error(f"Error processing tool calls: {e}")
goto = "planner"
# Do not return early - let code flow to unified return logic below
# Set clarification variables for legacy mode
clarification_rounds = 0
clarification_history = []
clarified_topic = research_topic
# ============================================================
# BRANCH 2: Clarification ENABLED (New Feature)
# ============================================================
@@ -735,16 +757,19 @@ def coordinator_node(
logger.error(f"Error processing tool calls: {e}")
goto = "planner"
else:
# No tool calls detected - fallback to planner instead of ending
logger.warning(
"LLM didn't call any tools. This may indicate tool calling issues with the model. "
"Falling back to planner to ensure research proceeds."
)
# Log full response for debugging
logger.debug(f"Coordinator response content: {response.content}")
logger.debug(f"Coordinator response object: {response}")
# Fallback to planner to ensure workflow continues
goto = "planner"
# No tool calls detected
if enable_clarification:
# BRANCH 2: Fallback to planner to ensure research proceeds
logger.warning(
"LLM didn't call any tools. This may indicate tool calling issues with the model. "
"Falling back to planner to ensure research proceeds."
)
logger.debug(f"Coordinator response content: {response.content}")
logger.debug(f"Coordinator response object: {response}")
goto = "planner"
else:
# BRANCH 1: No tool calls means end workflow gracefully (e.g., greeting handled)
logger.info("No tool calls in legacy mode - ending workflow gracefully")
# Apply background_investigation routing if enabled (unified logic)
if goto == "planner" and state.get("enable_background_investigation"):