Linkora 的定位很朴素:做一个能替我出现在钉钉、飞书、企微群聊和私聊里的「AI 分身」。但「替我出现」比想象中难得多——不是接个大模型就完事,难点全在平台差异、上下文边界和「何时该闭嘴交还真人」。
分层:适配器吃下平台差异
最底层是一组适配器,钉钉走 DWS、飞书/企微走各自的 SDK。它们都继承同一个 BaseIMAdapter 抽象,对外暴露统一的「收消息 / 发消息 / 标已读」接口。平台特有的字段(消息类型、openMessageId、媒体 mediaId)在适配器内部消化,上层逻辑完全不感知自己在哪条 IM 上。
一个踩过的坑:企微的「已读闸门」在某些版本里是静默降级的——基类返回空字典,上层以为成功其实没生效。后来改成启动期主动告警,避免长期以为「已读信号在工作」。
决策:RAG + 规则引擎协同
收到一条消息后,先过规则引擎做硬匹配(关键词、意图 39 类),命中的直接走确定性动作;没命中的才交给 LLM 结合 RAG 召回的文档知识生成回复。
设计原则:能用规则确定的,绝不浪费一个 Token 给模型;只有真正需要理解语义的,才暴露上下文给 LLM 让它自己判断。
门控:什么时候该收尾
「对话已经闭环还在追问工号手机号」是最典型的失控。我们用双向校验解决:进 LLM 前先 _reply_gate_reason 前置过滤(命中即省 Token),投递前再 _should_reply_now 做并发兜底。优先级是:自身 → 人工接管 → 真人在场 → DWS 已读。
仍在推进的
- RAG 分块按语义边界切分,
max_len作为软上限避免中间截断; - 文档清洗引入 LLM 替代纯正则;
- JWT / RBAC 安全加固已在 v0.4.0 落地(迁移至 pyjwt 标准实现,详见发布记录)。
完整代码在 GitHub,欢迎来提 issue。