Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions astrbot/core/astr_main_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
"Saturday",
"Sunday",
)
METADATA_VALUE_MAX_LENGTH = 128
METADATA_CONTROL_CHARS = dict.fromkeys(range(32), " ")
METADATA_CONTROL_CHARS[127] = " "
METADATA_ZERO_WIDTH_CHARS = "\u200b\u200c\u200d\ufeff"
WEB_SEARCH_CITATION_TOOL_NAMES = frozenset(
{
"web_search_baidu",
Expand All @@ -140,6 +144,47 @@
)


def _sanitize_metadata_value(value: object) -> str:
text = "" if value is None else str(value)
text = text.translate(METADATA_CONTROL_CHARS)
for char in METADATA_ZERO_WIDTH_CHARS:
text = text.replace(char, "")
text = text.replace("<", "<").replace(">", ">")
text = " ".join(text.split())
return text[:METADATA_VALUE_MAX_LENGTH]


def _sanitize_optional_metadata_value(value: object) -> str | None:
sanitized = _sanitize_metadata_value(value)
return sanitized or None
Comment on lines +157 to +159

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>>> _sanitize_optional_metadata_value("\t").__repr__()       
'None'
>>> _sanitize_optional_metadata_value(" ").__repr__() 
'None'
>>> _sanitize_optional_metadata_value("None").__repr__()
"'None'"

输入空白字符组成的字符串会输出None.
也许有风险?我不确定会不会有什么平台允许空白字符当昵称。

或许把两个方法反一下,_sanitize_metadata_value过滤None为字符串会好些?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

输入空白字符组成的字符串会输出None.
也许有风险?我不确定会不会有什么平台允许空白字符当昵称。

我觉得不应该改。这是符合我们需求的
真实昵称是可选补充信息,如果清洗后只剩空白,就应该视为“不可用”,然后回退到群昵称。我们甚至已经有测试覆盖这个行为:test_append_system_reminders_real_only_falls_back_after_sanitizing。如果这里不返回 None,反而可能导致“仅真实昵称”模式把正常群昵称替换成空昵称,这才是风险

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的。
我注意到你的代码路径中,原始用户群昵称也经过了sanitized_user_nickname.
如果原始昵称为空白字符的话,最终到达user_metadata.nickname的值会是一个空字符串,没有任何字符。这里会有问题吗?

@Sisyphbaous-DT-Project Sisyphbaous-DT-Project Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个问题确实存在,如果有平台可以将原始昵称和群昵称都改成空白,当前的代码状态的确也是传一个空白的昵称进去,不过这是原本就存在的一个小问题,而且当前的逻辑已经比较完备了。假设有平台能做到这样,那么原版的代码也是传一个空白昵称进去,我已经做了挺多加固了,比如说这个平台的某个用户,群昵称是正常的但是真实昵称改成了空白,当前的代码逻辑在不开启“仅使用真实昵称”的情况下也是把群昵称和真实昵称一起传进去,如果使用者开了只传真实昵称的这个功能,代码检测到某一个用户的昵称清洗完是空白,也会fallback到群昵称传进去,针对群昵称和原始昵称都是空白的状态,我目前还没有想到十分完美的解决方案,以后可能可以开个新的PR来单独讨论和优化一下



def _format_metadata(prefix: str, metadata: dict[str, object]) -> str:
sanitized = {
key: _sanitize_metadata_value(value)
for key, value in metadata.items()
if value is not None
}
metadata_json = json.dumps(sanitized, ensure_ascii=False, separators=(",", ":"))
return f"{prefix}: {metadata_json}"


def _get_real_sender_nickname(event: AstrMessageEvent) -> str | None:
raw_message = getattr(event.message_obj, "raw_message", None)
sender = getattr(raw_message, "sender", None)
if sender is None and isinstance(raw_message, dict):
sender = raw_message.get("sender")
if isinstance(sender, dict):
nickname = sender.get("nickname")
if nickname:
return str(nickname)
else:
nickname = getattr(sender, "nickname", None)
if nickname:
return str(nickname)
return None


@dataclass(slots=True)
class MainAgentBuildConfig:
"""The main agent build configuration.
Expand Down Expand Up @@ -868,8 +913,25 @@ def _append_system_reminders(
system_parts: list[str] = []
if cfg.get("identifier"):
user_id = event.message_obj.sender.user_id
user_nickname = event.message_obj.sender.nickname
system_parts.append(f"User ID: {user_id}, Nickname: {user_nickname}")
display_nickname = event.message_obj.sender.nickname
real_nickname_display_enabled = bool(cfg.get("real_nickname_display"))
real_nickname_only_enabled = bool(cfg.get("real_nickname_only"))
resolved_nickname = _sanitize_metadata_value(display_nickname)
append_real_nickname = None
if real_nickname_display_enabled:
real_nickname = _sanitize_optional_metadata_value(
_get_real_sender_nickname(event)
)
if real_nickname is not None:
if real_nickname_only_enabled:
resolved_nickname = real_nickname
elif real_nickname != resolved_nickname:
append_real_nickname = real_nickname

user_metadata = {"user_id": user_id, "nickname": resolved_nickname}
if append_real_nickname is not None:
user_metadata["real_nickname"] = append_real_nickname
system_parts.append(_format_metadata("User metadata", user_metadata))

if cfg.get("group_name_display") and event.message_obj.group_id:
if not event.message_obj.group:
Expand All @@ -880,7 +942,9 @@ def _append_system_reminders(
else:
group_name = event.message_obj.group.group_name
if group_name:
system_parts.append(f"Group name: {group_name}")
system_parts.append(
_format_metadata("Group metadata", {"name": group_name})
)

if cfg.get("datetime_system_prompt"):
now = None
Expand Down
25 changes: 25 additions & 0 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
"web_search_link": False,
"display_reasoning_text": False,
"identifier": False,
"real_nickname_display": False,
"real_nickname_only": False,
"group_name_display": False,
"datetime_system_prompt": True,
"default_personality": "default",
Expand Down Expand Up @@ -2804,6 +2806,12 @@
"identifier": {
"type": "bool",
},
"real_nickname_display": {
"type": "bool",
},
"real_nickname_only": {
"type": "bool",
},
"group_name_display": {
"type": "bool",
},
Expand Down Expand Up @@ -3659,6 +3667,23 @@
"type": "bool",
"hint": "启用后,会在提示词前包含用户 ID 信息。",
},
"provider_settings.real_nickname_display": {
"description": "追加用户真实昵称",
"type": "bool",
"hint": "启用后,会在支持的平台上向模型额外提供用户真实昵称。",
"condition": {
"provider_settings.identifier": True,
},
},
"provider_settings.real_nickname_only": {
"description": "仅使用真实昵称",
"type": "bool",
"hint": "启用后,模型将只看到用户真实昵称,不再看到群昵称。取不到真实昵称时会回退到原昵称。",
"condition": {
"provider_settings.identifier": True,
"provider_settings.real_nickname_display": True,
},
},
"provider_settings.group_name_display": {
"description": "显示群名称",
"type": "bool",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,14 @@
"description": "User Identification",
"hint": "When enabled, user ID information will be included in the prompt."
},
"real_nickname_display": {
"description": "Append User Real Nickname",
"hint": "When enabled, the user's real nickname will be additionally provided to the model on supported platforms."
},
"real_nickname_only": {
"description": "Use Real Nickname Only",
"hint": "When enabled, the model will only see the user's real nickname instead of the group nickname. Falls back to the original nickname when unavailable."
},
"group_name_display": {
"description": "Display Group Name",
"hint": "When enabled, group name information will be included in the prompt on supported platforms (OneBot v11)."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,14 @@
"description": "Идентификация пользователя",
"hint": "Если включено, информация об ID пользователя будет включена в промпт."
},
"real_nickname_display": {
"description": "Добавлять реальный ник пользователя",
"hint": "Если включено, реальный ник пользователя будет дополнительно передан модели на поддерживаемых платформах."
},
"real_nickname_only": {
"description": "Использовать только реальный ник",
"hint": "Если включено, модель будет видеть только реальный ник пользователя вместо группового ника. Если реальный ник недоступен, используется исходный ник."
},
"group_name_display": {
"description": "Отображать название группы",
"hint": "Если включено, название группы будет включено в промпт на поддерживаемых платформах (OneBot v11)."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@
"description": "用户识别",
"hint": "启用后,会在提示词前包含用户 ID 信息。"
},
"real_nickname_display": {
"description": "追加用户真实昵称",
"hint": "启用后,会在支持的平台上向模型额外提供用户真实昵称。"
},
"real_nickname_only": {
"description": "仅使用真实昵称",
"hint": "启用后,模型将只看到用户真实昵称,不再看到群昵称。取不到真实昵称时会回退到原昵称。"
},
"group_name_display": {
"description": "显示群名称",
"hint": "启用后,在支持的平台(OneBot v11)上会在提示词前包含群名称信息。"
Expand Down
Loading
Loading