Harness Engineering 从理论到实战:基于 Spring AI Alibaba 的完整实现指南

Harness Engineering 从理论到实战:基于 Spring AI Alibaba 的完整实现指南

一份涵盖七层架构、完整代码和深度解析的实践手册


Harness Engineering 实战:用 Spring AI Alibaba 构建可控的 AI 智能体:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/162488035

基于上述流程。

目录

  1. [什么是 Harness Engineering?](#什么是 Harness Engineering?)
  2. [Harness Engineering 的核心架构](#Harness Engineering 的核心架构)
  3. 环境准备与项目搭建
  4. 完整代码实现
  5. 七层架构深度解析
  6. 运行与测试
  7. 常见问题与解决方案
  8. 总结与进阶方向

一、什么是 Harness Engineering?

Harness Engineering(驾驭工程)是 2026 年 AI 工程化领域兴起的一种新范式。它的核心思想是:将重心从优化 AI 模型本身,转移到为 AI 智能体(Agent)构建一个可靠、可控、可维护的运行环境

核心公式

Agent = Model + Harness

如果把大语言模型比作一匹力量强大但难以预测的野马,那么 Harness(马具) 就是套在它身上用来引导和控制的整套装备。

Harness Engineering 的核心组件

组件 类型 作用 类比
Rules(规则) 软约束 声明式规范,告诉 Agent "什么不能做" 交通规则
Skills(技能) 半硬约束 步骤化操作手册,告诉 Agent "具体怎么做" 操作说明书
Gate(门禁) 硬约束 强制校验输出,"不通过就拦截" 安检闸机
State(状态) 上下文管理 记录 Agent 的进度和上下文 记事本
Instructions(指令) 行为指导 告诉 Agent 做什么、按什么顺序做 工作清单
Verification(验证) 质量保证 只有通过测试才算任务完成 质量检查员

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、Harness Engineering 的核心架构

七层架构总览

Harness Engineering 的七层架构是一个从内到外、层层递进的完整体系,其核心目标是将 AI 的不可控性通过工程化手段转化为确定性。

复制代码
┌─────────────────────────────────────────────────────────────┐
│  7. 评估与反馈层 (Evaluation & Feedback)                    │  ← 持续优化闭环
├─────────────────────────────────────────────────────────────┤
│  6. 多 Agent 架构层 (Multi-Agent Architecture)              │  ← 团队协作
├─────────────────────────────────────────────────────────────┤
│  5. 约束与防护层 (Constraints & Guardrails)                 │  ← 安全护栏
├─────────────────────────────────────────────────────────────┤
│  4. 上下文工程层 (Context Engineering)                      │  ← 记忆与知识
├─────────────────────────────────────────────────────────────┤
│  3. 项目搭建层 (Project Setup)                              │  ← 标准化地基
├─────────────────────────────────────────────────────────────┤
│  2. 工具编排层 (Tool Orchestration)                         │  ← 外部交互
├─────────────────────────────────────────────────────────────┤
│  1. 执行主循环层 (Execution Loop)                           │  ← 大脑与调度
└─────────────────────────────────────────────────────────────┘

各层职责速览

层级 核心职责 简单理解
1. 执行主循环 Agent 运行的"大脑"与调度核心 负责思考、决策、执行和反思的循环
2. 工具编排 Agent 与外部世界交互的"手脚" 安全、可控地调用外部 API 或工具
3. 项目搭建 标准化项目的"地基" 统一项目结构、依赖和规范
4. 上下文工程 Agent 的"记忆"与"知识库" 管理、优化注入给模型的信息
5. 约束与防护 Agent 的"安全护栏" 硬性校验输入输出,防止失控
6. 多 Agent 架构 团队协作的"管理模式" 让多个专业 Agent 协同工作
7. 评估与反馈 持续优化的"学习闭环" 通过测试和反馈不断改进系统

三、环境准备与项目搭建

3.1 Windows 本地环境要求

软件 版本要求 下载地址
JDK 17 或更高 Oracle JDK
Maven 3.8+ https://maven.apache.org/download.cgi
IntelliJ IDEA 2023.3+ https://www.jetbrains.com/idea/
curl 任意版本 Windows 10/11 内置
DashScope API Key 阿里云百炼平台获取 https://bailian.console.aliyun.com/

验证安装 :在命令行执行 java -versionmvn -version 确认环境变量配置正确。

3.2 项目结构

复制代码
spring-ai-harness-demo/
├── pom.xml
├── src/main/
│   ├── java/com/badao/ai/
│   │   ├── SpringAiHarnessDemoApplication.java   # 启动类
│   │   ├── config/
│   │   │   ├── HarnessAgentConfig.java           # 单Agent配置
│   │   │   └── MultiAgentConfig.java             # 多Agent配置
│   │   ├── controller/
│   │   │   └── HarnessController.java            # REST API
│   │   ├── service/
│   │   │   ├── HarnessAgentService.java          # 核心业务服务
│   │   │   └── MultiAgentService.java            # 多Agent协同服务
│   │   ├── model/
│   │   │   ├── ContactInfo.java                  # 联系人POJO
│   │   │   └── ProductReview.java                # 商品评价POJO
│   │   ├── harness/
│   │   │   ├── rules/
│   │   │   │   └── ExtractionRules.md            # 规则文件(软约束)
│   │   │   ├── skills/
│   │   │   │   └── ReviewAnalysisSkill.java      # 评价分析技能
│   │   │   ├── gates/
│   │   │   │   └── OutputValidator.java          # 输出门禁(硬约束)
│   │   │   └── tools/
│   │   │       ├── WeatherTool.java              # 天气查询工具
│   │   │       └── CalculatorTool.java           # 计算器工具
│   │   └── evaluation/
│   │       ├── QualityScorer.java                # 质量评分器
│   │       └── FeedbackLogger.java               # 反馈日志
│   └── resources/
│       ├── application.yml
│       └── harness/rules/
│           └── ExtractionRules.md

四、完整代码实现

4.1 Maven 项目配置(pom.xml)

xml 复制代码
<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
    </parent>

    <groupId>com.badao.ai</groupId>
    <artifactId>spring-ai-harness-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
        <jackson.version>2.17.2</jackson.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.fasterxml.jackson</groupId>
                <artifactId>jackson-bom</artifactId>
                <version>${jackson.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI Alibaba Agent Framework -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-agent-framework</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- DashScope 模型适配器 -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

注意 :必须显式管理 Jackson 版本,避免版本冲突导致 NoSuchMethodError

4.2 配置文件(application.yml)

yaml 复制代码
server:
  port: 885

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-max

logging:
  level:
    com.alibaba.cloud.ai: debug
    com.badao.ai: debug

4.3 数据模型(POJO)

ContactInfo.java(简单 POJO)
java 复制代码
package com.badao.ai.model;

public class ContactInfo {
    private String name;
    private String email;
    private String phone;

    public ContactInfo() {}

    public ContactInfo(String name, String email, String phone) {
        this.name = name;
        this.email = email;
        this.phone = phone;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getPhone() { return phone; }
    public void setPhone(String phone) { this.phone = phone; }

    @Override
    public String toString() {
        return "ContactInfo{name='" + name + "', email='" + email + "', phone='" + phone + "'}";
    }
}
ProductReview.java(嵌套 POJO)
java 复制代码
package com.badao.ai.model;

import java.util.Arrays;

public class ProductReview {
    private int rating;
    private String sentiment;  // "positive", "neutral", "negative"
    private String[] keyPoints;
    private ReviewDetails details;

    public ProductReview() {}

    public int getRating() { return rating; }
    public void setRating(int rating) { this.rating = rating; }
    public String getSentiment() { return sentiment; }
    public void setSentiment(String sentiment) { this.sentiment = sentiment; }
    public String[] getKeyPoints() { return keyPoints; }
    public void setKeyPoints(String[] keyPoints) { this.keyPoints = keyPoints; }
    public ReviewDetails getDetails() { return details; }
    public void setDetails(ReviewDetails details) { this.details = details; }

    public static class ReviewDetails {
        private String[] pros;
        private String[] cons;
        private String summary;

        public ReviewDetails() {}

        public String[] getPros() { return pros; }
        public void setPros(String[] pros) { this.pros = pros; }
        public String[] getCons() { return cons; }
        public void setCons(String[] cons) { this.cons = cons; }
        public String getSummary() { return summary; }
        public void setSummary(String summary) { this.summary = summary; }

        @Override
        public String toString() {
            return "ReviewDetails{pros=" + Arrays.toString(pros) +
                   ", cons=" + Arrays.toString(cons) +
                   ", summary='" + summary + "'}";
        }
    }

    @Override
    public String toString() {
        return "ProductReview{rating=" + rating +
               ", sentiment='" + sentiment + "'" +
               ", keyPoints=" + Arrays.toString(keyPoints) +
               ", details=" + details + "}";
    }
}

4.4 Harness 核心组件

① 规则层(软约束):ExtractionRules.md
markdown 复制代码
# 联系人提取规则

## 必填字段
- name: 必须提取完整姓名(中文或英文)
- email: 必须提取完整邮箱地址
- phone: 必须提取完整电话号码(含区号)

## 格式要求
- 所有字段值必须去除首尾空格
- 电话号统一为字符串格式,保留原始格式

## 禁止行为
- 不要编造任何字段值
- 如果某个字段在原文中找不到,设置为空字符串 ""
② 技能层(半硬约束):ReviewAnalysisSkill.java
java 复制代码
package com.badao.ai.harness.skills;

import com.badao.ai.model.ProductReview;
import org.springframework.stereotype.Component;

@Component
public class ReviewAnalysisSkill {

    public String buildPrompt(String reviewText) {
        return """
            请分析以下商品评价,按标准格式输出 JSON。

            【重要约束】
            1. 如果评价内容不足以提取优缺点,请基于评价的字面含义,给出合理的推断或直接输出空数组。
            2. keyPoints 必须是从评价中提取的具体产品维度(如"音质""价格""外观"),而不是对评价本身的描述。
            3. 如果评价少于5个字,你可以推断一个合理的评分,并在 sentiment 中标注 "neutral"。

            分析步骤:
            - 评分:1-5 整数
            - 情感:positive / neutral / negative
            - 关键点:至少1个,从评价中抽取具体维度
            - 优点/缺点:尽量提取,如果没有则保留空数组
            - 总结:一句话概括整体评价

            评价文本:
            """ + reviewText;
    }

    public ProductReview postProcess(ProductReview review) {
        if (review.getRating() < 1) review.setRating(1);
        if (review.getRating() > 5) review.setRating(5);
        if (review.getKeyPoints() == null || review.getKeyPoints().length == 0) {
            review.setKeyPoints(new String[]{"无关键点"});
        }
        if (review.getDetails() == null) {
            ProductReview.ReviewDetails details = new ProductReview.ReviewDetails();
            details.setPros(new String[0]);
            details.setCons(new String[0]);
            details.setSummary("无总结");
            review.setDetails(details);
        }
        return review;
    }
}
③ 门禁层(硬约束):OutputValidator.java
java 复制代码
package com.badao.ai.harness.gates;

import com.badao.ai.model.ContactInfo;
import com.badao.ai.model.ProductReview;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;

@Component
public class OutputValidator {

    public List<String> validateContact(ContactInfo contact) {
        List<String> errors = new ArrayList<>();
        if (contact == null) { errors.add("联系人信息为空"); return errors; }
        if (contact.getName() == null || contact.getName().trim().isEmpty())
            errors.add("姓名不能为空");
        if (contact.getEmail() == null || !contact.getEmail().contains("@"))
            errors.add("邮箱格式无效");
        if (contact.getPhone() == null || contact.getPhone().trim().isEmpty())
            errors.add("电话不能为空");
        return errors;
    }

    public List<String> validateReview(ProductReview review) {
        List<String> errors = new ArrayList<>();
        if (review == null) { errors.add("评价信息为空"); return errors; }
        if (review.getRating() < 1 || review.getRating() > 5)
            errors.add("评分必须在 1-5 之间");
        String sentiment = review.getSentiment();
        if (sentiment == null || !(sentiment.equals("positive") ||
            sentiment.equals("neutral") || sentiment.equals("negative")))
            errors.add("情感倾向必须是 positive/neutral/negative 之一");
        if (review.getKeyPoints() == null || review.getKeyPoints().length == 0)
            errors.add("关键点不能为空");
        return errors;
    }

    public boolean isValid(List<String> errors) {
        return errors == null || errors.isEmpty();
    }
}
④ 工具层:WeatherTool.java
java 复制代码
package com.badao.ai.harness.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class WeatherTool {

    @Tool(description = "根据城市名称查询当前天气")
    public String getWeather(@ToolParam(description = "城市名称,如Beijing") String city) {
        if ("Beijing".equalsIgnoreCase(city)) {
            return "北京:晴,25°C,湿度40%";
        } else if ("Shanghai".equalsIgnoreCase(city)) {
            return "上海:多云,28°C,湿度65%";
        } else {
            return city + ":天气未知,请稍后再查";
        }
    }
}
⑤ 工具层:CalculatorTool.java
java 复制代码
package com.badao.ai.harness.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class CalculatorTool {

    @Tool(description = "执行基本的四则运算")
    public double calculate(
            @ToolParam(description = "第一个操作数") double a,
            @ToolParam(description = "运算符,支持 + - * /") String operator,
            @ToolParam(description = "第二个操作数") double b) {
        return switch (operator) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("不支持的运算符: " + operator);
        };
    }
}

4.5 Agent 配置类

HarnessAgentConfig.java(单 Agent 配置)
java 复制代码
package com.badao.ai.config;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.badao.ai.model.ContactInfo;
import com.badao.ai.model.ProductReview;
import com.badao.ai.harness.skills.ReviewAnalysisSkill;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Configuration
public class HarnessAgentConfig {

    private final ReviewAnalysisSkill reviewAnalysisSkill;

    public HarnessAgentConfig(ReviewAnalysisSkill reviewAnalysisSkill) {
        this.reviewAnalysisSkill = reviewAnalysisSkill;
    }

    @Bean
    public ReactAgent contactAgent(ChatModel chatModel) throws IOException {
        String rules = loadRules("harness/rules/ExtractionRules.md");
        String systemPrompt = "你是一个联系人信息提取专家。请严格遵循以下规则:\n" + rules;
        return ReactAgent.builder()
                .name("contact_extractor")
                .model(chatModel)
                .systemPrompt(systemPrompt)
                .outputType(ContactInfo.class)
                .saver(new MemorySaver())
                .build();
    }

    @Bean
    public ReactAgent reviewAgent(ChatModel chatModel) {
        BeanOutputConverter<ProductReview> converter =
                new BeanOutputConverter<>(ProductReview.class);
        String schema = converter.getFormat();
        return ReactAgent.builder()
                .name("review_analyzer")
                .model(chatModel)
                .systemPrompt("你是一个商品评价分析专家,必须按以下 JSON Schema 格式输出:\n" + schema)
                .outputSchema(schema)
                .saver(new MemorySaver())
                .build();
    }

    private String loadRules(String path) throws IOException {
        ClassPathResource resource = new ClassPathResource(path);
        return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
    }
}
MultiAgentConfig.java(多 Agent 协同配置)
java 复制代码
package com.badao.ai.config;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.badao.ai.harness.tools.CalculatorTool;
import com.badao.ai.harness.tools.WeatherTool;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MultiAgentConfig {

    @Bean
    public ReactAgent plannerAgent(ChatModel chatModel) {
        return ReactAgent.builder()
                .name("planner")
                .model(chatModel)
                .systemPrompt("""
                        你是一个任务规划专家。用户会给出一个复杂需求,你需要将其拆解为1~3个明确的子任务,
                        并以JSON数组格式输出,每个子任务包含:description(描述)和 assigned_to(执行角色,只能是"executor")。
                        示例输出:[{"description":"查询北京天气","assigned_to":"executor"}]
                        """)
                .saver(new MemorySaver())
                .build();
    }

    @Bean
    public ReactAgent executorAgent(ChatModel chatModel,
                                    WeatherTool weatherTool,
                                    CalculatorTool calculatorTool) {
        return ReactAgent.builder()
                .name("executor")
                .model(chatModel)
                .systemPrompt("你是一个执行专家,负责具体执行用户分配的任务。你可以使用工具完成工作。")
                .methodTools(weatherTool, calculatorTool)  // 自动扫描 @Tool 注解
                .saver(new MemorySaver())
                .build();
    }
}

4.6 Service 层

HarnessAgentService.java(核心业务服务)
java 复制代码
package com.badao.ai.service;

import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.badao.ai.model.ContactInfo;
import com.badao.ai.model.ProductReview;
import com.badao.ai.harness.gates.OutputValidator;
import com.badao.ai.harness.skills.ReviewAnalysisSkill;
import com.badao.ai.evaluation.QualityScorer;
import com.badao.ai.evaluation.FeedbackLogger;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class HarnessAgentService {

    private final ReactAgent contactAgent;
    private final ReactAgent reviewAgent;
    private final ReviewAnalysisSkill reviewAnalysisSkill;
    private final OutputValidator outputValidator;
    private final ObjectMapper objectMapper;
    private final QualityScorer qualityScorer;
    private final FeedbackLogger feedbackLogger;

    public HarnessAgentService(ReactAgent contactAgent, ReactAgent reviewAgent,
                               ReviewAnalysisSkill reviewAnalysisSkill,
                               OutputValidator outputValidator,
                               ObjectMapper objectMapper,
                               QualityScorer qualityScorer,
                               FeedbackLogger feedbackLogger) {
        this.contactAgent = contactAgent;
        this.reviewAgent = reviewAgent;
        this.reviewAnalysisSkill = reviewAnalysisSkill;
        this.outputValidator = outputValidator;
        this.objectMapper = objectMapper;
        this.qualityScorer = qualityScorer;
        this.feedbackLogger = feedbackLogger;
    }

    // ========== 联系人提取 ==========
    public ContactInfo extractContact(String text, String sessionId) {
        RunnableConfig config = RunnableConfig.builder().threadId(sessionId).build();
        AssistantMessage response;
        try {
            response = contactAgent.call(text, config);
        } catch (GraphRunnerException e) {
            throw new RuntimeException("Agent 执行失败: " + e.getMessage(), e);
        }
        String json = response.getText();
        ContactInfo contact;
        try {
            contact = objectMapper.readValue(json, ContactInfo.class);
        } catch (Exception e) {
            throw new RuntimeException("解析结构化输出失败,原始 JSON: " + json, e);
        }
        // Gate 门禁校验
        List<String> errors = outputValidator.validateContact(contact);
        if (!outputValidator.isValid(errors)) {
            throw new RuntimeException("输出校验失败: " + String.join("; ", errors));
        }
        return contact;
    }

    public String extractContactRaw(String text, String sessionId) {
        RunnableConfig config = RunnableConfig.builder().threadId(sessionId).build();
        try {
            AssistantMessage response = contactAgent.call(text, config);
            return response.getText();
        } catch (GraphRunnerException e) {
            throw new RuntimeException("Agent 执行失败: " + e.getMessage(), e);
        }
    }

    // ========== 商品评价分析 ==========
    public ProductReview analyzeReview(String reviewText, String sessionId) {
        RunnableConfig config = RunnableConfig.builder().threadId(sessionId).build();
        String prompt = reviewAnalysisSkill.buildPrompt(reviewText);
        AssistantMessage response;
        try {
            response = reviewAgent.call(prompt, config);
        } catch (GraphRunnerException e) {
            throw new RuntimeException("Agent 执行失败: " + e.getMessage(), e);
        }
        String json = response.getText();
        ProductReview review;
        try {
            review = objectMapper.readValue(json, ProductReview.class);
        } catch (Exception e) {
            throw new RuntimeException("解析结构化输出失败,原始 JSON: " + json, e);
        }
        // Skill 后处理
        review = reviewAnalysisSkill.postProcess(review);
        // Gate 门禁校验
        List<String> errors = outputValidator.validateReview(review);
        if (!outputValidator.isValid(errors)) {
            throw new RuntimeException("输出校验失败: " + String.join("; ", errors));
        }
        return review;
    }

    // ========== 带评估的评价分析 ==========
    public ProductReview analyzeReviewWithEval(String reviewText, String sessionId) {
        ProductReview review = analyzeReview(reviewText, sessionId);
        // 质量评估
        int score = qualityScorer.scoreReview(review);
        if (score < 3) {
            feedbackLogger.logFailedCase(reviewText, review, "质量评分过低: " + score);
            feedbackLogger.triggerHumanReview(reviewText, review);
            throw new RuntimeException("质量门禁未通过(得分" + score + "/4),已转人工复核");
        }
        return review;
    }
}
MultiAgentService.java(多 Agent 协同服务)
java 复制代码
package com.badao.ai.service;

import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class MultiAgentService {

    private final ReactAgent plannerAgent;
    private final ReactAgent executorAgent;
    private final ObjectMapper objectMapper;

    public MultiAgentService(ReactAgent plannerAgent, ReactAgent executorAgent,
                             ObjectMapper objectMapper) {
        this.plannerAgent = plannerAgent;
        this.executorAgent = executorAgent;
        this.objectMapper = objectMapper;
    }

    public String executeComplexTask(String userRequest, String sessionId) {
        RunnableConfig config = RunnableConfig.builder().threadId(sessionId).build();

        // 1. 规划阶段
        AssistantMessage planResponse;
        try {
            planResponse = plannerAgent.call("请拆解以下任务:" + userRequest, config);
        } catch (GraphRunnerException e) {
            throw new RuntimeException("规划失败: " + e.getMessage(), e);
        }
        String planJson = planResponse.getText();

        // 解析规划结果
        List<JsonNode> tasks;
        try {
            tasks = objectMapper.readValue(planJson, objectMapper.getTypeFactory()
                    .constructCollectionType(List.class, JsonNode.class));
        } catch (Exception e) {
            throw new RuntimeException("规划结果解析失败: " + planJson, e);
        }

        // 2. 执行阶段
        List<String> results = new ArrayList<>();
        for (JsonNode task : tasks) {
            String description = task.get("description").asText();
            String assignedTo = task.get("assigned_to").asText();
            if (!"executor".equals(assignedTo)) {
                results.add("跳过不支持的角色: " + assignedTo);
                continue;
            }
            AssistantMessage execResponse;
            try {
                execResponse = executorAgent.call(description, config);
            } catch (GraphRunnerException e) {
                results.add("执行子任务失败: " + description + ",错误: " + e.getMessage());
                continue;
            }
            results.add(execResponse.getText());
        }

        return String.join("\n", results);
    }
}

4.7 评估与反馈组件

QualityScorer.java
java 复制代码
package com.badao.ai.evaluation;

import com.badao.ai.model.ProductReview;
import org.springframework.stereotype.Component;

@Component
public class QualityScorer {

    public int scoreReview(ProductReview review) {
        int score = 0;
        if (review.getRating() >= 1 && review.getRating() <= 5) score += 1;
        if (review.getSentiment() != null) score += 1;
        if (review.getKeyPoints() != null && review.getKeyPoints().length >= 2) score += 1;
        if (review.getDetails() != null && review.getDetails().getSummary() != null) score += 1;
        return score; // 满分4分
    }
}
FeedbackLogger.java
java 复制代码
package com.badao.ai.evaluation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class FeedbackLogger {

    private static final Logger logger = LoggerFactory.getLogger(FeedbackLogger.class);

    public void logFailedCase(Object input, Object output, String reason) {
        logger.warn("失败案例 - 输入: {}, 输出: {}, 原因: {}", input, output, reason);
    }

    public void triggerHumanReview(Object input, Object output) {
        logger.info("人工复核触发 - 输入: {}, 输出: {}", input, output);
    }
}

4.8 Controller 层

java 复制代码
package com.badao.ai.controller;

import com.badao.ai.model.ContactInfo;
import com.badao.ai.model.ProductReview;
import com.badao.ai.service.HarnessAgentService;
import com.badao.ai.service.MultiAgentService;
import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/api/harness")
public class HarnessController {

    private final HarnessAgentService harnessAgentService;
    private final MultiAgentService multiAgentService;

    public HarnessController(HarnessAgentService harnessAgentService,
                             MultiAgentService multiAgentService) {
        this.harnessAgentService = harnessAgentService;
        this.multiAgentService = multiAgentService;
    }

    // ===== 联系人提取 =====
    @PostMapping("/contact")
    public Map<String, Object> extractContact(
            @RequestParam String text,
            @RequestParam(required = false, defaultValue = "default") String sessionId) {
        ContactInfo contact = harnessAgentService.extractContact(text, sessionId);
        return Map.of("success", true, "data", contact, "sessionId", sessionId);
    }

    @PostMapping("/contact/raw")
    public Map<String, Object> extractContactRaw(
            @RequestParam String text,
            @RequestParam(required = false, defaultValue = "default") String sessionId) {
        String json = harnessAgentService.extractContactRaw(text, sessionId);
        return Map.of("success", true, "json", json, "sessionId", sessionId);
    }

    // ===== 商品评价分析 =====
    @PostMapping("/review")
    public Map<String, Object> analyzeReview(
            @RequestParam String reviewText,
            @RequestParam(required = false, defaultValue = "default") String sessionId) {
        ProductReview review = harnessAgentService.analyzeReview(reviewText, sessionId);
        return Map.of("success", true, "data", review, "sessionId", sessionId);
    }

    @PostMapping("/review/eval")
    public Map<String, Object> analyzeReviewWithEval(
            @RequestParam String reviewText,
            @RequestParam(required = false, defaultValue = "default") String sessionId) {
        ProductReview review = harnessAgentService.analyzeReviewWithEval(reviewText, sessionId);
        return Map.of("success", true, "data", review, "sessionId", sessionId);
    }

    // ===== 多 Agent 协同 =====
    @PostMapping("/complex")
    public Map<String, Object> complexTask(
            @RequestParam String request,
            @RequestParam(required = false, defaultValue = "default") String sessionId) {
        String result = multiAgentService.executeComplexTask(request, sessionId);
        return Map.of("success", true, "result", result, "sessionId", sessionId);
    }
}

4.9 启动类

java 复制代码
package com.badao.ai;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringAiHarnessDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAiHarnessDemoApplication.class, args);
    }
}

