inline 标签 renderer 注册表落地:@all 迁移 + 新增 @user/@face(ADR-0003 遗留缺口) #28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent
#2
Background
ADR-0003 的更新节描述了一套"括号协议收缩为仅管 inline 元素(如
@,在send_message的 text 参数内由 core 解析)"的机制——core 拥有 inline 标签的解析,host 注册具体标签的 renderer/风味。实现 #23(富媒体发送能力)时发现:这套机制从未被实际实现过——代码库里没有任何 inline 标签解析器、host 风味注册接口,或标签 renderer 抽象。ADR-0003 描述的是设计意图,不是已落地的代码。#23 只需要一个具体标签(
@all),已经用最小实现方式解决(send_message的content里直接检测字面字符串"@all",capabilities.at_all门控是否转成真实 @全体效果,不建通用框架)。本次会内 grill(2026-07-09)排查发现:#22(poke/reaction/profile_like)、#23 的 sticker/voice/quote 全部选择了独立工具调用或
send_message结构化参数,无一使用 inline 标签——"host 注册标签 renderer"此前只是未落地的意图。用户明确提出还需要@user:<id>(指定用户)与@face:<id>(平台特殊表情/内联表情)两个标签,遂借此把机制补齐,@all一并迁移。完整决策记录见 ADR-0003 更新节。范围锁定为 output 侧(
send_message的content文本 inline 标签解析)。ingress 侧ConversationInput.render()的<message>/<poke>/<reaction>XML 序列化不受影响,未来若要统一格式另开新 issue,本 issue 不覆盖。What to build
语法:
@前缀标签族。@all裸写(无参数);带参数标签用冒号,如@user:<sender_id>、@face:<face_id>。ArisePorts新增tag_renderers: dict[str, TagRenderer]字段,host 在configure()时随其它 port 一并注册。TagRenderer是一个小结构:同步render(param: str | None) -> str | None(@all场景param=None)+ 一句供拼进send_messageschema 的用法说明description: str。render(param)返回替换文本,原地拼回content里标签原来的位置;返回None表示这次渲染失败/参数不合法,core 把该标签整体从 content 剔除(不留占位文字,区别于 sticker/voice 的整条内容降级)。未在tag_renderers里注册的标签名同样整体剔除(容错处理模型幻觉出未声明的标签,不抛异常)。@all迁移到新机制:EgressPort.send()签名去掉at_all: bool参数——这是一次破坏性协议改动,但目前没有任何 host 实现消费这个 port(dxkuma-bot-ob11尚未接入nonebot_plugin_arise),无需兼容层。runtime_loop._resolve_at_all的硬编码字面量检测逻辑删除,改为统一的标签解析走tag_renderers注册表。不新增
CapabilitySet布尔位:CapabilitySet.at_all移除(迁移后无引用)。标签是否支持完全由tag_renderers是否注册决定,单一信源,避免布尔位与注册表状态不同步。send_message工具 schema 动态生成:SEND_MESSAGE_SCHEMA的content字段描述里关于 inline 标签的部分(现在硬编码"文本内容里可以包含「@all」标记整个群")改为按当前tag_renderers动态拼接每个已注册标签的description;tag_renderers为空时不包含任何 inline 标签用法提示,未注册的标签不会出现在模型可见的描述里(沿用 #22 定的"能力为假 = 模型看不到"哲学)。Acceptance criteria
TagRenderer(或等价结构)定义:同步render(param: str | None) -> str | None+description: strArisePorts新增tag_renderers: dict[str, TagRenderer]字段,configure()/get_ports()正常透传,默认可以是空字典content中@tagname/@tagname:param被正确识别并替换为tag_renderers[tagname].render(param)的返回值,原地替换(保留标签前后文本,不改变其它内容)content中出现多个标签(含重复同名标签)全部被正确处理render()返回None时,对应标签从content中整体剔除,不留任何占位字符content中出现未在tag_renderers注册的标签名时,同样整体剔除,不抛异常、不中断本次send_message处理EgressPort.send()签名移除at_all参数;CapabilitySet.at_all字段移除;代码库中无任何残留引用runtime_loop.py中_resolve_at_all被通用的标签解析逻辑取代(旧的@all字面量检测代码删除)build_tool_schemas(或SEND_MESSAGE_SCHEMA构造点)按当前tag_renderers动态生成content字段里 inline 标签的用法说明;未注册任何标签时不包含相关提示文字FakeEgress/测试 fixture 更新为不再需要at_all参数;新增测试覆盖@all(作为普通注册标签之一)、@user:<id>、@face:<id>三种标签在"已注册"与"未注册"两种场景下的行为test_runtime_loop.py(及其它引用at_all/_resolve_at_all的测试)全部迁移到新机制后通过,无回归ruff/basedpyright通过Out of scope(本次会内 grill 明确排除)
ConversationInput.render()的 XML→自然语言格式统一(另开新 issue)EgressPort具名方法形式的替代设计(仿poke_send逐个加方法,已否决,见 ADR-0003 更新节)at_all作为永久特例、与新注册表并行两套机制(已否决,选择统一迁移)Blocked by
None——不阻塞、不被阻塞。
#2 遗留