优秀的编程知识分享平台

网站首页 > 技术文章 正文

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

nanyue 2025-03-24 18:46:41 技术文章 7 ℃

上一篇文章讲了如何用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);

}

最终效果

最近发表
标签列表