jquery在文心智能体平台使用API方式部署智能体-AI客服
创建智能体
创建地址:https://agents.baidu.com/agent/list/codeless
查看需要部署智能体的ID和秘钥

这两哥参数,ID和秘钥,接口中要用到。
实现功能
参考文档:https://agents.baidu.com/docs/develop/out-deployment/conversation/
有两种方式可以实现与智能体进行对话并且获取非流式返回、流式返回 ,仅支持零代码方式创建的智能体
非流式对话接口是查询所有答案之后再返回,流式对话接口是边查询边返回,更友好一些。
我用的是流式对话接口的形式。

点击旁边的AI客服按钮,出现聊天弹框。

css:
typescript
.znt-dialog{
position: fixed;
top: 21%;
right: 4.5%;
display: none;
width: 346px;
height: 570px;
border: 1px solid #33615f;
background: #fff;
border-radius: 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgb(179 164 164 / 24%);
z-index: 999;
}
.znt-show{
display: block;
}
.znt-title{
height: 35px;
line-height: 35px;
/*text-align: right;*/
padding: 0 15px 0 15px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.znt-title-icon{
background: url("/images/before/kf/ZNKF.svg") no-repeat 0px 7px / 100% 100%;
background-size: 18px 18px;
padding-left: 22px;
display: inline-block;
}
.znt-close{
cursor: pointer;
}
.znt-cont{
position: absolute;
top: 35px;
left: 0px;
right: 0px;
bottom: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 聊天记录区(可滚动) */
#chatMessages {
flex: 1;
padding: 16px;
overflow-y: auto;
/*background: #f5f7fa;*/
background: url("/images/before/kf/znkf_bg.png") no-repeat 0px 0px / 100% 100%;
/* 阻止滚动穿透到背后的页面 */
overscroll-behavior: contain;
}
/* 消息气泡样式(区分用户/智能体) */
.message {
margin-bottom: 12px;
max-width: 70%;
padding: 8px 12px;
border-radius: 8px;
line-height: 1.5;
white-space: pre-line;
/* 宽度贴合内容,最大宽度 70% */
width: fit-content;
}
.user-message {
background: #2f54eb;
color: #fff;
margin-left: auto; /* 用户消息右对齐 */
}
.agent-message {
background: #e5e6eb;
color: #1d2129;
margin-right: auto; /* 智能体消息左对齐 */
}
/* 输入区域 */
#chatInputArea {
display: flex;
padding: 12px;
border-top: 1px solid #eee;
}
#userInput {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
outline: none;
}
#sendMsgBtn {
margin-left: 8px;
padding: 8px 16px;
background: #2f54eb;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
#sendMsgBtn:disabled {
background: #c9cdd4;
cursor: not-allowed;
}
html:
typescript
<!--点击-->
<li class="zxli cl1 znt">
<img class="img" src="xxx" alt="点击" title="点击" >
<p>AI客服</p>
</li>
<!--智能体弹窗-->
<div class="znt-dialog" >
<div class="znt-title">
<span class="znt-title-icon">
<!-- <img class="img" src="/images/before/kf/ZNKF.svg" style="width: 20px;height: 20px" >-->
AI客服
</span>
<span class="znt-close">X</span>
</div>
<div class="znt-cont">
<!-- 聊天记录区 -->
<div id="chatMessages"></div>
<!-- 输入区 -->
<div id="chatInputArea">
<input type="text" id="userInput" placeholder="请输入您的问题...">
<button id="sendMsgBtn">发送</button>
</div>
</div>
</div>
js:
typescript
$(function(){
var startWord = "我是xxx助理,我能帮助您提供xxx建议和技术服务,以及给您提供相关的智慧解决方案。请问您需要了解xxxxx什么问题呢?";
const chatState = {
threadId: "",
agentReplyBuffer: "",
isRequesting: false
};
$(document).on("click", ".znt", function () {
$("#userInput").val("");
const $dialog = $(".znt-dialog");
if ($dialog.hasClass('znt-show')) {
$dialog.removeClass('znt-show');
// $("#chatMessages").html('');
// chatState.threadId = "";
chatState.agentReplyBuffer = "";
} else {
$dialog.addClass('znt-show');
$("#userInput").focus();
// addMessageToUI("agent", startWord, "");
// 【新增】首次打开时,若聊天记录为空,才添加欢迎语
if ($("#chatMessages").html().trim() === "") {
addMessageToUI("agent", startWord, "");
}
}
});
$(document).on("click", ".znt-close", function () {
$("#userInput").val("");
$(".znt-dialog").removeClass('znt-show');
// $("#chatMessages").html('');
// chatState.threadId = "";
chatState.agentReplyBuffer = "";
});
$(document).on("click", "#sendMsgBtn", function () {
const userQuestion = $("#userInput").val().trim();
if (userQuestion && !chatState.isRequesting) {
addMessageToUI("user", userQuestion, "");
sendStreamingPostRequest(userQuestion);
$("#userInput").val("");
}
});
$(document).on("keydown", "#userInput", function (e) {
const userQuestion = $(this).val().trim();
if (e.key === 'Enter' && userQuestion &&!chatState.isRequesting) {
addMessageToUI("user", userQuestion, "");
sendStreamingPostRequest(userQuestion);
$(this).val('');
e.preventDefault();// 阻止输入框默认换行
}
});
async function sendStreamingPostRequest(userQuestion) {
const appId = localStorage.getItem("clientId");
const secretKey = localStorage.getItem("clientSecret");
if (!appId ||!secretKey) {
addMessageToUI("agent", "错误:未获取到智能体配置(appId/secretKey 缺失)", "");
return;
}
chatState.isRequesting = true;
const loadingMsgId = `loading-${Date.now()}`;
addMessageToUI("agent", "正在查询,请稍等...", "loadingDom", loadingMsgId);
const openId = generateSecureRandomString(10);
const requestParams = {
message: {
content: {
type: "text",
value: { showText: userQuestion }
}
},
source: appId,
from: "openapi",
openId: openId,
threadId: chatState.threadId || ""
};
try {
// 1. 发起 POST 请求,获取流式响应
const response = await fetch('https://agentapi.baidu.com/assistant/conversation?appId=' + appId + '&secretKey=' + secretKey, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestParams)
});
if (!response.ok) {
throw new Error(`HTTP 错误!状态码:${response.status}`);
}
// 2. 处理流式响应(核心:逐块读取并解析)
const reader = response.body.getReader();
const decoder = new TextDecoder();
let accumulatedText = ""; // 累计的智能体回复文本
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 3. 解码二进制数据为字符串,按行分割(SSE 每行是一个数据块)
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
// 4. 逐行解析数据
for (const line of lines) {
if (line.trim() === '') continue; // 跳过空行
try {
// 提取 data: 后的 JSON 字符串
const dataStr = line.startsWith('data:')? line.slice('data:'.length) : null;
if (!dataStr) continue;
const data = JSON.parse(dataStr);
handleStreamData(data, loadingMsgId, accumulatedText);
// 5. 提取并累计有效文本
const textFragment = extractTextFromData(data);
if (textFragment) {
accumulatedText += textFragment;
// 实时渲染累计的文本(带 Markdown 格式转换)
const formattedText = convertMarkdownToHtml(accumulatedText);
renderStreamingMessage("agent", formattedText);
}
} catch (err) {
console.error('解析数据块失败:', line, err);
}
}
}
chatState.isRequesting = false;
} catch (error) {
chatState.isRequesting = false;
chatState.agentReplyBuffer = "";
$(`#${loadingMsgId}`).remove();
addMessageToUI("agent", `请求失败:${error.message}`, "");
}
// 流式结束后,释放请求锁(若用 EventSource,需在 close 事件中设置)
chatState.isRequesting = false;
}
// 辅助函数:从数据中提取文本片段
function extractTextFromData(data) {
// 1. 过滤无效数据(data 为 null 或无 content)
if (!data || !data.data || !data.data.message || !data.data.message.content) {
return null;
}
// 2. 提取 content 数组中第一个元素的 text(假设是 markdown 类型)
const contentItem = data.data.message.content[0];
return contentItem?.data?.text || null;
}
// 辅助函数:处理单块流式数据(可扩展错误处理、endTurn 判断等)
function handleStreamData(data, loadingMsgId, accumulatedText) {
// 示例:存储 threadId(用于上下文关联)
if (data.data?.message?.threadId) {
chatState.threadId = data.data.message.threadId;
}
// 示例:判断是否为最后一块数据(endTurn: true)
if (data.data?.message?.endTurn === true) {
// 移除"正在查询"提示
$(`#${loadingMsgId}`).remove();
// 追加最终的智能体消息(用 addMessageToUI,确保是新容器)
addMessageToUI("agent", accumulatedText, "");
// 重置缓冲区
chatState.agentReplyBuffer = "";
// 自动聚焦输入框
$("#userInput").focus();
}
}
// 实时渲染智能体消息
function renderStreamingMessage(sender, formattedContent) {
const $chatContainer = $("#chatMessages");
let $agentMsg = $chatContainer.find(".agent-message:last");
if (!$agentMsg.length) {
$agentMsg = $(`<div class="message agent-message" ></div>`);
$chatContainer.append($agentMsg);
}
$agentMsg.html(formattedContent);
$chatContainer.scrollTop($chatContainer[0].scrollHeight);
}
// 将信息渲染到聊天框内
function addMessageToUI(sender, content, loadingDom, loadingMsgId = "") {
$(".loadingDom").remove();
const $chatContainer = $("#chatMessages");
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender === 'user'? 'user-message' : 'agent-message'} ${loadingDom || ''}`;
if (loadingMsgId) messageDiv.id = loadingMsgId;
const formattedContent = convertMarkdownToHtml(content);
messageDiv.innerHTML = formattedContent;
$chatContainer.append(messageDiv);
$chatContainer.scrollTop($chatContainer[0].scrollHeight);
}
function convertMarkdownToHtml(markdownText) {
if (!markdownText) return "";
let html = markdownText;
html = html.replace(/\n/g, "<br>");
html = html.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
html = html.replace(/^- (.*?)(<br>|$)/gm, "<li style='margin: 4px 0;'>$1</li>");
html = html.replace(/(<li>.*?<\/li>)(\s*<li>.*?<\/li>)+/g, "<ul style='padding-left: 20px; margin: 8px 0;'>$&</ul>");
html = html.replace(/### (.*?)(<br>|$)/g, "<h4 style='margin: 8px 0; font-size: 15px; font-weight: 600;'>$1</h4>$2");
return html;
}
function generateSecureRandomString(length = 10) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
});