diff --git a/python/configs/agents/super_agent.yaml b/python/configs/agents/super_agent.yaml index cb24432a7..fc5b336cd 100644 --- a/python/configs/agents/super_agent.yaml +++ b/python/configs/agents/super_agent.yaml @@ -26,7 +26,7 @@ models: # Provider-specific model mappings for fallback # Used when primary provider fails and fallback is attempted provider_models: - siliconflow: "zai-org/GLM-4.6" # Similar capability to Claude Haiku + siliconflow: "deepseek-ai/DeepSeek-V3.1-Terminus" # Similar capability to Claude Haiku google: "gemini-2.5-flash" # Fast and efficient like Claude Haiku # Environment Variable Overrides diff --git a/python/valuecell/core/coordinate/orchestrator.py b/python/valuecell/core/coordinate/orchestrator.py index 398cf608a..425bcfc92 100644 --- a/python/valuecell/core/coordinate/orchestrator.py +++ b/python/valuecell/core/coordinate/orchestrator.py @@ -298,7 +298,7 @@ async def _handle_new_request( super_outcome: SuperAgentOutcome = await self.super_agent_service.run( user_input ) - if super_outcome.decision == SuperAgentDecision.ANSWER: + if super_outcome.answer_content: ans = self.event_service.factory.message_response_general( StreamResponseEvent.MESSAGE_CHUNK, conversation_id, @@ -308,6 +308,7 @@ async def _handle_new_request( agent_name=self.super_agent_service.name, ) yield await self.event_service.emit(ans) + if super_outcome.decision == SuperAgentDecision.ANSWER: return if super_outcome.decision == SuperAgentDecision.HANDOFF_TO_PLANNER: diff --git a/python/valuecell/core/plan/planner.py b/python/valuecell/core/plan/planner.py index 4ed44abd3..081a1f7dc 100644 --- a/python/valuecell/core/plan/planner.py +++ b/python/valuecell/core/plan/planner.py @@ -95,6 +95,7 @@ def __init__( tools=[ # TODO: enable UserControlFlowTools when stable # UserControlFlowTools(), + self.tool_get_agent_description, self.tool_get_enabled_agents, ], debug_mode=agent_debug_mode_enabled(), diff --git a/python/valuecell/core/plan/prompts.py b/python/valuecell/core/plan/prompts.py index 20dfab20f..f08fa0dc8 100644 --- a/python/valuecell/core/plan/prompts.py +++ b/python/valuecell/core/plan/prompts.py @@ -17,21 +17,39 @@ - If `target_agent_name` is provided, use it as-is without validation. - Create exactly one task with the user's query unchanged and set `pattern` to `once` by default. +1a) Non-interference bias +- Minimize intervention. Prefer pass-through over rewording the user's query. +- Only pause or add guidance when strictly necessary (e.g., explicit schedule confirmation or missing schedule details). +- Avoid paternalistic tone; keep guidance concise and action-oriented. + 2) Agent selection when missing - If `target_agent_name` is missing or empty, call `tool_get_enabled_agents`, compare each agent's Description and Available Skills with the query, and pick the clearest match. -- If no agent clearly fits (confidence < 70%), fall back to "ResearchAgent". - Do not split into multiple tasks. +- agent_name MUST be valid: either exactly the provided `target_agent_name` or one selected from the set returned by `tool_get_enabled_agents`. Never invent new agent names. + +2a) Capability-aware scheduling decisions +- Use `tool_get_agent_description(agent_name)` to understand an agent's capabilities. +- Only consider proposing or creating recurring/scheduled tasks when the chosen agent clearly supports monitoring/notifications/push capabilities. + Examples of indicative signals include skill ids/names/tags like: `monitor`, `monitoring`, `alert`, `notify`, `notifications`, `push`, `push_notifications`, `scheduled`, `schedule`, `tracking`. +- Be robust to format variations: do not rely on a specific JSON shape or exact field names in descriptions/examples. Interpret semantically; the agent card format can vary. +- Prefer minimal intervention: if scheduling is not clearly supported, treat as a normal one-time task and avoid suggesting recurring flows. + - Do NOT delegate schedule creation to remote agents. Scheduling is orchestrated centrally by the Planner/system. Remote agents should only perform their task per invocation; do not ask them to "set up schedules" or "create alerts" themselves. 3) Special handling: recurring/scheduled intent only - Detect if the user's input suggests recurring monitoring or a schedule (e.g., "every hour", "daily at 9 AM"). -- If recurring intent is detected without a specific schedule, return `adequate: false` and ask the user to choose: one-time vs recurring; if recurring, ask for a concrete schedule (interval or daily time). -- If a specific schedule is present, you MUST ask for confirmation first by returning `adequate: false` with a concise `guidance_message` describing the task and the schedule. +- If recurring intent is detected without a specific schedule, proceed with a one-time task (`pattern: once`) and optionally add a brief note suggesting the user provide a schedule (interval or daily time) if they want recurring. Do not pause the flow. +- If a specific schedule is present: + * If the user's message already contains explicit confirmation (e.g., "confirm", "confirmed", "yes", "ok", "proceed", or CJK equivalents like "确认/已确认/好的/好/可以/行"), treat the schedule as confirmed and proceed without pausing. + * Otherwise, ask for confirmation by returning `adequate: false` with a concise `guidance_message` describing the task and the schedule. - After user confirms: * Retrieve the original query from conversation history * Transform it into single-execution form by removing schedule phrases and notification verbs (e.g., "notify/alert/remind") and converting to a direct action * Extract schedule to `schedule_config` separate from the query * Set `pattern` to `recurring` - CRITICAL: Do NOT create recurring tasks without an explicit schedule. If the user confirms recurring but provides no schedule, ask for the specific interval or daily time. + - If the selected agent does NOT advertise monitoring/notification/push capabilities, do not suggest or create recurring tasks; proceed as a one-time task. Optionally suggest switching to an agent that supports monitoring if the user wants recurring—but do not pause. + - Reserve `adequate: false` strictly for confirming explicit schedules. + - Never instruct subagents to "set up a schedule" or "enable alerts". Keep scheduling in `schedule_config` and pass subagents a direct, single-execution query. 4) Schedule configuration rules - Intervals: map phrases like "every hour", "every 30 minutes" to `schedule_config.interval_minutes`. @@ -42,12 +60,18 @@ - Short/contextual replies (e.g., "Go on", "tell me more") and user preferences/rules should be forwarded unchanged as a single task. - Confirmation detection: * If the last planner response had `adequate: false` with a `guidance_message` asking for confirmation, treat replies like "yes/confirm/ok/proceed/确认/好/可以" as confirmations. + * Also detect inline confirmations in the same user message that specifies a schedule (e.g., "confirm setting daily at 09:00"). If present, proceed without pausing. * Retrieve the original query from conversation history to create the task; do not use the confirmation text as the task query. 6) Title and language - Titles must be concise: English ≤ 10 words; CJK (Chinese/Japanese/Korean) ≤ 20 characters. - Always respond in the user's language. Both `guidance_message` and `query` must use the user's language. + + +- tool_get_agent_description(agent_name): Returns a human-readable description or card for the agent. Interpret the result flexibly; prioritize the presence of relevant Skills. For scheduling decisions, only consider recurring/monitoring flows if Skills clearly indicate capabilities such as monitoring, alerts/notifications, push notifications, or tracking. Do not overfit to exact key names or rigid formats—trust the agent's documented capabilities and avoid unnecessary intervention. +- tool_get_enabled_agents(): Returns the set of enabled agents and their cards/descriptions. Use this when `target_agent_name` is missing to shortlist and choose the best-fit agent by semantically comparing the user's query with each agent's Skills/Description/Tags. Be format-agnostic (the card structure may vary); focus on meaning, not exact keys. Select a single clearest match. Do not split into multiple tasks. You MUST choose `agent_name` from this set (or use the provided `target_agent_name`). Never fabricate agent names. + """ PLANNER_EXPECTED_OUTPUT = """ @@ -57,14 +81,16 @@ - Transparent proxy by default: create a single task with the original query unchanged when a target agent is specified or when no scheduling is involved. - Set `pattern` to `once` by default; only set `pattern` to `recurring` after the user explicitly confirms a schedule. - Provide a concise `title` (English ≤ 10 words, CJK ≤ 20 characters). -- Agent selection: use provided `target_agent_name` or select via `tool_get_enabled_agents` when missing; fall back to "ResearchAgent" if unclear. +- Agent selection: use provided `target_agent_name` or select via `tool_get_enabled_agents` when missing - For scheduled/recurring tasks after confirmation: transform the query into single-execution form (remove time phrases and notification verbs) and put timing into `schedule_config`. + - Only propose or create recurring tasks when the chosen agent's Skills indicate monitoring/alerts/notifications/push/tracking capabilities (as discovered via `tool_get_agent_description`). Otherwise, default to a one-time task and avoid suggesting recurring flows. + - Use `adequate: false` only to confirm explicit schedules. In all other cases, proceed with best effort and keep `adequate: true`. + - Do not instruct the downstream agent to configure schedules/alerts. Represent timing exclusively via `schedule_config`, and keep the agent's `query` as a single-execution action. -- Recurring intent without schedule → `adequate: false` with a short question asking one-time vs recurring; if recurring, ask for interval (minutes) or daily time (HH:MM 24h). -- Explicit schedule present → `adequate: false` and ask the user to confirm the described schedule before creating the task. -- When `adequate: false`, always provide a clear `guidance_message` in the user's language. + - Explicit schedule present → If the user's message already contains explicit confirmation (e.g., "confirm/confirmed/yes/ok/proceed" or CJK equivalents like "确认/已确认/好的/好/可以/行"), skip the pause and proceed. Otherwise, return `adequate: false` and ask the user to confirm the described schedule before creating the task. + - When `adequate: false`, always provide a clear `guidance_message` in the user's language. - Keep the `guidance_message` short, in the user's language. Example template (translate as needed): @@ -78,8 +104,7 @@ Output valid JSON only (no markdown, backticks, or comments): -{ - "tasks": [ + { "title": "Short task title", "query": "Original user query (unchanged for normal; transformed after schedule confirmation)", @@ -154,10 +179,17 @@ Output: { - "tasks": [], - "adequate": false, - "reason": "Recurring intent requires schedule.", - "guidance_message": "Would you like a one-time analysis or recurring monitoring? If recurring, please specify how often (e.g., every hour or daily at HH:MM)." + "tasks": [ + { + "title": "Apple earnings monitor", + "query": "Monitor Apple's quarterly earnings", + "agent_name": "ResearchAgent", + "pattern": "once" + } + ], + "adequate": true, + "reason": "No schedule provided; proceed with a one-time task. The user can provide a specific schedule later to enable recurring.", + "guidance_message": "If you'd like recurring monitoring, please specify an interval (e.g., every 60 minutes) or a daily time (HH:MM)." } Input: @@ -199,6 +231,32 @@ } + +Input: +{ + "target_agent_name": "ResearchAgent", + "query": "Confirm setting daily at 09:00 to monitor Apple's quarterly earnings" +} + +Output: +{ + "tasks": [ + { + "title": "Apple earnings monitor", + "query": "Monitor Apple's quarterly earnings", + "agent_name": "ResearchAgent", + "pattern": "recurring", + "schedule_config": { + "interval_minutes": null, + "daily_time": "09:00" + } + } + ], + "adequate": true, + "reason": "Inline confirmation detected; schedule confirmed without pausing." +} + + Input: { @@ -249,9 +307,9 @@ Output: { "tasks": [], - "adequate": false, - "reason": "Illegal activity.", - "guidance_message": "I cannot assist with illegal activities such as unauthorized access to accounts." + "adequate": true, + "reason": "Request involves illegal activity; offering safe alternatives instead.", + "guidance_message": "I can’t assist with unauthorized access. If your goal is account security, I can help with safe alternatives like securing accounts, password hygiene, and recognizing phishing. Would you like to proceed with that?" } diff --git a/python/valuecell/core/super_agent/prompts.py b/python/valuecell/core/super_agent/prompts.py index 83fd626d6..5d4d71243 100644 --- a/python/valuecell/core/super_agent/prompts.py +++ b/python/valuecell/core/super_agent/prompts.py @@ -16,11 +16,13 @@ -- Do your best to satisfy the user's request. Avoid saying "can't". +- Do your best to satisfy the user's request. Never use defeatist wording like "can't" or "cannot". - Be factual and concise. Do not hallucinate or include unrelated content. +- Base answers strictly on facts available in your current context. Do not claim to have performed actions (e.g., fetched data, called tools/APIs, ran calculations) that you did not actually perform. If external data or tools are needed, choose HANDOFF_TO_PLANNER. - If some details are missing but a safe default leads to a useful answer, proceed with a brief assumption note (e.g., "Assuming latest period..."). -- If a safe and useful direct answer is not possible, choose HANDOFF_TO_PLANNER with a short reason and a clear `enriched_query` that preserves the user's intent. +- If a safe and useful direct answer is not possible, confidently choose HANDOFF_TO_PLANNER with a short reason and a clear `enriched_query` that preserves the user's intent. Frame the handoff positively (e.g., "Handing this to the Planner to route to the right specialist agent"). - Always respond in the user's language. +- Do not hijack Planner-driven confirmations (e.g., schedule confirmations). When users provide or confirm schedules, forward that intent to the Planner via `handoff_to_planner` with an `enriched_query` that preserves the schedule and confirmation. @@ -32,11 +34,13 @@ - Only answer when you're confident the user expects an immediate short reply without additional tooling. - Provide best-effort, concise, and directly relevant answers. If you use a reasonable default, state it briefly. - Never use defeatist phrasing (e.g., "I can't"). If uncertain or unsafe, handoff_to_planner instead of refusing. +- Do not imply that you accessed live/updated data or executed tools. If the request needs current data or external retrieval, handoff_to_planner. 3) Handoff policy - If the question is complex, ambiguous, requires multi-step reasoning, external tools, or specialized agents, choose handoff_to_planner. - When handing off, return an `enriched_query` that succinctly restates the user's intent. Do not invent details. -- If your own capability is insufficient to answer safely and directly, handoff_to_planner. +- If your own capability is insufficient to answer safely and directly, handoff_to_planner. Do not say "cannot"; instead, communicate confidence in routing to specialized agents via the Planner. +- If the user includes scheduling details or explicit confirmation (e.g., "confirm setting daily at 09:00"), do not handle confirmation yourself; route the confirmation and schedule details to the Planner in the `enriched_query`. 4) No clarification rounds - Do not ask the user for more information. If the prompt is insufficient for a safe and useful answer, HANDOFF_TO_PLANNER with a short reason. @@ -66,6 +70,67 @@ - When decision == "handoff_to_planner": prefer including `enriched_query` that preserves the user intent. - Keep `reason` short and helpful. - Always generate `answer_content` and `enriched_query` in the user's language. Detect language from the user's query if no explicit locale is provided. -- Avoid defeatist phrasing like "I can't"; either provide a concise best-effort answer or hand off with a clear reason. +- Avoid defeatist phrasing like "I can't" or "I cannot"; either provide a concise best-effort answer or hand off with a clear, confident routing reason (e.g., "Routing to Planner to select the best specialist agent"). + - Ensure `answer_content` only contains information you could produce without external tools or retrieval. If not possible, choose `handoff_to_planner`. + + + + +Input: +{ + "query": "What is 2 + 2?" +} + +Output: +{ + "decision": "answer", + "answer_content": "4", + "reason": "Simple, factual question that can be answered directly." +} + + + +Input: +{ + "query": "Monitor Tesla SEC filings and alert me daily at 09:00 with a short summary." +} + +Output: +{ + "decision": "handoff_to_planner", + "enriched_query": "Monitor Tesla (TSLA) SEC filings and provide a brief daily 09:00 summary with alerts.", + "reason": "Routing to Planner to select the best specialist monitoring agent." +} + + + +Input: +{ + "query": "Compare AAPL vs MSFT performance and valuation over the last quarter and recommend which looks better." +} + +Output: +{ + "decision": "handoff_to_planner", + "enriched_query": "Compare Apple (AAPL) and Microsoft (MSFT) performance and valuation over the last quarter, then provide a concise recommendation with key metrics.", + "reason": "Requires multi-step analysis and tools; routing to Planner to engage the right specialist agents." +} + + + +Input: +{ + "query": "Confirm setting daily at 09:00 to monitor Tesla price and alert me" +} + +Output: +{ + "decision": "handoff_to_planner", + "enriched_query": "Confirm daily 09:00 schedule to monitor Tesla (TSLA) price and send an alert", + "reason": "Forwarding schedule confirmation to Planner to manage recurring task setup." +} + + + """ diff --git a/python/valuecell/core/task/executor.py b/python/valuecell/core/task/executor.py index 53759ba75..11c57c3ee 100644 --- a/python/valuecell/core/task/executor.py +++ b/python/valuecell/core/task/executor.py @@ -122,6 +122,7 @@ async def execute_plan( thread_id=thread_id, task_id=generate_task_id(), content=plan.guidance_message, + agent_name="Planner", ) yield await self._event_service.emit(response) return