五、七层架构深度解析

5.1 执行主循环层

知识点:这是 Agent 运行的"大脑",负责"规划-执行-反思"的 ReAct 循环。

代码体现

java 复制代码
// ReactAgent 内置了 ReAct 循环
ReactAgent.builder()
    .model(chatModel)      // 绑定模型
    .saver(new MemorySaver())  // 状态管理,支持错误恢复
    .build();

最佳实践

  • 避免无规划的扁平串行循环,必须加入前置规划和后置反思
  • 所有 LLM 调用应内置分级重试与降级策略
  • 使用 MemorySaver 实现状态持久化

5.2 工具编排层

知识点:Agent 与外部世界交互的"手脚",需要建立边界管控、权限校验和结果处理体系。

代码体现

java 复制代码
// 使用 @Tool 注解定义工具
@Tool(description = "根据城市名称查询当前天气")
public String getWeather(@ToolParam(description = "城市名称") String city) { ... }

// 在 Agent 中注册工具
.methodTools(weatherTool, calculatorTool)

最佳实践

  • 为每个工具定义清晰的 description,帮助模型决策
  • 工具结果应进行结构化处理和异常捕获
  • 建立工具黑白名单机制,防止危险操作

5.3 项目搭建层

知识点:标准化项目的"地基",统一项目结构、依赖和规范。

