Merge branch 'feature/rag_retrive_top_k' into onprem-release
This commit is contained in:
commit
7cb1043ae3
@ -88,9 +88,8 @@ skill-name/
|
|||||||
|
|
||||||
## Skill 加载优先级
|
## Skill 加载优先级
|
||||||
|
|
||||||
1. Skill MCP 配置(最高)
|
1. Skill MCP 配置
|
||||||
2. 默认 MCP 配置 (`mcp/mcp_settings.json`)
|
2. 用户传入参数(覆盖已有同名配置)
|
||||||
3. 用户传入参数(覆盖所有)
|
|
||||||
|
|
||||||
## 安全措施
|
## 安全措施
|
||||||
|
|
||||||
|
|||||||
@ -396,7 +396,6 @@ dataset_name/
|
|||||||
│ ├── document.txt # 原始文本内容
|
│ ├── document.txt # 原始文本内容
|
||||||
│ ├── serialization.txt # 结构化数据
|
│ ├── serialization.txt # 结构化数据
|
||||||
│ └── schema.json # 字段定义和元数据
|
│ └── schema.json # 字段定义和元数据
|
||||||
├── mcp_settings.json # MCP 工具配置
|
|
||||||
└── system_prompt.md # 系统提示词(可选)
|
└── system_prompt.md # 系统提示词(可选)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -405,7 +404,6 @@ dataset_name/
|
|||||||
- **document.txt**: 原始 Markdown 文本,提供完整上下文
|
- **document.txt**: 原始 Markdown 文本,提供完整上下文
|
||||||
- **serialization.txt**: 格式化结构数据,每行 `字段1:值1;字段2:值2`
|
- **serialization.txt**: 格式化结构数据,每行 `字段1:值1;字段2:值2`
|
||||||
- **schema.json**: 字段定义、枚举值映射和文件关联关系
|
- **schema.json**: 字段定义、枚举值映射和文件关联关系
|
||||||
- **mcp_settings.json**: MCP 工具配置,定义可用的数据处理工具
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -565,7 +563,6 @@ qwen-agent/
|
|||||||
│ ├── multi_keyword_search_server.py # 多关键词搜索服务
|
│ ├── multi_keyword_search_server.py # 多关键词搜索服务
|
||||||
│ ├── excel_csv_operator_server.py # Excel/CSV 操作服务
|
│ ├── excel_csv_operator_server.py # Excel/CSV 操作服务
|
||||||
│ ├── json_reader_server.py # JSON 读取服务
|
│ ├── json_reader_server.py # JSON 读取服务
|
||||||
│ ├── mcp_settings.json # MCP 配置文件
|
|
||||||
│ └── tools/ # 工具定义文件
|
│ └── tools/ # 工具定义文件
|
||||||
├── models/ # 模型文件
|
├── models/ # 模型文件
|
||||||
├── projects/ # 项目目录
|
├── projects/ # 项目目录
|
||||||
|
|||||||
@ -117,13 +117,6 @@ def read_system_prompt():
|
|||||||
return f.read().strip()
|
return f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
def read_mcp_settings():
|
|
||||||
"""读取MCP工具配置"""
|
|
||||||
with open("./mcp/mcp_settings.json", "r") as f:
|
|
||||||
mcp_settings_json = json.load(f)
|
|
||||||
return mcp_settings_json
|
|
||||||
|
|
||||||
|
|
||||||
async def get_tools_from_mcp(mcp):
|
async def get_tools_from_mcp(mcp):
|
||||||
"""从MCP配置中提取工具(带缓存)"""
|
"""从MCP配置中提取工具(带缓存)"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@ -195,8 +188,7 @@ async def init_agent(config: AgentConfig):
|
|||||||
final_system_prompt = await load_system_prompt_async(config)
|
final_system_prompt = await load_system_prompt_async(config)
|
||||||
final_mcp_settings = await load_mcp_settings_async(config)
|
final_mcp_settings = await load_mcp_settings_async(config)
|
||||||
|
|
||||||
# 如果没有提供mcp,使用config中的mcp_settings
|
mcp_settings = final_mcp_settings if final_mcp_settings else []
|
||||||
mcp_settings = final_mcp_settings if final_mcp_settings else read_mcp_settings()
|
|
||||||
system_prompt = final_system_prompt if final_system_prompt else read_system_prompt()
|
system_prompt = final_system_prompt if final_system_prompt else read_system_prompt()
|
||||||
|
|
||||||
config.system_prompt = mcp_settings
|
config.system_prompt = mcp_settings
|
||||||
|
|||||||
@ -5,6 +5,7 @@ Claude Plugins 模式的 Hook 加载器
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -116,7 +117,8 @@ async def merge_skill_mcp_configs(bot_id: str) -> List[Dict]:
|
|||||||
plugin_config = json.load(f)
|
plugin_config = json.load(f)
|
||||||
servers = plugin_config.get('mcpServers', {})
|
servers = plugin_config.get('mcpServers', {})
|
||||||
if servers:
|
if servers:
|
||||||
merged_servers.update(servers)
|
normalized_servers = _normalize_skill_mcp_servers(servers, skill_path)
|
||||||
|
merged_servers.update(normalized_servers)
|
||||||
logger.info(f"Loaded MCP config from skill: {skill_name}")
|
logger.info(f"Loaded MCP config from skill: {skill_name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to load mcpServers from {skill_name}: {e}")
|
logger.error(f"Failed to load mcpServers from {skill_name}: {e}")
|
||||||
@ -127,6 +129,47 @@ async def merge_skill_mcp_configs(bot_id: str) -> List[Dict]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_skill_mcp_servers(servers: Dict[str, Any], skill_path: str) -> Dict[str, Any]:
|
||||||
|
"""将 skill plugin 中 stdio MCP server 的相对路径归一化为基于 skill 目录的绝对路径。"""
|
||||||
|
normalized_servers = copy.deepcopy(servers)
|
||||||
|
|
||||||
|
for server_name, server_config in normalized_servers.items():
|
||||||
|
if not isinstance(server_config, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
transport = server_config.get('transport')
|
||||||
|
if not transport:
|
||||||
|
transport = 'http' if 'url' in server_config else 'stdio'
|
||||||
|
if transport != 'stdio':
|
||||||
|
continue
|
||||||
|
|
||||||
|
command = server_config.get('command')
|
||||||
|
if isinstance(command, str):
|
||||||
|
server_config['command'] = _resolve_skill_relative_path(command, skill_path)
|
||||||
|
|
||||||
|
args = server_config.get('args')
|
||||||
|
if isinstance(args, list):
|
||||||
|
server_config['args'] = [
|
||||||
|
_resolve_skill_relative_path(arg, skill_path) if isinstance(arg, str) else arg
|
||||||
|
for arg in args
|
||||||
|
]
|
||||||
|
|
||||||
|
return normalized_servers
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_skill_relative_path(value: str, skill_path: str) -> str:
|
||||||
|
"""将 ./ 或 ../ 开头且不含占位符的路径转为基于 skill 目录的绝对路径。"""
|
||||||
|
if '{' in value or '}' in value:
|
||||||
|
return value
|
||||||
|
|
||||||
|
if not value.startswith(('./', '../')):
|
||||||
|
return value
|
||||||
|
|
||||||
|
normalized_path = os.path.abspath(os.path.join(skill_path, value))
|
||||||
|
logger.debug(f"Resolved skill MCP path: {value} -> {normalized_path}")
|
||||||
|
return normalized_path
|
||||||
|
|
||||||
|
|
||||||
def _load_plugin_config(plugin_json_path: str) -> Dict:
|
def _load_plugin_config(plugin_json_path: str) -> Dict:
|
||||||
"""加载 plugin.json 配置"""
|
"""加载 plugin.json 配置"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -203,10 +203,9 @@ async def load_mcp_settings_async(config) -> List[Dict]:
|
|||||||
List[Dict]: 合并后的MCP设置列表
|
List[Dict]: 合并后的MCP设置列表
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
支持在 mcp_settings.json 的 args 中使用 {dataset_dir} 占位符,
|
支持在传入或合并后的 mcp_settings 的 args 中使用 {dataset_dir} 占位符,
|
||||||
会在 init_modified_agent_service_with_files 中被替换为实际的路径。
|
会在 init_modified_agent_service_with_files 中被替换为实际的路径。
|
||||||
"""
|
"""
|
||||||
from agent.config_cache import config_cache
|
|
||||||
|
|
||||||
# 从config中获取参数
|
# 从config中获取参数
|
||||||
project_dir = getattr(config, 'project_dir', None)
|
project_dir = getattr(config, 'project_dir', None)
|
||||||
@ -222,33 +221,6 @@ async def load_mcp_settings_async(config) -> List[Dict]:
|
|||||||
skill_mcp_servers = skill_mcp_settings[0].get('mcpServers', {})
|
skill_mcp_servers = skill_mcp_settings[0].get('mcpServers', {})
|
||||||
logger.info(f"Loaded {len(skill_mcp_servers)} MCP servers from skills")
|
logger.info(f"Loaded {len(skill_mcp_servers)} MCP servers from skills")
|
||||||
# ===========================================================================================
|
# ===========================================================================================
|
||||||
|
|
||||||
# 2. 读取默认MCP设置(使用缓存)
|
|
||||||
default_mcp_settings = []
|
|
||||||
try:
|
|
||||||
default_mcp_file = os.path.join("mcp", f"mcp_settings.json")
|
|
||||||
default_mcp_settings = await config_cache.get_json_file(default_mcp_file) or []
|
|
||||||
if default_mcp_settings:
|
|
||||||
logger.info(f"Using cached default mcp_settings from mcp folder")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to load default mcp_settings: {str(e)}")
|
|
||||||
default_mcp_settings = []
|
|
||||||
|
|
||||||
# 3. 合并默认设置到merged_settings(默认设置被skill覆盖)
|
|
||||||
if default_mcp_settings and len(default_mcp_settings) > 0:
|
|
||||||
default_mcp_servers = default_mcp_settings[0].get('mcpServers', {})
|
|
||||||
if merged_settings and len(merged_settings) > 0:
|
|
||||||
# skill配置已存在,将默认配置合并进去(skill优先)
|
|
||||||
skill_mcp_servers = merged_settings[0].get('mcpServers', {})
|
|
||||||
# 默认配置中不存在的才添加
|
|
||||||
for server_name, server_config in default_mcp_servers.items():
|
|
||||||
if server_name not in skill_mcp_servers:
|
|
||||||
skill_mcp_servers[server_name] = server_config
|
|
||||||
else:
|
|
||||||
# 没有skill配置,直接使用默认配置
|
|
||||||
merged_settings = default_mcp_settings.copy()
|
|
||||||
|
|
||||||
# 遍历mcpServers工具,给每个工具增加env参数
|
|
||||||
if merged_settings and len(merged_settings) > 0:
|
if merged_settings and len(merged_settings) > 0:
|
||||||
mcp_servers = merged_settings[0].get('mcpServers', {})
|
mcp_servers = merged_settings[0].get('mcpServers', {})
|
||||||
for server_name, server_config in mcp_servers.items():
|
for server_name, server_config in mcp_servers.items():
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"mcpServers": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"transport": "stdio",
|
"transport": "stdio",
|
||||||
"command": "python",
|
"command": "python",
|
||||||
"args": [
|
"args": [
|
||||||
"./skills/rag-retrieve-only/rag_retrieve_server.py",
|
"./rag_retrieve_server.py",
|
||||||
"{bot_id}"
|
"{bot_id}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
"transport": "stdio",
|
"transport": "stdio",
|
||||||
"command": "python",
|
"command": "python",
|
||||||
"args": [
|
"args": [
|
||||||
"./skills_autoload/rag-retrieve/rag_retrieve_server.py",
|
"./rag_retrieve_server.py",
|
||||||
"{bot_id}"
|
"{bot_id}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -401,23 +401,30 @@ def _extract_skills_to_robot(bot_id: str, skills: List[str], project_path: Path)
|
|||||||
skills_target_dir.mkdir(parents=True, exist_ok=True)
|
skills_target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
logger.info(f"Copying skills to {skills_target_dir}")
|
logger.info(f"Copying skills to {skills_target_dir}")
|
||||||
|
|
||||||
|
managed_skill_names = set()
|
||||||
|
for base_dir in skills_source_dirs:
|
||||||
|
if not base_dir.exists():
|
||||||
|
continue
|
||||||
|
for item in base_dir.iterdir():
|
||||||
|
if item.is_dir():
|
||||||
|
managed_skill_names.add(item.name)
|
||||||
|
|
||||||
# 清理不在列表中的多余 skill 文件夹
|
# 清理不在列表中的多余 skill 文件夹
|
||||||
expected_skill_names = {Path(skill.lstrip("@")).name for skill in skills}
|
expected_skill_names = {Path(skill.lstrip("@")).name for skill in skills}
|
||||||
if skills_target_dir.exists():
|
if skills_target_dir.exists():
|
||||||
for item in skills_target_dir.iterdir():
|
for item in skills_target_dir.iterdir():
|
||||||
if item.is_dir() and item.name not in expected_skill_names:
|
if not item.is_dir() or item.name in expected_skill_names:
|
||||||
logger.info(f" Removing stale skill directory: {item}")
|
continue
|
||||||
|
if item.name in managed_skill_names:
|
||||||
|
logger.info(f" Removing managed stale skill directory: {item}")
|
||||||
shutil.rmtree(item)
|
shutil.rmtree(item)
|
||||||
|
else:
|
||||||
|
logger.info(f" Keeping unmanaged skill directory: {item}")
|
||||||
|
|
||||||
for skill in skills:
|
for skill in skills:
|
||||||
skill_name = Path(skill.lstrip("@")).name
|
skill_name = Path(skill.lstrip("@")).name
|
||||||
target_dir = skills_target_dir / skill_name
|
target_dir = skills_target_dir / skill_name
|
||||||
|
|
||||||
# 如果目标目录已存在,跳过复制
|
|
||||||
if target_dir.exists():
|
|
||||||
logger.info(f" Skill '{skill}' already exists in {target_dir}, skipping")
|
|
||||||
continue
|
|
||||||
|
|
||||||
source_dir = None
|
source_dir = None
|
||||||
|
|
||||||
if skill.startswith("@"):
|
if skill.startswith("@"):
|
||||||
@ -440,7 +447,7 @@ def _extract_skills_to_robot(bot_id: str, skills: List[str], project_path: Path)
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.copytree(source_dir, target_dir)
|
shutil.copytree(source_dir, target_dir, dirs_exist_ok=True)
|
||||||
logger.info(f" Copied: {source_dir} -> {target_dir}")
|
logger.info(f" Synced: {source_dir} -> {target_dir}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f" Failed to copy {source_dir}: {e}")
|
logger.error(f" Failed to copy {source_dir}: {e}")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user