因为AI工具的兴起和繁荣,市面上涌现出了一大批AI“程序员”,可能他们对代码的机制甚至语法都一窍不通(是你吗?是你吗?Emmmmmmm);
但是他们通过cursor等集成AI的工具能成功生成所需要的程序工具,比如下面的主人公:
因为是我亲爱的客户M,奔着客户就是上帝,给钱就是义父的原则;我给必须给他打上马赛克...
故事的起因是这样的,M通过claude生成了一个python交易小程序,代码大概800行左右,然后咧:
它运行一段时间就崩溃了....
而且是偶发的....
这给亲爱的M急的直抓头皮,并且请教了 a bounch of 某宝客服.
But...
没解决
好!这下通过邻居小老弟老W的引荐,成功和我这个自吹“没什么bug解决不了”的15年开发经验的不秃中登,相遇了
并粘贴了下面的日志:
我一看,damn!!这能有任何关系?
Sppsvc.exe我就不介绍了。
日志他是来源于windows的事件日志系统:
这里有个小tip:
对于应用程序的崩溃你或许可以在这里看到崩溃日志,但是如果是带代码的程序开发,我们首先一定首先要锁定的程序本身。
所以在本案例中,首先要做的无非两件事:
1.程序的运行被外部环境干扰,比如安全软件的hook插件(因为他们是在你程序运行时跑到你的程序内部的),可能会在极端条件下诱发程序的崩溃,譬如360...
2.Python程序本身存在问题:
A.导入的库有问题。
B.写的代码有问题。
首先排除第一个点,我首先使用procexp.exe进行导入库的查找,看有没有可疑的dll,但事实证明他的电脑干净得不像话,(据说他为了解决这个问题把电脑都重装了.....
)
那么就只能去看第二个点,程序崩溃,那么首先我们要借助有效的IDE,首先对于python,我们运行pycharm进行调试肯定能抓到python层面的bug:
Debug模式跑起来!!!!!!
卒。。。
看来这个问题要解决起来绝非那么简单:
首先我们注意到一个熟悉的东西:
(0xC0000005)
这是个什么东西?
Windows 的异常代码 0xC0000005 就是 STATUS_ACCESS_VIOLATION,也就是“访问冲突”/“内存访问违规”
一般是编译型语言编译成二进制文件后,里面的一些操作,涉及到内存的违规访问,如果你是C/C++开发选手,这些应该很熟悉;如果不是,你只需要知道这种错误在python中是不存在的,既然在python中不存在,那么错误只会存在导入库中:
那么我们首先去看他的导入库有哪些:
Damn!!!!!这么多?那有没有快速的方法定位?
答案是:
有的
import faulthandler
faulthandler.enable(all_threads=True)
在你代码的顶部加入这两行代码,就可以在崩溃的时候在日志底部锁定出错的方法:
在本文中通过堆栈导致退出的函数最终锁定在:
通过函数的字面意思
convert_kline_to_image_tensor
可以知道这哥们M大概率是想把获取的K线图转化成一个CNN的Tensor
然后在这个函数里面崩了,这个函数主要涉及到下面的绘图库:
Matplotlib,
Okay我们一言不合先升级matplotlib到最近版本3.10.5
运行。。
Okay!看来是要从代码层面去解决(绕过)这个问题了。。
我们来问GPT。。用魔法打败魔法。。
GPT给我提供了N个解决方案。。但是都是去调整代码,试过了以后发现根本不行。。
那么我们只能另辟蹊径:
如果这个bug在现在的库中根本没解决,那么我就可以把这个函数作为一个单独的进程运行(你就算崩溃了,我重启你就行了,根本不会影响主交易程序),然后主交易程序作为一个单独的程序运行。。然后两个进程通信不就搞定了吗:
okay。。就这么干。。示例代码如下(看不懂也没关系反正就明白这么解决的就对了。。):
executor = ProcessPoolExecutor(max_workers=1)
def _render_kline_bytes(df_values, dates_values):
"""
子进程中运行的“渲染”函数示例:
接收纯数值和日期,返回 (1,1,64,64) 大小的 float32 tensor bytes
"""
# 这里省略实际绘图逻辑,直接返回一块空白张量的 bytes
arr = np.zeros((1, 1, 64, 64), dtype=np.float32)
return arr.tobytes()
def convert_kline_to_image_tensor(df_segment: pd.DataFrame) -> torch.Tensor:
"""
示例版:把 K 线数据打包给子进程,
超时或子进程崩溃时返回全零张量
"""
# 简单边界检查
if df_segment is None or len(df_segment) < 2:
return torch.zeros(1, 1, 64, 64)
# 准备纯数值和日期
df_vals = df_segment[['Open','High','Low','Close']].to_numpy()
dates_vals = df_segment.index.to_list()
global _executor
try:
fut = _executor.submit(_render_kline_bytes, df_vals, dates_vals)
raw = fut.result(timeout=2) # 最多等 2 秒
arr = np.frombuffer(raw, dtype=np.float32).copy()
return torch.from_numpy(arr).view(1, 1, 64, 64)
except TimeoutError:
fut.cancel()
return torch.zeros(1, 1, 64, 64)
except BrokenProcessPool:
# 子进程崩溃,重建池
try: _executor.shutdown(wait=False)
except: pass
_executor = ProcessPoolExecutor(max_workers=1)
return torch.zeros(1, 1, 64, 64)
except Exception:
return torch.zeros(1, 1, 64, 64)
最后:
感恩老板!
我的文章同步发表在我的公众号“夜读tech”,如果你感兴趣麻烦点一手关注谢谢