代码体现

xml 复制代码
<!-- pom.xml 统一版本管理 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
</parent>

<properties>
    <java.version>17</java.version>
    <spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
</properties>
yaml 复制代码
# application.yml 环境配置
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}  # 环境变量注入

最佳实践

  • 使用父 POM 统一管理依赖版本
  • 敏感信息通过环境变量注入,实现环境隔离
  • 建立模板仓库,包含基础依赖、安全策略和监控配置

5.4 上下文工程层

知识点:Agent 的"记忆"与"知识库",管理、优化注入给模型的信息。

代码体现

java 复制代码
// 1. 加载规则文件作为静态上下文
String rules = loadRules("harness/rules/ExtractionRules.md");
String systemPrompt = "你是一个联系人信息提取专家。\n" + rules;

// 2. Skill 构建结构化提示词
public String buildPrompt(String reviewText) {
    return """
        请分析以下商品评价,按标准格式输出 JSON。
        分析步骤:
        1. 提取评分(1-5星整数)
        2. 判断情感倾向:positive / neutral / negative
        ...
        """ + reviewText;
}

最佳实践

  • 使用分层缓存:基础层(项目配置)+ 会话层(当前任务)+ 临时层(用户输入)
  • 构建知识图谱,让 Agent 理解项目全貌
  • 对上下文进行版本控制,便于追溯

