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
32 changes: 13 additions & 19 deletions agents/matmaster_agent/base_agents/job_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.agents.invocation_context import InvocationContext
from google.adk.events import Event, EventActions
from pydantic import Field, BaseModel
from pydantic import Field

from agents.matmaster_agent.base_agents.callback import check_before_tool_callback_effect, default_before_tool_callback, \
catch_before_tool_callback_error, check_job_create, inject_username_ticket, inject_ak_projectId, \
Expand All @@ -32,7 +32,7 @@
get_BohriumStorage,
get_DFlowExecutor, OpenAPIJobAPI,
)
from agents.matmaster_agent.model import BohrJobInfo, DFlowJobInfo
from agents.matmaster_agent.model import BohrJobInfo, DFlowJobInfo, ParamsCheckComplete
from agents.matmaster_agent.prompt import (
ResultCoreAgentDescription,
SubmitRenderAgentDescription, gen_submit_core_agent_description, gen_submit_core_agent_instruction,
Expand Down Expand Up @@ -217,12 +217,6 @@ async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event,
yield error_event


class ParamsCheckComplete(BaseModel):
flag: bool
reason: str
analyzed_message: str


class ParamsCheckCompletedAgent(LlmAgent):
pass

Expand Down Expand Up @@ -637,7 +631,7 @@ async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event,
): # Only Query Job Result
pass
else:
cherry_pick_parts = cherry_pick_events(ctx)[:5]
cherry_pick_parts = cherry_pick_events(ctx)[-5:]
context_messages = '\n'.join([f'<{item[0].title()}> said: \n{item[1]}\n' for item in cherry_pick_parts])
logger.info(f"[BaseAsyncJobAgent] context_messages = {context_messages}")

Expand All @@ -647,18 +641,18 @@ async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event,
params_check_completed_json: dict = json.loads(response.choices[0].message.content)
params_check_completed = params_check_completed_json['flag']
params_check_reason = params_check_completed_json['reason']
params_check_msg = params_check_completed_json['analyzed_message']
params_check_msg = params_check_completed_json['analyzed_messages']

# 包装成function_call,来避免在历史记录中展示;同时模型可以在上下文中感知
for params_check_reason_event in context_function_event(ctx, self.name,
'system_params_check_result',
{'complete': params_check_completed,
'reason': params_check_reason,
'analyzed_messages': params_check_msg},
ModelRole):
yield params_check_reason_event

if not params_check_completed:
# Tell User Why Params Check Uncompleted
# 包装成function_call,来避免在历史记录中展示;同时模型可以在上下文中感知
for params_check_reason_event in context_function_event(ctx, self.name,
'system_params_check_block_reason',
{'reason': params_check_reason,
'analyzed_message': params_check_msg},
ModelRole):
yield params_check_reason_event

