L3-1、掌控多轮对话的节奏 -Prompt 结构与上下文管理全攻略

对话管理示意图

一、多轮对话中的上下文挑战与常见问题

在与大语言模型(LLM)进行多轮对话时,我们常常面临以下挑战:

  • 上下文丢失:模型"遗忘"之前提到的信息
  • 指代不明:难以理解代词指向的内容
  • 话题漂移:对话逐渐偏离最初目标
  • 信息过载:上下文窗口限制导致早期信息被截断
  • 一致性问题:模型回答前后矛盾

这些问题会导致用户体验下降,特别是在开发对话型应用时尤为明显。

二、结构化设计:初始化、提问、跟进、总结

高效的多轮对话需要清晰的结构设计,可以参考以下四段式结构:

1. 初始化阶段

设定对话基调与规则:

initial_prompt = """
你是一位金融顾问专家。请遵循以下规则:
- 提供专业、准确的金融建议
- 使用简洁明了的语言解释复杂概念
- 需要更多信息时,主动询问
- 避免讨论非金融相关话题
"""

2. 提问阶段

清晰明确地表达需求:

question = """
我有30万元的闲置资金,希望投资获得稳定收益,
风险承受能力中等,计划3-5年后用于子女教育。
请推荐合适的投资组合方案。
"""

3. 跟进阶段

针对回答深入探讨,澄清疑惑:

follow_up = """
关于你提到的ETF投资,我想了解:
1. 有哪些具体的低波动ETF推荐?
2. 这些ETF的历史收益率大约是多少?
3. 投资ETF与购买指数型基金相比有什么优势?
"""

4. 总结阶段

对对话内容进行归纳与确认:

summary_prompt = """
请总结我们讨论的投资方案要点:
1. 适合我的资产配置比例
2. 推荐的具体投资产品
3. 需要注意的风险因素
4. 建议的定期检视与调整频率
"""

三、上下文引用技巧:变量记忆、前文重提、内容引用

变量记忆

在应用程序中使用变量存储关键信息:

import streamlit as st

# 初始化会话状态
if 'user_info' not in st.session_state:
    st.session_state.user_info = {
        'risk_tolerance': None,
        'investment_amount': None,
        'time_horizon': None,
        'financial_goals': []
    }

# 收集用户信息
st.title("智能投资顾问")

investment_amount = st.number_input("投资金额(元)", min_value=0, step=10000)
if investment_amount > 0:
    st.session_state.user_info['investment_amount'] = investment_amount

risk_options = ["保守", "中等", "激进"]
risk_tolerance = st.select_slider("风险承受能力", options=risk_options)
st.session_state.user_info['risk_tolerance'] = risk_tolerance

time_horizon = st.slider("投资时间范围(年)", 1, 30, 5)
st.session_state.user_info['time_horizon'] = time_horizon

goals = st.multiselect("投资目标", 
                       ["退休规划", "子女教育", "购房", "旅行", "财务自由"])
st.session_state.user_info['financial_goals'] = goals

# 构建上下文提示
if st.button("获取投资建议"):
    context_prompt = f"""
    基于以下用户信息提供投资建议:
    - 投资金额: {st.session_state.user_info['investment_amount']}元
    - 风险承受能力: {st.session_state.user_info['risk_tolerance']}
    - 投资期限: {st.session_state.user_info['time_horizon']}年
    - 投资目标: {', '.join(st.session_state.user_info['financial_goals'])}
    """
    st.write("正在生成投资建议...")
    # 这里调用AI模型API,将context_prompt发送给模型

前文重提

在新问题中引用之前的讨论内容:

if 'conversation_history' not in st.session_state:
    st.session_state.conversation_history = []

user_input = st.text_input("您的问题:")
if user_input and st.button("发送"):
    st.session_state.conversation_history.append({"role": "user", "content": user_input})
    
    # 构建包含历史记录的提示
    full_prompt = "以下是我们之前的对话内容:\n\n"
    for message in st.session_state.conversation_history:
        prefix = "用户: " if message["role"] == "user" else "助手: "
        full_prompt += prefix + message["content"] + "\n\n"
    
    full_prompt += "请基于上述对话记录回答我的问题。"
    
    # 调用AI模型API,传入full_prompt
    # response = call_ai_api(full_prompt)
    
    # 显示响应并添加到历史记录
    # st.write("AI回复:", response)
    # st.session_state.conversation_history.append({"role": "assistant", "content": response})

