运行时补丁 (dowhen)
使用 dowhen 库在运行时对第三方库的方法进行精确补丁,无需 fork 或等待上游修复。
适用场景
- 第三方库的某个方法有 bug,但不想 fork 整个库
- 需要在特定条件下改变第三方库的行为
- 临时兼容性修复(等上游合并 PR 期间的过渡方案)
基本语法
from dowhen import when
# 在 SomeClass.some_method 中,找到 'target_line' 这行代码
# 当 condition 为真时,用 .do() 中的代码替换/插入
(
when(
SomeClass.some_method,
'target_line_of_code', # 定位到方法中的哪一行
condition='some_condition' # 可选:仅在条件成立时执行
)
.do("replacement_code")
)
实际案例
案例1:修改 Langfuse 回调的日志级别
当 LangGraph 流被主动中断(GeneratorExit)或前置检查异常时,Langfuse 会记录 ERROR 级别。用 dowhen 改为不记录:
from langfuse.langchain.CallbackHandler import LangchainCallbackHandler
from dowhen import when
(
when(
LangchainCallbackHandler.on_chain_error,
'observation = self._detach_observation(run_id)',
condition=(
'isinstance(error, GeneratorExit)'
'or "PreconsicousError" in str(type(error))'
'or "CancelledError" in str(type(error))'
)
)
.do("level = None") # 将 level 设为 None,跳过错误记录
)
案例2:修改工具错误的记录方式
将特定业务异常的工具错误从 ERROR 降级为 WARNING:
do2 = '''
if observation is not None:
observation.update(
status_message=str(error),
level="WARNING",
input=kwargs.get("inputs"),
cost_details={"total": 0},
).end()
observation = None
'''
(
when(
LangchainCallbackHandler.on_tool_error,
'if observation is not None:',
condition='(error.__class__.__name__ in ["ChangeToolError"])'
)
.do(do2)
)
案例3:兼容 DeepSeek reasoner 的多轮对话
DeepSeek reasoner 模型要求 assistant 消息必须包含 reasoning_content 字段,但 langchain-deepseek 没有正确传递:
from langchain_deepseek import ChatDeepSeek
do4 = '''
messages = self._convert_input(input_).to_messages()
reasoning_content_map = {}
for i, msg in enumerate(messages):
if hasattr(msg, "additional_kwargs"):
rc = msg.additional_kwargs.get("reasoning_content")
if rc is not None:
reasoning_content_map[i] = rc
for i, message in enumerate(payload["messages"]):
if message["role"] == "assistant":
if i in reasoning_content_map:
message["reasoning_content"] = reasoning_content_map[i]
elif "deepseek-reasoner" in self.model_name and "reasoning_content" not in message:
message["reasoning_content"] = " "
'''
(
when(
ChatDeepSeek._get_request_payload,
'for message in payload["messages"]:',
)
.do(do4)
)
使用建议
- 补丁代码集中放在一个文件中(如
utils/dowhen_man.py),在应用启动时 import - 补丁中添加注释说明:为什么需要这个补丁、对应的上游 issue
- 当上游修复后,及时移除补丁
condition参数可以精确控制触发条件,避免影响正常流程