Related Component
Backend (Python/FastAPI)
Category
Enhancement
Problem / Motivation
[Improvement] Rex 系统提示中硬编码的 IM Send Protocol 应当移除/降级为按需载入的 skill
摘要
flocks/agent/agents/rex/prompt_builder.py 中的 _build_im_send_section()(约 90 行 prompt 文本)被无条件地拼接进 Rex 的系统提示。这段内容存在两个问题:
- 对绝大多数会话来说没有用——大部分用户/任务并不涉及"发消息到 IM",这段 prompt 是纯粹的负担
- Token 浪费明显——粗算 ~1.4k token / 每次 Rex 调用,按 Claude Opus 输入 $15/M、1000 次会话/天计,约 $21/天浪费在告诉模型如何向飞书发消息
更深层的问题是:这段 prompt 把 channel 的运行时知识(channel id、title 前缀、channel_type 映射)硬编码进了 agent 的系统提示,自定义 channel 完全没有任何接入点。这与Issue 377 反映的是同一种设计倾向:框架开发者把"内置的少数 channel"当作了"channel 的全部"。
现状
涉及代码
flocks/agent/agents/rex/prompt_builder.py:208 —— 占位符 __IM_SEND_SECTION__ 的替换点
flocks/agent/agents/rex/prompt_builder.py:431 —— _build_im_send_section() 函数定义
Prompt 节选(节选展示问题,完整内容见源码)
### IM Send Protocol (MANDATORY when user asks to send a message to WeCom/Feishu/DingTalk)
**Trigger**: Any request that involves sending a message to an IM platform
(企业微信/WeCom、飞书/Feishu、钉钉/DingTalk).
**Execute this exact sequence — no deviations:**
#### Step 1 — Identify how the user is talking to you
... ...
#### Step 4 — Map title prefix to channel_type
| Title prefix | channel_type |
|--------------|--------------|
| `[Wecom]` | `wecom` |
| `[Feishu]` | `feishu` |
| `[Dingtalk]` | `dingtalk` |
#### Step 5 — Send
... ...
### IM Session Resolution for schedule_task_create (MANDATORY)
... ...
整段是一个6 步业务 SOP(Identify → Discover → Ask → Map → Send → Report),并且后面还跟着一个针对 schedule_task_create 的相似版本。
问题逐条分析
1. 实用价值低 —— 不发 IM 的会话也要带
无论:
- 用户当前是否启用了任何 IM channel
- 当前会话是否来自 IM
- 用户的请求是否与"发消息"有关
- 当前是否在做安全分析、查日志、写检测规则等完全不相关的任务
这段 ~1.4k token 都会被注入。Rex 是项目的核心 orchestrator,几乎所有会话都经过它,这段提示的"命中率"极低——保守估计 < 5%。
2. Token 浪费成本
| 项目 |
数值 |
| IM Send Section 体积 |
~1.4k token(中英混排,含表格、代码块、两套独立协议) |
| Claude Opus 输入价格 |
$15 / 1M token |
| 单次 Rex 调用浪费 |
≈ $0.021 |
| 团队规模 1000 会话/天 |
≈ $21/天 |
| 一年 |
≈ $7,665 |
更隐性的成本:上下文窗口被占用、模型注意力被分散、跟"如何发 IM"无关的任务里模型也要扫一遍这段(虽然不会用,但仍要消耗 attention)。
3. 硬编码 channel 类型 —— 把"发现机制"伪装成"知识"
| Title prefix | channel_type |
| [Wecom] | wecom |
| [Feishu] | feishu |
| [Dingtalk] | dingtalk |
这种映射本来应该由 ChannelRegistry 自己暴露——agent 应当询问框架,而不是依赖框架开发者手写进 prompt。
后果:
- 加新 channel 必须改
prompt_builder.py 这个与 agent 业务逻辑相关的文件
- prompt 与 channel 实现强耦合,违反开闭原则
- 第三方/自定义 channel 永远无法被 Rex 自然识别——即使一个用户级 channel 注册成功,Rex 也不知道
[Foo] 前缀对应什么 channel_type
- 提示里写 "only WeCom/Feishu/DingTalk" 等于在用户面前否认了自定义 channel 的存在
4. 把业务流程"冻结"在系统提示里
整段是 6 步业务 SOP,不是 prompt 应当承载的内容。它本质上应该是:
- 一个工具:
im_send_message(message, target=None) 内部封装"发现 session → 询问用户 → 发送"
- 或者一个 skill:
im-send,按需 skill_load
把流程逻辑塞进系统提示的具体危害:
- 每次会话都要让 LLM "重新读懂"流程,LLM 推理执行 SOP 不如代码可靠——会跳步、会幻觉
- 流程更新需要发新版 flocks(动 prompt template),而不是发布一个新工具/skill
- 无法做单元测试
- 无法被用户/agent 关闭或替换
5. Prompt 自身也有几个具体问题
- Step 2 让模型 "filter sessions whose title starts with
[Wecom] / [Feishu] / [Dingtalk]" —— 用 session 标题字符串前缀 作为 channel 类型判断,这种类型信息应当是 session 元数据的一等字段,不应靠 LLM 解析字符串
- "我不知道" 选项 被硬编码成中文,prompt 其它部分对多语言用户使用英文,混用导致非中文用户看到机器人界面会突兀
- Step 6 "Report" 完全是空话——LLM 本来就会在结尾汇报结果,不需要单独写一步
这两个 issue 反映的是同一种设计倾向:
| 子系统 |
表现 |
| Channel 加载(Issue 377) |
内置 channel 写死在 _register_builtin_channels,自定义 channel 走 plugin loader 出 bug |
| Channel prompt(本 issue) |
内置 IM 协议写死在 prompt_builder,自定义 channel 没有扩展点 |
根因都是:ChannelRegistry 没有作为"channel 真相之源"被尊重——其他子系统(gateway、prompt builder)都各自维护了一份对 channel 的局部认知。
我并不主张推翻 ChannelPlugin 抽象本身(meta / start / send / handle_webhook 是合理的),主张的是项目组没把 channel 抽象用到底——内置 channel 跳过 plugin loader、prompt 里硬编码 channel 类型,这些都是"知道有抽象但绕开它"的反例。
修复方案
方向:从 Rex 系统提示中移除 IM Send Section,降级为按需载入的 skill + 元数据驱动
第 1 步:从 Rex 系统提示中删除 __IM_SEND_SECTION__
flocks/agent/agents/rex/prompt_builder.py:
- 删除占位符
__IM_SEND_SECTION__ 的注入(L208)
- 保留
_build_im_send_section() 函数实体或移到 skill(见第 2 步)
Rex 系统提示中只保留一句话作为"指针":
When the user wants to send messages to IM platforms,
load the `im-send` skill first and follow its guidance.
预期:~1.4k token → ~30 token,节省约 98%。
第 2 步:把 SOP 落到 skill 或 tool 里
选项 2a(推荐):im-send skill
新建 flocks/skills/im-send/SKILL.md,内容是当前的 6 步 SOP。Rex 在用户明确请求"发消息到 IM"时调用 skill_load("im-send") 才注入相关内容。
选项 2b(更进一步):im_send_message tool
新增内置工具 im_send_message(message, target=None, schedule=None),内部封装:
- 检查 current session 是否来自 IM
- 列出可用 IM sessions
- 必要时通过
question 工具向用户提问选哪个
- 调用
channel_message 发送
- 返回结果
Rex prompt 里只需要一句话:
When the user wants to send messages to IM platforms,
call `im_send_message` and follow its guidance.
将"业务流程"从 LLM 推理转移到代码执行,准确率反而更高。
第 3 步:Channel 类型由 ChannelRegistry 提供,而非硬编码
让 ChannelRegistry 提供运行时元数据查询接口:
class ChannelRegistry:
def list_im_channels(self) -> list[ChannelMeta]:
"""返回所有支持 IM 语义的 channel 元数据。"""
...
skill 模板(或 tool 实现)通过这个接口动态生成"title 前缀 → channel_type"映射,而不是写死。
这样自定义 channel 只要正确实现 ChannelMeta,就能自动被 Rex 识别和路由。
第 4 步(独立改进):Session 元数据中 channel_type 改为一等字段
当前从 session.title 用 [Wecom] / [Feishu] / [Dingtalk] 前缀判断 channel 类型,是脆弱的字符串解析。
建议在 session 创建时就把 channel_type 和 channel_id 作为一等字段持久化,所有上游(prompt / tool / route)直接读字段,不再解析 title。
第 4 步可以独立推进,与本 issue 主体不强绑定。
优先级建议
| 改进 |
收益 |
风险 |
推荐顺序 |
| 第 1 步:从系统提示中移除 IM Send Section |
高(直接省 token + 解耦) |
低 |
先做 |
| 第 2 步:SOP 落地为 skill 或 tool |
中高 |
低 |
紧随第 1 步 |
| 第 3 步:ChannelRegistry 暴露 IM 元数据 |
中 |
低 |
第 2 步选 2b 时同步做;选 2a 时可延后 |
| 第 4 步:session.channel_type 一等字段 |
中 |
中(可能动到 DB schema) |
独立排期 |
期望
希望项目组能:
- 确认对"系统提示中硬编码 channel 业务流程"这一设计取向的看法
2.如果决定保留 IM Send Section,至少把它做成条件注入——比如检测当前会话来自 IM 时才注入,非 IM 会话不注入
附:与其他 prompt 段落的对比
prompt_builder.py 中其他段落(_build_anti_patterns_section、_build_skills_section、_build_workflows_section)都是根据当前环境动态生成的:
skills_section 只列出已安装的 skill
workflows_section 只列出已注册的 workflow
唯独 _build_im_send_section 是完全静态、与环境无关的硬编码字符串。这种不一致本身就值得修复。
Proposed Solution
希望项目组能:
- 确认对"系统提示中硬编码 channel 业务流程"这一设计取向的看法
- 如果决定保留 IM Send Section,至少把它做成条件注入——比如检测当前会话来自 IM 时才注入,非 IM 会话不注入
Alternatives Considered
No response
Suggested Priority
Medium - Worth improving
Willingness to Contribute
Additional Context
No response
Related Component
Backend (Python/FastAPI)
Category
Enhancement
Problem / Motivation
[Improvement] Rex 系统提示中硬编码的 IM Send Protocol 应当移除/降级为按需载入的 skill
摘要
flocks/agent/agents/rex/prompt_builder.py中的_build_im_send_section()(约 90 行 prompt 文本)被无条件地拼接进 Rex 的系统提示。这段内容存在两个问题:更深层的问题是:这段 prompt 把 channel 的运行时知识(channel id、title 前缀、channel_type 映射)硬编码进了 agent 的系统提示,自定义 channel 完全没有任何接入点。这与Issue 377 反映的是同一种设计倾向:框架开发者把"内置的少数 channel"当作了"channel 的全部"。
现状
涉及代码
flocks/agent/agents/rex/prompt_builder.py:208—— 占位符__IM_SEND_SECTION__的替换点flocks/agent/agents/rex/prompt_builder.py:431——_build_im_send_section()函数定义Prompt 节选(节选展示问题,完整内容见源码)
整段是一个6 步业务 SOP(Identify → Discover → Ask → Map → Send → Report),并且后面还跟着一个针对
schedule_task_create的相似版本。问题逐条分析
1. 实用价值低 —— 不发 IM 的会话也要带
无论:
这段 ~1.4k token 都会被注入。Rex 是项目的核心 orchestrator,几乎所有会话都经过它,这段提示的"命中率"极低——保守估计 < 5%。
2. Token 浪费成本
更隐性的成本:上下文窗口被占用、模型注意力被分散、跟"如何发 IM"无关的任务里模型也要扫一遍这段(虽然不会用,但仍要消耗 attention)。
3. 硬编码 channel 类型 —— 把"发现机制"伪装成"知识"
这种映射本来应该由
ChannelRegistry自己暴露——agent 应当询问框架,而不是依赖框架开发者手写进 prompt。后果:
prompt_builder.py这个与 agent 业务逻辑相关的文件[Foo]前缀对应什么channel_type4. 把业务流程"冻结"在系统提示里
整段是 6 步业务 SOP,不是 prompt 应当承载的内容。它本质上应该是:
im_send_message(message, target=None)内部封装"发现 session → 询问用户 → 发送"im-send,按需skill_load把流程逻辑塞进系统提示的具体危害:
5. Prompt 自身也有几个具体问题
[Wecom]/[Feishu]/[Dingtalk]" —— 用 session 标题字符串前缀 作为 channel 类型判断,这种类型信息应当是 session 元数据的一等字段,不应靠 LLM 解析字符串与 Issue 377 的关联
这两个 issue 反映的是同一种设计倾向:
_register_builtin_channels,自定义 channel 走 plugin loader 出 bugprompt_builder,自定义 channel 没有扩展点根因都是:
ChannelRegistry没有作为"channel 真相之源"被尊重——其他子系统(gateway、prompt builder)都各自维护了一份对 channel 的局部认知。我并不主张推翻
ChannelPlugin抽象本身(meta / start / send / handle_webhook 是合理的),主张的是项目组没把 channel 抽象用到底——内置 channel 跳过 plugin loader、prompt 里硬编码 channel 类型,这些都是"知道有抽象但绕开它"的反例。修复方案
方向:从 Rex 系统提示中移除 IM Send Section,降级为按需载入的 skill + 元数据驱动
第 1 步:从 Rex 系统提示中删除
__IM_SEND_SECTION__flocks/agent/agents/rex/prompt_builder.py:__IM_SEND_SECTION__的注入(L208)_build_im_send_section()函数实体或移到 skill(见第 2 步)Rex 系统提示中只保留一句话作为"指针":
预期:~1.4k token → ~30 token,节省约 98%。
第 2 步:把 SOP 落到 skill 或 tool 里
选项 2a(推荐):im-send skill
新建
flocks/skills/im-send/SKILL.md,内容是当前的 6 步 SOP。Rex 在用户明确请求"发消息到 IM"时调用skill_load("im-send")才注入相关内容。选项 2b(更进一步):im_send_message tool
新增内置工具
im_send_message(message, target=None, schedule=None),内部封装:question工具向用户提问选哪个channel_message发送Rex prompt 里只需要一句话:
将"业务流程"从 LLM 推理转移到代码执行,准确率反而更高。
第 3 步:Channel 类型由 ChannelRegistry 提供,而非硬编码
让
ChannelRegistry提供运行时元数据查询接口:skill 模板(或 tool 实现)通过这个接口动态生成"title 前缀 → channel_type"映射,而不是写死。
这样自定义 channel 只要正确实现
ChannelMeta,就能自动被 Rex 识别和路由。第 4 步(独立改进):Session 元数据中
channel_type改为一等字段当前从
session.title用[Wecom]/[Feishu]/[Dingtalk]前缀判断 channel 类型,是脆弱的字符串解析。建议在 session 创建时就把
channel_type和channel_id作为一等字段持久化,所有上游(prompt / tool / route)直接读字段,不再解析 title。优先级建议
期望
希望项目组能:
2.如果决定保留 IM Send Section,至少把它做成条件注入——比如检测当前会话来自 IM 时才注入,非 IM 会话不注入
附:与其他 prompt 段落的对比
prompt_builder.py中其他段落(_build_anti_patterns_section、_build_skills_section、_build_workflows_section)都是根据当前环境动态生成的:skills_section只列出已安装的 skillworkflows_section只列出已注册的 workflow唯独
_build_im_send_section是完全静态、与环境无关的硬编码字符串。这种不一致本身就值得修复。Proposed Solution
希望项目组能:
Alternatives Considered
No response
Suggested Priority
Medium - Worth improving
Willingness to Contribute
Additional Context
No response