Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions backend/app/agent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
from app.agent.agent_model import agent_model
from app.agent.factory import (
browser_agent,
create_coordinator_and_task_agents,
create_new_worker_agent,
developer_agent,
document_agent,
get_task_result_with_optional_summary,
mcp_agent,
multi_modal_agent,
question_confirm_agent,
question_confirm,
social_media_agent,
task_summary_agent,
summary_subtasks_result,
summary_task,
)
from app.agent.listen_chat_agent import ListenChatAgent
from app.agent.tools import get_mcp_tools, get_toolkits
Expand All @@ -32,11 +36,15 @@
"get_mcp_tools",
"get_toolkits",
"browser_agent",
"create_coordinator_and_task_agents",
"create_new_worker_agent",
"developer_agent",
"document_agent",
"get_task_result_with_optional_summary",
"mcp_agent",
"multi_modal_agent",
"question_confirm_agent",
"question_confirm",
"social_media_agent",
"task_summary_agent",
"summary_subtasks_result",
"summary_task",
]
20 changes: 16 additions & 4 deletions backend/app/agent/factory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,29 @@
from app.agent.factory.document import document_agent
from app.agent.factory.mcp import mcp_agent
from app.agent.factory.multi_modal import multi_modal_agent
from app.agent.factory.question_confirm import question_confirm_agent
from app.agent.factory.question_confirm import question_confirm
from app.agent.factory.social_media import social_media_agent
from app.agent.factory.task_summary import task_summary_agent
from app.agent.factory.task_summary import (
get_task_result_with_optional_summary,
summary_subtasks_result,
summary_task,
)
from app.agent.factory.workforce_agents import (
create_coordinator_and_task_agents,
create_new_worker_agent,
)

__all__ = [
"browser_agent",
"create_coordinator_and_task_agents",
"create_new_worker_agent",
"developer_agent",
"document_agent",
"get_task_result_with_optional_summary",
"mcp_agent",
"multi_modal_agent",
"question_confirm_agent",
"question_confirm",
"social_media_agent",
"task_summary_agent",
"summary_subtasks_result",
"summary_task",
]
101 changes: 99 additions & 2 deletions backend/app/agent/factory/question_confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,112 @@
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from app.agent.agent_model import agent_model
from app.agent.prompt import QUESTION_CONFIRM_SYS_PROMPT
from app.agent.prompt import (
QUESTION_CONFIRM_PROMPT,
QUESTION_CONFIRM_SYS_PROMPT,
SIMPLE_ANSWER_PROMPT,
)
from app.agent.utils import NOW_STR
from app.model.chat import Chat
from app.utils.context import build_conversation_context

if TYPE_CHECKING:
from app.service.task import TaskLock

logger = logging.getLogger(__name__)


def question_confirm_agent(options: Chat):
def _create_question_agent(options: Chat):
"""Create a question classification agent."""
return agent_model(
"question_confirm_agent",
QUESTION_CONFIRM_SYS_PROMPT.format(now_str=NOW_STR),
options,
)


async def question_confirm(
prompt: str, options: Chat, task_lock: TaskLock
) -> bool:
"""Classify whether a user query is a complex task or simple question.

Creates and caches the question agent on task_lock.question_agent
for reuse across multi-turn conversations.

Returns True for complex tasks, False for simple questions.
"""
if (
not hasattr(task_lock, "question_agent")
or task_lock.question_agent is None
):
task_lock.question_agent = _create_question_agent(options)

agent = task_lock.question_agent

context_prompt = build_conversation_context(
task_lock, header="=== Previous Conversation ==="
)

full_prompt = QUESTION_CONFIRM_PROMPT.format(
context_prompt=context_prompt, user_query=prompt
)

try:
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

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

result_str = "complex task" if is_complex else "simple question"
logger.info(
f"Question confirm result: {result_str}",
extra={"response": content, "is_complex": is_complex},
)

return is_complex

except Exception as e:
logger.error(f"Error in question_confirm: {e}")
raise


