網(wǎng)站專題頁(yè)面設(shè)計(jì)欣賞騰訊企業(yè)郵箱登錄入口
概述
在與大型語(yǔ)言模型(如ChatGPT)交互的過(guò)程中,我們常常體驗(yàn)到與智能助手進(jìn)行連貫多輪對(duì)話的便利性。那么,當(dāng)我們開啟一個(gè)新的聊天時(shí),系統(tǒng)是如何管理聊天上下文的呢?
一、初始上下文的建立
1. 創(chuàng)建新會(huì)話
當(dāng)用戶開啟一個(gè)新的聊天時(shí),應(yīng)用程序后端會(huì)為該對(duì)話創(chuàng)建一個(gè)獨(dú)立的會(huì)話(session),并分配一個(gè)唯一的會(huì)話ID。這確保了每個(gè)對(duì)話都是獨(dú)立的,防止不同對(duì)話之間的混淆。
2. 系統(tǒng)提示的引入
在新會(huì)話的開始,系統(tǒng)會(huì)向模型提供一段隱藏的系統(tǒng)提示(System Prompt)。這段提示用于設(shè)定模型在整個(gè)對(duì)話中的角色、語(yǔ)氣和行為準(zhǔn)則。例如:
- 角色設(shè)定:讓模型扮演助理、教師、技術(shù)專家等特定身份。
- 語(yǔ)言風(fēng)格:規(guī)定回復(fù)使用正式、友好、幽默等特定語(yǔ)氣。
- 行為準(zhǔn)則:避免生成不適當(dāng)內(nèi)容,遵守倫理規(guī)范。
系統(tǒng)提示對(duì)用戶是不可見的,但對(duì)模型的回復(fù)有著深遠(yuǎn)影響,它確保了模型在整個(gè)對(duì)話過(guò)程中保持一致的行為。
二、上下文的積累與管理
1. 對(duì)話歷史的記錄
隨著用戶與模型的交互進(jìn)行,系統(tǒng)會(huì)將每一次的用戶輸入和模型回復(fù)按照時(shí)間順序累積,形成當(dāng)前會(huì)話的消息隊(duì)列。這使得模型在生成回復(fù)時(shí),可以參考之前的對(duì)話內(nèi)容,保持連貫性和一致性。
2. 上下文窗口的限制
大型語(yǔ)言模型在處理輸入時(shí),有一個(gè)固定的上下文窗口(Context Window),表示模型一次能處理的最大文本長(zhǎng)度。例如,GPT-3的上下文窗口為4096個(gè)Token。
當(dāng)對(duì)話長(zhǎng)度超過(guò)上下文窗口時(shí),系統(tǒng)需要對(duì)輸入進(jìn)行截?cái)?。為了確保模型繼續(xù)遵循最初的系統(tǒng)提示,應(yīng)用程序會(huì):
- 優(yōu)先保留系統(tǒng)提示:系統(tǒng)提示始終位于輸入的開頭,不被截?cái)唷?/li>
- 截?cái)嘣缙趯?duì)話:從最早的用戶和模型對(duì)話開始移除,保留最近的交互內(nèi)容。
3. 上下文組裝
在生成回復(fù)時(shí),應(yīng)用程序會(huì)將以下內(nèi)容按順序拼接,形成當(dāng)前的輸入上下文:
- 系統(tǒng)提示:設(shè)定模型行為的隱藏指令。
- 重要信息:用戶提供的關(guān)鍵數(shù)據(jù)或參數(shù)(如果有)。
- 最近的對(duì)話歷史:包括最近幾輪的用戶輸入和模型回復(fù)。
通過(guò)這種方式,模型能夠在一次交互中獲得必要的上下文信息,生成符合預(yù)期的回復(fù)。
三、系統(tǒng)提示的重要作用
1. 保證模型行為一致性
由于模型在每次生成回復(fù)時(shí),只能參考當(dāng)前的輸入文本,因此系統(tǒng)提示需要在每次輸入中提供,確保模型始終按照設(shè)定的角色和風(fēng)格進(jìn)行回復(fù)。
2. 防止不當(dāng)內(nèi)容生成
系統(tǒng)提示中包含的行為準(zhǔn)則和禁止事項(xiàng),有助于模型避免生成不合規(guī)或不適當(dāng)?shù)膬?nèi)容,提升對(duì)話的安全性和可靠性。
3. 提高用戶體驗(yàn)
通過(guò)精心設(shè)計(jì)的系統(tǒng)提示,模型能夠更好地理解用戶意圖,提供高質(zhì)量、個(gè)性化的回復(fù),提升用戶的交互體驗(yàn)。
四、技術(shù)實(shí)現(xiàn)細(xì)節(jié)
1. 會(huì)話管理
- 創(chuàng)建會(huì)話ID:為每個(gè)新對(duì)話分配唯一的會(huì)話ID,用于區(qū)分不同用戶的會(huì)話。
- 狀態(tài)跟蹤:記錄每個(gè)會(huì)話的狀態(tài)信息,便于后續(xù)的上下文管理。
2. 消息隊(duì)列維護(hù)
- 記錄交互內(nèi)容:保存當(dāng)前會(huì)話中的所有用戶輸入和模型回復(fù)。
- 長(zhǎng)度檢查:在發(fā)送給模型之前,檢查輸入的總長(zhǎng)度,確保不超過(guò)上下文窗口限制。
3. 上下文優(yōu)化
- 截?cái)嗖呗?/strong>:當(dāng)超過(guò)上下文窗口限制時(shí),從早期對(duì)話開始移除內(nèi)容。
- 摘要處理:對(duì)于重要但較早的內(nèi)容,通過(guò)生成摘要的方式保留關(guān)鍵信息。
五、模型與應(yīng)用的職責(zé)劃分
需要明確的是,大型語(yǔ)言模型本身并不具備會(huì)話管理、消息隊(duì)列維護(hù)或上下文組裝的能力。這些功能由應(yīng)用程序在模型之上實(shí)現(xiàn)。具體來(lái)說(shuō):
- 模型的職責(zé):根據(jù)輸入生成下一段文本。
- 應(yīng)用的職責(zé):管理對(duì)話上下文、用戶會(huì)話、內(nèi)容過(guò)濾等。
通過(guò)合理的職責(zé)劃分,應(yīng)用程序能夠充分發(fā)揮模型的能力,提供豐富多樣的應(yīng)用場(chǎng)景。
六、用戶數(shù)據(jù)的安全與隱私
- 獨(dú)立的會(huì)話:每個(gè)新對(duì)話都是獨(dú)立的,模型不會(huì)記住之前會(huì)話中的信息,保護(hù)用戶隱私。
- 數(shù)據(jù)限制:用戶的輸入和模型的回復(fù)都嚴(yán)格限定在當(dāng)前會(huì)話內(nèi),不會(huì)跨會(huì)話傳播。
七、總結(jié)
大型語(yǔ)言模型在新聊天中管理上下文,主要通過(guò)以下方式實(shí)現(xiàn):
- 創(chuàng)建新會(huì)話,重置上下文:確保每個(gè)對(duì)話的獨(dú)立性。
- 使用系統(tǒng)提示:設(shè)定模型的角色、風(fēng)格和行為準(zhǔn)則,確保模型行為一致。
- 維護(hù)消息隊(duì)列:記錄對(duì)話歷史,供模型參考,提高回復(fù)的連貫性。
- 上下文管理:在上下文窗口限制內(nèi),優(yōu)化輸入內(nèi)容,保證模型有效處理。
示例
使用多輪會(huì)話示例代碼
下面的代碼,演示如何在代碼中實(shí)現(xiàn)與大型語(yǔ)言模型的多輪對(duì)話。我們將引入一個(gè)循環(huán),允許用戶多次輸入,并維護(hù)會(huì)話的上下文,使模型的回復(fù)能夠參考之前的對(duì)話內(nèi)容。
代碼
import torch
import logging
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor
from qwen_vl_utils import process_vision_info# 設(shè)置日志配置,包含Transformers庫(kù)的日志
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',level=logging.INFO # 設(shè)置全局日志級(jí)別為INFO,避免過(guò)多日志輸出
)
# 獲取Transformers庫(kù)的logger并設(shè)置級(jí)別為INFO
transformers_logger = logging.getLogger('transformers')
transformers_logger.setLevel(logging.INFO)# 設(shè)置模型緩存目錄
cache_dir = '/data/model/'# 加載模型,啟用GPU加速
model = Qwen2_5_VLForConditionalGeneration.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct",torch_dtype=torch.bfloat16,attn_implementation="sdpa",device_map="auto",cache_dir=cache_dir
)
logging.info("模型已加載到設(shè)備:%s,使用attn_implementation='sdpa'", model.device)# 設(shè)置視覺(jué)令牌范圍以平衡性能和成本
min_pixels = 256 * 28 * 28
max_pixels = 1280 * 28 * 28# 加載處理器
processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct",min_pixels=min_pixels,max_pixels=max_pixels,cache_dir=cache_dir
)
logging.info("處理器已加載,設(shè)置了自定義的視覺(jué)令牌范圍。")# 初始化消息內(nèi)容列表,包含系統(tǒng)提示(可選)
messages = [# 可以添加系統(tǒng)提示,設(shè)定模型的行為{"role": "system","content": [{"type": "text", "text": "你是一位友好的智能助手,樂(lè)于回答用戶的問(wèn)題并提供幫助。"},],}
]# 多輪會(huì)話循環(huán)
while True:user_input = input("用戶:")if user_input.lower() in ['退出', 'exit', 'quit']:print("結(jié)束對(duì)話。")break# 將用戶輸入添加到消息列表messages.append({"role": "user","content": [{"type": "text", "text": user_input}]})# 準(zhǔn)備推理輸入logging.info("開始準(zhǔn)備推理輸入...")text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)image_inputs, video_inputs = process_vision_info(messages)inputs = processor(text=[text],images=image_inputs,videos=video_inputs,padding=True,return_tensors="pt",)inputs = inputs.to(model.device)logging.info("推理輸入已準(zhǔn)備完畢。")# 進(jìn)行推理并生成輸出logging.info("開始生成輸出...")generated_ids = model.generate(**inputs, max_new_tokens=512)logging.info("輸出生成完畢。")# 處理生成的輸出generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]output_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] # 取第一個(gè)元素print("助手:" + output_text.strip())# 將模型的回復(fù)添加到消息列表messages.append({"role": "assistant","content": [{"type": "text", "text": output_text.strip()}]})# 為了防止超過(guò)上下文長(zhǎng)度限制,可以在這里檢查并截?cái)嘞⒘斜?/span># 例如,只保留最近的n輪對(duì)話max_history = 5 # 保留最近5輪對(duì)話(可根據(jù)需要調(diào)整)# 保留系統(tǒng)提示加上最近的max_history*2條消息(用戶和助手各一條,所以乘以2)if len(messages) > max_history * 2 + 1: # +1是因?yàn)橄到y(tǒng)提示算一條messages = [messages[0]] + messages[-max_history*2:]logging.info("消息列表已截?cái)?#xff0c;保留最近的 %d 輪對(duì)話。", max_history)
代碼說(shuō)明
-
引入多輪會(huì)話循環(huán):使用
while True
循環(huán),不斷讀取用戶輸入,實(shí)現(xiàn)多輪對(duì)話。 -
管理消息列表:使用
messages
列表維護(hù)對(duì)話歷史,在每一輪中將用戶和助手的消息添加到列表中。 -
處理用戶退出指令:如果用戶輸入
退出
、exit
或quit
,程序?qū)⒔Y(jié)束對(duì)話循環(huán)。 -
準(zhǔn)備推理輸入:在每一輪對(duì)話中,使用
processor.apply_chat_template
方法將messages
列表轉(zhuǎn)換為模型可接受的輸入格式。 -
調(diào)用模型生成回復(fù):使用
model.generate
方法生成模型的回復(fù),并將其解碼為文本。 -
顯示模型回復(fù)并添加到對(duì)話歷史:將模型的回復(fù)打印出來(lái),并添加到
messages
列表中,以在后續(xù)對(duì)話中提供上下文。 -
管理上下文長(zhǎng)度:為了防止超過(guò)模型的上下文窗口限制(即最大輸入長(zhǎng)度),在每輪對(duì)話后檢查
messages
列表的長(zhǎng)度,并截?cái)嘣缙诘膶?duì)話內(nèi)容,只保留最近的max_history
輪對(duì)話。
示例運(yùn)行
拖到最右側(cè),重點(diǎn)看輸入給大模型的messages在不斷的累積
2025-02-27 04:00:07,656 - root - INFO - 處理器已加載,設(shè)置了自定義的視覺(jué)令牌范圍。
用戶:你好!
2025-02-27 04:00:49,596 - root - INFO - 開始準(zhǔn)備推理輸入...
2025-02-27 04:00:49,596 - root - INFO - [{'role': 'system', 'content': [{'type': 'text', 'text': '你是一位友好的智能助手,樂(lè)于回答用戶的問(wèn)題并提供幫助。'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你好!'}]}]
2025-02-27 04:00:49,609 - root - INFO - 推理輸入已準(zhǔn)備完畢。
2025-02-27 04:00:49,609 - root - INFO - 開始生成輸出...
2025-02-27 04:00:59,579 - root - INFO - 輸出生成完畢。
助手:你好!有什么可以幫助你的嗎?
用戶:你能給我講個(gè)笑話嗎?
2025-02-27 04:01:11,942 - root - INFO - 開始準(zhǔn)備推理輸入...
2025-02-27 04:01:11,942 - root - INFO - [{'role': 'system', 'content': [{'type': 'text', 'text': '你是一位友好的智能助手,樂(lè)于回答用戶的問(wèn)題并提供幫助。'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你好!'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '你好!有什么可以幫助你的嗎?'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你能給我講個(gè)笑話嗎?'}]}]
2025-02-27 04:01:11,943 - root - INFO - 推理輸入已準(zhǔn)備完畢。
2025-02-27 04:01:11,943 - root - INFO - 開始生成輸出...
2025-02-27 04:01:32,729 - root - INFO - 輸出生成完畢。
助手:當(dāng)然可以!這是一個(gè)經(jīng)典的笑話:為什么電腦經(jīng)常生病?因?yàn)樗拇皯?#xff08;Windows)總是開著!
用戶:哈哈,很有趣。再講一個(gè)腦筋急轉(zhuǎn)彎?
2025-02-27 04:02:08,591 - root - INFO - 開始準(zhǔn)備推理輸入...
2025-02-27 04:02:08,591 - root - INFO - [{'role': 'system', 'content': [{'type': 'text', 'text': '你是一位友好的智能助手,樂(lè)于回答用戶的問(wèn)題并提供幫助。'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你好!'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '你好!有什么可以幫助你的嗎?'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你能給我講個(gè)笑話嗎?'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '當(dāng)然可以!這是一個(gè)經(jīng)典的笑話:為什么電腦經(jīng)常生病?因?yàn)樗拇皯?#xff08;Windows)總是開著!'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '哈哈,很有趣。再講一個(gè)腦筋急轉(zhuǎn)彎?'}]}]
2025-02-27 04:02:08,592 - root - INFO - 推理輸入已準(zhǔn)備完畢。
2025-02-27 04:02:08,593 - root - INFO - 開始生成輸出...
2025-02-27 04:02:34,326 - root - INFO - 輸出生成完畢。
助手:好的,這個(gè)腦筋急轉(zhuǎn)彎挺有趣的:什么東西越洗越臟?答案是水。
用戶:謝謝你的回答
2025-02-27 04:03:03,807 - root - INFO - 開始準(zhǔn)備推理輸入...
2025-02-27 04:03:03,807 - root - INFO - [{'role': 'system', 'content': [{'type': 'text', 'text': '你是一位友好的智能助手,樂(lè)于回答用戶的問(wèn)題并提供幫助。'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你好!'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '你好!有什么可以幫助你的嗎?'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '你能給我講個(gè)笑話嗎?'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '當(dāng)然可以!這是一個(gè)經(jīng)典的笑話:為什么電腦經(jīng)常生病?因?yàn)樗拇皯?#xff08;Windows)總是開著!'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '哈哈,很有趣。再講一個(gè)腦筋急轉(zhuǎn)彎?'}]}, {'role': 'assistant', 'content': [{'type': 'text', 'text': '好的,這個(gè)腦筋急轉(zhuǎn)彎挺有趣的:什么東西越洗越臟?答案是水。'}]}, {'role': 'user', 'content': [{'type': 'text', 'text': '謝謝你的回答'}]}]
2025-02-27 04:03:03,809 - root - INFO - 推理輸入已準(zhǔn)備完畢。
2025-02-27 04:03:03,809 - root - INFO - 開始生成輸出...
2025-02-27 04:03:27,048 - root - INFO - 輸出生成完畢。
助手:不客氣,隨時(shí)歡迎你來(lái)提問(wèn)!
用戶:退出
結(jié)束對(duì)話。
另外多模態(tài)大模型可以支持復(fù)雜的會(huì)話messages,單次輸入給大模型的輸入可以如下:
conversation = [{"role": "user","content": [{"type": "image"}, {"type": "text", "text": "Hello, how are you?"}],},{"role": "assistant","content": "I'm doing well, thank you for asking. How can I assist you today?",},{"role": "user","content": [{"type": "text", "text": "Can you describe these images and video?"},{"type": "image"},{"type": "image"},{"type": "video"},{"type": "text", "text": "These are from my vacation."},],},{"role": "assistant","content": "I'd be happy to describe the images and video for you. Could you please provide more context about your vacation?",},{"role": "user","content": "It was a trip to the mountains. Can you see the details in the images and video?",},
]# default:
prompt_without_id = processor.apply_chat_template(conversation, add_generation_prompt=True
)
# Excepted output: '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>Hello, how are you?<|im_end|>\n<|im_start|>assistant\nI'm doing well, thank you for asking. How can I assist you today?<|im_end|>\n<|im_start|>user\nCan you describe these images and video?<|vision_start|><|image_pad|><|vision_end|><|vision_start|><|image_pad|><|vision_end|><|vision_start|><|video_pad|><|vision_end|>These are from my vacation.<|im_end|>\n<|im_start|>assistant\nI'd be happy to describe the images and video for you. Could you please provide more context about your vacation?<|im_end|>\n<|im_start|>user\nIt was a trip to the mountains. Can you see the details in the images and video?<|im_end|>\n<|im_start|>assistant\n'# add ids
prompt_with_id = processor.apply_chat_template(conversation, add_generation_prompt=True, add_vision_id=True
)
# Excepted output: '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\nPicture 1: <|vision_start|><|image_pad|><|vision_end|>Hello, how are you?<|im_end|>\n<|im_start|>assistant\nI'm doing well, thank you for asking. How can I assist you today?<|im_end|>\n<|im_start|>user\nCan you describe these images and video?Picture 2: <|vision_start|><|image_pad|><|vision_end|>Picture 3: <|vision_start|><|image_pad|><|vision_end|>Video 1: <|vision_start|><|video_pad|><|vision_end|>These are from my vacation.<|im_end|>\n<|im_start|>assistant\nI'd be happy to describe the images and video for you. Could you please provide more context about your vacation?<|im_end|>\n<|im_start|>user\nIt was a trip to the mountains. Can you see the details in the images and video?<|im_end|>\n<|im_start|>assistant\n'
注意事項(xiàng)
-
上下文長(zhǎng)度限制:大型語(yǔ)言模型對(duì)輸入文本的長(zhǎng)度是有最大限制的(例如4096個(gè)Token)。在實(shí)際應(yīng)用中,需要根據(jù)模型的實(shí)際限制,調(diào)整
max_history
的值,或者采用更加復(fù)雜的截?cái)嗪驼呗浴?/p> -
視覺(jué)信息處理:示例代碼中包含了對(duì)圖像和視頻輸入的處理。如果當(dāng)前對(duì)話不涉及圖像或視頻,可以簡(jiǎn)化相關(guān)處理,或者在需要時(shí)動(dòng)態(tài)地添加圖像或視頻信息到
messages
中。 -
系統(tǒng)提示的作用:在
messages
列表中添加"role": "system"
的消息,可以設(shè)定模型的整體行為和風(fēng)格。系統(tǒng)提示通常只需在對(duì)話開始時(shí)添加一次,后續(xù)對(duì)話中無(wú)需重復(fù)。 -
日志級(jí)別設(shè)置:為了避免過(guò)多的日志輸出,將全局日志級(jí)別從
DEBUG
調(diào)整為INFO
。根據(jù)需要,可以進(jìn)一步調(diào)整日志級(jí)別。 -
模型性能與資源:運(yùn)行此代碼需要具備支持相應(yīng)模型大小的計(jì)算資源(例如GPU內(nèi)存)。在實(shí)際應(yīng)用中,根據(jù)硬件條件選擇合適的模型規(guī)模。