Files
zhuangang/topics/cs/algorithms/scenarios/business-algorithms.md
T

3.4 KiB

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id title type domain stack tags level prerequisites est_hours related week
cs.algorithms.scenario.business-algorithms 业务中的算法:热门问题 Top K、任务依赖排序、相似问题去重和缓存淘汰 scenario cs
algorithms
redis
langgraph
Top K
拓扑排序
编辑距离
LRU
去重
复杂度
intermediate
cs.algorithms.complexity-patterns
1.5
cs.algorithms.interview
16

业务中的算法:热门问题 Top K、任务依赖排序、相似问题去重和缓存淘汰

业务背景与问题

AI 客服运营后台需要:

  1. 每小时展示用户最常问的 20 个问题(每小时几十万条消息);
  2. 知识库导入任务有依赖(先解析、再分块、再嵌入、再建索引),要按正确顺序执行,并发现配置中的循环依赖;
  3. 标注 Bad Case 时,大量问题只是错别字或说法略有不同,需要聚合去重;
  4. 进程内缓存最近查询的文档,内存有限。

约束

  • 统计要在秒级完成。
  • 去重不能误合并意思不同的问题。

方案设计

  1. Top K:先对问题文本做规范化(去空白、统一全半角),用哈希表计数,再用大小为 K 的堆取前 K,O(n log K);多实例时用 Redis 有序集合 ZINCRBY + ZREVRANGE 汇总。
  2. 任务依赖:用拓扑排序得到执行顺序,检测到环时拒绝保存配置;同层没有依赖关系的任务可以并行。
  3. 相似去重:先按规范化文本精确去重;再对候选对计算编辑距离(短文本)或向量相似度(语义),超过阈值的合并为一组,人工确认后生效。编辑距离是 O(mn),只对长度相近的候选对计算,避免 O(n²) 两两比较全部。
  4. 缓存:LRU 淘汰;Python 中可直接用 functools.lru_cache 或 OrderedDict 实现。

关键代码

from patterns import edit_distance, top_k_frequent, topo_order

hot = top_k_frequent((normalize(m.text) for m in messages), 20)

order = topo_order({"index": ["embed"], "embed": ["chunk"], "chunk": ["parse"]})
if order is None:
    raise ValueError("任务配置存在循环依赖")

def similar(a: str, b: str) -> bool:
    if abs(len(a) - len(b)) > 2:                     # 先用长度剪枝
        return False
    return edit_distance(a, b) <= max(1, len(a) // 10)

完整实现和测试:examples/patterns。

取舍

选择 优点 缺点
精确计数 + 堆(本方案) 结果准确 内存随不同问题数增长
近似算法(Count-Min Sketch 等) 内存固定 有误差
编辑距离去重 适合错别字 识别不了同义改写,需要配合向量相似度

易错点

  • 没有规范化就计数,同一问题被拆成多个。
  • 对全部问题两两计算相似度,O(n²) 跑不完。
  • 去重阈值过松,把“退货”和“退款”合并(它们的编辑距离只有 1,但业务含义不同)——相似度只是候选,需要人工确认或结合业务规则。

面试怎么讲

“运营后台的热门问题,我先做文本规范化,用哈希表计数加大小为 K 的堆取 Top K,复杂度 O(n log K),多实例用 Redis 有序集合汇总。导入任务的依赖用拓扑排序确定顺序并检测环。Bad Case 去重先精确去重,再按长度剪枝后算编辑距离,阈值只产生候选,避免把‘退货’和‘退款’这种只差一个字但意思不同的问题合并。”