内容引用

允许用户和AI明确引用之前提到的特定内容:

st.subheader("对话历史")
for i, message in enumerate(st.session_state.conversation_history):
    role = "用户" if message["role"] == "user" else "AI助手"
    with st.expander(f"{role} ({i+1})"):
        st.write(message["content"])
        if message["role"] == "assistant":
            if st.button(f"引用这条回复 #{i+1}", key=f"ref_{i}"):
                ref_text = f"关于你在回复#{i+1}中提到的'{message['content'][:30]}...',"
                st.session_state.reference_text = ref_text
                st.experimental_rerun()

# 在用户输入框预填引用文本
reference_placeholder = ""
if 'reference_text' in st.session_state:
    reference_placeholder = st.session_state.reference_text
    
user_input = st.text_input("您的问题:", value=reference_placeholder)

四、信息遗忘与"记住"的策略实现(含示例)

记忆策略

为了解决LLM的"遗忘"问题,我们可以采用以下策略:

1. 总结记忆 + 细节按需检索

import streamlit as st
from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI

st.title("具备记忆能力的AI助手")

# 初始化记忆组件
if 'memory' not in st.session_state:
    st.session_state.memory = ConversationSummaryMemory(
        llm=OpenAI(temperature=0),
        max_token_limit=100
    )

# 显示当前对话摘要
if st.checkbox("显示对话摘要"):
    summary = st.session_state.memory.buffer
    st.info(f"当前对话摘要:\n{summary}")

# 用户输入
user_input = st.text_input("您的问题:")
if user_input and st.button("发送"):
    # 从记忆中获取摘要
    memory_summary = st.session_state.memory.buffer
    
    # 构建提示
    prompt = f"""
    以下是我们对话的摘要:
    {memory_summary}
    
    用户的新问题是:{user_input}
    
    请基于以上上下文回答问题。
    """
    
    # 这里调用AI API获取回答
    # response = call_ai_api(prompt)
    response = "这是AI的示例回答"
    
    # 更新记忆
    st.session_state.memory.save_context(
        {"input": user_input},
        {"output": response}
    )
    
    # 显示回答
    st.write("AI回复:", response)

2. 关键信息提取与状态追踪

import streamlit as st
import json

st.title("信息提取与状态追踪示例")

# 初始化状态
if 'entity_memory' not in st.session_state:
    st.session_state.entity_memory = {
        "user_preferences": {},
        "discussed_topics": [],
        "action_items": []
    }

# 分栏布局
col1, col2 = st.columns([2, 1])

with col1:
    st.subheader("对话界面")
    user_input = st.text_input("您的问题或指令:")
    
    if user_input and st.button("发送"):
        # 模拟发送给AI并获取回复
        ai_response = "这是AI的模拟回复"
        
        # 模拟信息提取过程
        extraction_prompt = f"""
        从以下对话中提取关键信息:
        用户: {user_input}
        AI: {ai_response}
        
        提取以下类别的信息(JSON格式):
        1. 用户偏好
        2. 讨论的主题
        3. 需要跟进的事项
        """
        
        # 模拟AI提取的信息(实际应用中应调用AI API)
        extracted_info = {
            "user_preferences": {"喜欢的编程语言": "Python"},
            "discussed_topics": ["AI记忆机制"],
            "action_items": ["研究LangChain的记忆组件"]
        }
        
        # 更新存储的信息
        for category, items in extracted_info.items():
            if isinstance(items, dict):
                st.session_state.entity_memory[category].update(items)
            elif isinstance(items, list):
                st.session_state.entity_memory[category].extend(items)
        
        # 显示对话
        st.write("用户:", user_input)
        st.write("AI:", ai_response)