# Call ParamsCheckInfoAgent to generate params needing check
async for params_check_info_event in self.params_check_info_agent.run_async(ctx):
yield params_check_info_event
Expand Down
8 changes: 5 additions & 3 deletions agents/matmaster_agent/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,15 @@ async def matmaster_check_transfer(callback_context: CallbackContext, llm_respon
result: dict = json.loads(response.choices[0].message.content)
is_transfer = bool(result.get('is_transfer', False))
target_agent = str(result.get('target_agent', ''))

reason = str(result.get('reason', ''))
logger.info(f"[matmaster_check_transfer] target_agent = {target_agent}, is_transfer = {is_transfer}"
f"response_text = {llm_response.content.parts[0].text}, reason = {reason}")
if (
is_transfer and
not has_function_call(llm_response)
):
logger.warning(f"[matmaster_check_transfer] target_agent = {target_agent}")
function_call_id = f"call_{str(uuid.uuid4()).replace('-', '')[:24]}"
logger.warning(f"[matmaster_check_transfer] add `transfer_to_agent`")
function_call_id = f"added_{str(uuid.uuid4()).replace('-', '')[:24]}"
llm_response.content.parts.append(Part(function_call=FunctionCall(id=function_call_id, name='transfer_to_agent',
args={'agent_name': target_agent})))

Expand Down
7 changes: 7 additions & 0 deletions agents/matmaster_agent/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,16 @@ class TargetAgentEnum(str, Enum):
TrajAnalysisAgent = TrajAnalysisAgentName


class ParamsCheckComplete(BaseModel):
flag: bool
reason: str
analyzed_messages: List[str]


class TransferCheck(BaseModel):
is_transfer: bool
target_agent: TargetAgentEnum
reason: str


class UserContent(BaseModel):
Expand Down
63 changes: 30 additions & 33 deletions agents/matmaster_agent/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,48 +668,35 @@ def gen_result_agent_description():

def gen_params_check_completed_agent_instruction():
return """
Your task is to determine if the parameters requiring user confirmation have been fully presented and a confirmation is being requested in that message.
Analyze the messages from `user` and `model` listed below (only listed Latest 5 messages):
Your task is to determine if the parameters requiring user confirmation have been fully presented and a confirmation has been confirmed in the `context messages`.
Analyze the `context_messages` from [User] and [Model] listed below (only listed Latest 5 messages):

Context Messages (Including User and Model Conversation. The most recent conversation is at the bottom):
------------------
{context_messages}
------------------

Your output MUST be a valid JSON object with the following structure:
{{
"flag": <boolean>,
"reason": <string>, // *Present reason if flag is False, else return empty string*
"analyzed_message": <string> // *Quote the specific message snippet that was analyzed to make this determination.*
"reason": <string>, // *A concise explanation of the reasoning behind the judgment, covering both positive and negative evidence found in the context messages. Return empty string only if there is absolutely no relevant content to analyze.*
"analyzed_message": List[<string>] // *Quote the key messages that were analyzed to make this determination.*
}}

Return `flag: true` ONLY IF ALL of the following conditions are met:
1. The message explicitly and finally lists all parameters that need user confirmation (e.g., element, structure type, dimensions).
2. The message's intent is to conclude the parameter collection phase and advance the conversation to the next step (typically, awaiting a "yes" or "no" response from the user to proceed with an action).
3. The message does not indicate that the parameter discussion is still ongoing (e.g., lacks phrases like "also need," "next, please provide," "what is the...").
1. The context messages explicitly and finally list all parameters that user confirmed (e.g., element, structure type, dimensions).
2. The context messages's intent is to conclude the parameter collection phase and advance the conversation to the next step.
3. The context messages does not indicate that the parameter discussion is still ongoing (e.g., lacks phrases like "also need," "next, please provide," "what is the...").

Return `flag: false` in ANY of these cases:
1. The message does not mention any specific parameters to confirm.
2. The message is asking for or soliciting new parameter information (e.g., "What element would you like?", "Please provide the lattice constant.").
3. The message states or implies that parameter collection is not yet finished and further questions will follow.
1. The context messages don't mention any specific parameters to confirm.
2. The context messages are asking for or soliciting new parameter information (e.g., "What element would you like?", "Please provide the lattice constant.").
3. The context messages state or imply that parameter collection is not yet finished and further questions will follow.
4. There are currently no parameters awaiting user confirmation.
* For any of these cases, the "reason" field must be populated with a concise explanation based on the violated condition(s).*

**语言要求 (Language Requirement):** 在输出JSON时,请观察对话上下文使用的主要语言。如果上下文主要是中文,那么`reason`字段必须用中文书写。如果上下文主要是英文或其他语言,则使用相应的语言。请确保语言选择与对话上下文保持一致。

**Critical Guidance:** The act of clearly listing parameters and explicitly asking for confirmation (e.g., "Please confirm the following parameters:...") is considered the completion of the parameter presentation task. Therefore, return `true` for the message where that request is made, NOT after the user has confirmed. Look for the most recent message where parameters are presented for confirmation, even if it's not the very last message.

**Examples:**
- Message: "Please confirm the following parameters to build the FCC copper crystal: Element: Copper (Cu), Structure: FCC, using default lattice parameters. Please confirm if this is correct?"
- **Analysis:** Parameters are explicitly listed (Cu, FCC), and a confirmation is requested to proceed. Collection is concluded.
- **Output:** {{"flag": true, "reason": "", "analyzed_message": "Please confirm the following parameters to build the FCC copper crystal: Element: Copper (Cu), Structure: FCC, using default lattice parameters. Please confirm if this is correct?"}}

- Message: "To build the crystal, what element should I use?"
- **Analysis:** This is a request for a new parameter, not a request for confirmation of existing ones. (Violates Condition 2 for 'true' / Matches Condition 2 for 'false')
- **Output (英文上下文):** {{"flag": false, "reason": "Message is soliciting new parameter information ('what element') rather than requesting confirmation.", "analyzed_message": "To build the crystal, what element should I use?"}}
- **Output (中文上下文):** {{"flag": false, "reason": "消息正在征求新的参数信息('使用什么元素'),而不是请求确认。", "analyzed_message": "To build the crystal, what element should I use?"}}

- Message: "Element is set to Copper. Now, what is the desired lattice constant?"
- **Analysis:** One parameter is noted, but the conversation is actively moving to collect the next parameter. Collection is not concluded. (Violates Condition 1 and 3 for 'true' / Matches Condition 3 for 'false')
- **Output (英文上下文):** {{"flag": false, "reason": "Parameter collection is not finished; the message is asking for the next parameter ('lattice constant').", "analyzed_message": "Element is set to Copper. Now, what is the desired lattice constant?"}}
- **Output (中文上下文):** {{"flag": false, "reason": "参数收集未完成;消息正在询问下一个参数('晶格常数')。", "analyzed_message": "Element is set to Copper. Now, what is the desired lattice constant?"}}
**Critical Guidance:** The act of clearly listing parameters and explicitly confirmed is considered the completion of the parameter presentation task. Therefore, return `true` for the message where that request is made, NOT after the user has confirmed.

Based on the rules above, output a JSON object.
"""
Expand Down Expand Up @@ -755,15 +742,25 @@ def get_transfer_check_prompt():
Provide your evaluation in the following JSON format:
{{
"is_transfer": <true or false>,
"target_agent": "xxx agent" (if transfer detected) or null (if no transfer)
"target_agent": "xxx agent" (if transfer detected) or null (if no transfer),
"reason": <string> // *A concise explanation of the reasoning behind the judgment, covering both positive and negative evidence found in the response text. Return empty string only if there is absolutely no relevant content to analyze.*
}}

Examples for reference:
- Case1 (false): "使用结构生成智能体(structure_generate_agent)根据用户要求创建 FCC Cu 的块体结构" - only mentions agent, no transfer action
- Case2 (true): "正在转移到structure_generate_agent进行结构生成" - explicit transfer action with target agent
- Case3 (true): "I will now use the structure_generate_agent to create the bulk structure" - immediate action with target agent
- Case4 (false): "Next I will generate the Pt bulk structure" - no agent transfer mentioned
- Case5 (true): `{{"agent_name":"traj_analysis_agent"}}` - explicit JSON object instructing transfer
- Case1 (false): "使用结构生成智能体(structure_generate_agent)根据用户要求创建 FCC Cu 的块体结构"
-> Reason: "Only mentions the agent's function but lacks any explicit transfer verbs or immediate action indicators."

- Case2 (true): "正在转移到structure_generate_agent进行结构生成"
-> Reason: "Contains explicit transfer phrase '正在转移到' (transferring to) followed by a clear target agent name."

- Case3 (true): "I will now use the structure_generate_agent to create the bulk structure"
-> Reason: "Uses immediate action indicator 'I will now use' followed by a specific agent name, demonstrating transfer intent."

- Case4 (false): "Next I will generate the Pt bulk structure"
-> Reason: "Describes a future action but does not mention any agent or transfer mechanism."

- Case5 (true): `{{"agent_name":"traj_analysis_agent"}}`
-> Reason: "Standalone JSON object with an 'agent_name' key is an explicit programmatic instruction to transfer."
"""


Expand Down
6 changes: 5 additions & 1 deletion agents/matmaster_agent/utils/event_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import traceback
import uuid
from typing import Iterable
Expand All @@ -10,6 +11,8 @@
from agents.matmaster_agent.constant import ModelRole
from agents.matmaster_agent.utils.helper_func import update_session_state

logger = logging.getLogger(__name__)


# event check funcs
def has_part(event: Event):
Expand Down Expand Up @@ -141,7 +144,7 @@ def context_function_response_event(ctx: InvocationContext, author: str, functio

def context_function_event(ctx: InvocationContext, author: str, function_call_name: str, response: Optional[dict],
role: str, args: Optional[dict] = None):
function_call_id = f"call_{str(uuid.uuid4()).replace('-', '')[:24]}"
function_call_id = f"added_{str(uuid.uuid4()).replace('-', '')[:24]}"
yield context_function_call_event(ctx, author, function_call_id, function_call_name, role, args)
yield context_function_response_event(ctx, author, function_call_id, function_call_name, response, role)

Expand All @@ -152,6 +155,7 @@ def context_multipart2function_event(ctx: InvocationContext, author: str, event:
yield from context_function_event(ctx, author, function_call_name, {'msg': part.text},
ModelRole)
elif part.function_call:
logger.warning(f"[context_multipart2function_event] function_name = {part.function_call.name}")
yield context_function_call_event(ctx, author, function_call_id=part.function_call.id,
function_call_name=part.function_call.name, role=ModelRole,
args=part.function_call.args)
Expand Down
Loading