Skip to content

Commit abf3f0c

Browse files
committed
šŸ› Bugfix: The intelligent generation agent cannot recognize the selected tool
1 parent 2dd7f06 commit abf3f0c

File tree

6 files changed

+84
-136
lines changed

6 files changed

+84
-136
lines changed

ā€Žbackend/apps/prompt_app.pyā€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ async def generate_and_save_system_prompt_api(
2727
task_description=prompt_request.task_description,
2828
user_id=user_id,
2929
tenant_id=tenant_id,
30-
language=language
30+
language=language,
31+
tool_ids=prompt_request.tool_ids,
32+
sub_agent_ids=prompt_request.sub_agent_ids
3133
), media_type="text/event-stream")
3234
except Exception as e:
3335
logger.exception(f"Error occurred while generating system prompt: {e}")

ā€Žbackend/consts/model.pyā€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ class GeneratePromptRequest(BaseModel):
193193
task_description: str
194194
agent_id: int
195195
model_id: int
196+
tool_ids: Optional[List[int]] = None # Optional: tool IDs from frontend (takes precedence over database query)
197+
sub_agent_ids: Optional[List[int]] = None # Optional: sub-agent IDs from frontend (takes precedence over database query)
196198

197199

198200
class GenerateTitleRequest(BaseModel):

ā€Žbackend/services/prompt_service.pyā€Ž

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
import logging
33
import queue
44
import threading
5+
from typing import Optional, List
56

67
from jinja2 import StrictUndefined, Template
78
from smolagents import OpenAIServerModel
89

9-
from consts.const import LANGUAGE, MODEL_CONFIG_MAPPING, MESSAGE_ROLE, THINK_END_PATTERN, THINK_START_PATTERN
10+
from consts.const import LANGUAGE, MESSAGE_ROLE, THINK_END_PATTERN, THINK_START_PATTERN
1011
from consts.model import AgentInfoRequest
11-
from database.agent_db import update_agent, query_sub_agents_id_list, search_agent_info_by_agent_id
12+
from database.agent_db import update_agent, search_agent_info_by_agent_id
1213
from database.model_management_db import get_model_by_model_id
1314
from database.tool_db import query_tools_by_ids
14-
from services.agent_service import get_enable_tool_id_by_agent_id
15-
from utils.config_utils import tenant_config_manager, get_model_name_from_config
15+
from utils.config_utils import get_model_name_from_config
1616
from utils.prompt_template_utils import get_prompt_generate_prompt_template
1717

