当前位置: 首页 > article >正文

怎么实现AI思考过程

unsetunset前言unsetunset

在做多Agent系统时,因为整个系统可能会经过多次Agent处理,耗时会比较高,如果不给前端展示Agent处理的过程,那么用户的体验就会非常不好,在用户的视角,你的程序可能卡死了,但其实你的Agent在背后干活以求获得好的结果。

解决方案也比较直观,就是将Agent的处理过程在前端返回出来,这也是很多产品的做法,即所谓的有一个thinking过程的展示。

本文就是实现一个这样的效果,教育目的,所以会写的经量简单。

unsetunset实现unsetunset

为了简化,我们这里就不弄多Agent系统来讲解,直接用Llamaindex构建一个基本的RAG,然后我们将RAG的过程展示为thinking过程,至于多Agent其实也一样的,然后用React来做前端。

Github: https://github.com/ayuLiao/show_llm_thinking_example

效果如下:

2af02fd01b8d049b414cc3530f3cc50a.png

unsetunset后端技术细节unsetunset

我们定义了Message对象,每个Message对象会有关联的sub_processes对象,sub_processes对象存储着处理过程信息。

class ProcessType(str, Enum):
    THINKING = "thinking"
    SEARCHING = "searching"
    ANALYZING = "analyzing"
    GENERATING = "generating"


class SubProcess(BaseModel):
    id: str
    type: ProcessType
    status: str
    detail: str


class Message(BaseModel):
    content: str
    sub_processes: List[SubProcess]

当用户提问时,我们会先创建一个拥有回复的Message对象,一开始Message对象的content是空的,然后我们为它添加sub_process,表示开始处理了,然后在通过send_chain这个异步队列将带有sub_process的Message对象返回(添加到队列里,API那边会while True一直取,从而实现实时将状态反馈给前端)

content = ""
message = Message(content=content, sub_processes=[])

# 思考阶段
thinking_process = SubProcess(
    id=str(uuid.uuid4()),
    type=ProcessType.THINKING,
    status="running",
    detail="正在思考问题...",
)
message.sub_processes.append(thinking_process)
await send_chan.put({"data": json.dumps(message.model_dump())})
await asyncio.sleep(0.5)

thinking_process.status = "completed"
await send_chan.put({"data": json.dumps(message.model_dump())})

从上面代码可知,sub_process本身也有状态,完成了,status会设置成completed,比如我们搜索文档时,状态是SEARCHING,然后完成后,则是completed。

# 搜索阶段
search_process = SubProcess(
    id=str(uuid.uuid4()),
    type=ProcessType.SEARCHING,
    status="running",
    detail="搜索相关文档...",
)
message.sub_processes.append(search_process)
await send_chan.put({"data": json.dumps(message.model_dump())})

# 实际调用 LlamaIndex 查询
response = query_engine.query(question)

search_process.status = "completed"
await send_chan.put({"data": json.dumps(message.model_dump())})

然后,我们流式返回,真正的结果,会添加到message.content中。

# 生成回答阶段
generating_process = SubProcess(
    id=str(uuid.uuid4()),
    type=ProcessType.GENERATING,
    status="running",
    detail="生成回答中...",
)
message.sub_processes.append(generating_process)
await send_chan.put({"data": json.dumps(message.model_dump())})

# 逐字输出回答
for word in str(response).split():
    content += word + " "
    message.content = content
    await send_chan.put({"data": json.dumps(message.model_dump())})
    await asyncio.sleep(0.1)

generating_process.status = "completed"
await send_chan.put({"data": json.dumps(message.model_dump())})

await send_chan.put(None)

整个流程非常直观。

如果你是多Agent,那么也是先构建一个Message对象,然后一直将Message对象返回,只是Message对象里的sub_processes随着Agent处理在变化,然后返回给前端。

unsetunset前端技术细节unsetunset

前端就没什么好讲的,创建好React项目后,修改App.tsx,当用户点击提交后,一直监听后端返回的流式信息,相关代码如下:

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (!question.trim() || isLoading) return;

  setIsLoading(true);
  setMessage({ content: "", sub_processes: [] });

  const eventSource = new EventSource(
    `http://localhost:8000/chat?question=${encodeURIComponent(question)}`
  );

  eventSource.onmessage = (event) => {
    console.log("Received event:", event.data); // 调试日志
    try {
      const newMessage = JSON.parse(event.data);
      setMessage(newMessage);
    } catch (error) {
      console.error("Error parsing message:", error);
    }
  };

  eventSource.onerror = (error) => {
    console.error("EventSource error:", error); // 调试日志
    eventSource.close();
    setIsLoading(false);
  };

  // 添加onopen处理
  eventSource.onopen = () => {
    console.log("EventSource connected"); // 调试日志
  };

  // 清理函数
  return () => {
    eventSource.close();
    setIsLoading(false);
  };
};

然后页面上,展示sub_processes就实现了展示thinking过程的效果。

<div className="message-container">
  <div className="process-list">
    {message.sub_processes.map((proc) => (
      <div key={proc.id} className="process-item">
        <span className={`status ${proc.status}`}>
          {proc.status === "running" ? "⏳" : "✅"}
        </span>
        <span>{proc.detail}</span>
      </div>
    ))}
  </div>

  <div className="content">{message.content || "等待回答..."}</div>
</div>

unsetunset尾部unsetunset

最近团队扩张招人,我们需要:

  • 海外运营(了解SEO、海外社媒运营、英文读写过关的)

  • Python开发(了解RAG、Agent框架、基本后端)

  • React开发(了解Nextjs)

在广州的同学,可以给我发简历,本司不加班、扁平化,全员配MacOS+2K显示器,报销Cursor等各种AI提效产品,能了解到最新的AI技术的变化,有兴趣就来一起开发能落地的多Agent系统吧。


http://www.kler.cn/a/534570.html

相关文章:

  • “AI隐患识别系统,安全多了道“智能护盾”
  • axios如何利用promise无痛刷新token
  • CSS的媒体查询语法
  • DeepSeek-R1:开源机器人智能控制系统的革命性突破
  • Java进阶笔记(中级)
  • 每日一题洛谷P5721 【深基4.例6】数字直角三角形c++
  • 【前端】【Ts】TypeScript的关键知识点
  • css小知识
  • Windows图形界面(GUI)-QT-C/C++ - QT Dock Widget
  • 【12】深入理解Golang值传递与引用传递:避坑指南与性能优化
  • 前端学习数据库知识
  • React组件中的列表渲染与分隔符处理技巧
  • YOLOv11实时目标检测 | 摄像头视频图片文件检测
  • ZZNUOJ(C/C++)基础练习1061——1070(详解版)
  • 《redis的pub/sub机制》
  • Vue 3 中的 el-tooltip 详解:语法、示例及与其他框架对比
  • 谈谈对IOC的理解
  • 反向代理模块anns
  • 笔记:新能源汽车零部件功率级测试怎么进行?
  • 文心一言指令词宝典之职场效率篇
  • Java 大视界 -- Java 大数据在智慧文旅中的应用与体验优化(74)
  • 快速幂,错位排序笔记
  • 【字节青训营-6】:Gorm的基础使用
  • DeepSeek与llama本地部署(含WebUI)
  • ESXI虚拟机中部署docker会降低服务器性能
  • C# 压缩图片并保存到本地