前言
在上一篇文章中,我们研究了怎么样主动把ChatOpenAI
塞进去,那么接下来就是得研究怎么把ChatOpenAI
被动地塞进来。
触发机制
与上一篇文章的ChatRender
不同的是,如果被动地塞进来,那就不可以使用这种主动的类,而必须采用invoke
或者stream
相关的callback
过程。
这是个什么过程呢?
在stream
或者invoke
方法中,如果我们配置了config
字典中的callback
属性,他就会开启CallbackManager
,然后创建一个你传入的YourCallbackHandler
实例。这个YourCallbackHandler
继承自BaseCallbackHandler
,他虽然父类特别多,但是也有一个核心的东西:CallbackManagerMixin
。这个类中就定义了on_llm_start
等钩子函数。
那也就是说,如果我们继承BaseCallbackHandler
类,然后定义了其中on_llm_new_token
或者on_llm_end
钩子,就会自然而然地触发我们自定义的逻辑。比如说,我们定义on_llm_new_token
中检测到大模型输出爆炸就真的把电脑炸到天花板上,当invoke
或者stream
执行的时候,电脑就开始准备发光发热了(物理)。
实现代码
由于大致的逻辑跟上一篇文章大差不差,所以就不再多做什么解释了。
我们直接试试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class ChatRenderCallbackHandler(BaseCallbackHandler): """把 LLM 流式 token 渲染到 Streamlit,双容器分流。""" def __init__( self, role: str = "assistant", save: bool = True, label: str = "🤔 思考过程", ): super().__init__() self.role = role self.save = save self.label = label
with st.chat_message(role): self.answer_holder = st.empty()
self.expander = None self.think_holder = None
self.buffer = ""
def on_llm_new_token(self, token: str, **kwargs: Any) -> None: """收到新 token 就增量刷新 UI。""" self._update(token)
def on_llm_end(self, response, **kwargs: Any) -> None: """流式结束,把完整内容写进 session_state。""" if self.save: st.session_state.dashscope["messages"].append( {"role": self.role, "content": self.buffer} )
def _update(self, delta: str) -> None: if not delta: return self.buffer += delta
think_parts = "\n".join(_THINK_RE.findall(self.buffer)) answer_text = _THINK_RE.sub("", self.buffer).strip() or " "
if think_parts and self.expander is None: self.expander = st.expander(self.label, expanded=False) self.think_holder = self.expander.empty()
if self.think_holder is not None: self.think_holder.markdown(think_parts) self.answer_holder.markdown(answer_text)
|
使用
既然都完成了,那就开始用吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from utils.chat_render import ChatRenderCallbackHandler if prompt := st.chat_input("你得注意你的言行……求你了……(´;ω;`)"): st.session_state.dashscope["messages"].append( {'role': 'user', 'content': prompt} ) st.chat_message('user').markdown(prompt) _ = list( llm.stream( [HumanMessage(content = prompt)], config = {"callbacks": [ ChatRenderCallbackHandler(role = "assistant") ]} ) )
|
然后,就有效果了:
![效果呈现]()
其实本质上是和上一篇文章的效果是一模一样的。