5.5 约束与防护层

知识点:Agent 的"安全护栏",硬性校验输入输出,防止失控。

代码体现

java 复制代码
// Gate 门禁校验
public List<String> validateContact(ContactInfo contact) {
    List<String> errors = new ArrayList<>();
    if (contact.getName() == null || contact.getName().trim().isEmpty())
        errors.add("姓名不能为空");
    if (contact.getEmail() == null || !contact.getEmail().contains("@"))
        errors.add("邮箱格式无效");
    return errors;
}

// 在 Service 中执行熔断
List<String> errors = outputValidator.validateContact(contact);
if (!outputValidator.isValid(errors)) {
    throw new RuntimeException("输出校验失败: " + String.join("; ", errors));
}

最佳实践

  • 输入验证:正则表达式过滤非法请求
  • 输出过滤:检查敏感信息泄露或危险操作
  • 熔断机制:异常行为自动触发回滚或降级

5.6 多 Agent 架构层

知识点:团队协作的"管理模式",让多个专业 Agent 协同工作。

代码体现

java 复制代码
// 主从模式
@Bean
public ReactAgent plannerAgent(ChatModel chatModel) {
    return ReactAgent.builder()
        .name("planner")
        .systemPrompt("你是一个任务规划专家...")
        .build();
}

@Bean
public ReactAgent executorAgent(ChatModel chatModel) {
    return ReactAgent.builder()
        .name("executor")
        .systemPrompt("你是一个执行专家...")
        .methodTools(weatherTool, calculatorTool)
        .build();
}

