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 请求完成时