Skip to content

Commit f8d2591

Browse files
authored
Merge pull request #718 from SchrodingersCattt/dev-gmy
fix: sanitize braces to avoid variable identification
2 parents 94ffcc2 + afce80b commit f8d2591

10 files changed

Lines changed: 119 additions & 6 deletions

File tree

agents/matmaster_agent/core_agents/comp_agents/recommend_summary_agent/tool_call_info_agent/prompt.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
from agents.matmaster_agent.utils.sanitize_braces import with_sanitized_braces
2+
3+
4+
@with_sanitized_braces('user_prompt', 'agent_prompt')
15
def gen_tool_call_info_instruction(
26
user_prompt, agent_prompt, tool_doc, tool_schema, tool_args_recommend_prompt
37
):
8+
user_prompt = user_prompt or ''
9+
agent_prompt = agent_prompt or ''
410
return f"""
511
You are an AI agent that matches user requests to available tools. Your task is to analyze the user's query against the complete parameter schema.
612

agents/matmaster_agent/flow_agents/agent.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
ReportUploadParams,
148148
upload_report_md_to_oss,
149149
)
150+
from agents.matmaster_agent.utils.sanitize_braces import sanitize_braces
150151

151152
logger = logging.getLogger(__name__)
152153
logger.addFilter(PrefixFilter(MATMASTER_AGENT_NAME))
@@ -370,9 +371,9 @@ async def _run_expand_agent(
370371
yield expand_event
371372

372373
async def _build_icl_prompt(self, ctx: InvocationContext):
373-
UPDATE_USER_CONTENT = (
374-
'\nUSER INPUT FOR THIS TASK:\n'
375-
+ ctx.session.state['expand']['update_user_content']
374+
raw_content = ctx.session.state['expand']['update_user_content']
375+
UPDATE_USER_CONTENT = '\nUSER INPUT FOR THIS TASK:\n' + sanitize_braces(
376+
raw_content
376377
)
377378
icl_update_examples = select_update_examples(
378379
ctx.session.state['expand']['update_user_content'],

agents/matmaster_agent/flow_agents/analysis_agent/prompt.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
from agents.matmaster_agent.utils.sanitize_braces import sanitize_braces
2+
3+
14
def get_analysis_instruction(plan):
5+
plan_str = plan if isinstance(plan, str) else str(plan)
6+
plan_str = sanitize_braces(plan_str)
27
return f"""
38
分析 plan 变量的结果和之前的对话历史,来对本次计划的执行进行总结后展示给用户:
49
510
<Plan Content>
6-
{plan}
11+
{plan_str}
712
813
<Plan Structure>
914
{{

agents/matmaster_agent/flow_agents/execution_agent/agent.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
context_function_event,
4646
update_state_event,
4747
)
48+
from agents.matmaster_agent.utils.sanitize_braces import sanitize_braces
4849

4950
logger = logging.getLogger(__name__)
5051
logger.addFilter(PrefixFilter(MATMASTER_AGENT_NAME))
@@ -211,8 +212,15 @@ async def _tool_result_validation(
211212
current_tool_description = ctx.session.state[PLAN]['steps'][index][
212213
STEP_DESCRIPTION
213214
]
215+
user_text = (
216+
ctx.user_content.parts[0].text
217+
if ctx.user_content and ctx.user_content.parts
218+
else ''
219+
)
220+
user_text = sanitize_braces(user_text)
221+
current_tool_description = sanitize_braces(current_tool_description or '')
214222
lines = (
215-
f"用户原始请求: {ctx.user_content.parts[0].text}",
223+
f"用户原始请求: {user_text}",
216224
f"当前步骤描述: {current_tool_description}",
217225
f"工具名称: {current_tool_name}",
218226
'请根据以上信息判断,工具的参数配置及对应的执行结果是否严格满足用户原始需求。',

agents/matmaster_agent/flow_agents/expand_agent/prompt.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from agents.matmaster_agent.utils.sanitize_braces import with_sanitized_braces
2+
13
# --- Context section headers (injected before EXPAND_INSTRUCTION when present) ---
24
SECTION_SHORT_TERM_MEMORY = 'SHORT-TERM WORKING MEMORY'
35
SECTION_SESSION_FILES = 'SESSION FILES'
@@ -13,6 +15,7 @@
1315
)
1416

1517

18+
@with_sanitized_braces('short_term_memory_block', 'session_file_summary')
1619
def build_expand_context(
1720
short_term_memory_block: str = '',
1821
session_file_summary: str = '',

agents/matmaster_agent/flow_agents/plan_make_agent/prompt.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from agents.matmaster_agent.utils.sanitize_braces import with_sanitized_braces
2+
3+
14
def get_static_plan_system_block(available_tools_with_info: str) -> str:
25
"""
36
Immutable content: persona, tool list (heaviest component), output format and constraints.
@@ -100,6 +103,7 @@ def get_static_plan_system_block(available_tools_with_info: str) -> str:
100103
"""
101104

102105

106+
@with_sanitized_braces('thinking_context', 'session_file_summary', 'short_term_memory')
103107
def get_dynamic_plan_user_block(
104108
thinking_context: str = '',
105109
session_file_summary: str = '',

agents/matmaster_agent/flow_agents/report_agent/prompt.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from typing import Any, Mapping
22

3+
from agents.matmaster_agent.utils.sanitize_braces import sanitize_braces
4+
35

46
def get_report_instruction(plan: Mapping[str, Any]) -> str:
57
"""Return an instruction prompt to generate a self-contained markdown report."""
8+
plan_str = sanitize_braces(str(plan))
69

710
return f"""
811
@@ -115,7 +118,7 @@ def get_report_instruction(plan: Mapping[str, Any]) -> str:
115118
- [网页标题](URL) - 说明它支持了报告中的哪条事实/结论
116119
117120
<Plan Content>
118-
{plan}
121+
{plan_str}
119122
120123
(提示:你可以从对话/工具输出中抽取“用户输入文件”“执行时间”“执行概况”等信息;无法确定就用“-”。)
121124

agents/matmaster_agent/flow_agents/thinking_agent/prompt.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
from typing import TypedDict
99

10+
from agents.matmaster_agent.utils.sanitize_braces import with_sanitized_braces
11+
1012

1113
class ThinkingPromptBlocks(TypedDict):
1214
"""Separated blocks for system (static) and user (dynamic) message assembly."""
@@ -92,6 +94,12 @@ def get_static_system_block(available_tools_with_info: str) -> str:
9294
"""
9395

9496

97+
@with_sanitized_braces(
98+
'session_file_summary',
99+
'original_query',
100+
'expanded_query',
101+
'short_term_memory',
102+
)
95103
def get_dynamic_user_block(
96104
session_file_summary: str,
97105
original_query: str,
@@ -202,6 +210,13 @@ def get_static_revision_system_block(available_tools_with_info: str) -> str:
202210
"""
203211

204212

213+
@with_sanitized_braces(
214+
'session_file_summary',
215+
'original_query',
216+
'expanded_query',
217+
'previous_reasoning',
218+
'short_term_memory',
219+
)
205220
def get_dynamic_revision_user_block(
206221
session_file_summary: str,
207222
original_query: str,

agents/matmaster_agent/memory/prompt.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
from agents.matmaster_agent.utils.sanitize_braces import (
2+
with_sanitized_braces,
3+
)
4+
15
# Threshold (chars) above which we treat user context as "literature / long context".
26
LONG_CONTEXT_THRESHOLD = 2500
37

48
# Max insights for long context (literature / expert intuition).
59
LONG_CONTEXT_MAX_INSIGHTS = 25
610

711

12+
@with_sanitized_braces('user_context', 'plan_intro')
813
def get_memory_writer_instruction(
914
user_context: str,
1015
plan_intro: str,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Escape curly braces in dynamic text so {xx} is not interpreted as a variable placeholder.
3+
4+
Used when embedding user/model content into templates that use {variable} substitution
5+
(e.g. ADK instruction inject_session_state). If the runtime is changed later, remove
6+
the decorator or calls and braces no longer need escaping.
7+
"""
8+
9+
import functools
10+
import inspect
11+
12+
13+
def sanitize_braces(text: str) -> str:
14+
"""Escape `{` and `}` in text so they are not treated as variable placeholders.
15+
16+
Use for any dynamic content (user input, model output, plan text, etc.) before
17+
inserting into a template that does {name} substitution. Empty/None returns as-is.
18+
"""
19+
if not text:
20+
return text
21+
return text.replace('\\', '\\\\').replace('{', '\\{').replace('}', '\\}')
22+
23+
24+
def with_sanitized_braces(*param_names: str):
25+
"""Decorator: sanitize the listed string parameters before calling the function.
26+
27+
Use on functions that build instruction/template strings from dynamic args.
28+
If you stop using a runtime that interprets {var}, remove this decorator and
29+
no other logic changes are needed.
30+
"""
31+
param_set = set(param_names)
32+
33+
def deco(f):
34+
sig = inspect.signature(f)
35+
36+
@functools.wraps(f)
37+
def wrapper(*args, **kwargs):
38+
bound = sig.bind(*args, **kwargs)
39+
bound.apply_defaults()
40+
for name in param_set:
41+
if name in bound.arguments and isinstance(bound.arguments[name], str):
42+
bound.arguments[name] = sanitize_braces(bound.arguments[name])
43+
args_list = []
44+
kwargs_dict = {}
45+
for name in sig.parameters:
46+
p = sig.parameters[name]
47+
val = bound.arguments.get(name)
48+
if p.kind == inspect.Parameter.VAR_POSITIONAL:
49+
args_list.extend(val)
50+
elif p.kind == inspect.Parameter.VAR_KEYWORD:
51+
kwargs_dict.update(val)
52+
elif p.kind in (
53+
inspect.Parameter.POSITIONAL_ONLY,
54+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
55+
):
56+
args_list.append(val)
57+
else:
58+
kwargs_dict[name] = val
59+
return f(*args_list, **kwargs_dict)
60+
61+
return wrapper
62+
63+
return deco

0 commit comments

Comments
 (0)