From 6eb8a51c70597cc6c74b89548ed063b8da07c4f0 Mon Sep 17 00:00:00 2001
From: Soulter <905617992@qq.com>
Date: Sun, 3 May 2026 20:14:15 +0800
Subject: [PATCH] docs: system prompt guide
---
.../dev/star/guides/listen-message-event.md | 29 ++++++++++++++++-
.../dev/star/guides/listen-message-event.md | 32 +++++++++++++++++--
docs/zh/dev/star/plugin.md | 29 ++++++++++++++++-
3 files changed, 86 insertions(+), 4 deletions(-)
diff --git a/docs/en/dev/star/guides/listen-message-event.md b/docs/en/dev/star/guides/listen-message-event.md
index a789bb10f..b63609818 100644
--- a/docs/en/dev/star/guides/listen-message-event.md
+++ b/docs/en/dev/star/guides/listen-message-event.md
@@ -266,10 +266,37 @@ from astrbot.api.provider import ProviderRequest
@filter.on_llm_request()
async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # Note there are three parameters
print(req) # Print the request text
- req.system_prompt += "Custom system_prompt"
+ req.system_prompt += "Custom system_prompt" # If there is another suitable approach, avoid using this to append prompts that change every round. It can break prompt caching and greatly increase cost (7 - 20x).
```
+> [!WARNING]
+> **About appending prompts**
+>
+> `req.system_prompt += ...` is suitable for stable, long-lived role settings or global rules. Do not append content that changes every round to `system_prompt`, such as the current time, affinity score, status panel, short-term memory snippets, or retrieval summaries. Doing so makes the system prompt different for each request, which can break provider-side prompt caching and significantly increase both cost and time to first token.
+>
+> For small or medium-sized dynamic prompts that change every round, prefer appending them through `req.extra_user_content_parts`. These parts are added after the current user input as extra user-message content, which is more suitable for dynamic context such as "current time", "character affinity", or "relevant memory snippets":
+>
+> ```python
+> from astrbot.core.agent.message import TextPart
+>
+> @filter.on_llm_request()
+> async def add_dynamic_prompt(self, event: AstrMessageEvent, req: ProviderRequest):
+> req.extra_user_content_parts.append(
+> TextPart(
+> text=(
+> "\n"
+> "Current time: 2026-05-03 20:00\n"
+> "Affinity: 72\n"
+> "Relevant memory: The user prefers concise and direct answers.\n"
+> ""
+> )
+> )
+> )
+> ```
+>
+> For long-term memory, knowledge bases, or external system queries that may be large or unnecessary for every round, do not put everything directly into the prompt. Prefer registering them as `llm_tool` functions so the model can call them when needed, or retrieve only a small relevant summary in your plugin and append that summary through `extra_user_content_parts`.
+
> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly.
#### On LLM Response Complete
diff --git a/docs/zh/dev/star/guides/listen-message-event.md b/docs/zh/dev/star/guides/listen-message-event.md
index 35030869b..9cf96f436 100644
--- a/docs/zh/dev/star/guides/listen-message-event.md
+++ b/docs/zh/dev/star/guides/listen-message-event.md
@@ -269,6 +269,8 @@ async def on_waiting_llm(self, event: AstrMessageEvent):
#### LLM 请求时
+> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。
+
在 AstrBot 默认的执行流程中,在调用 LLM 前,会触发 `on_llm_request` 钩子。
可以获取到 `ProviderRequest` 对象,可以对其进行修改。
@@ -282,11 +284,37 @@ from astrbot.api.provider import ProviderRequest
@filter.on_llm_request()
async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # 请注意有三个参数
print(req) # 打印请求的文本
- req.system_prompt += "自定义 system_prompt"
+ req.system_prompt += "自定义 system_prompt" # 如果有其他替代方法,不建议使用此种方式来追加每轮对话都会改变的提示词,否则会破坏缓存,大大增加价格(约增加 7-20 倍的价格)。
+ req.extra_user_content_parts.append(...)
```
-> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。
+> [!WARNING]
+> **关于提示词的追加**
+>
+> `req.system_prompt += ...` 适合追加稳定、长期有效的角色设定或全局规则。不建议把每轮都会变化的内容追加到 `system_prompt`,例如当前时间、好感度、状态栏、短期记忆片段、检索摘要等。这类写法会让系统提示词在每轮请求中变化,容易破坏模型服务端的提示词缓存,显著增加请求成本和首 token 延迟。
+>
+> 对于每轮都会变化、内容量中小的提示词,优先通过 `req.extra_user_content_parts` 追加。它会作为额外的用户消息内容块放在本轮用户输入之后,更适合承载"当前时间""角色好感度""本轮相关记忆片段"等动态上下文:
+>
+> ```python
+> from astrbot.core.agent.message import TextPart
+>
+> @filter.on_llm_request()
+> async def add_dynamic_prompt(self, event: AstrMessageEvent, req: ProviderRequest):
+> req.extra_user_content_parts.append(
+> TextPart(
+> text=(
+> "\n"
+> "当前时间:2026-05-03 20:00\n"
+> "好感度:72\n"
+> "相关记忆:用户喜欢简洁直接的回答。\n"
+> ""
+> )
+> )
+> )
+> ```
+>
+> 对于长期记忆、知识库、外部系统查询等内容量较大或不一定每轮都需要的信息,不建议全部塞进提示词。可以优先注册为 `llm_tool`,让模型在需要时调用;也可以先在插件中检索出本轮真正相关的少量摘要,再放入 `extra_user_content_parts`。
#### LLM 请求完成时
diff --git a/docs/zh/dev/star/plugin.md b/docs/zh/dev/star/plugin.md
index 84ac03ff4..9e576f1c1 100644
--- a/docs/zh/dev/star/plugin.md
+++ b/docs/zh/dev/star/plugin.md
@@ -519,10 +519,37 @@ from astrbot.api.provider import ProviderRequest
@filter.on_llm_request()
async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # 请注意有三个参数
print(req) # 打印请求的文本
- req.system_prompt += "自定义 system_prompt"
+ req.system_prompt += "自定义 system_prompt" # 如果有其他替代方法,不建议使用此种方式来追加每轮对话都会改变的提示词,否则会破坏缓存,大大增加价格(约增加 7-20 倍的价格)。
```
+> [!WARNING]
+> **关于提示词的追加**
+>
+> `req.system_prompt += ...` 适合追加稳定、长期有效的角色设定或全局规则。不建议把每轮都会变化的内容追加到 `system_prompt`,例如当前时间、好感度、状态栏、短期记忆片段、检索摘要等。这类写法会让系统提示词在每轮请求中变化,容易破坏模型服务端的提示词缓存,显著增加请求成本和首 token 延迟。
+>
+> 对于每轮都会变化、内容量中小的提示词,优先通过 `req.extra_user_content_parts` 追加。它会作为额外的用户消息内容块放在本轮用户输入之后,更适合承载"当前时间""角色好感度""本轮相关记忆片段"等动态上下文:
+>
+> ```python
+> from astrbot.core.agent.message import TextPart
+>
+> @filter.on_llm_request()
+> async def add_dynamic_prompt(self, event: AstrMessageEvent, req: ProviderRequest):
+> req.extra_user_content_parts.append(
+> TextPart(
+> text=(
+> "\n"
+> "当前时间:2026-05-03 20:00\n"
+> "好感度:72\n"
+> "相关记忆:用户喜欢简洁直接的回答。\n"
+> ""
+> )
+> )
+> )
+> ```
+>
+> 对于长期记忆、知识库、外部系统查询等内容量较大或不一定每轮都需要的信息,不建议全部塞进提示词。可以优先注册为 `llm_tool`,让模型在需要时调用;也可以先在插件中检索出本轮真正相关的少量摘要,再放入 `extra_user_content_parts`。
+
> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。
##### LLM 请求完成时