-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat(context): 压缩上下文时保留尾部发言,以免丢失用户最新指示 #8423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||||||||||
| from typing import TYPE_CHECKING, Protocol, runtime_checkable | ||||||||||||||
|
|
||||||||||||||
| from ..message import Message | ||||||||||||||
| from ..message import Message, TextPart | ||||||||||||||
|
|
||||||||||||||
| if TYPE_CHECKING: | ||||||||||||||
| from astrbot import logger | ||||||||||||||
|
|
@@ -17,6 +17,35 @@ | |||||||||||||
|
|
||||||||||||||
| from ..context.truncator import ContextTruncator | ||||||||||||||
|
|
||||||||||||||
| # Maximum number of characters to preserve from the tail of summarized messages. | ||||||||||||||
| PRESERVE_TAIL_CHARS = 10000 | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def extract_text_from_messages(messages: list[Message]) -> str: | ||||||||||||||
| """Extract text content from a list of messages into a single string. | ||||||||||||||
|
|
||||||||||||||
| Each message is formatted as "[role]: content" for readability. | ||||||||||||||
|
|
||||||||||||||
| Args: | ||||||||||||||
| messages: The messages to extract text from. | ||||||||||||||
|
|
||||||||||||||
| Returns: | ||||||||||||||
| A concatenated string of all text content. | ||||||||||||||
| """ | ||||||||||||||
| parts: list[str] = [] | ||||||||||||||
| for msg in messages: | ||||||||||||||
| if msg.content is None: | ||||||||||||||
| continue | ||||||||||||||
| if isinstance(msg.content, str): | ||||||||||||||
| parts.append(f"[{msg.role}]: {msg.content}") | ||||||||||||||
| elif isinstance(msg.content, list): | ||||||||||||||
| text_segments = [ | ||||||||||||||
| part.text for part in msg.content if isinstance(part, TextPart) | ||||||||||||||
| ] | ||||||||||||||
| if text_segments: | ||||||||||||||
| parts.append(f"[{msg.role}]: {''.join(text_segments)}") | ||||||||||||||
| return "\n".join(parts) | ||||||||||||||
|
Comment on lines
+24
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When compressing messages, assistant messages often contain We should update def extract_text_from_messages(messages: list[Message]) -> str:
"""Extract text content from a list of messages into a single string.
Each message is formatted as "[role]: content" for readability.
Args:
messages: The messages to extract text from.
Returns:
A concatenated string of all text content.
"""
parts: list[str] = []
for msg in messages:
msg_text = ""
if isinstance(msg.content, str):
msg_text = msg.content
elif isinstance(msg.content, list):
text_segments = [
part.text for part in msg.content if isinstance(part, TextPart)
]
if text_segments:
msg_text = "".join(text_segments)
if msg.tool_calls:
tool_calls_desc = []
for tool_call in msg.tool_calls:
if isinstance(tool_call, ToolCall):
name = tool_call.function.name
args = tool_call.function.arguments
elif isinstance(tool_call, dict):
func = tool_call.get("function", {})
name = func.get("name", "")
args = func.get("arguments", "")
else:
continue
tool_calls_desc.append(f"call tool: {name}({args})")
if tool_calls_desc:
extra = "; ".join(tool_calls_desc)
msg_text = f"{msg_text} [{extra}]" if msg_text else f"[{extra}]"
if msg_text:
parts.append(f"[{msg.role}]: {msg_text}")
return "\n".join(parts) |
||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| @runtime_checkable | ||||||||||||||
| class ContextCompressor(Protocol): | ||||||||||||||
|
|
@@ -227,14 +256,29 @@ async def __call__(self, messages: list[Message]) -> list[Message]: | |||||||||||||
| logger.warning("LLM context compression returned an empty summary.") | ||||||||||||||
| return messages | ||||||||||||||
|
|
||||||||||||||
| # Extract the tail of the original conversation text to preserve recent details. | ||||||||||||||
| # This ensures the compressed context retains both a high-level summary | ||||||||||||||
| # and the most recent raw conversation from the summarized portion. | ||||||||||||||
| tail_text = extract_text_from_messages(messages_to_summarize) | ||||||||||||||
| if len(tail_text) > PRESERVE_TAIL_CHARS: | ||||||||||||||
| tail_text = tail_text[-PRESERVE_TAIL_CHARS:] | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the tail text exceeds
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| # build result | ||||||||||||||
| result = [] | ||||||||||||||
| result.extend(system_messages) | ||||||||||||||
|
|
||||||||||||||
| compressed_content = ( | ||||||||||||||
| f"Our previous history conversation summary:\n{summary_content}" | ||||||||||||||
| ) | ||||||||||||||
| if tail_text: | ||||||||||||||
| compressed_content += ( | ||||||||||||||
| f"\n\n---\nRecent conversation details before compression:\n{tail_text}" | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| result.append( | ||||||||||||||
| Message( | ||||||||||||||
| role="user", | ||||||||||||||
| content=f"Our previous history conversation summary: {summary_content}", | ||||||||||||||
| content=compressed_content, | ||||||||||||||
| ) | ||||||||||||||
| ) | ||||||||||||||
| result.append( | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To support extracting tool call details from messages, we should import
ToolCallfrom the message module.