如何在LangChain中消除历史影响,实现独立学生代码评测
批量评测学生Java代码时结果互相干扰的问题排查
问题描述
我用LangChain开展学生代码题答案评测,单独评测单个学生提交的Java代码(如简单类、单元测试)能得到合理分数与反馈,但批量评测时,低分学生的分数会被拉高,不同学生的评测结果互相影响。已将ChatOpenAI模型、Prompt、Chroma DB的创建逻辑移至循环内,也移除了聊天历史,但问题仍存在。想确认是模型选择错误,还是遗漏了核心环节?
相关代码片段:
for answer in answers: model = ChatOpenAI(api_key=api_key, model="gpt-4") if len(answer['answer']) > 200: java_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.JAVA, chunk_size=2000, chunk_overlap=200 ) texts = java_splitter.split_text(answer['answer']) db=Chroma(persist_directory="data/"+answer['username']).from_texts(texts, OpenAIEmbeddings(openai_api_key=open_ai_key, disallowed_special=())) retriever = db.as_retriever( search_type="mmr", # Also test "similarity" search_kwargs={"k": 1}, ) prompt = ChatPromptTemplate.from_messages( [ ( "system", "Answer the user's questions, Ensure the output is a well-formed Python dictionary, remember to escape single quotes and don't use brackets, based on the below context:\n\n{context}", ), ##("placeholder", "{chat_history}"), ("user", "{input}"), ], ).partial(format_instructions=format_instructions) document_chain = create_stuff_documents_chain(model, prompt) qa = create_retrieval_chain(retriever, document_chain) result = qa.invoke({"input": lecturer_question}) print(responses)
核心问题分析
- Chroma DB持久化数据残留:使用
data/"+answer['username']作为持久化目录时,若目录已存在,from_texts会默认追加数据而非覆盖,导致不同学生的向量数据混存,检索时会拉取到其他学生的代码片段,干扰评测结果。 - 代码处理逻辑不一致:仅对长度>200的代码做向量检索处理,短代码未走相同流程,可能复用了之前循环的链或模型资源,造成上下文污染。
- Prompt约束不足:系统提示未明确要求「仅基于当前学生代码评测」,也未给出清晰的评分标准,模型可能因自由度过高,出现隐性的结果漂移。
- 资源未完全隔离:虽在循环内创建模型实例,但未彻底清理旧的向量数据库、检索器等资源,可能存在内存残留的交叉引用。
解决方案
完全隔离向量数据
- 放弃持久化目录,改用内存模式Chroma,每次循环创建全新的内存数据库,避免数据残留:
# 移除persist_directory参数,使用内存模式 db = Chroma.from_texts(texts, OpenAIEmbeddings(openai_api_key=open_ai_key, disallowed_special=())) - 若需持久化,每次循环前强制删除旧目录:
import shutil import os dir_path = f"data/{answer['username']}" if os.path.exists(dir_path): shutil.rmtree(dir_path) db = Chroma(persist_directory=dir_path).from_texts(...)
- 放弃持久化目录,改用内存模式Chroma,每次循环创建全新的内存数据库,避免数据残留:
统一所有代码的处理流程
- 无论代码长度,都通过向量检索流程处理,避免资源复用:
student_code = answer['answer'] if len(student_code) > 200: java_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.JAVA, chunk_size=2000, chunk_overlap=200 ) texts = java_splitter.split_text(student_code) else: texts = [student_code] # 后续检索、链创建逻辑统一
- 无论代码长度,都通过向量检索流程处理,避免资源复用:
强化Prompt约束
- 明确要求仅基于当前学生代码评测,同时给出清晰评分标准:
prompt = ChatPromptTemplate.from_messages( [ ( "system", """你需要对当前学生提交的Java代码进行评测,仅基于下方提供的该学生代码上下文,严格遵循以下评分标准输出结果。输出必须是格式正确的Python字典,转义单引号,不得使用括号。
- 明确要求仅基于当前学生代码评测,同时给出清晰评分标准:
评分标准:
- 代码正确性:符合题目要求、逻辑无错误(4分)
- 代码规范性:命名规范、注释清晰(3分)
- 代码效率:算法复杂度合理(2分)
- 完整性:覆盖所有测试场景(1分)
上下文:
{context}""",
),
("user", "{input}"),
]
).partial(format_instructions=format_instructions)
```
- 显式清理资源
- 循环末尾删除不再需要的变量,避免内存残留:
result = qa.invoke({"input": lecturer_question}) responses.append(result) # 清理当前循环的资源 del db, retriever, qa, prompt, document_chain
- 循环末尾删除不再需要的变量,避免内存残留:
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

