刷题 Agent 的多轮对话设计:怎样让模型记住上一轮的推理链
刷题 Agent 的多轮对话设计怎样让模型记住上一轮的推理链一、当对话失忆时AI 刷题助手还懂你吗如果让一个刷题 Agent 陪你做一道树形 DP 的题目它需要在第一轮帮你画递归树、在第二轮帮你推导状态转移方程、在第三轮帮你验证边界条件。但如果第二轮对话时它已经把第一轮的推理忘得一干二净那你面对的就不是一个助手而是一个每次都要重新开始的陌生人。这种失忆在日常使用 AI 辅助刷题时并不罕见。每一次对话模型都需要足够的上下文来理解你现在的卡点而这个上下文往往不仅包括当前轮的问题还应该包括上一轮它自己的推理链条。推理链Reasoning Chain是模型逐步分析问题时形成的一系列中间结论是理解用户意图、保持解题一致性的核心信息。如果 Agent 记不住上一轮的推理链会出现什么问题你问这里为什么用后序遍历它可能给出一个跟你刚才讨论的完全无关的解释。你追问这个状态转移方程为什么少了 k 的维度它可能从头开始重新推导而不是在上一次推理的基础上做修正。这种断裂不仅浪费你的时间更可能让你对 AI 辅助刷题这件事本身失去信心。那么怎么让刷题 Agent 在多轮对话中始终能记住——并有效利用——上一轮的推理链flowchart LR A[用户提问轮1] -- B[模型生成推理链R1] B -- C[用户追问轮2] C -- D{Agent 如何处理?} D --|方案A: 无记忆| E[重新推理, 丢失 R1] D --|方案B: 全量记忆| F[携带完整对话历史] D --|方案C: 推理链摘要| G[提取 R1 核心并注入] E -- H[结果不一致] F -- I[Token 消耗过大] G -- J[高效且一致]二、为什么直接带上历史解决不了推理链记忆问题一个直觉上的方案是把之前所有的对话记录全部带上下一次请求。这在 RAG 或 Chat API 中很容易实现但它有两个根本性问题。第一Token 膨胀。在解决算法题时用户可能在多轮对话中输入大量代码片段、测试用例、甚至完整的题目描述。这些内容加上模型自己的输出很快就超出上下文窗口。即便 GPT-4 支持 128K Tokens也经不起连续 10 轮以上的刷题对话。第二噪声干扰。不是所有的历史信息都对下一轮有用。比如你在前几轮讨论的是树形结构的表示方法而当前轮你要问的是 DP 的状态设计那些关于TreeNode 定义的对话就是噪声。让模型在噪声中提取有用信息本身就是一种不稳定的做法。对于刷题 Agent 来说真正需要记住的不是对话全文而是模型的推理链——包括它用什么思路拆解了问题、它得出了哪些中间结论、它发现了哪些需要特别注意的边界。这三类信息构成了推理链的三要素flowchart TB subgraph 推理链三要素 A[思路拆解路径] -- |为什么选 DP 而不是贪心| D[核心推理摘要] B[中间关键结论] -- |dp[i][j] 表示前 i 个物品, 容量 j 的最大值| D C[边界警示] -- |注意 j 从大到小遍历| D end D -- E[注入下一轮 System Prompt] E -- F[Agent 在推理链基础上增量推理]三、实现一个带推理链记忆的刷题对话管理器下面是一个简化版的推理链管理器实现。它的核心逻辑是在每轮对话结束后自动提取模型的推理摘要并存储在下一轮对话开始时将摘要注入到 system prompt。 推理链记忆管理器 设计思路 1. 每轮对话结束后让模型自己总结推理链 2. 将摘要以结构化 JSON 存入对话状态 3. 下一轮自动注入保持推理连续性 import json from typing import List, Dict from dataclasses import dataclass, field dataclass class ReasoningStep: 推理链中的单个步骤 step_type: str # decomposition | conclusion | warning content: str # 该步骤的具体内容 step_id: int # 步骤序号用于引用 dataclass class DialogState: 对话状态核心是推理链的维护 session_id: str problem_id: str # 当前在刷哪道题 reasoning_chain: List[ReasoningStep] field(default_factorylist) history_summary: str # 压缩后的历史摘要兜底用 class ReasoningMemoryManager: 推理链管理器 负责在每轮对话间维护推理链的连续性 def __init__(self, llm_client): llm_client: 大模型调用客户端 注入模型能力用于自动提取推理摘要 self.llm llm_client self.sessions: Dict[str, DialogState] {} def start_problem(self, session_id: str, problem_id: str): 开始一道新题时重置推理链 self.sessions[session_id] DialogState( session_idsession_id, problem_idproblem_id ) def build_system_prompt(self, session_id: str) - str: 构建带推理链的 system prompt 为什么这样设计在 system prompt 层面注入推理链 让模型在生成回答前就已经知道之前的推理过程 而不是在对话中重新推导。 state self.sessions.get(session_id) if not state or not state.reasoning_chain: return 你是一个算法刷题助手。 # 将推理链格式化为易读的文本 chain_text 【之前的推理链请在以下基础上继续】\n for step in state.reasoning_chain: if step.step_type decomposition: chain_text f思路拆解{step.content}\n elif step.step_type conclusion: chain_text f关键结论{step.content}\n elif step.step_type warning: chain_text f边界警示{step.content}\n chain_text \n请基于上述推理链回答用户问题不要重复已经得出的结论。 return chain_text def extract_and_store_chain(self, session_id: str, model_response: str): 从模型回复中自动提取推理链并存储 这里使用 LLM 自身做提取避免规则匹配的不稳定 state self.sessions.get(session_id) if not state: return # 让模型用结构化格式总结自己的推理过程 # 这样做的优势比正则匹配更稳定能识别隐含的推理逻辑 extraction_prompt f 请从以下回复中提取推理链用 JSON 格式输出 {{ steps: [ {{type: decomposition|conclusion|warning, content: ...}} ] }} 回复内容 {model_response} try: extraction_result self.llm.complete(extraction_prompt) steps_data json.loads(extraction_result).get(steps, []) # 将新步骤追加到推理链 next_id len(state.reasoning_chain) 1 for step_data in steps_data: state.reasoning_chain.append(ReasoningStep( step_typestep_data[type], contentstep_data[content], step_idnext_id )) next_id 1 # 推理链过长时做截断保留最近 20 步 # 为什么是 20实测一道题的推理链通常在 5-15 步 # 20 步提供了足够的安全余量 if len(state.reasoning_chain) 20: state.reasoning_chain state.reasoning_chain[-20:] except (json.JSONDecodeError, KeyError) as e: # 降级方案解析失败时用模型回复的摘要兜底 # 不做静默失败记录日志方便排查 print(f推理链提取失败: {e}使用降级方案) state.history_summary model_response[:500] # 截取前 500 字 def chat(self, session_id: str, user_input: str) - str: 执行一轮带推理链的对话 system_prompt self.build_system_prompt(session_id) # 组装消息仅带推理链摘要不带全量历史 messages [ {role: system, content: system_prompt}, {role: user, content: user_input} ] response self.llm.chat(messages) # 对话完成后自动提取本轮推理链 self.extract_and_store_chain(session_id, response) return response四、推理链记忆的边界与权衡推理链方案虽然有效但并非银弹。它有以下几个明确的边界跨题推理不适用。每道题的推理链是独立的。如果用户从最长公共子序列切换到背包问题之前关于 LCS 的推理链应该被清空否则会误导模型。解决方案是在start_problem时显式重置。推理链长度存在上限。即使做截断当用户的追问次数超过 20 步时早期的推理可能丢失。折中方案是做分级存储最近 5 步完整保留5-15 步做关键词压缩15 步以上只保留类型标签。自动提取的准确率不是 100%。当模型输出不够结构化时JSON 解析可能失败。需要配合正则兜底和人工校验机制。在工程实践中建议再加一层推理链变更检测——如果新一轮推理与历史链矛盾主动提示用户是否重新开始。不应该替代对话树。推理链提供的是纵向的推理深度但用户在刷题时也可能需要横向探索比如对比两种解法。对于这种情况更好的方案是维护一棵推理树而非单链。五、总结推理链记忆的本质是把多轮对话从一个重新开始的模式变成增量推理的模式。它不需要保留完整的对话历史而是保留推理中最关键的三个组成部分思路拆解、中间结论和边界警示。在工程实现上核心要点有三个用模型自身的结构化抽取替代规则匹配、在 system prompt 层面注入而非在对话中混入、为每道题独立维护推理链并在切换题目时清理。对于刷题 Agent 而言记住推理链不是锦上添花而是让 AI 从一次性解题工具进化到持续性学习伙伴的关键一步。

相关新闻

最新新闻

日新闻

周新闻

月新闻