Ant Design Pro和Ant Design X连接Ollama本地大模型开发AI应用(2)

Ant Design Pro和Ant Design X连接Ollama本地大模型开发AI应用(2)

技术教程gslnedu2025-06-23 17:50:213A+A-

上一篇文章讲了如何用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 <div dangerouslySetInnerHTML={{ __html: md.render(content) }} />

};

接着修改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: <Spin key="Waiting" />,

},

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>)(.*?)(<\/think>)/gs;

const thinkPattern1 = /<div class="think-container">(.*?)<\/div>/gs;

for await (const chunk of stream) {

// console.log(chunk)

// tempContent += chunk.choices[0].delta.content

if (chunk.choices[0].delta?.content == '<think>') {

tempContent = tempContent + '<div class="think-container"><span class="think-process think-process-show">正在深度思考...</span><br />' + chunk.choices[0].delta?.content

} else if (chunk.choices[0].delta?.content == '</think>') {

if (tempContent.indexOf('think-container') == -1) {

tempContent = '<div class="think-container"><span class="think-process think-process-show">已深度思考</span><br /><think>' + tempContent

}

tempContent = tempContent + chunk.choices[0].delta?.content + '</div>'

tempContent = tempContent.replace('<span class="think-process think-process-show">正在深度思考...</span><br />', '<span class="think-process think-process-show">已深度思考</span><br />')

} else {

tempContent = tempContent + chunk.choices[0].delta?.content

}

// think空内容

const match = tempContent.match(thinkPattern)

if (match != null && (match[0] == '<think>\n\n</think>' || match[0] == '<think>\n</think>') && tempContent.indexOf('think-process') != -1) {

tempContent = tempContent.replace(thinkPattern1, '$1').replace('<span class="think-process think-process-show">正在深度思考...</span><br />', '').replace('<span class="think-process think-process-show">已深度思考</span><br />', '')

}

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);

}

最终效果

点击这里复制本文地址 以上内容由朽木教程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

朽木教程网 © All Rights Reserved.  蜀ICP备2024111239号-8