Web开发者快速上手AI Agent:基于Function Calling的多步交互提示词优化实战
图片来源网络,侵权联系删。

文章目录
- [1. 从Web表单到AI多步交互,逻辑从未改变](#1. 从Web表单到AI多步交互,逻辑从未改变)
- [2. Web开发与AI Agent的天然衔接点](#2. Web开发与AI Agent的天然衔接点)
- [3. Function Calling核心原理:AI版的RPC调用](#3. Function Calling核心原理:AI版的RPC调用)
- [4. 实战:用Java+React构建多步交互Agent](#4. 实战:用Java+React构建多步交互Agent)
-
- [4.1 后端:Spring Boot定义工具函数](#4.1 后端:Spring Boot定义工具函数)
- [4.2 Agent主逻辑:多轮交互控制器](#4.2 Agent主逻辑:多轮交互控制器)
- [4.3 前端:React可视化多步交互](#4.3 前端:React可视化多步交互)
- [5. 常见问题与Web开发者解决方案](#5. 常见问题与Web开发者解决方案)
-
- [5.1 问题:模型不调用函数,或参数错误](#5.1 问题:模型不调用函数,或参数错误)
- [5.2 问题:多步执行超时或中断](#5.2 问题:多步执行超时或中断)
- [5.3 问题:敏感函数被滥用(如删除数据库)](#5.3 问题:敏感函数被滥用(如删除数据库))
- [6. 总结与Web开发者的AI学习路径](#6. 总结与Web开发者的AI学习路径)
1. 从Web表单到AI多步交互,逻辑从未改变
在传统Web开发中,我们经常处理这样的场景:用户填写一个表单(比如"查北京明天天气"),前端发送请求到后端,后端调用第三方API(如和风天气),拿到数据后再返回给前端渲染。整个过程是线性、可控、可调试的。
而如今,当我们将大语言模型(LLM)引入应用时,如果只是简单地"问一句答一句",就浪费了AI的真正潜力。Function Calling(函数调用)机制 ,正是让AI具备"主动调用工具"能力的关键------它允许模型在回答过程中,自主决定是否需要调用外部函数,并等待结果后再继续推理。
这就像让AI拥有了"后端开发能力":它能像你写的Controller一样,根据用户意图决定调用哪个Service。
对于Web开发者而言,Function Calling不是新概念,而是你已掌握的API调用思维在AI世界的自然延伸。本文将带你用熟悉的Java+React技术栈,实战构建一个支持多步交互的AI Agent应用。

2. Web开发与AI Agent的天然衔接点
很多Web开发者误以为AI开发必须从零学起Python、PyTorch。其实不然------现代AI Agent应用的核心架构,与Web服务高度相似:
| Web开发概念 | AI Agent对应概念 | 类比说明 |
|---|---|---|
| RESTful API | Function (Tool) | 每个工具函数就是一个可被调用的"端点" |
| Controller | Agent Prompt + LLM | 决定调用哪个工具、如何组合结果 |
| Request Body | Function Arguments | 模型生成的JSON参数,直接传给函数 |
| JWT / OAuth | Tool Permission Control | 控制哪些Agent能调用敏感函数 |
| Frontend State | Conversation History | 多轮对话上下文 = 前端状态管理 |
举个例子:你在Vue中用
axios.post('/api/weather', {city: '北京'}),在Agent中就是模型生成{"name": "get_weather", "arguments": {"city": "北京"}}。
这种一致性意味着:你不需要成为AI专家,只需把已有Web工程能力"平移"到AI场景。

3. Function Calling核心原理:AI版的RPC调用
Function Calling的本质,是让大模型在生成文本前,先输出一个"函数调用指令"。这个指令包含:
- 要调用的函数名(
name) - 函数所需的参数(
arguments,JSON格式)
然后由你的程序执行该函数,将结果"塞回"给模型,让它继续生成最终回答。
用Web思维理解三步流程:
Your Function (e.g., WeatherService) LLM Agent User Your Function (e.g., WeatherService) LLM Agent User "北京明天天气怎么样?" 分析意图 → 需要调用get_weather 调用 get_weather(city="北京") 返回 {"temp": 5, "desc": "晴"} "北京明天晴,气温5°C。"
这不就是典型的"前端→后端→第三方服务→后端→前端"流程吗?只不过现在"后端决策者"换成了AI。
关键在于:你定义的函数描述(function schema)必须足够清晰,就像写Swagger文档一样。模型靠这个"文档"决定何时调用、传什么参数。
外链图片转存中...(img-ZsoJXt2j-1767541792166)
4. 实战:用Java+React构建多步交互Agent
我们将构建一个支持"查天气+查新闻"的Agent。用户问:"北京明天天气如何?有什么重要新闻?",Agent应分两步调用不同工具。
4.1 后端:Spring Boot定义工具函数
首先,定义两个工具函数(Service):
java
// WeatherService.java
@Service
public class WeatherService {
public String getWeather(String city) {
// 模拟调用第三方API
return "{\"temp\": 5, \"desc\": \"晴\"}";
}
}
// NewsService.java
@Service
public class NewsService {
public String getTopNews(String category) {
return "[{\"title\": \"AI Agent技术突破\", \"source\": \"TechNews\"}]";
}
}
然后,构建Function Calling所需的函数描述列表(相当于API文档):
java
// FunctionRegistry.java
@Component
public class FunctionRegistry {
public List<ChatCompletionTool> getTools() {
return Arrays.asList(
ChatCompletionTool.builder()
.type(ToolType.FUNCTION)
.function(FunctionDefinition.builder()
.name("get_weather")
.description("获取指定城市的天气信息")
.parameters(ObjectMapperFactory.create() // 定义参数结构
.readValue("{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"]}", JsonNode.class))
.build())
.build(),
ChatCompletionTool.builder()
.type(ToolType.FUNCTION)
.function(FunctionDefinition.builder()
.name("get_top_news")
.description("获取最新新闻")
.parameters(ObjectMapperFactory.create()
.readValue("{\"type\":\"object\",\"properties\":{\"category\":{\"type\":\"string\"}},\"required\":[]}", JsonNode.class))
.build())
.build()
);
}
}
4.2 Agent主逻辑:多轮交互控制器
java
// AgentService.java
@Service
public class AgentService {
@Autowired private OpenAiClient openAiClient; // 假设已集成OpenAI SDK
@Autowired private FunctionRegistry functionRegistry;
@Autowired private WeatherService weatherService;
@Autowired private NewsService newsService;
public String handleUserQuery(String userMessage, List<Message> history) {
List<Message> messages = new ArrayList<>(history);
messages.add(new UserMessage(userMessage));
while (true) {
ChatCompletionResponse response = openAiClient.chatCompletion(messages, functionRegistry.getTools());
if (response.hasToolCalls()) {
// 模型要求调用函数
ToolCall toolCall = response.getToolCalls().get(0);
String result = executeTool(toolCall);
// 将函数结果"塞回"对话历史
messages.add(new AssistantMessage("", toolCall));
messages.add(new ToolMessage(result, toolCall.getId()));
} else {
// 无函数调用,返回最终答案
return response.getContent();
}
}
}
private String executeTool(ToolCall toolCall) {
switch (toolCall.getFunction().getName()) {
case "get_weather":
String city = parseArgument(toolCall, "city");
return weatherService.getWeather(city);
case "get_top_news":
return newsService.getTopNews("tech");
default:
throw new IllegalArgumentException("Unknown function: " + toolCall.getFunction().getName());
}
}
}
4.3 前端:React可视化多步交互
使用useReducer管理对话状态,清晰展示每一步:
jsx
// AgentChat.jsx
import { useReducer, useEffect } from 'react';
const initialState = { messages: [], status: 'idle' };
function chatReducer(state, action) {
switch (action.type) {
case 'user_send':
return { ...state, messages: [...state.messages, { role: 'user', content: action.text }] };
case 'agent_think':
return { ...state, status: 'thinking' };
case 'agent_tool_call':
return {
...state,
messages: [...state.messages, {
role: 'assistant',
tool_calls: [{ name: action.toolName, args: action.args }]
}]
};
case 'tool_result':
return {
...state,
messages: [...state.messages, {
role: 'tool',
content: action.result,
tool_call_id: action.id
}]
};
case 'agent_reply':
return {
...state,
messages: [...state.messages, { role: 'assistant', content: action.text }],
status: 'idle'
};
default:
return state;
}
}
export default function AgentChat() {
const [state, dispatch] = useReducer(chatReducer, initialState);
const handleSubmit = async (text) => {
dispatch({ type: 'user_send', text });
dispatch({ type: 'agent_think' });
const response = await fetch('/api/agent', {
method: 'POST',
body: JSON.stringify({ message: text, history: state.messages })
});
const data = await response.json();
// 假设后端返回完整的交互步骤(简化版)
data.steps.forEach(step => {
if (step.type === 'tool_call') {
dispatch({ type: 'agent_tool_call', toolName: step.name, args: step.args });
dispatch({ type: 'tool_result', id: step.id, result: step.result });
} else if (step.type === 'reply') {
dispatch({ type: 'agent_reply', text: step.content });
}
});
};
return (
<div>
{state.messages.map((msg, i) => (
<div key={i} className={`message ${msg.role}`}>
{msg.role === 'tool' ? `[工具调用结果: ${msg.content}]` : msg.content}
</div>
))}
{/* 输入框... */}
</div>
);
}
通过这种方式,前端不仅能展示最终答案,还能让用户看到AI"思考过程"------极大提升可信度。

5. 常见问题与Web开发者解决方案
5.1 问题:模型不调用函数,或参数错误
原因:函数描述(schema)不够清晰,如同API文档缺失字段说明。
解决:
- 使用严格JSON Schema定义参数(必填/类型/示例);
- 在提示词中强调:"你必须使用提供的工具,不要自行编造数据"。
5.2 问题:多步执行超时或中断
类比:Web请求超时。
解决:
- 设置Agent最大调用步数(如最多3次函数调用);
- 前端增加"取消"按钮,中断长流程。
5.3 问题:敏感函数被滥用(如删除数据库)
类比:未鉴权的DELETE接口。
解决:
-
在
executeTool中加入权限检查:javaif ("delete_user".equals(toolName) && !currentUser.isAdmin()) { throw new SecurityException("无权限"); } -
对高危函数设置白名单。

6. 总结与Web开发者的AI学习路径
Function Calling不是魔法,而是将你已有的Web工程能力扩展到AI领域的桥梁。通过本文实战,你应该意识到:
- AI Agent = 智能Controller:它根据用户输入决定调用哪些"Service"(工具函数);
- 提示词 = 路由规则 + 业务逻辑注释:告诉模型"你能做什么、怎么调用";
- 多步交互 = 可视化的工作流:前端展示每一步,提升用户体验。
推荐学习路径(针对Web开发者):
- 入门:用JavaScript/Java封装几个简单工具函数(如计算器、时间查询),接入OpenAI或Ollama;
- 进阶:学习LangChain.js或LlamaIndex.ts,它们提供了Web友好的Agent抽象;
- 工程化:将Agent部署为微服务,通过API网关统一管理;
- 创新:结合前端框架(如Next.js)构建全栈AI应用。
真实可靠资源推荐:
- 📚 LangChain.js 官方文档:专为JavaScript开发者设计
- 🧪 Ollama + Web UI 示例:本地运行大模型,支持Function Calling
记住:你不需要成为AI科学家,只需做会用AI的Web工程师------这才是当前市场的稀缺能力。
