
平时工作用大部分时间都是qwen/deepseek/豆包, 今天准备睡觉突然来兴趣上ChatGPT问一个之前在deepseek已经over的问题,结果回答得让我精神一振毫无睡意。
回答得实在太好,比deepseek强不知道多少倍!也不知道是不是因为ChatGPT可以实时翻阅GitHub导致,质量优秀得无话可说。估计后面好一段时间又得跑GPT了。
平时工作用大部分时间都是qwen/deepseek/豆包, 今天准备睡觉突然来兴趣上ChatGPT问一个之前在deepseek已经over的问题,结果回答得让我精神一振毫无睡意。
回答得实在太好,比deepseek强不知道多少倍!也不知道是不是因为ChatGPT可以实时翻阅GitHub导致,质量优秀得无话可说。估计后面好一段时间又得跑GPT了。
其实也做了有好一段时间, 一直没时间归档, 今天决定mark一下.
由于工作需要, 最终效果需要具备移植性, 所以我这里Dify平台也尝试了本地部署, 以保证最终可交付.
准备就绪, 接下来就是创建ChatFlow
先捋一下编排构思:
Dify当前是原生支持http请求节点, 安装工具也能够支持database. 所以取数问题不大. 刚开始先直连数据库做个demo, 大概流程如上图所示
实际调试下来, 需要增加很多的优化节点才能保证有一个比较高的准确性, 上图是花了近一天时间调试下来的效果, 而且还没有足够完善, 比如数据库查询这一个环节, 有几率会出现生成的SQL无法执行, 需要增加重试机制(不是简单的重新执行, 而是把错误结果作为BuildSQL的一部分进行重试)
贴几处提高准确率办法:
1. 提取关键字再进行知识检索能提高检索准确率
2. 自动编写SQL, 需要对做好限定条件, 且保留sql到最终回复(便于持续优化)
3. 固定逻辑写Python处理, 而不是全靠LLM
4. 保留好异常结果(不要过于依赖后台预览, 发布后仍然需要持续优化)
整体用下来基本都是拖拉拽, 测试, 调整prompt, 测试, 增删节点,调整promtp, 测试……
目前有发现影响比较大的问题: 预览测试失败时, 失败节点如果在循环体内, 无法显示是循环体内的哪个节点有问题, 这个超级不方便(我用的1.3.0版本)
多个Echarts结果无法连结同一个变量聚合器, 导致柱状图/饼图/线型图都需要分别做结果处理
DSL导出导入经常不可用,怀疑是版本匹配问题
昨天研究HashMap(版本8)底层。
发现jdk的工程师对于HashMap定义容量的处理超级聪明。
对于手动输入容量值,底层会计算出一个比她大,而且最近的一个 二次幂的数
比如输入7,会得到8,输入9,会得到16,输入17会32。
底层用的是位运算。因为2次幂数二进制都是1开头后面全是0。
2==>10
4==>100
8==>1000
16==>10000
二进制规范(int)一下如下
0000 0000 0000 0000 0000 0000 0000 0010
0000 0000 0000 0000 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0000 1000
0000 0000 0000 0000 0000 0000 0001 0000
所以只要知道输入的值的二进制,前面加个1,后面清零即可得到最邻近的二次幂数
/** * Returns a power of two size for the given target capacity. */ static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
上面是JDK工程师是这样写的,通过右移并对自己进行或运算,“把自己全部变成1”,然后再+1得到结果。
看完先默默膜拜个一秒钟。
自个儿的回顾了一下二进制,大概就是自问自答,自己考自己吧。 然后发现自己对于“ 怎么用位运算快速得到 int 区间的最大值和最小值 ”竟然回答不出来!
事发在昨天晚上,今天早上自己跟自己较劲了一整个上午时间,流水账一下总结:
因为 int 类 占4个字节,每个字节占8个bit位置。所以每个int都需要占用32个bit的空间。
最小当然是全部都是0,最大是全部都是1。
微笑脸,这是错的。
因为一开始的那一位是用来代表正负的(1代表负,0代表正数),
所以实际上可以用来存值只有31个bit的位置。
而每个bit有两个状态:1和0。所以 int 可以代表的范围:
开头第一个bit是0时(正数) 有 2个2个2…一共有31个2。即2的31次方。
开头第一个bit是1时(负数) 有 2个2个2…一共有31个2…
所以一共有2的31次方再乘2 也就是2的32次方。
这样从后往前推的理解跟一开始学计算机时候理解的:“32个bit 每个bit代表1和0 所以int的取值范围是2的32次方”,丰富多了。
顺带还解决了我以前的一个疑惑:
为什么科教数上对于 int 的定义都是
-2147483648~ -1,0 ~2147483647
而不是
-2147483647~ 0, 1~2147483648
因为0 二进制的32个0,它的第一位也是0。
这过程还复习了一些很陌生的运算符:“~” 和 “>>>”
“~”是二进制的取反,比如
0的二进制:0000 0000 0000 0000 0000 0000 0000 0000
~0的二进制:1111 1111 1111 1111 1111 1111 1111 1111
那~0后再全部用 >>1 移一个bit的位置不就是
0111 1111 1111 1111 1111 1111 1111 1111
了吗,这不就是正数时候 int 的最大值
理论没错,但是使用 >>1 时候会根据最开始的位数进行补充。
换句话说,因为~0的第一位是1 所以补充的也是1,嗯,帮倒忙变成了:
1111 1111 1111 1111 1111 1111 1111 1111
//扶额。并没有任何变化! 我希望补充的是0而不是1可以吗
可以!
使用 “>>>1” 就可以了
所以 int 的最大值可以是 ~0>>>1
通过这样得到 int 的极限值 比以前的那种通过 int 溢出得到边缘数好玩多了!
/*
多手尝试了一下<<1和<<<1会不会也是这种套路
结果是输入第三个<后直接就冒红警告了
*/
还有一些跟常识有点冲突的理解,
比如上面的 ~0 二进制是全部都是1。用常规的理解就是,负数情况的极限值
即int 的负边缘数: -2147483648 ?
按这个套路,二进制是
1000 0000 0000 0000 0000 0000 0000 0001
的时候,就是负数时候第一个数,即右边缘数, -1 ?
答案是错的。这就是跟常规的冲突所在了。
我们普通的理解下 前面加个 “-” 后面的数字越大,其实质上是越小,因为她是朝着负的方向头走的,所以走的越远,越小。
但是这里不能这样套路,int 的第一个 bit 不可以直接用 “1是 – ”,“0是+” 进行直接翻译。
想透彻了解有点辛苦,大概就是 二进制10为什么是int的2 这样的问题。理解10变成2的话就自然懂了。
从简单推导也可以辩证出。 比如简单的二进制加减 这个入手。
1+1 二进制
0000 0000 0000 0000 0000 0000 0000 0001
+
0000 0000 0000 0000 0000 0000 0000 0001
逢2进1。变成
0000 0000 0000 0000 0000 0000 0000 0010
自然也不难理解 为什么int的最大值 2147483647 即二进制
0111 1111 1111 1111 1111 1111 1111 1111
为什么加一变成负数。因为加1后变成了
1000 0000 0000 0000 0000 0000 0000 0000
也就是符号被填充了1,成了负数。
回到刚才,0-1大家都知道等于-1。
即全部bit都是零的二进制减1,需要一直问前面的借借数,一直到最左边顶端的0也成为了1。
即全部1
1111 1111 1111 1111 1111 1111 1111 1111
代表的是 0-1= -1
这就是负数情况下的 “最大” 值。
计算机是对的。觉得冲突其实很简单,因为我们口头上说最大负数时候有时候会理解成那个“最左边”的数
比如
-999,-998…-1,0,1…998,999
的-999
然而“最大的负数”其实一直都不是 -999 而是 -1 啊。
所以 int 的最小负数 也就是开头是1 后面全是0
1000 0000 0000 0000 0000 0000 0000 0000
代表的是
-2147483648
一点毛病都没有!
-2147483648 + 1 = -2147483647 (1000 0000 0000 0000 0000 0000 0000 0001)
-2147483647 + 1 = -2147483646 (1000 0000 0000 0000 0000 0000 0000 0010)
…
一直加上去 直到到了 int 的最大负数 -1 (1111 1111 1111 1111 1111 1111 1111 1111)
-1+1 又变成了全部都是0
嗯,-1 + 1 当然等于0
没毛病!
source https://blog.csdn.net/yongroot/article/details/89431136
CSDN是越来越讨厌了,大概这辈子都不会再用。。
先科普:
雪花算法是一种由Twitter(现在是X)推广的分布式全局唯一ID的算法。关于雪花这个名称的解释,我自以为是因为雪花是随机飘落然后越堆越高的,前天同事问我雪花的由来是不是因为每片雪花都不一样,我觉得这个解释更浪漫。
回归主题,简单说这是一种生成不重复ID的算法,有别于UUID, 雪花具备自增性的,方便数据库存储和使用(可排序,体积也不大)
原理是一个63位(二进制)的正整数Long值,前41位保存毫秒级时间戳,中间10位存放机器唯一标识,剩下12位是用于1毫秒内序列递增, 如果1毫秒内递增序号值满12位,那就等待直至时间毫秒数+1再继续。
我手上有老一个项目,使用的是一套自编的ID算法:
这套算法有一个问题, 就是每个环境id进度不一样, 导致dev的配置数据无法直接迁移到sit,同理sit无法迁移到uat,导致配置成本很大。 所以我提议并主导引入雪花算法,经过一轮技术预研+组会讨论影响+1周dev+1月sit….. 雪花算法被推送到了生产。
然!
生产环境竟然出现ID冲突,初期以为因为机器码冲突
ps:dev发现Long值太长导致前端模块出现了异常,所以雪花缩水成53位,主要是机器码由10位缩成4位,序号从12位缩成8位,机器码基于hutool实现(使用ip)
然后就是夜里挑灯看…bug。经过了两日两夜的emjo:
先试调整算法,机器码增到到8,序号缩到4,问题没解决(其实这时候有种不好的预感,8位机器码即可容纳256个pod 竟然这么容易冲突?)
通宵添加了一个唯一机器码计算程序(给serviceName增加一个数字,每次应用上线时从服务注册中心下载清单校验唯一性),问题没解决
第二天白天研读代码发现Snowflake类存在多例情况(Snowflake创建在一个数据逻辑层底层对象类, 基于spring框架先入为主以为是单例,检查发现竟然不是,导致Snowflake类实际被创建多次),逐把Snowflake类属性增加static,信心满满以为解决,发版测试发现还是没解决
到了这个时候, 从代码已经检查不出问题了,后面模拟线上环境编写单元测试没问题, 通过日志埋点(仍怀疑workerId冲突),数据分析等等等一系列调试, 都没有发现问题。
直至现在
嗯, 现在也还没解决, 目前是代码逻辑增加一个外部引入的boolean做算法路由,先暂时切回到了传统算法, 这个五一假期继续研究吧。
回来补一下后续
后面再过滤几轮压测, 都没有再复现了, 有几个可能性: