Skip to content
Merged
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
152 changes: 96 additions & 56 deletions agents/matmaster_agent/flow_agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,51 @@
ChatAgentGlobalInstruction,
ChatAgentInstruction,
)
from agents.matmaster_agent.flow_agents.constant import MATMASTER_FLOW
from agents.matmaster_agent.flow_agents.execution_agent.agent import (
MatMasterSupervisorAgent,
)
from agents.matmaster_agent.flow_agents.expand_agent.agent import ExpandAgent
from agents.matmaster_agent.flow_agents.expand_agent.constant import EXPAND_AGENT
from agents.matmaster_agent.flow_agents.expand_agent.prompt import EXPAND_INSTRUCTION
from agents.matmaster_agent.flow_agents.expand_agent.schema import ExpandSchema
from agents.matmaster_agent.flow_agents.handle_upload_agent.agent import (
HandleUploadAgent,
)
from agents.matmaster_agent.flow_agents.intent_agent.constant import INTENT_AGENT
from agents.matmaster_agent.flow_agents.intent_agent.model import IntentEnum
from agents.matmaster_agent.flow_agents.intent_agent.prompt import INTENT_INSTRUCTION
from agents.matmaster_agent.flow_agents.intent_agent.schema import IntentSchema
from agents.matmaster_agent.flow_agents.plan_confirm_agent.constant import (
PLAN_CONFIRM_AGENT,
)
from agents.matmaster_agent.flow_agents.plan_confirm_agent.prompt import (
PlanConfirmInstruction,
)
from agents.matmaster_agent.flow_agents.plan_confirm_agent.schema import (
PlanConfirmSchema,
)
from agents.matmaster_agent.flow_agents.plan_info_agent.callback import (
filter_plan_info_llm_contents,
)
from agents.matmaster_agent.flow_agents.plan_info_agent.prompt import (
get_plan_info_instruction,
PLAN_INFO_INSTRUCTION,
)
from agents.matmaster_agent.flow_agents.plan_info_agent.schema import PlanInfoSchema
from agents.matmaster_agent.flow_agents.plan_make_agent.agent import PlanMakeAgent
from agents.matmaster_agent.flow_agents.plan_make_agent.callback import (
filter_plan_make_llm_contents,
)
from agents.matmaster_agent.flow_agents.plan_make_agent.prompt import (
get_plan_make_instruction,
)
from agents.matmaster_agent.flow_agents.plan_make_agent.schema import (
create_dynamic_multi_plans_schema,
)
from agents.matmaster_agent.flow_agents.scene_agent.constant import SCENE_AGENT
from agents.matmaster_agent.flow_agents.scene_agent.prompt import SCENE_INSTRUCTION
from agents.matmaster_agent.flow_agents.scene_agent.schema import SceneSchema
from agents.matmaster_agent.flow_agents.schema import FlowStatusEnum, PlanSchema
from agents.matmaster_agent.flow_agents.schema import FlowStatusEnum
from agents.matmaster_agent.flow_agents.step_title_agent.callback import (
filter_llm_contents,
)
Expand All @@ -75,7 +92,6 @@
)
from agents.matmaster_agent.flow_agents.utils import (
check_plan,
create_dynamic_plan_schema,
get_tools_list,
should_bypass_confirmation,
)
Expand Down Expand Up @@ -129,7 +145,7 @@ def after_init(self):
)

self._intent_agent = DisallowTransferAndContentLimitSchemaAgent(
name='intent_agent',
name=INTENT_AGENT,
model=MatMasterLlmConfig.tool_schema_model,
description='识别用户的意图',
instruction=INTENT_INSTRUCTION,
Expand All @@ -138,7 +154,7 @@ def after_init(self):
)

self._expand_agent = ExpandAgent(
name='expand_agent',
name=EXPAND_AGENT,
model=MatMasterLlmConfig.tool_schema_model,
description='扩写用户的问题',
instruction=EXPAND_INSTRUCTION,
Expand All @@ -147,7 +163,7 @@ def after_init(self):
)

self._scene_agent = DisallowTransferAndContentLimitSchemaAgent(
name='scene_agent',
name=SCENE_AGENT,
model=MatMasterLlmConfig.tool_schema_model,
description='把用户的问题划分到特定的场景',
instruction=SCENE_INSTRUCTION,
Expand All @@ -159,24 +175,28 @@ def after_init(self):
name='plan_make_agent',
model=MatMasterLlmConfig.tool_schema_model,
description='根据用户的问题依据现有工具执行计划,如果没有工具可用,告知用户,不要自己制造工具或幻想',
output_schema=PlanSchema,
state_key='plan',
state_key='multi_plans',
before_model_callback=filter_plan_make_llm_contents,
)

self._plan_confirm_agent = DisallowTransferAndContentLimitSchemaAgent(
name='plan_confirm_agent',
name=PLAN_CONFIRM_AGENT,
model=MatMasterLlmConfig.tool_schema_model,
description='判断用户对计划是否认可',
instruction=PlanConfirmInstruction,
output_schema=PlanConfirmSchema,
state_key='plan_confirm',
)

self._plan_info_agent = DisallowTransferAndContentLimitLlmAgent(
self._plan_info_agent = DisallowTransferAndContentLimitSchemaAgent(
name='plan_info_agent',
model=MatMasterLlmConfig.default_litellm_model,
model=MatMasterLlmConfig.tool_schema_model,
global_instruction=GLOBAL_INSTRUCTION,
description='根据 materials_plan 返回的计划进行总结',
# instruction=PLAN_INFO_INSTRUCTION,
instruction=PLAN_INFO_INSTRUCTION,
output_schema=PlanInfoSchema,
state_key='plan_info',
before_model_callback=filter_plan_info_llm_contents,
)

# execution_agent
Expand Down Expand Up @@ -391,6 +411,7 @@ async def _run_async_impl(
) in self.plan_confirm_agent.run_async(ctx):
yield plan_confirm_event

# 用户说确认计划,但 plan_confirm 误判为 False
if ctx.user_content.parts[
0
].text == '确认计划' and not ctx.session.state[
Expand All @@ -404,8 +425,28 @@ async def _run_async_impl(
yield update_state_event(
ctx, state_delta={'plan_confirm': {'flag': True}}
)
# 没有计划,但 plan_confirm 误判为 True
elif ctx.session.state['plan_confirm'].get(
'flag', False
) and not ctx.session.state.get('multi_plans', {}):
logger.warning(
f'{ctx.session.id} plan_confirm = True, but no multi_plans, manually setting plan_confirm -> False'
)
yield update_state_event(
ctx, state_delta={'plan_confirm': {'flag': False}}
)

plan_confirm = ctx.session.state['plan_confirm'].get('flag', False)
if plan_confirm:
selected_plan_id = ctx.session.state['plan_confirm'][
'selected_plan_id'
]
selected_plan = ctx.session.state['multi_plans']['plans'][
selected_plan_id
]
yield update_state_event(
ctx, state_delta={'plan': selected_plan}
)

# 判断要不要制定计划(1. 无计划;2. 计划未通过;3. 计划已完成)
if (
Expand Down Expand Up @@ -444,8 +485,8 @@ async def _run_async_impl(
+ UPDATE_USER_CONTENT
+ TOOLCHAIN_EXAMPLES_PROMPT
)
self.plan_make_agent.output_schema = create_dynamic_plan_schema(
available_tools
self.plan_make_agent.output_schema = (
create_dynamic_multi_plans_schema(available_tools)
)
async for plan_event in self.plan_make_agent.run_async(ctx):
yield plan_event
Expand All @@ -454,7 +495,7 @@ async def _run_async_impl(
for matmaster_flow_event in context_function_event(
ctx,
self.name,
'matmaster_flow',
MATMASTER_FLOW,
None,
ModelRole,
{
Expand All @@ -466,23 +507,38 @@ async def _run_async_impl(
},
):
yield matmaster_flow_event
plan_steps = ctx.session.state['plan'].get('steps', [])
tool_names = [
step.get('tool_name')
for step in plan_steps
if step.get('tool_name')
]
self.plan_info_agent.instruction = get_plan_info_instruction(
tool_names
)
async for plan_summary_event in self.plan_info_agent.run_async(
ctx
):
yield plan_summary_event
plan_info = ctx.session.state['plan_info']
intro = plan_info['intro']
plans = plan_info['plans']
overall = plan_info['overall']

for matmaster_flow_plans_event in context_function_event(
ctx,
self.name,
'matmaster_flow_plans',
None,
ModelRole,
{
'plans_result': json.dumps(
{
'invocation_id': ctx.invocation_id,
'intro': intro,
'plans': plans,
'overall': overall,
}
)
},
):
yield matmaster_flow_plans_event

for matmaster_flow_event in context_function_event(
ctx,
self.name,
'matmaster_flow',
MATMASTER_FLOW,
None,
ModelRole,
{
Expand All @@ -496,16 +552,21 @@ async def _run_async_impl(
yield matmaster_flow_event

# 更新计划为可执行的计划
update_plan = copy.deepcopy(ctx.session.state['plan'])
origin_steps = ctx.session.state['plan']['steps']
actual_steps = []
for step in origin_steps:
if step.get('tool_name'):
actual_steps.append(step)
else:
break
update_plan['steps'] = actual_steps
yield update_state_event(ctx, state_delta={'plan': update_plan})
update_multi_plans = copy.deepcopy(
ctx.session.state['multi_plans']
)
for update_plan in update_multi_plans['plans']:
origin_steps = update_plan['steps']
actual_steps = []
for step in origin_steps:
if step.get('tool_name'):
actual_steps.append(step)
else:
break
update_plan['steps'] = actual_steps
yield update_state_event(
ctx, state_delta={'multi_plans': update_multi_plans}
)

# 检查是否应该跳过用户确认步骤
if should_bypass_confirmation(ctx):
Expand All @@ -519,27 +580,6 @@ async def _run_async_impl(
}
},
)
else:
for generate_plan_confirm_event in context_function_event(
ctx,
self.name,
'matmaster_generate_follow_up',
{},
ModelRole,
{
'follow_up_result': json.dumps(
{
'invocation_id': ctx.invocation_id,
'title': i18n.t('PlanOperation'),
'list': [
i18n.t('ConfirmPlan'),
i18n.t('RePlan'),
],
}
),
},
):
yield generate_plan_confirm_event

# 计划未确认,暂停往下执行
if ctx.session.state['plan_confirm']['flag']:
Expand Down
1 change: 1 addition & 0 deletions agents/matmaster_agent/flow_agents/constant.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
MATMASTER_SUPERVISOR_AGENT = 'matmaster_supervisor_agent'
MATMASTER_FLOW = 'matmaster_flow'
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXPAND_AGENT = 'expand_agent'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INTENT_AGENT = 'intent_agent'
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PLAN_CONFIRM_AGENT = 'plan_confirm_agent'
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@
**Output Format:** Return a valid JSON object with the following structure:
{{
"flag": true | false,
"selected_plan_id": 0,
"reason": "A concise explanation citing the specific words or phrases from the user's response that led to this judgment."
}}

**Critical Instructions:**
- Your analysis should be reasonable but strict. Assume lack of approval unless there is clear indication of acceptance.
- `selected_plan_id` must be a **0-based index** into the presented plans:
- If the user says "方案1" / "plan 1" / "option 1", then `selected_plan_id` = 0
- If the user says "方案2" / "plan 2" / "option 2", then `selected_plan_id` = 1
- And so on.
- If `flag` is `false` or no plan is clearly selected, set `selected_plan_id` to 0.
- Return **only** the raw JSON object. Do not include any other text, commentary, or formatting outside the JSON structure.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

class PlanConfirmSchema(BaseModel):
flag: bool
selected_plan_id: int
reason: str
55 changes: 55 additions & 0 deletions agents/matmaster_agent/flow_agents/plan_info_agent/callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import logging
from typing import Optional

from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmRequest, LlmResponse
from google.genai.types import Content, Part

from agents.matmaster_agent.constant import MATMASTER_AGENT_NAME, ModelRole
from agents.matmaster_agent.flow_agents.constant import MATMASTER_FLOW
from agents.matmaster_agent.flow_agents.expand_agent.constant import EXPAND_AGENT
from agents.matmaster_agent.flow_agents.intent_agent.constant import INTENT_AGENT
from agents.matmaster_agent.flow_agents.plan_confirm_agent.constant import (
PLAN_CONFIRM_AGENT,
)
from agents.matmaster_agent.flow_agents.scene_agent.constant import SCENE_AGENT
from agents.matmaster_agent.flow_agents.utils import is_content_has_keywords
from agents.matmaster_agent.logger import PrefixFilter

logger = logging.getLogger(__name__)
logger.addFilter(PrefixFilter(MATMASTER_AGENT_NAME))
logger.setLevel(logging.INFO)


async def filter_plan_info_llm_contents(
callback_context: CallbackContext, llm_request: LlmRequest
) -> Optional[LlmResponse]:
contents = []
for content in llm_request.contents[::-1]:
if is_content_has_keywords(
content,
[
PLAN_CONFIRM_AGENT,
SCENE_AGENT,
INTENT_AGENT,
EXPAND_AGENT.replace('_agent', '_schema'),
MATMASTER_FLOW,
],
):
continue
else:
contents.insert(0, content)

logger.info(
f'{callback_context.session.id} {callback_context.agent_name} contents = {contents}'
)

if not contents:
contents = [
Content(
role=ModelRole,
parts=[Part(text='Default Text')],
)
]

llm_request.contents = contents
Loading
Loading