最佳实践

  • 主从模式:主 Agent 分配任务,子 Agent 执行
  • 标准化通信协议,定义清晰的任务描述格式
  • 建立冲突解决机制,处理子 Agent 结果不一致的情况

5.7 评估与反馈层

知识点:持续优化的"学习闭环",通过测试和反馈不断改进系统。

代码体现

java 复制代码
// 质量评分
public int scoreReview(ProductReview review) {
    int score = 0;
    if (review.getRating() >= 1 && review.getRating() <= 5) score += 1;
    if (review.getSentiment() != null) score += 1;
    if (review.getKeyPoints() != null && review.getKeyPoints().length >= 2) score += 1;
    if (review.getDetails() != null && review.getDetails().getSummary() != null) score += 1;
    return score;
}

// 质量门禁
if (score < 3) {
    feedbackLogger.logFailedCase(input, output, "质量评分过低");
    feedbackLogger.triggerHumanReview(input, output);
    throw new RuntimeException("质量门禁未通过");
}

最佳实践

  • 建立多维度的评估指标(准确性、完整性、安全性)
  • 设置质量门禁阈值,未达标触发人工复核
  • 收集用户修改行为,作为后续微调的数据

六、运行与测试

6.1 启动应用

在项目根目录执行:

cmd 复制代码
mvn clean package
java -jar target/spring-ai-harness-demo-1.0.0.jar

