ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

用FunctionCall实现文件解析(七):被动地等ChatOpenAI塞进来

2026/8/5 19:20:16 拓冰建站 浏览量
用FunctionCall实现文件解析(七):被动地等ChatOpenAI塞进来

文章目录

  • 前言
  • 触发机制
  • 实现代码
  • 使用

前言

在上一篇文章中,我们研究了怎么样主动把ChatOpenAI塞进去,那么接下来就是得研究怎么把ChatOpenAI被动地塞进来。

所有的东西都放在了Github仓库中。

欢迎来玩。

触发机制

与上一篇文章的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执行的时候,电脑就开始准备发光发热了(物理)。

实现代码

由于大致的逻辑跟上一篇文章大差不差,所以就不再多做什么解释了。

我们直接试试:

class ChatRenderCallbackHandler(BaseCallbackHandler):"""把 LLM 流式 token 渲染到 Streamlit,双容器分流。"""def __init__(self,role: str = "assistant",save: bool = True,label: str = "🤔 思考过程",):super().__init__()self.role   = roleself.save   = saveself.label  = label# UI 占位:只有 answer 先占位,<think> 动态生成with st.chat_message(role):self.answer_holder = st.empty()self.expander     = Noneself.think_holder = None# 累积完整文本self.buffer = ""# ===== Callback API =====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: returnself.buffer += delta# 拆分 <think>think_parts = "\n".join(_THINK_RE.findall(self.buffer))answer_text = _THINK_RE.sub("", self.buffer).strip() or " "# 第一次检测到 <think> ⇒ 创建 expanderif think_parts and self.expander is None:self.expander     = st.expander(self.label, expanded=False)self.think_holder = self.expander.empty()# 刷新 UIif self.think_holder is not None:self.think_holder.markdown(think_parts)self.answer_holder.markdown(answer_text)

使用

既然都完成了,那就开始用吧:

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")]}))

然后,就有效果了:

效果呈现

其实本质上是和上一篇文章的效果是一模一样的。