with col2:
    st.subheader("已提取的信息")
    
    st.write("用户偏好:")
    st.json(st.session_state.entity_memory["user_preferences"])
    
    st.write("已讨论主题:")
    st.write(", ".join(set(st.session_state.entity_memory["discussed_topics"])))
    
    st.write("待办事项:")
    for item in set(st.session_state.entity_memory["action_items"]):
        st.checkbox(item, key=f"todo_{item}")

3. 记忆/对话重置功能

st.sidebar.title("对话控制")

if st.sidebar.button("清除记忆"):
    if 'entity_memory' in st.session_state:
        st.session_state.entity_memory = {
            "user_preferences": {},
            "discussed_topics": [],
            "action_items": []
        }
    if 'conversation_history' in st.session_state:
        st.session_state.conversation_history = []
    if 'memory' in st.session_state:
        st.session_state.memory.clear()
    st.sidebar.success("已重置所有记忆和对话历史!")

memory_type = st.sidebar.radio(
    "记忆类型",
    ["完整历史", "摘要记忆", "关键信息提取"]
)
st.sidebar.info(f"当前使用: {memory_type}")

五、实战演示:对话型 AI 模板与提示优化建议

对话AI模板

全功能对话助手示例

import streamlit as st
import uuid
import time

# 设置页面
st.set_page_config(page_title="高级对话助手", layout="wide")

# 初始化会话状态
if 'conversation_id' not in st.session_state:
    st.session_state.conversation_id = str(uuid.uuid4())
if 'messages' not in st.session_state:
    st.session_state.messages = []
if 'memory_mode' not in st.session_state:
    st.session_state.memory_mode = "完整记忆"
if 'context_items' not in st.session_state:
    st.session_state.context_items = {}

# 侧边栏控制
st.sidebar.title("对话设置")
assistant_role = st.sidebar.selectbox(
    "选择助手角色",
    ["通用助手", "技术导师", "写作教练", "金融顾问"]
)

st.sidebar.divider()
memory_mode = st.sidebar.radio(
    "记忆模式",
    ["完整记忆", "摘要记忆", "有限回合(最近N轮)"]
)
st.session_state.memory_mode = memory_mode

if memory_mode == "有限回合(最近N轮)":
    memory_turns = st.sidebar.slider("保留最近几轮对话", 1, 10, 3)

st.sidebar.divider()
st.sidebar.subheader("对话上下文")
new_context_key = st.sidebar.text_input("添加上下文项(键)")
new_context_value = st.sidebar.text_area("值")
if new_context_key and new_context_value and st.sidebar.button("添加到上下文"):
    st.session_state.context_items[new_context_key] = new_context_value
    st.sidebar.success(f"已添加 '{new_context_key}' 到上下文")

# 显示和管理上下文项
if st.session_state.context_items:
    st.sidebar.divider()
    st.sidebar.subheader("当前上下文项")
    for key in list(st.session_state.context_items.keys()):
        if st.sidebar.checkbox(key, True, key=f"ctx_{key}"):
            pass  # 保持选中
        else:
            del st.session_state.context_items[key]  # 取消选中则移除

# 重置对话
if st.sidebar.button("开始新对话"):
    st.session_state.messages = []
    st.session_state.conversation_id = str(uuid.uuid4())
    st.sidebar.success("已创建新对话!")

# 主界面
st.title("高级对话助手")
st.caption(f"对话ID: {st.session_state.conversation_id} | 角色: {assistant_role} | 记忆模式: {memory_mode}")

# 显示对话历史
for i, message in enumerate(st.session_state.messages):
    role = "🧑‍💻 用户" if message["role"] == "user" else "🤖 助手"
    with st.chat_message(message["role"]):
        st.write(message["content"])
        
        # 为每条消息添加引用按钮
        if st.button(f"引用", key=f"ref_msg_{i}"):
            quote_text = f'引用: "{message["content"][:50]}..."'
            st.session_state.quote_text = quote_text

# 引用文本预填充
input_placeholder = ""
if 'quote_text' in st.session_state:
    input_placeholder = st.session_state.quote_text
    # 使用后清除,避免重复
    del st.session_state.quote_text

# 用户输入
user_input = st.chat_input("输入您的问题...", value=input_placeholder)