或在 IDEA 中直接运行 SpringAiHarnessDemoApplication

6.2 测试接口

① 联系人提取
cmd 复制代码
curl -X POST "http://localhost:885/api/harness/contact?text=从以下信息提取联系方式:王五,wangwu@outlook.com,+86 139-9999-8888&sessionId=test01"

预期返回

json 复制代码
{
  "success": true,
  "data": {
    "name": "王五",
    "email": "wangwu@outlook.com",
    "phone": "+86 139-9999-8888"
  },
  "sessionId": "test01"
}
② 商品评价分析
cmd 复制代码
curl -X POST "http://localhost:885/api/harness/review?reviewText=这款耳机音质不错,降噪效果好,但佩戴舒适度一般,价格略高。&sessionId=test02"

预期返回

json 复制代码
{
  "success": true,
  "data": {
    "rating": 4,
    "sentiment": "positive",
    "keyPoints": ["音质不错", "降噪效果好", "佩戴舒适度一般", "价格略高"],
    "details": {
      "pros": ["音质不错", "降噪效果好"],
      "cons": ["佩戴舒适度一般", "价格略高"],
      "summary": "整体满意,舒适度和价格有改进空间"
    }
  },
  "sessionId": "test02"
}
③ 带质量评估的评价分析
cmd 复制代码
curl -X POST "http://localhost:885/api/harness/review/eval?reviewText=还可以吧&sessionId=eval01"

