| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- package com.agent.management.parser;
- import com.agent.management.model.dto.IOField;
- import com.agent.management.model.dto.SkillDTO;
- import com.fasterxml.jackson.core.type.TypeReference;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Component;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.nio.file.Files;
- import java.nio.file.Path;
- import java.util.List;
- import java.util.Optional;
- /**
- * SKILL.md 文件解析器
- * 解析文件头部的 name、description、inputs、outputs 字段
- */
- @Slf4j
- @Component
- public class SkillMarkdownParser {
- private static final String NAME_PREFIX = "name:";
- private static final String DESC_PREFIX = "description:";
- private static final String INPUTS_PREFIX = "inputs:";
- private static final String OUTPUTS_PREFIX = "outputs:";
- private final ObjectMapper objectMapper = new ObjectMapper();
- /**
- * 解析 SKILL.md 文件,提取 name、description、inputs、outputs
- *
- * @param skillMdPath SKILL.md 文件路径
- * @return 解析后的 SkillDTO,若解析失败返回 empty
- */
- public Optional<SkillDTO> parse(Path skillMdPath) {
- SkillDTO dto = new SkillDTO();
- dto.setPath(skillMdPath.getParent().toString());
- dto.setFolderName(skillMdPath.getParent().getFileName().toString());
- try (BufferedReader reader = Files.newBufferedReader(skillMdPath, StandardCharsets.UTF_8)) {
- String name = null;
- String description = null;
- List<IOField> inputs = null;
- List<IOField> outputs = null;
- String line;
- int lineCount = 0;
- int maxLinesToScan = 30;
- while ((line = reader.readLine()) != null && lineCount < maxLinesToScan) {
- lineCount++;
- String trimmed = line.trim();
- // 跳过空行、注释行、Markdown 标题行、frontmatter 分隔符
- if (trimmed.isEmpty() || trimmed.startsWith("<!--") || trimmed.startsWith("#") || trimmed.equals("---")) {
- continue;
- }
- if (name == null && trimmed.toLowerCase().startsWith(NAME_PREFIX)) {
- name = trimmed.substring(NAME_PREFIX.length()).trim();
- continue;
- }
- if (description == null && trimmed.toLowerCase().startsWith(DESC_PREFIX)) {
- description = trimmed.substring(DESC_PREFIX.length()).trim();
- continue;
- }
- if (inputs == null && trimmed.toLowerCase().startsWith(INPUTS_PREFIX)) {
- inputs = parseJsonArray(trimmed.substring(INPUTS_PREFIX.length()).trim());
- continue;
- }
- if (outputs == null && trimmed.toLowerCase().startsWith(OUTPUTS_PREFIX)) {
- outputs = parseJsonArray(trimmed.substring(OUTPUTS_PREFIX.length()).trim());
- continue;
- }
- // 所有字段都已解析完成,提前退出
- if (name != null && description != null && inputs != null && outputs != null) {
- break;
- }
- }
- if (name == null || name.isEmpty() || description == null || description.isEmpty()) {
- log.warn("SKILL.md 解析失败,缺少必要字段: name={}, description={}, path={}",
- name, description, skillMdPath);
- return Optional.empty();
- }
- dto.setName(name);
- dto.setDescription(description);
- dto.setInputs(inputs);
- dto.setOutputs(outputs);
- return Optional.of(dto);
- } catch (IOException e) {
- log.error("读取 SKILL.md 文件失败: {}", skillMdPath, e);
- return Optional.empty();
- }
- }
- private List<IOField> parseJsonArray(String json) {
- if (json == null || json.isEmpty()) {
- return null;
- }
- try {
- return objectMapper.readValue(json, new TypeReference<List<IOField>>() {});
- } catch (IOException e) {
- log.warn("解析 JSON 数组失败: {}", e.getMessage());
- return null;
- }
- }
- }
|