04 · 核心能力

按"客户会问什么"组织。每个能力都标了改哪里,方便二次开发。


1. 自动回复 v2(aivbot_service.py)

决策表:房间类型 × 模式

room_type含义可选 mode行为
1外部群reply_all / track_at / at_only / silent全部回 / @ 后 180s 内自行判断 / 只回 @ / 静默
2外部私聊reply / silent回 / 静默
3内部群同外部群
4内部私聊同外部私聊

配置存在 reply_strategies.config:

{"1": {"mode": "track_at", "merge_window": 30, "track_duration": 180},
 "2": {"mode": "reply",    "merge_window": 30}}

三个容易踩的机制

  1. 合并窗口(merge_window,秒,默认 30):同一设备 + 同一房间 + 同一发言者的消息在窗口内攒成一批,一次交给 LLM。队列按人拆开,所以群里两个人同时说话不会互相污染上下文。真机联调建议先调到 4~5 秒。
  2. @ 判定:设备上报的 101 事件里没有 at 字段,只能从正文认。本机企微名来自 503 上报(内存缓存,重启失效,设备页有「获取名字」按钮手动刷新)。名字已知时只认精确相等(@郭栋 唤醒,@郭栋-客服 不唤醒);名字未知时退回"含 @ 就算"。
  3. 房间名必须取自会话列表行,不能取自头部标题:无自定义群名的外部群头部显示「群聊(N)」,而列表行显示"成员A、成员B"。存错名字的后果是 LLM 回消息时设备进不了房间。服务端对"纯数字/页面占位词"的房间名会打 ⚠️ 房间名可疑 告警。

设备级限额

max_concurrent_llm(同时最多几个 LLM 调用)、max_calls_per_min(每分钟上限)。多设备互相不影响。


2. 智能体(agent_service.py)

  • 服务端不代发消息:模型自己调 device_command_203 发;send_reply 只决定"回复规则"如何注入提示词。
  • 工具集按需注入:联网、知识库检索、表格读写(按表授权)、建任务、跑工作流(按流程授权)、设备指令(按指令号逐个授权)。
  • 永远没有 table_delete,工作流的 delete 节点一律拒绝执行。
  • 每次工具调用落 tool_call_audit(失败也记),页面 /admin/audit。
  • 20+ 种设备指令可授权(203 发送、204 指定回复@、205 转发、206 建群、207 改群、213 改备注/加好友、218 推文件、221 待办、224 推链接、234 删联系人、501~509 各类读取)。完整清单与参数契约见 05-通信协议。

3. 长期记忆(memory_service.py + memory_cleanup_service.py)

和知识库刻意分开:知识库 = 组织的资料(相似度检索、只读);记忆 = 客户的事实(精确命中、可读可写)。

  • 三层:memory_sets(记忆集)→ memory_customers(客户,aliases 认别名,同一客户跨群一行)→ memory_items(一条事实,customer_id=0 是"通用桶")
  • 挂在设备上,不挂智能体(robot_memory_sets):换智能体不换记忆,解绑设备自动失效;同一套记忆可挂多台设备 → 多账号共享客户事实
  • 客户键只做「精确名 + 别名」,不做子串(子串会误伤:张三 命中 张三丰)
  • 事实只追加、永不改写:唯一键 (set_id, customer_id, content_md5) + INSERT IGNORE
  • 人工导入只看规范名:名字撞上别人的别名时另建客户,不并进别人家
  • 别名填了就覆盖、留空不动

护栏(写死,页面上有一张只出现一次的说明卡)

限制值
单条事实200 字
每客户注入50 条
单次注入总量1500 字符
每套事实 / 客户数5000 条 / 500 位
客户名册注入2000 字符

记忆清理(可选):设了频率(0=不清理 / 每天 / 每 3 天 / 每周 / 每 30 天)+ 一个整理智能体才启用。边界全在代码里:

  1. 只处理"自上次清理以来有新的智能体写入事实"的客户(人工导入不触发);
  2. 只删改 source='agent',人工事实一条不动,模型若把人工事实写进结果会按 md5 丢弃;
  3. 模型返回空数组/解析失败/异常 → 什么都不删,只记 skipped/failed。

单次最多 20 位客户(控成本),无论个别成败都推进 last_cleanup_at;memory_cleanup_log 存 before/after 清单。⚠️ 0 是有意义的值,读配置时别写 int(x or 默认值)。


4. 工作流(workflow_engine.py / routes/workflows.py)

纯显式节点图:每个节点显式声明出口去向,节点顺序不参与运行语义;留空 = 到此结束。

节点类型:llm / table_query / table_insert / table_update / table_create / http_get / http_post / device_command / task_create / condition / switch / code / output

  • LLM 节点可选智能体(默认=调用方)+ 可挂技能(params.skill_ids)
  • 运行日志:workflow_runs / workflow_node_logs,页面 /admin/workflow/{id}/run/{run_id}
  • 工作流分敏感/普通,敏感的要额外授权才能被智能体或任务调用

5. 任务(task_service.py)

触发器 × 动作正交:

  • 触发器 schedule:cron / interval / once / offset
  • 触发器 condition:friend_accepted(好友通过)/ message_from(来自某人/某类消息)
  • 动作:跑 workflow 或问 agent

条件检测三层兜底:设备事件流(101/502)→ 轻量信号匹配 → 共享轮询(507 好友快照 diff,每设备一个低频 poller)。


6. 技能(skill_service.py)

触发条件:群类型 / 关键词 / 正则。命中后作为软约束注入会话 system(不是硬指令,模型可判断不适用)。工作流 LLM 节点也可挂载。适合放"话术风格""行业黑话""禁止承诺的事"。

7. 知识库(knowledge_service.py)

上传文档 → 切块 → 向量化 → 按相似度检索后注入。两种向量后端:本地哈希(零依赖、512 维)或 OpenAI 兼容 embedding 接口(如 bge-m3、1024 维)。

⚠️ 换检索方式/换模型/换维度必须重建向量,否则存量 chunk 是空向量,检索静默返回空。重建脚本在服务端仓 tests/manual_reembed.py。

8. 数据表格(table_service.py)

建表 → 智能体按表读写(agent_table_grants 逐表授权)→ 管理端筛选/导出 CSV(多条件 AND、带 BOM、公式注入防护)。智能体永远不能删表。

9. 外网客户控制台 /ext(routes/ext.py)

给最终客户用:看设备/智能体/知识库/表格(只读)、改模型的服务商与 base_url、开关 LLM 推送、维护长期记忆(唯一可写资源)。

三道门:X-Tunnel-Secret 头(反向代理注入)→ 会话 cookie → 组织归属。未配置 EXT_TUNNEL_SECRET 时 /ext 整体 403,默认关闭。

10. AI 配置组织(provision_service.py)

管理员在 /admin/orgs/provisioner/chat 用自然语言描述需求,模型调用 10 个 provision_* 工具把组织、智能体、表格、工作流、知识库、任务、设备、账号建出来。配置智能体是全局唯一的一行(agents.kind='provisioner'),不属于任何组织、不出现在智能体列表里。只创建、不删除。

需要有人帮忙落地?可以联系定制开发、私有化部署、联合交付或技术咨询。