上一篇文章讲了如何用Ant Design X来快速开发本地大模型AI应用,今天接着上篇文章的内容,继续优化升级该本地大模型AI应用。上篇文章的内容没有对AI输出的内容进行格式化,最终看到的输出内容很不友好,全都在一个段落里面显示,效果如下
大模型语言的输出内容都是markdown这种格式,所以我们用markdownit插件来格式化输出内容, 将输出内容格式化成html格式显示,格式化后效果如下,是不是看起来舒服多了
首先,先安装下markdownit插件
npm install markdown-it@14.1.0
然后在代码中引入应用该插件,同时定义一个格式化输出函数
import markdownit from 'markdown-it';
const md = markdownit({ html: true, breaks: true });
const renderMarkdown: any = (content: any) => {
return
};
接着修改useXAgent模型调度和useXChat数据管理代码,这边我把原来信息的content字符串格式改成了对象格式,以便更灵活的来控制内容显示格式及样式,或是添加额外的显示内容等
const [agent] = useXAgent({
request: async ({ message }: any, { onSuccess, onUpdate }) => {
// onSuccess(`Mock success return. You said: ${message}`);
const postMsg: any = [{
role: 'user',
content: message.content,
}];
const stream = await client.chat.completions.create({
model: 'qwen2.5',
messages: postMsg,
stream: true,
});
let tempContent = ''
const itemMsg: any = { type: 'ai', content: tempContent }
for await (const chunk of stream) {
// console.log(chunk)
tempContent += chunk.choices[0].delta.content
itemMsg.content = tempContent
onUpdate(itemMsg)
}
onSuccess(itemMsg)
},
});
const { onRequest, messages, setMessages, parsedMessages } = useXChat({
agent,
requestPlaceholder: {
type: 'ai',
content:
, },
parser: (agentMessages: any) => {
return [{
role: agentMessages.type,
content: agentMessages.content,
}]
},
});
接着改输入框的提交函数,把信息内容改成对象格式
const onSubmit = (nextContent: string) => {
if (!nextContent) return;
// onRequest(nextContent);
onRequest({ type: 'local', content: nextContent })
setContent('');
};
最后修改气泡列表组件Bubble.List的items属性,parsedMessages就是上面修改的useXChat数据管理parser函数返回的格式化信息数据
<Bubble.List
items={parsedMessages.length > 0 ?
parsedMessages.map(({ id, message, status }: any) => {
let msg: any = {
key: id,
...message,
}
return msg
})
: [{ content: placeholderNode, variant: 'borderless' }]}
roles={roles}
className={styles.messages}
/>
这样改完,AI的输出内容就可以显示html格式了
现在我们继续优化输出的内容,像deepseek-R1以及最近阿里开源的通义千问QwQ-32B这类深度思考推理的大模型,输出的时候都是带深度思考推理内容的,现在我们对这部分内容也进行格式化显示下,下面是我自己想的办法,不知道markdown有没有相关的插件可以用,我是没找到,所以自己先搞下,主要修改了useXAgent模型调度的request函数以及添加相应的样式
request: async ({ message }: any, { onSuccess, onUpdate }) => {
// onSuccess(`Mock success return. You said: ${message}`);
const postMsg: any = [{
role: 'user',
content: message.content,
}];
const stream = await client.chat.completions.create({
model: 'deepseek-r1:14b',
messages: postMsg,
stream: true,
});
let tempContent = ''
const itemMsg: any = { type: 'ai', content: tempContent }
const thinkPattern = /(
)(.*?)(<\/think>)/gs; const thinkPattern1 = /
(.*?)<\/div>/gs;for await (const chunk of stream) {
// console.log(chunk)
// tempContent += chunk.choices[0].delta.content
if (chunk.choices[0].delta?.content == '
') { tempContent = tempContent + '
正在深度思考...
' + chunk.choices[0].delta?.content} else if (chunk.choices[0].delta?.content == '') {
if (tempContent.indexOf('think-container') == -1) {
tempContent = '
已深度思考'' + tempContent }
tempContent = tempContent + chunk.choices[0].delta?.content + '
tempContent = tempContent.replace('正在深度思考...
', '已深度思考
')} else {
tempContent = tempContent + chunk.choices[0].delta?.content
}
// think空内容
const match = tempContent.match(thinkPattern)
if (match != null && (match[0] == '
\n\n ' || match[0] == '\n ') && tempContent.indexOf('think-process') != -1) {tempContent = tempContent.replace(thinkPattern1, '$1').replace('正在深度思考...
', '').replace('已深度思考
', '')}
itemMsg.content = tempContent
onUpdate(itemMsg)
}
onSuccess(itemMsg)
toggleThink()
},
chat: css`
height: 100%;
width: 100%;
max-width: 700px;
margin: 0 auto;
box-sizing: border-box;
display: flex;
flex-direction: column;
padding: ${token.paddingLG}px;
gap: 16px;
.think-container {
color: #8b8b8b;
border-left: 2px solid #fff;
border-bottom: 2px solid #fff;
padding: 10px;
display: inline-block;
background-color: #fff;
width: 100%;
margin-bottom: 10px;
padding-bottom: 0;
}
.think-process {
font-weight: bold;
background: #f7f7f7;
padding: 5px 10px;
border-radius: 10px;
margin-bottom: 5px;
display: inline-block;
width: 100%;
cursor: pointer;
position: relative;
margin-right: 30px;
}
.think-process-show::after {
content: "\\00BB";
position: absolute;
right: 10px;
transform: rotate(-90deg);
}
.think-process-hide::after {
content: "\\00BB";
position: absolute;
right: 10px;
transform: rotate(90deg);
}
最后再添加是点击事件
const toggleThink = () => {
setTimeout(() => {
const thinks = document.querySelectorAll('.think-process');
thinks.forEach(function (item: any) {
item.onclick = function () {
const curthink: any = item.parentElement.querySelector("think")
if (curthink.style.display === "none") {
curthink.style.display = "block"
item.classList.remove("think-hide");
item.classList.add("think-show");
} else {
curthink.style.display = "none"
item.classList.remove("think-show");
item.classList.add("think-hide");
}
}
})
}, 1000);
}
最终效果
猜你喜欢
- 2025-03-24 一篇文章带你了解CSS3 3D 转换知识
- 2025-03-24 居然可以通过动画快速学习 css(css动画原理)
- 2025-03-24 不借助后台和 JS,只用 CSS 让一个列表编号倒序,你会怎么做?
- 2025-03-24 Pygame Zero 详细使用教程(python中zeros的用法)
- 2025-03-24 AI写代码哪家强?使用DeepSeek开发搭建转盘小程序
- 2025-03-24 让交互更加生动!有意思的鼠标跟随 3D 旋转动效
- 2025-03-24 用纯CSS实现优惠券剪卡风格(css 优惠券内凹)
- 2025-03-24 一文让你掌握22个神经网络训练技巧
- 2025-03-24 CSS原来样式表也能这么“智能”!(css样式表技术)
- 2025-03-24 「程序员空闲时光」之用代码绘制国旗 13:土耳其国旗