【第42篇】Graph - big-tool
一、项目概述:AI 的"工具箱"思维
1.1 什么是 big-tool?
“25 的 5 次方是多少”?高手不会凭空算,他会拿起计算器(工具),输入 25^5,然后告诉你结果 9765625。
big-tool 项目模拟的就是这个过程——让 AI 学会"拿起合适的工具"来解决问题。
1.2 项目定位
| 属性 | 说明 |
|---|---|
| 定位 | Spring AI Alibaba Graph 框架的工具调用入门示例 |
| 核心能力 | 智能数学计算助手——AI 自动识别数学问题,调用 Java 的 Math 类方法 |
| 学习价值 | 理解 Function Calling(函数调用) 的核心概念 |
| 难度 | ⭐⭐ 初级(比 chatflow 简单,但引入了"向量检索"新概念) |
| 端口 | 8080 |
1.3 核心功能一览
| 功能阶段 | 说明 | 类比人类行为 |
|---|---|---|
| 关键词提取 | 从用户问题中提炼关键信息 | 学生读题,圈出重点 |
| 向量检索 | 在"工具知识库"中语义搜索 | 在工具箱里翻找合适的螺丝刀 |
| 智能选工具 | 自动匹配最恰当的 Math 方法 | 工匠选择最佳工具 |
| 自动执行 | 调用选中的方法完成计算 | 动手拧螺丝 |
二、技术架构:从 HTTP 请求到计算结果的完整旅程
2.1 整体架构:四层协作
2.2 核心流程详解:一个请求的完整生命周期
让我们跟踪一个真实请求:"25的5次方是多少"
2.3 状态流转:数据如何在节点间传递
三、核心代码深度解析
3.1 向量库初始化:给 AI 建一个"工具说明书"仓库
为什么要做这件事?
AI 不认识 Java 的 Math.pow 方法。我们需要给每个方法写"说明书",存入向量库。当用户问数学问题时,AI 能通过语义搜索找到最相关的说明书。
核心代码详解:
private void initializeVectorStore() {
List<Tool> allTools = new ArrayList<>();
// 🔍 反射:像镜子一样照出 Math 类的所有方法
for (Method method : Math.class.getMethods()) {
// 只拿静态方法(不需要创建对象就能调用的)
if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
// 把 Method 对象转换成我们定义的 Tool 对象
Tool tool = MethodUtils.convertMethodToTool(method);
if (tool != null) {
allTools.add(tool);
}
}
}
// 📚 每个 Tool 变成一份"文档"存入向量库
allTools.forEach(tool -> documents.add(new Document(
IdUtil.fastSimpleUUID(), // 唯一 ID
tool.getDescription(), // 📄 文档内容:方法的描述文字
Map.of( // 🏷️ 元数据:额外信息
Constant.METHOD_NAME, tool.getName(),
Constant.METHOD_PARAMETER_TYPES, tool.getParameterTypes()
)
)));
// 💾 批量存入向量库(这里会调用 Embedding 模型把文字转成向量)
vectorStoreService.addDocuments(documents);
}
MethodUtils.convertMethodToTool 做了什么?
它把 Java 方法的元数据翻译成人类能读懂的描述:
| Java 方法 | 生成的 Tool 描述 |
|---|---|
Math.pow(double a, double b) |
“返回第一个参数的第二个参数次幂的值” |
Math.sqrt(double a) |
“返回正确舍入的 double 值的正平方根” |
Math.sin(double a) |
“返回角度的三角正弦值” |
设计亮点与改进建议:
3.2 工具选择节点(ToolAgent):AI 的"读题圈重点"能力
这是整个系统的第一个智能节点。它的任务就像考试时的学生:先读题,圈出关键词,然后去工具箱找合适的工具。
完整代码解析:
public class ToolAgent implements NodeAction {
// 📝 给 LLM 的"角色设定"——你是一个关键词提取引擎
private static final String CLASSIFIER_PROMPT_TEMPLATE = """
### Job Description
You are a text keyword extraction engine. Your task is to analyze
user questions and extract the most relevant keywords for
searching mathematical tools.
### Task
Extract one or more keywords from this sentence that best describe
the mathematical operation needed.
### Constraint
- Return ONLY keywords, separated by spaces
- Include both Chinese and English terms when applicable
- Example: "25的5次方" → "25 5 次方 power exponent"
""";
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 1️⃣ 从状态中获取用户原始输入
this.inputText = (String) state.value(inputTextKey).orElse(this.inputText);
// 2️⃣ 调用 LLM 提取关键词(第一次 AI 调用)
ChatResponse response = chatClient.prompt()
.system(CLASSIFIER_PROMPT_TEMPLATE) // 给 AI 设定角色
.user(inputText) // 用户问题
.call() // 执行调用
.chatResponse();
String keywords = response.getResult().getOutput().getText();
// 假设返回:"25 5 次方 power"
// 3️⃣ 用关键词去向量库搜索(语义匹配,不是精确匹配)
List<Document> hitTool = vectorStoreService.search(keywords, 3);
// 返回最相关的 3 个工具:
// [Math.pow(次方), Math.exp(指数), Math.sqrt(平方根)...]
// 4️⃣ 更新状态,传递给下一个节点
Map<String, Object> updatedState = new HashMap<>();
updatedState.put(Constant.HIT_TOOL, hitTool); // 🎯 候选工具列表
updatedState.put(inputTextKey, keywords); // 📝 提取的关键词
return updatedState; // ➡️ 流向 calculate_agent 节点
}
}
为什么用"两阶段选择"?
直接让用户问题搜向量库效果不好(句子太长,噪音多)。先提炼关键词,就像搜索引擎的"query 理解",能大幅提升检索准确度。
3.3 计算节点(CalculateAgent):AI 的"动手解题"能力
这是第二个智能节点。ToolAgent 给了它一堆候选工具,它要决定用哪个、怎么传参数、最后算出结果。
根据架构推断的工作流程:
public class CalculateAgent implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 1️⃣ 读取上游传递的数据
String userQuestion = (String) state.value(Constant.INPUT_KEY).orElse("");
List<Document> candidateTools = (List<Document>) state.value(Constant.HIT_TOOL).orElse(List.of());
// 2️⃣ 构建提示词:告诉 AI 有哪些工具可选
String toolList = candidateTools.stream()
.map(doc -> doc.getMetadata().get(Constant.METHOD_NAME) + ": " + doc.getText())
.collect(Collectors.joining("\n"));
// 3️⃣ 调用 LLM 做"工具选择 + 参数解析"(第二次 AI 调用)
String prompt = """
用户问题:%s
可用工具列表:
%s
请分析:
1. 哪个工具最适合解决这个问题?
2. 参数应该是什么?
3. 直接返回工具调用结果。
""".formatted(userQuestion, toolList);
// 4️⃣ LLM 返回类似 "使用 Math.pow(25, 5),结果是 9765625"
// 或者直接返回可解析的 JSON:{"tool":"pow","args":[25,5]}
// 5️⃣ 解析 LLM 输出,反射调用真实 Java 方法
// 这里需要安全的反射执行逻辑...
// 6️⃣ 返回最终结果
Map<String, Object> result = new HashMap<>();
result.put(Constant.SOLUTION, calculatedResult);
return result;
}
}
3.4 主流程配置:把节点串成流水线
配置代码详解:
// 🗝️ 状态策略:定义每个 key 的更新方式
KeyStrategyFactory keyStrategyFactory = new KeyStrategyFactoryBuilder()
// ReplaceStrategy:新值直接覆盖旧值(适合单次计算场景)
.addPatternStrategy(Constant.INPUT_KEY, new ReplaceStrategy())
.addPatternStrategy(Constant.HIT_TOOL, new ReplaceStrategy())
.addPatternStrategy(Constant.SOLUTION, new ReplaceStrategy())
.addPatternStrategy(Constant.TOOL_LIST, new ReplaceStrategy())
.build();
// 🏗️ 构建状态图(StateGraph)
StateGraph stateGraph = new StateGraph("Consumer Service Workflow Demo", keyStrategyFactory)
// 注册节点:名字 + 异步包装
.addNode("tools", AsyncNodeAction.node_async(tools)) // 🔍 工具选择
.addNode("calculate_agent", AsyncNodeAction.node_async(calculateAgent)) // 🧮 计算执行
// 连接边:定义执行顺序
.addEdge(StateGraph.START, "tools") // 起点 → 工具选择
.addEdge("tools", "calculate_agent") // 工具选择 → 计算执行
.addEdge("calculate_agent", StateGraph.END); // 计算执行 → 终点
// 🔨 编译:生成可执行的图对象
this.compiledGraph = stateGraph.compile();
状态策略科普:
| 策略 | 作用 | 适用场景 |
|---|---|---|
ReplaceStrategy |
新值覆盖旧值 | 单次计算,不需要历史 |
AppendStrategy |
追加到列表 | 多轮对话,保留历史 |
MergeStrategy |
合并 Map | 复杂状态叠加 |
四、配置与运行
4.1 最小化配置
server:
port: 8080
spring:
application:
name: spring-ai-alibaba-graph-big-tool-example
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 🔑 从环境变量读取
为什么不需要配置 model?
Spring AI Alibaba 的自动配置会默认使用 qwen-turbo 或 qwen-plus,适合这种简单任务。
4.2 环境变量设置
# Linux/Mac
export AI_DASHSCOPE_API_KEY=sk-your-key
# Windows PowerShell
$env:AI_DASHSCOPE_API_KEY="sk-your-key"
# 验证
echo $AI_DASHSCOPE_API_KEY
五、接口调用与测试
5.1 接口规范
| 项目 | 说明 |
|---|---|
| 路径 | GET /bigtool/search |
| 参数 | query - 用户数学问题 |
| 返回 | 纯文本计算结果 |
| Content-Type | text/plain |
5.2 测试用例集
# 🟢 幂运算(最基本功能)
curl "http://localhost:8080/bigtool/search?query=25的5次方是多少"
# 预期:9765625
# 🟢 平方根
curl "http://localhost:8080/bigtool/search?query=16的平方根是多少"
# 预期:4.0
# 🟡 三角函数(考验关键词提取)
curl "http://localhost:8080/bigtool/search?query=sin(30度)是多少"
# 预期:0.5(注意:Math.sin 接受弧度,这里 AI 需要做转换)
# 🟡 对数运算
curl "http://localhost:8080/bigtool/search?query=log10(100)是多少"
# 预期:2.0
# 🔴 边界测试:非数学问题
curl "http://localhost:8080/bigtool/search?query=今天天气怎么样"
# 预期:可能返回空或报错(项目只注册了 Math 工具)
六、项目结构全景
七、扩展与优化路线图
7.1 短期优化(1-2 天)
7.2 中期扩展(1 周)
7.3 长期演进(1 月)
八、常见问题深度解析
Q1:为什么第一次请求很慢?
Q2:向量搜索没找到工具怎么办?
根本原因:关键词提取与工具描述的语义鸿沟。
Q3:AI 选错工具或参数怎么办?
这是 Function Calling 的经典难题。解决方案:
- Few-shot 提示:在 prompt 里给 3-5 个正确示例
- 强制 JSON 输出:要求 LLM 返回结构化数据
{"tool":"pow","args":[25,5]} - 参数校验:执行前检查参数类型和范围
- 重试机制:失败时换下一个候选工具
九、小结
9.1 核心概念图谱
9.2 与 chatflow 的对比
| 维度 | chatflow | big-tool |
|---|---|---|
| 核心能力 | 多轮对话 + 意图识别分支 | 工具调用 + 自动执行 |
| 流程结构 | 条件分支(if-else) | 线性流水线(两阶段) |
| AI 调用次数 | 每轮对话 1 次 | 单次请求 2 次(提取关键词 + 选择工具) |
| 状态管理 | 复杂(保留对话历史) | 简单(ReplaceStrategy 覆盖) |
| 适用场景 | 客服、问答 | 计算、工具执行 |
| 学习曲线 | ⭐⭐⭐ 中等 | ⭐⭐ 较简单 |
9.3 学完能做什么?
十、部署实操(精简版)
10.1 本地开发(3 分钟)
# 1. 克隆项目
git clone https://github.com/alibaba/spring-ai-alibaba.git
cd spring-ai-alibaba/spring-ai-alibaba-graph-example/big-tool
# 2. 配置环境变量
export AI_DASHSCOPE_API_KEY=sk-your-key
# 3. 编译运行
mvn clean spring-boot:run
# 4. 测试
curl "http://localhost:8080/bigtool/search?query=2的10次方"
# 输出:1024
10.2 Docker 部署(生产推荐)
# Dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENV AI_DASHSCOPE_API_KEY=""
ENTRYPOINT ["java", "-jar", "app.jar"]
# 构建 & 运行
docker build -t big-tool .
docker run -d -p 8080:8080 -e AI_DASHSCOPE_API_KEY=sk-xxx big-tool
10.3 Systemd 服务化(Linux 服务器)
# /etc/systemd/system/big-tool.service
[Unit]
Description=Big Tool AI Service
After=network.target
[Service]
Type=simple
User=app
WorkingDirectory=/opt/big-tool
ExecStart=/usr/bin/java -Xms512m -Xmx1g -jar big-tool.jar
Restart=always
Environment="AI_DASHSCOPE_API_KEY=sk-your-key"
[Install]
WantedBy=multi-user.target
sudo systemctl enable big-tool
sudo systemctl start big-tool
sudo systemctl status big-tool
附录:依赖详解
<!-- AI 能力:阿里云 DashScope -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Spring AI 自动配置 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
</dependency>
<!-- Graph 编排框架 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
<!-- 工具库:UUID 生成等 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.38</version>
</dependency>
<!-- HTML 解析(预留扩展) -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.18.3</version>
</dependency>
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)