按"客户会问什么"组织。每个能力都标了改哪里,方便二次开发。
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}}
三个容易踩的机制
- 合并窗口(
merge_window,秒,默认 30):同一设备 + 同一房间 + 同一发言者的消息在窗口内攒成一批,一次交给 LLM。队列按人拆开,所以群里两个人同时说话不会互相污染上下文。真机联调建议先调到 4~5 秒。 - @ 判定:设备上报的 101 事件里没有
at字段,只能从正文认。本机企微名来自 503 上报(内存缓存,重启失效,设备页有「获取名字」按钮手动刷新)。名字已知时只认精确相等(@郭栋唤醒,@郭栋-客服不唤醒);名字未知时退回"含 @ 就算"。 - 房间名必须取自会话列表行,不能取自头部标题:无自定义群名的外部群头部显示「群聊(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 天)+ 一个整理智能体才启用。边界全在代码里:
- 只处理"自上次清理以来有新的智能体写入事实"的客户(人工导入不触发);
- 只删改
source='agent',人工事实一条不动,模型若把人工事实写进结果会按 md5 丢弃; - 模型返回空数组/解析失败/异常 → 什么都不删,只记 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'),不属于任何组织、不出现在智能体列表里。只创建、不删除。