预期返回(通过质量门禁时):

json 复制代码
{
  "success": true,
  "data": {
    "rating": 3,
    "sentiment": "neutral",
    "keyPoints": ["态度中立", "缺乏细节"],
    "details": {
      "pros": [],
      "cons": [],
      "summary": "评价者仅表示"还可以吧",态度中立,没有明确指出优劣。"
    }
  },
  "sessionId": "eval01"
}
④ 多 Agent 协同
cmd 复制代码
curl -X POST "http://localhost:885/api/harness/complex?request=查询北京天气并计算25+17&sessionId=multi01"

预期返回

json 复制代码
{
  "success": true,
  "result": "北京:晴,25°C,湿度40%\n42.0",
  "sessionId": "multi01"
}

七、常见问题与解决方案

问题 1:Cannot resolve method 'tools(List<E>)'

原因ReactAgent.Buildertools 方法不直接接受 Spring Bean 实例。

解决方案 :使用 methodTools 方法,它会自动扫描 @Tool 注解。

java 复制代码
// ❌ 错误
.tools(weatherToolCallback, calculatorToolCallback)

// ✅ 正确
.methodTools(weatherTool, calculatorTool)

问题 2:Cannot resolve method 'builder(String, WeatherTool)'

原因FunctionToolCallback.builder("getWeather", weatherTool) 的 API 在当前版本不支持。