async def simple_answer(
question: str, options: Chat, task_lock: TaskLock
) -> str:
"""Generate a direct answer to a simple question using the cached agent."""
if (
not hasattr(task_lock, "question_agent")
or task_lock.question_agent is None
):
task_lock.question_agent = _create_question_agent(options)

context_prompt = build_conversation_context(
task_lock, header="=== Previous Conversation ==="
)
prompt = SIMPLE_ANSWER_PROMPT.format(
context_prompt=context_prompt, user_query=question
)

resp = task_lock.question_agent.step(prompt)
if resp and resp.msgs and resp.msgs[0].content:
return resp.msgs[0].content
return "I understand your question, but I'm having trouble generating a response right now."
105 changes: 103 additions & 2 deletions backend/app/agent/factory/task_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,115 @@
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========

import logging

from camel.tasks import Task

from app.agent.agent_model import agent_model
from app.agent.prompt import TASK_SUMMARY_SYS_PROMPT
from app.agent.prompt import (
SUBTASKS_SUMMARY_PROMPT,
TASK_SUMMARY_PROMPT,
TASK_SUMMARY_SYS_PROMPT,
)
from app.model.chat import Chat

logger = logging.getLogger(__name__)


def task_summary_agent(options: Chat):
def _create_summary_agent(options: Chat):
"""Create a task summary agent."""
return agent_model(
"task_summary_agent",
TASK_SUMMARY_SYS_PROMPT,
options,
)


async def summary_task(task: Task, options: Chat) -> str:
"""Generate a short name and summary for a task."""
agent = _create_summary_agent(options)
prompt = TASK_SUMMARY_PROMPT.format(task_string=task.to_string())
logger.debug("Generating task summary", extra={"task_id": task.id})
try:
res = agent.step(prompt)
summary = res.msgs[0].content
logger.info("Task summary generated", extra={"summary": summary})
return summary
except Exception as e:
logger.error(
"Error generating task summary",
extra={"error": str(e)},
exc_info=True,
)
raise


async def summary_subtasks_result(task: Task, options: Chat) -> str:
"""Summarize the aggregated results from all subtasks.

Args:
task: The main task containing subtasks and their aggregated results
options: Chat options for creating the summary agent

Returns:
A concise summary of all subtask results
"""
agent = _create_summary_agent(options)

subtasks_info = ""
for i, subtask in enumerate(task.subtasks, 1):
subtasks_info += f"\n**Subtask {i}**\n"
subtasks_info += f"Description: {subtask.content}\n"
subtasks_info += f"Result: {subtask.result or 'No result'}\n"
subtasks_info += "---\n"

prompt = SUBTASKS_SUMMARY_PROMPT.format(
task_content=task.content, subtasks_info=subtasks_info
)

res = agent.step(prompt)
summary = res.msgs[0].content

logger.info(
"Generated subtasks summary for "
f"task {task.id} with "
f"{len(task.subtasks)} subtasks"
)

return summary


async def get_task_result_with_optional_summary(
task: Task, options: Chat
) -> str:
"""Get the task result, with LLM summary if there are multiple subtasks.

Args:
task: The task to get result from
options: Chat options for creating summary agent

Returns:
The task result (summarized if multiple subtasks, raw otherwise)
"""
result = str(task.result or "")

if task.subtasks and len(task.subtasks) > 1:
logger.info(
f"Task {task.id} has "
f"{len(task.subtasks)} subtasks, "
"generating summary"
)
try:
summarized_result = await summary_subtasks_result(task, options)
result = summarized_result
logger.info(f"Successfully generated summary for task {task.id}")
except Exception as e:
logger.error(f"Failed to generate summary for task {task.id}: {e}")
elif task.subtasks and len(task.subtasks) == 1:
logger.info(f"Task {task.id} has only 1 subtask, skipping LLM summary")
if result and "--- Subtask" in result and "Result ---" in result:
parts = result.split("Result ---", 1)
if len(parts) > 1:
result = parts[1].strip()

return result
Loading