# 让AI学会“动手”:企业级Agent编排实战
让AI学会“动手”:企业级Agent编排实战

引言:AI应用的新范式
如果说ChatGPT是能说会道的“嘴强王者”,那么Agent就是既能说又能干的“六边形战士”。
大模型的能力边界正在被不断刷新,从最初单纯的自然语言理解与生成,到如今能够调用外部工具、执行具体操作、完成复杂任务。这种转变的核心驱动力,正是Agent(智能体)技术——它让AI不再只是被动回答问题的聊天机器,而是能够自主规划、决策并采取行动的“数字员工”。
然而,从Demo到生产环境,中间隔着一道名为“工程化”的鸿沟。如何用Java生态优雅地构建企业级Agent?如何让AI稳定地调用几十甚至上百个工具?如何在复杂业务场景中编排工具调用流程?本文将结合Spring AI与LangChain两大框架,深入剖析企业级Agent构建与工具调用编排的实战之道。
一、Agent为何需要“工具”
在讨论技术实现之前,先厘清一个核心问题:为什么Agent必须依赖工具?
大模型的知识截至训练日期,无法获取实时信息,也无法执行实际操作。比如,当用户问“今天的天气如何,顺便帮我订一张机票”,大模型本身无法查询天气,更无法完成订票操作。
工具调用(Tool Calling / Function Calling)正是解决这一问题的关键机制:大模型识别用户意图后,生成结构化的工具调用请求,系统执行相应函数并返回结果,模型再基于结果生成最终回复。这个过程中,大模型扮演的是“大脑”角色——负责思考和决策,而工具则是“手和脚”——负责执行。
二、企业级Agent核心架构
无论是基于Spring AI还是LangChain,一个成熟的企业级Agent都需要具备以下核心模块:
| 模块 | 职责 | 关键技术 |
|---|---|---|
| 规划引擎 | 理解用户意图,拆解任务步骤 | Prompt工程、ReAct模式 |
| 工具注册表 | 管理所有可调用工具的元数据 | 注解驱动、动态注册 |
| 执行器 | 调用工具并处理返回结果 | 同步/异步执行、超时控制 |
| 记忆系统 | 维护对话上下文和工具调用历史 | 多级记忆压缩、向量存储 |
| 可观测性 | 记录调用链路、监控性能 | 日志、链路追踪、指标采集 |
架构设计的核心原则是“关注点分离”——将业务逻辑、AI推理和工具调度解耦,每个模块独立演进、可替换、可测试。
三、Spring AI实践:以ToolCallAdvisor为核心的Agent编排
Spring AI从1.1.0-M4版本开始引入递归顾问(Recursive Advisor) 机制,将工具调用循环提升为顾问链中的一等公民,实现了对Agent迭代工作流的原生支持。
3.1 核心机制:ToolCallAdvisor
在Spring AI 1.x中,工具执行逻辑内嵌在ChatModel实现内部,开发者无法干预调用过程。2.0版本彻底重构了这一设计——ToolCallAdvisor作为递归顾问接管了整个工具调用生命周期。
关键流程如下:
- 定义工具:通过
@Tool注解标记方法 - 注册工具:在ChatClient构建时传入
ToolCallback - 执行循环:ChatClient将请求发给LLM → LLM返回含工具调用的响应 → ToolCallAdvisor截获并执行对应工具 → 将工具结果追加到对话历史 → 再次调用LLM → 直到LLM返回不含工具调用的最终答案
代码实现如下:
// 1. 定义工具
@Component
public class WeatherTools {
@Tool(description = "获取指定城市的当前天气")
public String getCurrentWeather(
@ToolParam(description = "城市名称,如:北京") String city) {
// 实际项目中可调用真实天气API
return city + ":晴,25°C";
}
@Tool(description = "预订机票")
public BookingConfirmation bookFlight(
@ToolParam(description = "出发城市") String origin,
@ToolParam(description = "目的城市") String destination,
@ToolParam(description = "日期,格式YYYY-MM-DD") String date) {
return flightService.book(origin, destination, date);
}
}
// 2. 构建ChatClient并注册工具
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, WeatherTools weatherTools) {
return ChatClient.builder(chatModel)
.defaultToolCallbacks(
FunctionToolCallback.builder("getCurrentWeather", weatherTools::getCurrentWeather)
.description("获取指定城市的当前天气")
.inputType(WeatherRequest.class)
.build()
)
.defaultAdvisors(new ToolCallAdvisor())
.build();
}
}
// 3. 业务调用
@Service
public class AgentService {
private final ChatClient chatClient;
public String processUserRequest(String userInput) {
return chatClient.prompt()
.user(userInput)
.call()
.content();
}
}
3.2 记忆管理:将记忆顾问置于工具循环内部
一个容易被忽视的关键设计是记忆(Memory)与工具循环(Tool Loop)的交互。默认情况下,MessageChatMemoryAdvisor(顺序:HIGHEST_PRECEDENCE + 200)在ToolCallAdvisor(顺序:HIGHEST_PRECEDENCE + 300)之前执行,这意味着工具调用的请求和响应不会被写入记忆存储——它只记录最终的User和Assistant消息。
如果想让LLM拥有完整的“反思能力”——知道之前尝试过哪些工具、返回了什么结果——就需要将记忆顾问置于工具循环内部:
// 将记忆顾问的顺序设置为高于ToolCallAdvisor,使其在循环内部执行
var memoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)
.order(ToolCallAdvisor.DEFAULT_ORDER + 1) // 关键:放在ToolCallAdvisor之后
.build();
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(memoryAdvisor, new ToolCallAdvisor())
.build();
Spring AI 2.0中,当检测到记忆顾问在循环内部时,ToolCallAdvisor会自动禁用其内部对话历史,避免重复写入。支持完整工具消息持久化的内置存储包括InMemoryChatMemoryRepository、RedisChatMemoryRepository和Neo4jChatMemoryRepository。
四、LangChain实践:工具调用与编排
如果说Spring AI是Java生态的“正规军”,那么LangChain就是Python生态的“特种部队”。LangChain的Agent框架同样提供了完善的工具调用能力。
4.1 工具定义与注册
LangChain中,工具通过继承BaseTool类或使用@tool装饰器定义:
from langchain.tools import BaseTool
from typing import Type, Optional
from pydantic import BaseModel, Field
import requests
class APITestInput(BaseModel):
endpoint: str = Field(description="API端点地址")
method: str = Field(description="HTTP方法,如GET、POST")
payload: Optional[dict] = Field(None, description="请求体")
expected_status: int = Field(200, description="期望的状态码")
class APITestTool(BaseTool):
name = "api_test_tool"
description = "执行API测试并验证响应"
args_schema: Type[BaseModel] = APITestInput
def _run(self, endpoint: str, method: str,
payload: dict = None, expected_status: int = 200):
"""同步执行"""
try:
if method.upper() == "GET":
response = requests.get(endpoint, params=payload)
elif method.upper() == "POST":
response = requests.post(endpoint, json=payload)
else:
return {"error": f"不支持的HTTP方法: {method}"}
success = response.status_code == expected_status
return {
"success": success,
"status_code": response.status_code,
"response_body": response.json() if response.content else None,
"message": f"状态码验证{'通过' if success else '失败'}"
}
except Exception as e:
return {"error": f"API测试异常: {str(e)}"}
async def _arun(self, endpoint: str, method: str,
payload: dict = None, expected_status: int = 200):
"""异步执行"""
return self._run(endpoint, method, payload, expected_status)
4.2 Agent构建与执行
使用ReAct模式构建Agent,通过create_react_agent和AgentExecutor完成编排:
from langchain.agents import AgentExecutor, create_react_agent
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
# 测试Agent专用Prompt——注入测试工程师的思维链
TEST_AGENT_PROMPT = PromptTemplate.from_template(
"""你是一名资深自动化测试工程师。请按以下步骤执行任务:
1. **需求分析**:理解测试目标,识别测试类型
2. **环境检查**:确认测试环境可用性
3. **测试设计**:设计测试场景,考虑边界条件
4. **工具选择**:选择合适的测试工具
5. **执行验证**:执行测试并验证结果
6. **结果分析**:给出结论和建议
当前任务:{input}
可用工具:{tools}
{agent_scratchpad}"""
)
# 初始化LLM(温度设为0.1,保证测试结果的确定性)
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0.1)
# 构建工具集
tools = [APITestTool(), UITestTool(), DBValidationTool()]
# 创建Agent
agent = create_react_agent(
llm=llm,
tools=tools,
prompt=TEST_AGENT_PROMPT
)
# 创建执行器(带记忆)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
memory=ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
),
verbose=True,
handle_parsing_errors=True,
max_iterations=10 # 防止无限循环
)
# 执行测试任务
result = agent_executor.invoke({
"input": "测试用户登录流程:使用test_user@example.com登录系统,验证登录成功后跳转到首页"
})
4.3 企业级增强:并发执行与自愈
生产环境中,Agent往往需要处理批量任务。LangChain结合concurrent.futures可实现并发执行:
from concurrent.futures import ThreadPoolExecutor, as_completed
class ConcurrentTestRunner:
def __init__(self, agent_executor, max_workers=5):
self.executor = ThreadPoolExecutor(max_workers=max_workers)
self.agent = agent_executor
def run_concurrent_tests(self, test_cases):
futures = {}
for test_case in test_cases:
future = self.executor.submit(
self.agent.invoke,
{"input": f"执行测试:{test_case['description']}"}
)
futures[future] = test_case["id"]
results = []
for future in as_completed(futures):
test_id = futures[future]
try:
result = future.result(timeout=60)
results.append({"test_id": test_id, "result": result})
except Exception as e:
results.append({"test_id": test_id, "error": str(e)})
return results
五、最佳实践与避坑指南
5.1 终止条件是生命线
递归顾问和Agent循环如果没有明确的终止条件,可能造成无限调用,既消耗大量Token费用,又可能导致系统宕机。务必设置:
max_iterations:最大迭代次数(Spring AI中需在自定义顾问中实现,LangChain的AgentExecutor默认支持)maxRepeatAttempts:结构化输出验证的最大重试次数
5.2 工具数量爆炸:渐进式披露
当工具数量超过30个,将所有工具定义一次性塞入上下文会引发上下文膨胀、准确率下降、Token成本飙升三大问题。
Spring AI 2.0提供了ToolSearchToolCallingAdvisor,通过渐进式工具披露解决该问题:初始只暴露一个toolSearch工具,LLM通过自然语言查询按需获取相关工具定义,再执行后续调用。实测可减少34-64% 的Token消耗。
启用方式:
spring.ai.chat.client.tool-search-advisor.enabled=true
spring.ai.chat.client.tool-search-advisor.tool-index-type=vector
5.3 可观测性:别等出问题才后悔
生产环境的Agent调用链往往包含多次LLM调用和工具执行,没有链路追踪几乎无法排查问题。建议:
- 记录每次工具调用的输入参数、输出结果、耗时
- 使用Spring AI的Micrometer集成或LangSmith进行全链路追踪
- 设置关键告警:工具调用失败率、平均迭代次数、Token消耗速率
六、总结与展望
Agent的本质,是让LLM从“思考者”进化为“行动者”。无论是Spring AI的ToolCallAdvisor递归循环,还是LangChain的ReAct Agent框架,核心都围绕着同一套逻辑:理解意图 → 调用工具 → 处理结果 → 迭代决策。
随着Spring AI 2.0将工具调用提升为顾问链中的一等公民,并引入渐进式工具披露、MCP协议集成等企业级特性,Java生态在AI工程化领域的竞争力正在快速追赶。而LangChain凭借丰富的生态和灵活的Python表达力,依然是快速验证和原型开发的首选。
选择框架是战术问题,理解“工具编排”的设计哲学才是战略问题。无论你使用哪套技术栈,清晰的分层架构、可靠的终止条件、完善的可观测性,才是企业级Agent落地的三大基石。
未来的Agent,将不再是单打独斗的“孤勇者”,而是通过MCP等协议实现跨系统、跨组织、跨语言的协作网络。而这,正是AI从“对话工具”走向“数字生产力”的必经之路。
更多推荐

所有评论(0)