in devel
This commit is contained in:
parent
f174e571bc
commit
f184c199c9
|
|
@ -17,6 +17,7 @@ llm:
|
||||||
model_path: ""
|
model_path: ""
|
||||||
ollama_host: "http://localhost:11434"
|
ollama_host: "http://localhost:11434"
|
||||||
|
|
||||||
|
skills_directory: "./skills" # 新增:SKILL.md 文件所在目录
|
||||||
# ── 本地 MCP Server 配置 ───────────────────────────────────────
|
# ── 本地 MCP Server 配置 ───────────────────────────────────────
|
||||||
mcp:
|
mcp:
|
||||||
server_name: "DemoMCPServer"
|
server_name: "DemoMCPServer"
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ class AppConfig:
|
||||||
memory: MemoryConfig,
|
memory: MemoryConfig,
|
||||||
logging: LoggingConfig,
|
logging: LoggingConfig,
|
||||||
agent: AgentConfig,
|
agent: AgentConfig,
|
||||||
|
skills_directory: str = "./skills"
|
||||||
):
|
):
|
||||||
self.llm = llm
|
self.llm = llm
|
||||||
self.mcp = mcp
|
self.mcp = mcp
|
||||||
|
|
@ -291,6 +292,7 @@ class AppConfig:
|
||||||
self.memory = memory
|
self.memory = memory
|
||||||
self.logging = logging
|
self.logging = logging
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
|
self.skills_directory = skills_directory
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled_mcp_skills(self) -> list[MCPSkillConfig]:
|
def enabled_mcp_skills(self) -> list[MCPSkillConfig]:
|
||||||
|
|
@ -373,6 +375,7 @@ class ConfigLoader:
|
||||||
memory=cls._build_memory(raw.get("memory", {})),
|
memory=cls._build_memory(raw.get("memory", {})),
|
||||||
logging=cls._build_logging(raw.get("logging", {})),
|
logging=cls._build_logging(raw.get("logging", {})),
|
||||||
agent=cls._build_agent(raw.get("agent", {})),
|
agent=cls._build_agent(raw.get("agent", {})),
|
||||||
|
skills=raw.get("skills", [])
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── LLM ───────────────────────────────────────────────────
|
# ── LLM ───────────────────────────────────────────────────
|
||||||
|
|
|
||||||
3791
logs/agent.log
3791
logs/agent.log
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,82 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict, List, Tuple
|
||||||
|
from utils.logger import get_logger
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
logger = get_logger("mcp.SkillLoader")
|
||||||
|
|
||||||
|
class SkillLoader:
|
||||||
|
"""加载和解析 SKILL.md 文件"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_skills_from_directory(directory: str) -> Dict[str, Any]:
|
||||||
|
skills = {}
|
||||||
|
for skill_directory in os.listdir(directory):
|
||||||
|
skill_md = f"{skill_directory}/SKILL.md"
|
||||||
|
if not os.path.isfile(skill_md):
|
||||||
|
logger.warning(f"未在{skill_directory}发现SKILL.md文件")
|
||||||
|
continue
|
||||||
|
skill_name = os.path.basename(skill_directory) # 去掉 .md 后缀
|
||||||
|
skill_info = SkillLoader.load_skill(skill_md)
|
||||||
|
if not skill_info:
|
||||||
|
continue
|
||||||
|
skills[skill_info['name']] = skill_info
|
||||||
|
return skills
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_skill(filepath: str) -> Dict[str, Any]:
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
skill_info = SkillLoader.parse_skill(content)
|
||||||
|
return skill_info
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_md_frontmatter(content: str) -> Dict[str, Any]:
|
||||||
|
# 使用正则表达式提取 frontmatter 部分
|
||||||
|
frontmatter_match = re.search(r'^---\n(.*?)\n---', content, re.DOTALL | re.MULTILINE)
|
||||||
|
if frontmatter_match:
|
||||||
|
frontmatter_content = frontmatter_match.group(1).strip()
|
||||||
|
# 使用 PyYAML 将 frontmatter 字符串解析为字典
|
||||||
|
frontmatter_data = yaml.safe_load(frontmatter_content)
|
||||||
|
return frontmatter_data
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_skill(content: str) -> Dict[str, Any]:
|
||||||
|
return SkillLoader.parse_md_frontmatter(content)
|
||||||
|
|
||||||
|
lines = content.strip().splitlines()
|
||||||
|
skill_info = {
|
||||||
|
'name': lines[0].replace('# ', '').strip(),
|
||||||
|
'description': lines[1].replace('## Description\n', '').strip(),
|
||||||
|
'parameters': {},
|
||||||
|
'example': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 解析参数
|
||||||
|
param_section = False
|
||||||
|
for line in lines[2:]:
|
||||||
|
if line.startswith('## Parameters'):
|
||||||
|
param_section = True
|
||||||
|
continue
|
||||||
|
if param_section:
|
||||||
|
if line.startswith('## Example'):
|
||||||
|
break
|
||||||
|
if line.startswith('-'):
|
||||||
|
param_line = line[1:].strip().split(':')
|
||||||
|
if len(param_line) == 2:
|
||||||
|
param_name = param_line[0].strip()
|
||||||
|
param_desc = param_line[1].strip()
|
||||||
|
skill_info['parameters'][param_name] = param_desc
|
||||||
|
|
||||||
|
# 解析示例
|
||||||
|
if '## Example' in content:
|
||||||
|
example_start = content.index('## Example') + len('## Example\n')
|
||||||
|
example_json = content[example_start:].strip().split('\n```')[0].strip()
|
||||||
|
skill_info['example'] = json.loads(example_json)
|
||||||
|
|
||||||
|
return skill_info
|
||||||
|
|
@ -15,6 +15,7 @@ from typing import Any
|
||||||
|
|
||||||
from config.settings import settings
|
from config.settings import settings
|
||||||
from mcp.mcp_skill_client import MCPSkillClient, RemoteTool, ToolCallResult
|
from mcp.mcp_skill_client import MCPSkillClient, RemoteTool, ToolCallResult
|
||||||
|
from mcp.skill_loader import SkillLoader
|
||||||
from utils.logger import get_logger
|
from utils.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger("MCP.SkillRegistry")
|
logger = get_logger("MCP.SkillRegistry")
|
||||||
|
|
@ -81,6 +82,15 @@ class SkillRegistry:
|
||||||
self._remote: dict[str, tuple[MCPSkillClient, RemoteTool]] = {}
|
self._remote: dict[str, tuple[MCPSkillClient, RemoteTool]] = {}
|
||||||
# 在线 Skill 客户端列表(用于生命周期管理)
|
# 在线 Skill 客户端列表(用于生命周期管理)
|
||||||
self._clients: list[MCPSkillClient] = []
|
self._clients: list[MCPSkillClient] = []
|
||||||
|
# 指定 SKILL.md 文件所在目录
|
||||||
|
self.load_skills_from_md(settings.skills_directory)
|
||||||
|
|
||||||
|
def load_skills_from_md(self, directory: str) -> None:
|
||||||
|
skills = SkillLoader.load_skills_from_directory(directory)
|
||||||
|
for skill_name, skill_info in skills.items():
|
||||||
|
logger.info(f"📦 加载技能: {skill_name}")
|
||||||
|
# 将技能注册到远端工具表
|
||||||
|
self._remote[skill_name] = skill_info
|
||||||
|
|
||||||
# ── 注册本地工具 ──────────────────────────────────────────
|
# ── 注册本地工具 ──────────────────────────────────────────
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue