Langchain4j + Skills使用示例

LangChain4j 1.12.1版本开始支持Skill。

Skills是一种为大型语言模型配备可重复使用、独立的行为指令的机制。一个技能包含名称、简短描述以及指令内容(其主体),还可以附带可选资源(例如参考文献、资产、模板等)。大型语言模型按需加载技能,初始上下文保持精简,仅在实际需要时才引入详细指令。

1、创建Maven项目。

依赖pom.xml添加LangChain4j相关依赖包。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sfccn.tools</groupId>
    <artifactId>aiservice</artifactId>
    <version>1.0.0</version>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>1.12.2</langchain4j.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-core</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-skills</artifactId>
            <version>1.12.2-beta22</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>https://maven.aliyun.com/repository/public/</url>
        </repository>
    </repositories>
</project>

2、创建Skill。

一个 Skill 包含:

  • name:技能名称
  • description:简短描述
  • content:详细的执行指令(步骤、注意事项、示例等)
  • resources:可选的参考资料(配置文件、文档等)

创建文件skills\code-security-audit\SKILL.md

---
name: code-security-audit
description: 全面检查代码中的安全漏洞,包括SQL注入、XSS攻击、命令注入、反序列化漏洞、敏感信息泄露等安全问题
---

作为代码安全审计专家,请按以下步骤进行审计:

1. **漏洞识别**:仔细扫描代码,寻找以下类型的安全问题:
   - SQL注入:直接拼接SQL语句
   - XSS跨站脚本攻击:未转义输出用户输入
   - 命令注入:直接拼接执行系统命令
   - 反序列化漏洞:不安全的反序列化操作
   - 敏感信息泄露:硬编码密码、密钥等
   - 认证与授权问题
   - 路径遍历漏洞
   - 不安全的随机数使用

2. **风险评估**:对每个发现的问题评定风险等级:
   - 🔴 严重:需要立即修复
   - 🟠 高风险:应尽快修复
   - 🟡 中等:建议修复
   - 🟢 低风险:可选修复

3. **修复建议**:为每个漏洞提供具体的修复代码示例

4. **输出格式**:
```
=== 代码安全审计报告 ===

📊 总体评估:[安全/存在风险/危险]

🔍 发现的漏洞:

1. [漏洞类型] - [风险等级]
   位置:[代码位置描述]
   问题代码:
```
[有问题的代码片段]
```
安全风险:[详细说明]
修复建议:
```
[修复后的代码示例]
```

📝 安全改进建议总结:
[列出关键改进点]
```

3、创建LangChain4j应用。

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.skills.FileSystemSkill;
import dev.langchain4j.skills.FileSystemSkillLoader;
import dev.langchain4j.skills.Skills;
import java.nio.file.Path;
import java.util.List;
import static java.time.Duration.ofSeconds;

/**
 * 使用 LangChain4j Skill 进行代码安全校验的示例
 *
 * 本示例展示如何创建和使用自定义 Skill 来检查代码中的安全漏洞,
 * 包括 SQL 注入、XSS 攻击、命令注入等常见安全问题。
 *
 * Skill 定义位于:doc/skills/code-security-audit/SKILL.md
 */
public class SkillExample {

    static ChatModel model = OpenAiChatModel.builder()
            .baseUrl(ApiKeys.API_URL)
            .apiKey(ApiKeys.OPENAI_API_KEY)
            .modelName(ApiKeys.MODEL_NAME)
            .timeout(ofSeconds(120))
            .temperature(0.2)
            .logRequests(true)
            .build();

    public static void main(String[] args) {
        // 1. 从文件系统加载 Skills
        List<FileSystemSkill> skillList = FileSystemSkillLoader.loadSkills(Path.of("doc/skills/"));
        Skills skills = Skills.from(skillList);

        // 2. 创建 AI 服务
        CodeSecurityAuditService service = AiServices.builder(CodeSecurityAuditService.class)
                .chatModel(model)
                .toolProvider(skills.toolProvider())
                .systemMessage(
                        "你是一个专业的代码安全审计专家。\n" +
                                "你的任务是仔细分析代码,识别潜在的安全漏洞。\n" +
                                "可用的技能:\n" +
                                skills.formatAvailableSkills() +
                                "\n当需要审计代码安全性时,请使用 activate_skill 工具激活 code-security-audit 技能。"
                )
                .build();

        // 3. 示例:审计一段有安全问题的代码
        String unsafeCode = """
                public class UserService {
                    public User getUserById(String userId) {
                        String sql = "SELECT * FROM users WHERE id = '" + userId + "'";
                        ResultSet rs = statement.executeQuery(sql);
                        return extractUser(rs);
                    }
                
                    public void renderComment(String userComment) {
                        String html = "<div class='comment'>" + userComment + "</div>";
                        response.getWriter().write(html);
                    }
                
                    public void executeBackup(String path) {
                        Runtime.getRuntime().exec("tar -czf backup.tar.gz " + path);
                    }
                }
                """;

        System.out.println("=== 开始代码安全审计 ===\n");
        String result = service.auditCode(unsafeCode);
        System.out.println(result);
    }

    interface CodeSecurityAuditService {
        String auditCode(String code);
    }
}

4、运行结果

运行上边的示例代码,控制台将会输出以下内容:

=== 开始代码安全审计 ===

现在让我对提供的代码进行全面的安全审计:

```
=== 代码安全审计报告 ===

📊 总体评估:危险 - 存在多个严重安全漏洞

🔍 发现的漏洞:

1. SQL注入漏洞 - 🔴 严重
   位置:UserService.getUserById() 方法
   问题代码:
```
public User getUserById(String userId) {
    String sql = "SELECT * FROM users WHERE id = '" + userId + "'";
    ResultSet rs = statement.executeQuery(sql);
    return extractUser(rs);
}
```
安全风险:直接拼接用户输入的 userId 到 SQL 语句中,攻击者可以注入恶意 SQL 代码,如:`' OR '1'='1` 或 `'; DROP TABLE users; --`,导致数据泄露、数据篡改或数据库破坏。

修复建议:
```
public User getUserById(String userId) {
    String sql = "SELECT * FROM users WHERE id = ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, userId);
    ResultSet rs = pstmt.executeQuery();
    return extractUser(rs);
}
```

2. XSS跨站脚本攻击漏洞 - 🔴 严重
   位置:UserService.renderComment() 方法
   问题代码:
```
public void renderComment(String userComment) {
    String html = "<div class='comment'>" + userComment + "</div>";
    response.getWriter().write(html);
}
```
安全风险:直接将用户输入的评论内容输出到 HTML 中,攻击者可以注入恶意 JavaScript 代码,如:`<script>alert('XSS')</script>` 或 `<img src=x onerror=stealCookies()>`,导致会话劫持、数据窃取或恶意重定向。

修复建议:
```
public void renderComment(String userComment) {
    // 使用HTML转义
    String escapedComment = StringEscapeUtils.escapeHtml4(userComment);
    String html = "<div class='comment'>" + escapedComment + "</div>";
    response.getWriter().write(html);
}
```
或者使用现代框架的模板引擎自动转义功能。

3. 命令注入漏洞 - 🔴 严重
   位置:UserService.executeBackup() 方法
   问题代码:
```
public void executeBackup(String path) {
    Runtime.getRuntime().exec("tar -czf backup.tar.gz " + path);
}
```
安全风险:直接拼接用户输入的路径到系统命令中,攻击者可以注入恶意命令,如:`/home/user; rm -rf /` 或 `/home/user && cat /etc/passwd`,导致系统命令执行、文件系统破坏或敏感信息泄露。

修复建议:
```
public void executeBackup(String path) {
    // 验证路径是否安全
    if (!isValidPath(path)) {
        throw new SecurityException("Invalid path provided");
    }
    
    // 使用参数数组而不是字符串拼接
    String[] cmd = {"tar", "-czf", "backup.tar.gz", path};
    Process process = Runtime.getRuntime().exec(cmd);
    
    // 或者使用ProcessBuilder
    ProcessBuilder pb = new ProcessBuilder("tar", "-czf", "backup.tar.gz", path);
    Process process = pb.start();
}
```

4. 缺少输入验证 - 🟠 高风险
   位置:所有方法
   安全风险:所有方法都直接使用用户输入而没有进行任何验证,可能导致空指针异常、格式错误或恶意输入。

修复建议:
```
public User getUserById(String userId) {
    // 输入验证
    if (userId == null || userId.trim().isEmpty()) {
        throw new IllegalArgumentException("User ID cannot be null or empty");
    }
    
    // 验证格式(例如:应该是数字)
    if (!userId.matches("\\d+")) {
        throw new IllegalArgumentException("Invalid user ID format");
    }
    
    // 使用参数化查询
    String sql = "SELECT * FROM users WHERE id = ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, userId);
    ResultSet rs = pstmt.executeQuery();
    return extractUser(rs);
}
```

5. 缺少错误处理 - 🟡 中等
   位置:所有方法
   安全风险:代码中没有适当的错误处理,可能导致敏感信息泄露(如堆栈跟踪)或系统状态不一致。

修复建议:
```
public User getUserById(String userId) {
    try {
        // 输入验证
        validateUserId(userId);
        
        // 使用参数化查询
        String sql = "SELECT * FROM users WHERE id = ?";
        PreparedStatement pstmt = connection.prepareStatement(sql);
        pstmt.setString(1, userId);
        ResultSet rs = pstmt.executeQuery();
        
        if (rs.next()) {
            return extractUser(rs);
        } else {
            return null;
        }
    } catch (SQLException e) {
        // 记录错误但不暴露敏感信息
        logger.error("Database error while fetching user", e);
        throw new ServiceException("Unable to retrieve user information");
    } catch (IllegalArgumentException e) {
        // 输入验证错误
        throw e;
    }
}
```

📝 安全改进建议总结:
1. **立即修复**SQL注入、XSS和命令注入漏洞都是严重风险,需要立即修复
2. **使用参数化查询**:所有数据库操作都应使用PreparedStatement
3. **输出编码**:所有用户输入输出到HTML时都应进行适当的编码
4. **命令执行安全**:避免直接拼接命令,使用参数数组或ProcessBuilder
5. **输入验证**:对所有用户输入进行严格的验证和清理
6. **最小权限原则**:数据库连接和系统命令执行应使用最小必要权限
7. **错误处理**:实现适当的错误处理,避免泄露敏感信息
8. **安全编码培训**:开发团队需要接受安全编码培训
9. **代码审查**:建立安全代码审查流程
10. **安全测试**:实施自动化安全测试,包括SASTDAST工具

这些漏洞如果被利用,可能导致数据泄露、系统破坏、权限提升等严重后果,建议立即修复。
Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