qwen_agent/utils/prompt_loader.py
2025-10-30 21:19:39 +08:00

162 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
System prompt and MCP settings loader utilities
"""
import os
import json
from typing import List, Dict, Optional
def load_system_prompt(project_dir: str, language: str = None, system_prompt: str=None, robot_type: str = "agent", bot_id: str="") -> str:
# 获取语言显示名称
language_display_map = {
'zh': '中文',
'en': 'English',
'ja': '日本語',
'jp': '日本語'
}
language_display = language_display_map.get(language, language if language else 'English')
# 如果存在{language} 占位符,那么就直接使用 system_prompt
if system_prompt and "{language}" in system_prompt:
return system_prompt.replace("{language}", language_display).replace('{bot_id}', bot_id) or ""
elif robot_type == "agent" or robot_type == "catalog_agent":
"""
优先使用项目目录的system_prompt_catalog_agent.md没有才使用默认的system_prompt_default.md
Args:
project_dir: 项目目录路径
language: 语言代码,如 'zh', 'en', 'jp' 等(此参数将被忽略)
system_prompt: 可选的系统提示词,优先级高于项目配置
robot_type: 机器人类型,取值 AGENT/CATALOG_AGENT
Returns:
str: 加载到的系统提示词内容,如果都未找到则返回空字符串
"""
system_prompt_default = None
try:
default_prompt_file = os.path.join("prompt", f"system_prompt_{robot_type}.md")
with open(default_prompt_file, 'r', encoding='utf-8') as f:
system_prompt_default = f.read()
print(f"Using default system prompt for catalog_agent from prompt folder")
except Exception as e:
print(f"Failed to load default system prompt for catalog_agent: {str(e)}")
system_prompt_default = None
readme = ""
readme_path = os.path.join(project_dir, "README.md")
if os.path.exists(readme_path):
with open(readme_path, "r", encoding="utf-8") as f:
readme = f.read().strip()
system_prompt_default = system_prompt_default.replace("{readme}", str(readme))
return system_prompt_default.replace("{language}", language_display).replace("{extra_prompt}", system_prompt or "").replace('{bot_id}', bot_id) or ""
else:
return system_prompt.replace("{language}", language_display).replace('{bot_id}', bot_id) or ""
def replace_mcp_placeholders(mcp_settings: List[Dict], dataset_dir: str, bot_id: str) -> List[Dict]:
"""
替换 MCP 配置中的占位符
"""
if not mcp_settings or not isinstance(mcp_settings, list):
return mcp_settings
def replace_placeholders_in_obj(obj):
"""递归替换对象中的占位符"""
if isinstance(obj, dict):
for key, value in obj.items():
if key == 'args' and isinstance(value, list):
# 特别处理 args 列表
obj[key] = [item.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id) if isinstance(item, str) else item
for item in value]
elif isinstance(value, (dict, list)):
obj[key] = replace_placeholders_in_obj(value)
elif isinstance(value, str):
obj[key] = value.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id)
elif isinstance(obj, list):
return [replace_placeholders_in_obj(item) if isinstance(item, (dict, list)) else
item.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id) if isinstance(item, str) else item
for item in obj]
return obj
return replace_placeholders_in_obj(mcp_settings)
def load_mcp_settings(project_dir: str, mcp_settings: list=None, bot_id: str="", robot_type: str = "agent") -> List[Dict]:
"""
始终读取默认MCP设置然后与传入的mcp_settings合并合并方式为合并[0].mcpServers对象
Args:
project_dir: 项目目录路径
mcp_settings: 可选的MCP设置将与默认设置合并
bot_id: 机器人项目ID
robot_type: 机器人类型,取值 agent/catalog_agent
Returns:
List[Dict]: 合并后的MCP设置列表
Note:
支持在 mcp_settings.json 的 args 中使用 {dataset_dir} 占位符,
会在 init_modified_agent_service_with_files 中被替换为实际的路径。
"""
# 1. 首先读取默认MCP设置
default_mcp_settings = []
try:
default_mcp_file = os.path.join("mcp", f"mcp_settings_{robot_type}.json")
if os.path.exists(default_mcp_file):
with open(default_mcp_file, 'r', encoding='utf-8') as f:
default_mcp_settings = json.load(f)
print(f"Loaded default mcp_settings_{robot_type} from mcp folder")
else:
print(f"No default mcp_settings_{robot_type} found, using empty default settings")
except Exception as e:
print(f"Failed to load default mcp_settings_{robot_type}: {str(e)}")
default_mcp_settings = []
# 2. 处理传入的mcp_settings参数
input_mcp_settings = []
if mcp_settings is not None:
if isinstance(mcp_settings, list):
input_mcp_settings = mcp_settings
elif mcp_settings:
input_mcp_settings = [mcp_settings]
print(f"Warning: mcp_settings is not a list, converting to list format")
# 3. 合并默认设置和传入设置
merged_settings = []
# 如果有默认设置,以此为基准
if default_mcp_settings:
merged_settings = default_mcp_settings.copy()
# 如果有传入设置合并mcpServers对象
if input_mcp_settings and len(input_mcp_settings) > 0 and len(merged_settings) > 0:
default_mcp_servers = merged_settings[0].get('mcpServers', {})
input_mcp_servers = input_mcp_settings[0].get('mcpServers', {})
# 合并mcpServers对象传入的设置覆盖默认设置中相同的key
default_mcp_servers.update(input_mcp_servers)
merged_settings[0]['mcpServers'] = default_mcp_servers
print(f"Merged mcpServers: default + {len(input_mcp_servers)} input servers")
# 如果没有默认设置但有传入设置,直接使用传入设置
elif input_mcp_settings:
merged_settings = input_mcp_settings.copy()
# 确保返回的是列表格式
if not merged_settings:
merged_settings = []
elif not isinstance(merged_settings, list):
print(f"Warning: merged_settings is not a list, converting to list format")
merged_settings = [merged_settings] if merged_settings else []
# 计算 dataset_dir 用于替换 MCP 配置中的占位符
dataset_dir = os.path.join(project_dir, "dataset")
# 替换 MCP 配置中的 {dataset_dir} 占位符
merged_settings = replace_mcp_placeholders(merged_settings, dataset_dir, bot_id)
return merged_settings