增加了新的会话记忆方式 fixed #3

This commit is contained in:
LzSkyline 2025-04-10 18:07:24 +08:00
parent 69864b26e3
commit 894f6dd0ef
3 changed files with 76 additions and 33 deletions

View File

@ -5,8 +5,11 @@ DIFY_API_KEYS=app-xxxxxxxxxxxxxxxx,app-yyyyyyyyyyyyyyyy,app-zzzzzzzzzzzzzzzz
# Dify API Base URL # Dify API Base URL
DIFY_API_BASE="https://api.dify.example.com/v1" DIFY_API_BASE="https://api.dify.example.com/v1"
# Keep Conversationtrue/false # 会话记忆功能模式
ENABLE_CONVERSATION_MEMORY=true # 0: 不开启会话记忆
# 1: 构造history_message附加到消息中的模式
# 2: 当前的零宽字符模式(默认)
CONVERSATION_MEMORY_MODE=2
# Server Configuration # Server Configuration
SERVER_HOST="127.0.0.1" SERVER_HOST="127.0.0.1"

View File

@ -136,21 +136,25 @@ for chunk in response:
### 会话记忆功能 ### 会话记忆功能
该代理支持自动记忆会话上下文,无需客户端进行额外处理。当启用此功能时 该代理支持自动记忆会话上下文,无需客户端进行额外处理。提供了三种会话记忆模式
- 在每个新会话的第一条回复中会自动嵌入不可见的会话ID 1. **不开启会话记忆**:每次对话都是独立的,无上下文关联
- 后续的消息会自动继承会话上下文,保持对话连贯性 2. **history_message模式**:将历史消息直接附加到当前消息中,支持客户端编辑历史消息
- 使用零宽字符编码,(大部分情况下)不会影响消息的正常显示 3. **零宽字符模式**在每个新会话的第一条回复中会自动嵌入不可见的会话ID后续消息自动继承上下文
可以通过环境变量控制此功能: 可以通过环境变量控制此功能:
```shell ```shell
# 在 .env 文件中设置 # 在 .env 文件中设置会话记忆模式
ENABLE_CONVERSATION_MEMORY=true # 启用会话记忆功能 # 0: 不开启会话记忆
ENABLE_CONVERSATION_MEMORY=false # 禁用会话记忆功能 # 1: 构造history_message附加到消息中的模式
# 2: 当前的零宽字符模式(默认)
CONVERSATION_MEMORY_MODE=2
``` ```
默认情况下此功能是启用的。如果您的应用场景不需要保持会话上下文,可以选择关闭此功能。 默认情况下使用零宽字符模式。对于需要支持客户端编辑历史消息的场景推荐使用history_message模式。
> 注意history_message模式会将所有历史消息追加到当前消息中可能会消耗更多的token。
### 流式输出优化 ### 流式输出优化

80
main.py
View File

@ -24,8 +24,11 @@ load_dotenv()
# 从环境变量读取有效的API密钥逗号分隔 # 从环境变量读取有效的API密钥逗号分隔
VALID_API_KEYS = [key.strip() for key in os.getenv("VALID_API_KEYS", "").split(",") if key] VALID_API_KEYS = [key.strip() for key in os.getenv("VALID_API_KEYS", "").split(",") if key]
# 获取会话ID记忆功能开关配置 # 获取会话记忆功能模式配置
ENABLE_CONVERSATION_MEMORY = os.getenv('ENABLE_CONVERSATION_MEMORY', 'true').lower() == 'true' # 0: 不开启会话记忆
# 1: 构造history_message附加到消息中的模式
# 2: 当前的零宽字符模式(默认)
CONVERSATION_MEMORY_MODE = int(os.getenv('CONVERSATION_MEMORY_MODE', '2'))
class DifyModelManager: class DifyModelManager:
def __init__(self): def __init__(self):
@ -117,23 +120,56 @@ def transform_openai_to_dify(openai_request, endpoint):
# 尝试从历史消息中提取conversation_id # 尝试从历史消息中提取conversation_id
conversation_id = None conversation_id = None
if len(messages) > 1:
# 遍历历史消息找到最近的assistant消息
for msg in reversed(messages[:-1]): # 除了最后一条消息
if msg.get("role") == "assistant":
content = msg.get("content", "")
# 尝试解码conversation_id
conversation_id = decode_conversation_id(content)
if conversation_id:
break
dify_request = { if CONVERSATION_MEMORY_MODE == 2: # 零宽字符模式
"inputs": {}, if len(messages) > 1:
"query": messages[-1]["content"] if messages else "", # 遍历历史消息找到最近的assistant消息
"response_mode": "streaming" if stream else "blocking", for msg in reversed(messages[:-1]): # 除了最后一条消息
"conversation_id": conversation_id, if msg.get("role") == "assistant":
"user": openai_request.get("user", "default_user") content = msg.get("content", "")
} # 尝试解码conversation_id
conversation_id = decode_conversation_id(content)
if conversation_id:
break
dify_request = {
"inputs": {},
"query": messages[-1]["content"] if messages else "",
"response_mode": "streaming" if stream else "blocking",
"conversation_id": conversation_id,
"user": openai_request.get("user", "default_user")
}
elif CONVERSATION_MEMORY_MODE == 1: # history_message模式
# 使用history_messages直接作为上下文
user_query = messages[-1]["content"] if messages else ""
if len(messages) > 1:
# 构造历史消息
history_messages = []
for msg in messages[:-1]: # 除了最后一条消息
role = msg.get("role", "")
content = msg.get("content", "")
if role and content:
history_messages.append(f"{role}: {content}")
# 将历史消息添加到查询中
if history_messages:
history_context = "\n\n".join(history_messages)
user_query = f"<history>\n{history_context}\n</history>\n\n用户当前问题: {user_query}"
dify_request = {
"inputs": {},
"query": user_query,
"response_mode": "streaming" if stream else "blocking",
"user": openai_request.get("user", "default_user")
}
else: # 不开启会话记忆
dify_request = {
"inputs": {},
"query": messages[-1]["content"] if messages else "",
"response_mode": "streaming" if stream else "blocking",
"user": openai_request.get("user", "default_user")
}
return dify_request return dify_request
@ -145,8 +181,8 @@ def transform_dify_to_openai(dify_response, model="claude-3-5-sonnet-v2", stream
if not stream: if not stream:
answer = dify_response.get("answer", "") answer = dify_response.get("answer", "")
# 只在启用会话记忆功能时处理conversation_id # 只在零宽字符会话记忆模式时处理conversation_id
if ENABLE_CONVERSATION_MEMORY: if CONVERSATION_MEMORY_MODE == 2:
conversation_id = dify_response.get("conversation_id", "") conversation_id = dify_response.get("conversation_id", "")
history = dify_response.get("conversation_history", []) history = dify_response.get("conversation_history", [])
@ -495,8 +531,8 @@ def chat_completions():
yield send_char(char, msg_id) yield send_char(char, msg_id)
time.sleep(0.001) # 固定使用最小延迟快速输出剩余内容 time.sleep(0.001) # 固定使用最小延迟快速输出剩余内容
# 只在启用会话记忆功能时处理conversation_id # 只在零宽字符会话记忆模式时处理conversation_id
if ENABLE_CONVERSATION_MEMORY: if CONVERSATION_MEMORY_MODE == 2:
conversation_id = dify_chunk.get("conversation_id") conversation_id = dify_chunk.get("conversation_id")
history = dify_chunk.get("conversation_history", []) history = dify_chunk.get("conversation_history", [])