messages.py

# 03 - Messages: 消息类型与对话管理
# 核心要点(只讲难点)
# 初始化模型
from init_model import get_chat_model

chat_model = get_chat_model()

# 角色    字典格式    对象格式    用途
# System    {"role": "system", ...}    SystemMessage(...)    系统提示
# User    {"role": "user", ...}    HumanMessage(...)    用户输入
# Assistant    {"role": "assistant", ...}    AIMessage(...)    AI 回复


# 推荐:直接用字典,简洁!
def demo1():
    message = [
        {"role": "system", "content": "你是一个专业的编程导师,擅长解释复杂概念。"},
        {"role": "user", "content": "什么是列表?"},
    ]

    # ❌ 不推荐(太啰嗦)
    from langchain_core.messages import SystemMessage, HumanMessage

    messages = [SystemMessage(content="你是助手"), HumanMessage(content="你好")]


# 2. 对话历史管理(核心难点)
# 🔴 关键规则
# 每次调用必须传递完整的对话历史!
def demo2():
    # 第一次
    r1 = chat_model.invoke("我叫张三")
    # 第二次(没传历史)
    r2 = chat_model.invoke("我叫什么?")  # AI 不记得!


# ✅ 正确做法
def demo3():
    conversation = []
    conversation.append({"role": "user", "content": "我叫张三"})
    r1 = chat_model.invoke(conversation)
    conversation.append({"role": "assistant", "content": r1.content})
    # 第二次(传递完整历史)
    conversation.append({"role": "user", "content": "我叫什么?"})
    r2 = chat_model.invoke(conversation)  # AI 记得!


# 3. 对话历史优化(避免太长)
# 🔴 问题
# 对话历史会越来越长,消耗大量 tokens 和成本。
# ✅ 解决方案
# 只保留最近 N 轮对话:


def keep_recent_messages(messages, max_pairs=2):
    """
    保留最近的 N 轮对话

    max_pairs: 保留的对话轮数(每轮 = user + assistant)
    """

    if not messages:
        return []
    # 1. 处理 System 消息:通常只保留第一条作为系统指令
    # 如果你的业务逻辑需要保留所有 system 消息,可以改回原来的列表推导式
    system_msg = next((m for m in messages if m.get("role") == "system"), None)

    # 2. 提取纯对话流 (排除 system)
    conversation = [m for m in messages if m.get("role") != "system"]
    if not conversation:
        return [system_msg] if system_msg else []

    # 3. 智能截取最近对话
    # 我们需要保留最多 max_pairs 轮。
    # 一轮定义为:User -> Assistant
    # 特殊情况:最后一条如果是 User,也要保留(即使没有对应的 Assistant)
    recent_conversation = []
    pairs_found = 0
    # 从后往前遍历,每次找到 User 就添加它和对应的 Assistant(如果有)
    i = len(conversation) - 1
    while i >= 0 and pairs_found < max_pairs:
        current_msg = conversation[i]

        # 如果是 Assistant 消息
        if current_msg.get("role") == "assistant":
            # 尝试找前一条是否为 User
            if i > 0 and conversation[i - 1].get("role") == "user":
                # 找到完整的一对
                recent_conversation.insert(0, conversation[i - 1])
                recent_conversation.insert(1, current_msg)
                pairs_found += 1
                i -= 2
            else:
                # 孤立的 Assistant 消息(异常情况),单独保留或跳过?
                # 这里选择保留它以防丢失上下文,但不计入完整轮数
                recent_conversation.insert(0, current_msg)
                i -= 1
        else:
            # 如果是 User 消息
            # 情况 A: 它是最后一条消息(当前正在提问),必须保留
            # 情况 B: 它是某轮的开始,但后面没有 Assistant(数据损坏),保留
            # 只要还没凑够 max_pairs,且这是剩下的最后一条 User,就保留
            if pairs_found < max_pairs:
                recent_conversation.insert(0, current_msg)
            i -= 1
        # 4. 组装结果
    result = []
    if system_msg:
        result.append(system_msg)
    result.extend(recent_conversation)

    return result


conversation_history = [
    {"role": "system", "content": "你是一个助手"},
    {"role": "user", "content": "你好"},
    {"role": "assistant", "content": "你好!有什么事吗?"},
    {"role": "user", "content": "今天天气如何"},
    {"role": "assistant", "content": "天气不错"},
    {"role": "user", "content": "那明天呢?"},  # 当前最新的问题,尚未回答
]
# 使用
optimized = keep_recent_messages(conversation_history, max_pairs=5)
response = chat_model.invoke(optimized)


# 核心总结
# 要点    说明
# 格式    用字典,不用消息对象
# 历史    每次必须传递完整历史
# 保存    必须保存 AI 的回复
# 优化    只保留最近 N 轮
# System    总是保留 system 消息
添加新评论