if user_input:
    # 添加用户消息
    st.session_state.messages.append({"role": "user", "content": user_input})
    
    # 在UI中显示用户消息
    with st.chat_message("user"):
        st.write(user_input)
    
    # 构建发送给AI的上下文
    prompt_context = ""
    
    # 添加角色设定
    role_descriptions = {
        "通用助手": "你是一位乐于助人的AI助手,能回答各种问题。",
        "技术导师": "你是一位编程专家,擅长解释技术概念并帮助解决代码问题。",
        "写作教练": "你是一位写作教练,帮助用户提升写作技巧,提供有建设性的反馈。",
        "金融顾问": "你是一位金融顾问,提供个人理财和投资建议。"
    }
    
    prompt_context += f"## 角色\n{role_descriptions[assistant_role]}\n\n"
    
    # 添加记忆上下文
    prompt_context += "## 对话历史\n"
    if memory_mode == "完整记忆":
        # 包含所有历史消息
        history_messages = st.session_state.messages[:-1]  # 除去最新用户消息
    elif memory_mode == "摘要记忆":
        # 这里应该调用AI生成摘要,为简化示例,只保留第一条和最近几条
        history_messages = [st.session_state.messages[0]] if len(st.session_state.messages) > 1 else []
        history_messages += st.session_state.messages[-3:-1] if len(st.session_state.messages) > 2 else []
    else:  # 有限回合
        # 保留最近N轮
        start_idx = max(0, len(st.session_state.messages) - 1 - memory_turns*2)
        history_messages = st.session_state.messages[start_idx:-1]
    
    for msg in history_messages:
        role_label = "用户" if msg["role"] == "user" else "助手"
        prompt_context += f"{role_label}: {msg['content']}\n\n"
    
    # 添加用户上下文项
    if st.session_state.context_items:
        prompt_context += "## 用户上下文\n"
        for key, value in st.session_state.context_items.items():
            prompt_context += f"{key}: {value}\n"
    
    # 添加最新的用户问题
    prompt_context += f"\n## 当前问题\n{user_input}\n"
    
    # 在实际应用中,这里应该调用AI API
    # ai_response = call_ai_api(prompt_context)
    
    # 模拟AI响应
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        
        # 模拟打字效果
        simulated_response = "这是一个模拟的AI回复,展示了多轮对话中的上下文管理。在实际应用中,这里会连接到OpenAI、Azure OpenAI或其他LLM提供商的API。"
        for chunk in simulated_response.split():
            full_response += chunk + " "
            time.sleep(0.05)
            message_placeholder.write(full_response + "▌")
        message_placeholder.write(full_response)
    
    # 保存AI响应到历史记录
    st.session_state.messages.append({"role": "assistant", "content": full_response})

# 显示调试信息
if st.sidebar.checkbox("显示调试信息"):
    st.sidebar.subheader("完整提示构建")
    st.sidebar.code(prompt_context)

提示优化建议

提示优化

  1. 明确角色与期望:为AI助手定义清晰的角色和行为准则

  2. 使用标记分隔上下文区域

    ## 角色定义
    你是...
    
    ## 历史信息
    ...
    
    ## 当前输入
    ...
    
  3. 增加元指令

    在回答前,请先考虑:
    1. 我是否有足够信息回答这个问题?
    2. 用户可能有什么隐含需求?
    3. 我应该以什么格式展示回答?
    
  4. 指定回复格式

    请使用以下格式回答用户问题:
    - 简短总结(1-2句)
    - 详细解释(2-3段)
    - 推荐行动(若适用)
    
  5. 优化提示的演进:通过A/B测试和用户反馈持续优化提示模板

通过结合这些技术,你可以显著提升多轮对话的质量和用户体验,有效应对上下文管理的挑战。


这篇博客介绍了多轮对话中上下文管理的关键技术与实践策略,并通过Streamlit示例展示了具体实现方法。掌握这些技巧,你将能够构建更智能、更自然的对话式AI应用。

posted @ 2025-04-23 10:39  何双新  阅读(1276)  评论(0)    收藏  举报