1818
# Configure logging
@@ -34,7 +34,7 @@ def _process_thinking_tokens(new_token: str, is_thinking: bool, token_join: list
3434
"""
3535
# Handle thinking mode
3636
if is_thinking:
37-
return not (THINK_END_PATTERN in new_token)
37+
return THINK_END_PATTERN not in new_token
3838

3939
# Handle start of thinking
4040
if THINK_START_PATTERN in new_token:
@@ -98,14 +98,16 @@ def call_llm_for_system_prompt(model_id: int, user_prompt: str, system_prompt: s
9898
raise e
9999

100100

101-
def gen_system_prompt_streamable(agent_id: int, model_id: int, task_description: str, user_id: str, tenant_id: str, language: str):
101+
def gen_system_prompt_streamable(agent_id: int, model_id: int, task_description: str, user_id: str, tenant_id: str, language: str, tool_ids: Optional[List[int]] = None, sub_agent_ids: Optional[List[int]] = None):
102102
for system_prompt in generate_and_save_system_prompt_impl(
103103
agent_id=agent_id,
104104
model_id=model_id,
105105
task_description=task_description,
106106
user_id=user_id,
107107
tenant_id=tenant_id,
108-
language=language
108+
language=language,
109+
tool_ids=tool_ids,
110+
sub_agent_ids=sub_agent_ids
109111
):
110112
# SSE format, each message ends with \n\n
111113
yield f"data: {json.dumps({'success': True, 'data': system_prompt}, ensure_ascii=False)}\n\n"
@@ -116,17 +118,35 @@ def generate_and_save_system_prompt_impl(agent_id: int,
116118
task_description: str,
117119
user_id: str,
118120
tenant_id: str,
119-
language: str):
120-
# Get description of tool and agent
121-
# In create mode (agent_id=0), return empty lists
122-
if agent_id == 0:
121+
language: str,
122+
tool_ids: Optional[List[int]] = None,
123+
sub_agent_ids: Optional[List[int]] = None):
124+
# Get description of tool and agent from frontend-provided IDs
125+
# Frontend always provides tool_ids and sub_agent_ids (could be empty arrays)
126+
127+
# Handle tool IDs
128+
if tool_ids and len(tool_ids) > 0:
129+
tool_info_list = query_tools_by_ids(tool_ids)
130+
logger.debug(f"Using frontend-provided tool IDs: {tool_ids}")
131+
else:
123132
tool_info_list = []
133+
logger.debug("No tools selected (empty tool_ids list)")
134+
135+
# Handle sub-agent IDs
136+
if sub_agent_ids and len(sub_agent_ids) > 0:
124137
sub_agent_info_list = []
138+
for sub_agent_id in sub_agent_ids:
139+
try:
140+
sub_agent_info = search_agent_info_by_agent_id(
141+
agent_id=sub_agent_id, tenant_id=tenant_id)
142+
sub_agent_info_list.append(sub_agent_info)
143+
except Exception as e:
144+
logger.warning(
145+
f"Failed to get sub-agent info for agent_id {sub_agent_id}: {str(e)}")
146+
logger.debug(f"Using frontend-provided sub-agent IDs: {sub_agent_ids}")
125147
else:
126-
tool_info_list = get_enabled_tool_description_for_generate_prompt(
127-
tenant_id=tenant_id, agent_id=agent_id)
128-
sub_agent_info_list = get_enabled_sub_agent_description_for_generate_prompt(
129-
tenant_id=tenant_id, agent_id=agent_id)
148+
sub_agent_info_list = []
149+
logger.debug("No sub-agents selected (empty sub_agent_ids list)")
130150

131151
# 1. Real-time streaming push
132152
final_results = {"duty": "", "constraint": "", "few_shots": "", "agent_var_name": "", "agent_display_name": "",
@@ -292,27 +312,3 @@ def join_info_for_generate_system_prompt(prompt_for_generate, sub_agent_info_lis
292312
"assistant_description": assistant_description
293313
})
294314
return content
295-
296-
297-
def get_enabled_tool_description_for_generate_prompt(agent_id: int, tenant_id: str):
298-
# Get tool information
299-
logger.info("Fetching tool instances")
300-
tool_id_list = get_enable_tool_id_by_agent_id(
301-
agent_id=agent_id, tenant_id=tenant_id)
302-
tool_info_list = query_tools_by_ids(tool_id_list)
303-
return tool_info_list
304-
305-
306-
def get_enabled_sub_agent_description_for_generate_prompt(agent_id: int, tenant_id: str):
307-
logger.info("Fetching sub-agents information")
308-
309-
sub_agent_id_list = query_sub_agents_id_list(
310-
main_agent_id=agent_id, tenant_id=tenant_id)
311-
312-
sub_agent_info_list = []
313-
for sub_agent_id in sub_agent_id_list:
314-
sub_agent_info = search_agent_info_by_agent_id(
315-
agent_id=sub_agent_id, tenant_id=tenant_id)
316-
317-
sub_agent_info_list.append(sub_agent_info)
318-
return sub_agent_info_list

ā€Žfrontend/app/[locale]/agents/AgentConfiguration.tsxā€Ž

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,25 @@ export default forwardRef<AgentConfigHandle, AgentConfigProps>(function AgentCon
161161
const currentAgentName = agentName;
162162
const currentAgentDisplayName = agentDisplayName;
163163

164+
// Extract tool IDs from selected tools (convert string IDs to numbers)
165+
// Always pass tool_ids array (empty array means no tools selected, undefined means use database)
166+
// In edit mode, we want to use current selection, so pass the array even if empty
167+
const toolIds = selectedTools.map((tool) => Number(tool.id));
168+
169+
// Get sub-agent IDs from enabledAgentIds
170+
// Always pass sub_agent_ids array (empty array means no sub-agents selected, undefined means use database)
171+
// In edit mode, we want to use current selection, so pass the array even if empty
172+
const subAgentIds = [...enabledAgentIds];
173+
164174
// Call backend API to generate agent prompt
175+
// Pass tool_ids and sub_agent_ids to use frontend selection instead of database query
165176
await generatePromptStream(
166177
{
167178
agent_id: agentIdToUse,
168179
task_description: businessLogic,
169180
model_id: selectedModel?.id?.toString() || "",
181+
tool_ids: toolIds,
182+
sub_agent_ids: subAgentIds,
170183
},
171184
(data) => {
172185
// Process streaming response data

ā€Žfrontend/types/agentConfig.tsā€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ export interface GeneratePromptParams {
323323
agent_id: number;
324324
task_description: string;
325325
model_id: string;
326+
tool_ids?: number[]; // Optional: tool IDs selected in frontend (takes precedence over database query)
327+
sub_agent_ids?: number[]; // Optional: sub-agent IDs selected in frontend (takes precedence over database query)
326328
}
327329

328330
/**

ā€Žtest/backend/services/test_prompt_service.pyā€Ž

Lines changed: 29 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@
3636
call_llm_for_system_prompt,
3737
generate_and_save_system_prompt_impl,
3838
gen_system_prompt_streamable,
39-
get_enabled_tool_description_for_generate_prompt,
40-
get_enabled_sub_agent_description_for_generate_prompt,
4139
generate_system_prompt,
4240
join_info_for_generate_system_prompt,
4341
_process_thinking_tokens
@@ -91,21 +89,21 @@ def test_call_llm_for_system_prompt(self, mock_get_model_name, mock_openai, mock
9189
)
9290

9391
@patch('backend.services.prompt_service.generate_system_prompt')
94-
@patch('backend.services.prompt_service.get_enabled_sub_agent_description_for_generate_prompt')
95-
@patch('backend.services.prompt_service.get_enabled_tool_description_for_generate_prompt')
92+
@patch('backend.services.prompt_service.query_tools_by_ids')
93+
@patch('backend.services.prompt_service.search_agent_info_by_agent_id')
9694
@patch('backend.services.prompt_service.update_agent')
97-
def test_generate_and_save_system_prompt_impl(self, mock_update_agent, mock_get_tool_desc,
98-
mock_get_agent_desc, mock_generate_system_prompt):
95+
def test_generate_and_save_system_prompt_impl(self, mock_update_agent, mock_search_agent_info,
96+
mock_query_tools, mock_generate_system_prompt):
9997
# Setup
10098
mock_tool1 = {"name": "tool1", "description": "Tool 1 desc",
10199
"inputs": "input1", "output_type": "output1"}
102100
mock_tool2 = {"name": "tool2", "description": "Tool 2 desc",
103101
"inputs": "input2", "output_type": "output2"}
104-
mock_get_tool_desc.return_value = [mock_tool1, mock_tool2]
102+
mock_query_tools.return_value = [mock_tool1, mock_tool2]
105103

106104
mock_agent1 = {"name": "agent1", "description": "Agent 1 desc"}
107105
mock_agent2 = {"name": "agent2", "description": "Agent 2 desc"}
108-
mock_get_agent_desc.return_value = [mock_agent1, mock_agent2]
106+
mock_search_agent_info.side_effect = [mock_agent1, mock_agent2]
109107

110108
# Mock the generator to return the expected data structure
111109
def mock_generator(*args, **kwargs):
@@ -121,33 +119,34 @@ def mock_generator(*args, **kwargs):
121119

122120
mock_generate_system_prompt.side_effect = mock_generator
123121

124-
# Execute - test as a generator
122+
# Execute - test as a generator with frontend-provided IDs
125123
result_gen = generate_and_save_system_prompt_impl(
126124
agent_id=123,
127125
model_id=self.test_model_id,
128126
task_description="Test task",
129127
user_id="user123",
130128
tenant_id="tenant456",
131-
language="zh"
129+
language="zh",
130+
tool_ids=[1, 2],
131+
sub_agent_ids=[10, 20]
132132
)
133133
result = list(result_gen) # Convert generator to list for assertion
134134

135135
# Assert
136136
self.assertGreater(len(result), 0)
137137

138-
mock_get_tool_desc.assert_called_once_with(
139-
agent_id=123, tenant_id="tenant456")
140-
mock_get_agent_desc.assert_called_once_with(
141-
agent_id=123, tenant_id="tenant456")
138+
# Verify tools and agents were queried using frontend-provided IDs
139+
mock_query_tools.assert_called_once_with([1, 2])
140+
self.assertEqual(mock_search_agent_info.call_count, 2)
141+
mock_search_agent_info.assert_any_call(agent_id=10, tenant_id="tenant456")
142+
mock_search_agent_info.assert_any_call(agent_id=20, tenant_id="tenant456")
142143

143-
mock_generate_system_prompt.assert_called_once_with(
144-
mock_get_agent_desc.return_value,
145-
"Test task",
146-
mock_get_tool_desc.return_value,
147-
"tenant456",
148-
self.test_model_id,
149-
"zh"
150-
)
144+
# Verify generate_system_prompt was called with correct parameters
145+
mock_generate_system_prompt.assert_called_once()
146+
call_args = mock_generate_system_prompt.call_args
147+
self.assertEqual(call_args[0][0], [mock_agent1, mock_agent2]) # sub_agent_info_list
148+
self.assertEqual(call_args[0][1], "Test task") # task_description
149+
self.assertEqual(call_args[0][2], [mock_tool1, mock_tool2]) # tool_info_list
151150

152151
# Verify update_agent was called with the correct parameters
153152
mock_update_agent.assert_called_once()
@@ -162,11 +161,8 @@ def mock_generator(*args, **kwargs):
162161
self.assertEqual(agent_info.business_description, "Test task")
163162

164163
@patch('backend.services.prompt_service.generate_system_prompt')
165-
@patch('backend.services.prompt_service.get_enabled_sub_agent_description_for_generate_prompt')
166-
@patch('backend.services.prompt_service.get_enabled_tool_description_for_generate_prompt')
167164
@patch('backend.services.prompt_service.update_agent')
168-
def test_generate_and_save_system_prompt_impl_create_mode(self, mock_update_agent, mock_get_tool_desc,
169-
mock_get_agent_desc, mock_generate_system_prompt):
165+
def test_generate_and_save_system_prompt_impl_create_mode(self, mock_update_agent, mock_generate_system_prompt):
170166
"""Test generate_and_save_system_prompt_impl in create mode (agent_id=0)"""
171167
# Setup - Mock the generator to return the expected data structure
172168
def mock_generator(*args, **kwargs):
@@ -182,25 +178,22 @@ def mock_generator(*args, **kwargs):
182178

183179
mock_generate_system_prompt.side_effect = mock_generator
184180

185-
# Execute - test as a generator with agent_id=0 (create mode)
181+
# Execute - test as a generator with agent_id=0 (create mode) and empty tool/sub-agent IDs
186182
result_gen = generate_and_save_system_prompt_impl(
187183
agent_id=0,
188184
model_id=self.test_model_id,
189185
task_description="Test task",
190186
user_id="user123",
191187
tenant_id="tenant456",
192-
language="zh"
188+
language="zh",
189+
tool_ids=[],
190+
sub_agent_ids=[]
193191
)
194192
result = list(result_gen) # Convert generator to list for assertion
195193

196194
# Assert
197195
self.assertGreater(len(result), 0)
198196

199-
# In create mode, should NOT call get_enabled_tool_description_for_generate_prompt
200-
# and get_enabled_sub_agent_description_for_generate_prompt
201-
mock_get_tool_desc.assert_not_called()
202-
mock_get_agent_desc.assert_not_called()
203-
204197
# Should call generate_system_prompt with empty lists for tools and sub-agents
205198
mock_generate_system_prompt.assert_called_once_with(
206199
[], # Empty sub_agent_info_list
@@ -246,7 +239,9 @@ def test_gen_system_prompt_streamable(self, mock_generate_impl):
246239
task_description="Test task",
247240
user_id="user123",
248241
tenant_id="tenant456",
249-
language="zh"
242+
language="zh",
243+
tool_ids=None,
244+
sub_agent_ids=None,
250245
)
251246

252247
# Verify output format - should be SSE format
@@ -473,68 +468,6 @@ def test_join_info_for_generate_system_prompt(self, mock_template):
473468
self.assertEqual(
474469
template_vars["task_description"], mock_task_description)
475470

476-
@patch('backend.services.prompt_service.get_enable_tool_id_by_agent_id')
477-
@patch('backend.services.prompt_service.query_tools_by_ids')
478-
def test_get_enabled_tool_description_for_generate_prompt(self, mock_query_tools, mock_get_tool_ids):
479-
# Setup
480-
mock_get_tool_ids.return_value = [1, 2, 3]
481-
mock_tools = [{"id": 1, "name": "tool1"}, {
482-
"id": 2, "name": "tool2"}, {"id": 3, "name": "tool3"}]
483-
mock_query_tools.return_value = mock_tools
484-
485-
# Execute
486-
result = get_enabled_tool_description_for_generate_prompt(
487-
agent_id=123,
488-
tenant_id="tenant456"
489-
)
490-
491-
# Assert
492-
self.assertEqual(result, mock_tools)
493-
mock_get_tool_ids.assert_called_once_with(
494-
agent_id=123, tenant_id="tenant456")
495-
mock_query_tools.assert_called_once_with([1, 2, 3])
496-
497-
@patch('backend.services.prompt_service.search_agent_info_by_agent_id')
498-
@patch('backend.services.prompt_service.query_sub_agents_id_list')
499-
def test_get_enabled_sub_agent_description_for_generate_prompt(self, mock_query_sub_agents_id_list, mock_search_agent_info):
500-
# Setup
501-
mock_query_sub_agents_id_list.return_value = [1, 2, 3]
502-
503-
# Mock search_agent_info_by_agent_id to return different agent info for each ID
504-
def mock_search_agent_info_side_effect(agent_id, tenant_id):
505-
agent_info_map = {
506-
1: {"id": 1, "name": "agent1", "enabled": True},
507-
2: {"id": 2, "name": "agent2", "enabled": False},
508-
3: {"id": 3, "name": "agent3", "enabled": True}
509-
}
510-
return agent_info_map.get(agent_id, {})
511-
512-
mock_search_agent_info.side_effect = mock_search_agent_info_side_effect
513-
514-
# Execute
515-
result = get_enabled_sub_agent_description_for_generate_prompt(
516-
agent_id=123,
517-
tenant_id="tenant456"
518-
)
519-
520-
# Assert
521-
expected_result = [
522-
{"id": 1, "name": "agent1", "enabled": True},
523-
{"id": 2, "name": "agent2", "enabled": False},
524-
{"id": 3, "name": "agent3", "enabled": True}
525-
]
526-
self.assertEqual(result, expected_result)
527-
mock_query_sub_agents_id_list.assert_called_once_with(
528-
main_agent_id=123, tenant_id="tenant456")
529-
530-
# Verify search_agent_info_by_agent_id was called for each sub agent ID
531-
self.assertEqual(mock_search_agent_info.call_count, 3)
532-
mock_search_agent_info.assert_any_call(
533-
agent_id=1, tenant_id="tenant456")
534-
mock_search_agent_info.assert_any_call(
535-
agent_id=2, tenant_id="tenant456")
536-
mock_search_agent_info.assert_any_call(
537-
agent_id=3, tenant_id="tenant456")
538471

539472
@patch('backend.services.prompt_service.get_model_by_model_id')
540473
@patch('backend.services.prompt_service.OpenAIServerModel')

0 commit comments

Comments
Ā (0)