在 AI 应用开发中,流式对话是提升用户体验的关键特性 ------ 它能让 AI 响应像人类聊天一样 "边想边说",而非等待完整结果后一次性展示。本文将以一个招聘场景的 AI 助手为例,详解如何基于 Dify 平台实现流式对话功能,包括组件设计、流程编排和核心技术细节。
涉及到的组件库:https://ant-design-x.antgroup.com/docs/react/introduce-cn
一、场景与核心需求
我们要实现的是一个 "招聘 AI 助手",核心功能包括:
- 支持用户与 AI 的自然语言流式对话
- 提供结构化表单输入(如职位信息填写)
- 展示 AI 思考过程(节点步骤 + 推理内容)
- 生成可编辑的职位描述(JD)并同步至第三方系统(如北森)
整个流程需要实现 "输入→流式响应→结果处理→第三方同步" 的完整闭环,而 Dify 作为 AI 中间件,负责处理对话逻辑和流式输出。
二、核心组件设计
为实现上述功能,我们将系统拆分为 4 个核心模块,各司其职又协同工作:
1. 主容器组件(HrChatMessageList)
作为页面入口,管理全局状态和核心流程,主要职责包括:
- 维护聊天记录(
messages状态) - 处理用户输入(文本 / 表单)
- 协调 AI 响应的渲染
- 管理加载状态和错误处理
核心状态设计:
// 存储所有聊天消息(用户/AI/表单)
const [messages, setMessages] = useState([]);
// 输入框内容
const [inputValue, setInputValue] = useState("");
// 累积AI响应内容(普通消息/JD内容)
const assistantMessage = useRef("");
const jdMessage = useRef("");
// 思考链节点记录
const thoughtChainRef = useRef([]);
2. Dify 交互钩子(useDifyAgent)
封装与 Dify API 的通信逻辑,是连接前端与 AI 的核心桥梁。它的核心功能包括:
- 初始化 Dify 客户端(配置 API 密钥和基础 URL)
- 提供
sendMessage方法,支持流式请求 - 解析 Dify 的流式响应(区分节点事件、消息事件、结束事件)
- 通过回调函数将数据传递给主组件
关键代码片段:
const [agent] = useXAgent({
baseURL: "https://...", dify链接
dangerouslyApiKey: "Bearer dify密钥",
});
const { sendMessage } = useXChat({ agent });
// 发送消息并处理流式响应
const sendMessage = async (params) => {
agent.request(
{ response_mode: "streaming", ...params },
{
onUpdate: (messages) => {
const data = JSON.parse(messages.data);
// 处理节点开始事件(更新思考链)
if (data.event === "node_started") {
onNode(decodeURIComponent(data.data.title));
}
// 处理消息事件(实时更新UI)
if (data.event === "message") {
onMessage({ currentText: data.answer });
}
// 处理结束事件(完成响应)
if (data.event === "message_end") {
onSuccess();
}
}
}
);
};
3. 结构化表单组件(JdFormComponent)
提供职位信息的结构化输入,通过ref暴露验证和数据格式化能力:
// 父组件通过ref调用表单验证
useImperativeHandle(ref, () => ({
validateAndGetData: () => {
return form.validateFields().then(values => {
// 格式化表单数据(如将多行文本转为数组)
return {
MainResponsibilities: values.coreResponsibilities.split('\n'),
...
};
});
}
}));
4. 思考链展示组件(ThoughtChainList)
可视化 AI 的思考过程,支持展开 / 收起,增强用户对 AI 决策的理解:
// 展示节点步骤和思考内容
<ThoughtChain items={items} />
{think && <div dangerouslySetInnerHTML={{ __html: md.render(think) }} />}
三、核心状态与 Ref 说明(父组件关键数据)
| 状态 / Ref | 作用 |
|---|---|
| messages | 存储所有聊天消息(用户 / AI / 表单) |
| inputValue | 输入框内容 |
| assistantMessage | 累积 AI 的普通响应内容 |
| jdMessage | 累积 AI 生成的 JD 内容 |
| thoughtChainRef | 存储 AI 对话的节点步骤(思考链) |
| isCreateJdRef | 标记是否处于「JD 生成」节点 |
| difyConversationIdRef | 存储 Dify 对话 ID,用于关联历史消息 |
四、关键回调函数流转
useDifyAgent 是回调流转的核心,各回调的作用:
handleMessage(父组件):接收 Dify 的消息内容,更新 AI 回复的实时渲染。handleError(父组件):处理 API 错误,更新 UI 提示。handleSuccess(父组件):API 请求结束后,完善 AI 消息的 UI(添加表单 / JD 编辑框)。handleNode(父组件):更新思考链节点状态(新增、标记成功)。handleSavetotaTokens(父组件):记录对话消耗的 token 数。
五、完整流程详解
1. 初始化与历史加载
- 页面加载时通过 URL 参数获取对话 ID
- 调用
getMessageInfoList接口拉取历史消息 - 解析历史消息,区分用户消息和 AI 消息,重建思考链状态
- 若为首次进入,自动触发欢迎消息的流式请求
2. 用户输入与消息发送
支持两种输入方式,最终都通过sendMessage提交给 Dify:
方式 1:文本输入
const sendDifyMessage = (value) => {
// 添加用户消息和AI加载状态到聊天列表
addUserMessage([{ content: value, role: "user" }, loadingMessage]);
// 保存用户消息到本地
saveUserConversation({ content: value, ... });
// 调用Dify接口
sendMessage({ query: value, ... });
};
方式 2:表单提交
const handleSubmit = async () => {
// 验证并获取表单数据
const formData = await formRef.current.validateAndGetData();
// 格式化并发送表单数据
addSubmittedFormMessage(formData);
sendMessage({ query: formattedContent, ... });
};
3. 流式响应处理
这是整个流程的核心,通过 Dify 的流式输出实时更新 UI:
-
节点开始事件 :当 Dify 开始处理某个节点(如 "分析需求"、"生成 JD"),前端收到
node_started事件,更新思考链节点状态:const handleNode = (value) => {
thoughtChainRef.current.push({
title: value,
status: "pending",
icon: <LoadingOutlined />
});
// 更新UI展示新节点
setMessages(prev => [...prev.slice(0, -1), newAiMessage]);
}; -
消息内容事件:Dify 返回实时生成的文本,前端累积内容并渲染:
const handleMessage = (msg) => {
// 累积AI响应内容
assistantMessage.current += msg.currentText;
// 实时更新UI
setMessages(prev => {
const lastMsg = prev[prev.length - 1];
return [...prev.slice(0, -1), { ...lastMsg, content: renderedContent }];
});
}; -
响应结束事件:当 Dify 完成所有处理,前端完善 UI(如添加操作按钮):
const handleSuccess = () => {
// 更新最后一条AI消息,添加JD编辑框和同步按钮
setMessages(prev => {
const lastMsg = prev[prev.length - 1];
return [...prev.slice(0, -1), { ...lastMsg, footer: syncButton }];
});
};
4. 结果处理与第三方同步
以 JD 生成为例,完成 AI 响应后支持:
-
手动编辑 AI 生成的 JD 内容
-
生成 Word 文档并上传至 OSS
-
同步至北森系统:
const createJdFile = async () => {
// 1. 生成Word文档
const doc = new Document({ ... });
const blob = await Packer.toBlob(doc);// 2. 上传至OSS const formData = new FormData(); formData.append('file', new File([blob], 'JD.docx')); await uploadToOSS(formData); // 3. 同步至北森 await saveJobDescription({ url: ossUrl, ... });};
六、技术亮点与注意事项
-
状态管理技巧 :使用
ref存储临时累积的内容(如 AI 响应、思考链),避免频繁触发重渲染;用useState管理需要展示的状态(如messages)。 -
流式渲染优化 :通过
dangerouslySetInnerHTML配合 markdown 解析器,支持富文本实时渲染;控制更新频率,避免性能问题。 -
错误处理 :在
onError回调中统一处理 API 错误,更新 UI 提示用户,同时清理临时状态。 -
第三方集成:通过签名 URL 方式安全上传文件至 OSS,避免前端暴露密钥;封装同步接口,便于替换不同的第三方系统。
七、效果
完整流程(历史对话)

对话过程中(收集进度未完成)
所有完成之后会走到同步至北森逻辑

总结
基于 Dify 实现流式对话的核心是:通过封装 API 交互层(useDifyAgent)分离业务逻辑与 UI 渲染,同时设计合理的状态管理策略,让前端既能实时响应 AI 的流式输出,又能保持良好的用户体验。
这种架构不仅适用于招聘场景,还可推广到客服、教育等需要 AI 实时交互的领域。关键在于理解流式响应的事件机制,以及如何设计组件间的通信方式,让整个流程清晰可维护。