解决方案 :改用 methodTools,或使用 lambda 方式构建。

问题 3:解析结构化输出失败

原因:模型返回的 JSON 格式与 POJO 不完全匹配。

解决方案

  1. systemPrompt 中明确要求 JSON 格式
  2. 使用 outputTypeoutputSchema 强制格式
  3. postProcess 中增加兜底处理

问题 4:质量门禁总是失败

原因:评估阈值设置过高或评分逻辑过严。

解决方案

  1. 根据实际测试结果调整阈值
  2. 增加评分维度,使评分更精确
  3. 区分"必须通过"和"建议通过"的指标

八、总结与进阶方向

8.1 总结

通过本示例,我们完成了:

目标 完成情况
理解 Harness Engineering 概念 ✅ 核心组件 + 七层架构
Windows 本地环境搭建 ✅ JDK 17 + Maven + DashScope
完整代码实现 ✅ 6 个模块,20+ 个类
七层架构落地 ✅ 每层都有对应的代码实现
运行与测试 ✅ 4 个接口均可正常调用

8.2 Harness Engineering 在本示例中的映射

Harness 组件 实现位置 作用
Rules(规则) ExtractionRules.md 软约束,告诉 Agent 必须提取哪些字段
Skills(技能) ReviewAnalysisSkill.java 半硬约束,封装标准化提示词和后处理
Gate(门禁) OutputValidator.java 硬约束,强制校验输出结构
State(状态) MemorySaver + threadId 管理会话上下文
Instructions(指令) systemPrompt + outputType 明确输出格式和行为边界
Verification(验证) 异常处理 + Gate 校验 只有通过才算任务完成
Tools(工具) WeatherTool + CalculatorTool 扩展 Agent 能力边界
Evaluation(评估) QualityScorer 量化输出质量
Feedback(反馈) FeedbackLogger 收集失败案例,触发人工复核

8.3 进阶方向

  1. 动态规则加载:将 Rules 从本地文件迁移到配置中心,实现热更新
  2. 更复杂的工具集成:接入数据库查询、Web 搜索、文件读写等
  3. 多 Agent 深度协作:实现流水线模式或对等协商模式
  4. 模型微调:将失败案例加入训练集,提升模型表现
  5. 可观测性:集成 Prometheus + Grafana 监控 Agent 运行状态
  6. 大规模部署:使用 Kubernetes 部署多个 Harness 实例

参考资源

相关推荐
linuxoffer1 小时前
MYSQL回收碎片空间
数据库·mysql
hangyuekejiGEO1 小时前
临沂GEO技术服务方案对比分析
大数据·人工智能·python
happyprince1 小时前
08-PEFT源码阅读-使用指南与实战案例
人工智能·peft
IT_陈寒1 小时前
Vue的响应式更新把我坑惨了,原来是这个原因
前端·人工智能·后端
weixin_549808361 小时前
AI招聘的下半场:当“匹配”从技巧变成组织能力
人工智能
武子康1 小时前
调查研究-217 Fortress:当浏览器 Agent 开始被网站识别,开源 Chromium 反拦截引擎来了
人工智能·爬虫·agent
名字还没想好☜1 小时前
Go 并发实战:用 channel 实现 worker pool
java·数据库·后端·golang·go
大龄牛码1 小时前
AI大模型在金融领域的垂直应用实践:从信息检索到自动化报告
人工智能·金融·自动化
新知图书2 小时前
多模态智能体开发的核心挑战与解决方案
人工智能·agent·多模态·ai agent·智能体·langgraph