在前面的幾篇文章如《針對Qwen-Agent框架的Function Call及ReAct的源碼閱讀與解析:Agent基類篇》 、《基于Qwen-Agent框架的Function Call及ReAct方式調用自定義工具》、
《針對Qwen-Agent框架的源碼閱讀與解析:FnCallAgent與ReActChat篇》中,我們已經理解了Agent的具體運作機制與原理,這里我們再以文件相關操作為例,學習一下Agent的實際應用。
事實上,Qwen-Agent是一個強大的智能助手框架,它集成了RAG(檢索增強生成)能力和函數調用能力,能夠處理各種復雜任務。本文將深入探討Qwen-Agent框架中的文件相關操作,特別是從Assistant
類到BasicDocQA
類的實現,分析代碼結構、工作原理以及各個組件的功能。
文章目錄
- 2. Assistant類分析
- 2.1 類定義與初始化
- 2.2 知識格式化函數
- 2.3 核心運行方法
- 2.4 知識提示預處理
- 2.5 輔助函數
- 3. BasicDocQA類分析
- 3.1 類定義與初始化
- 3.2 提示模板
- 3.3 核心運行方法
- 4. 兩個類的比較
- 4.1 相同點
- 4.2 區別點
- 5. 工作流程分析
- 5.1 Assistant類工作流程
- 5.2 BasicDocQA類工作流程
- 6. 實際應用場景
- 6.1 Assistant類適用場景
- 6.2 BasicDocQA類適用場景
- 7. 總結
🎉進入大模型應用與實戰專欄 | 🚀查看更多專欄內容
2. Assistant類分析
2.1 類定義與初始化
Assistant
類是Qwen-Agent框架中的一個核心類,它繼承自FnCallAgent
,集成了RAG能力和函數調用能力。
class Assistant(FnCallAgent):"""This is a widely applicable agent integrated with RAG capabilities and function call ability."""def __init__(self,function_list: Optional[List[Union[str, Dict, BaseTool]]] = None,llm: Optional[Union[Dict, BaseChatModel]] = None,system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE,name: Optional[str] = None,description: Optional[str] = None,files: Optional[List[str]] = None,rag_cfg: Optional[Dict] = None):super().__init__(function_list=function_list,llm=llm,system_message=system_message,name=name,description=description,files=files,rag_cfg=rag_cfg)
初始化參數包括:
function_list
:可調用的函數列表llm
:語言模型實例system_message
:系統消息name
和description
:Agent的名稱和描述files
:文件列表rag_cfg
:RAG配置
2.2 知識格式化函數
format_knowledge_to_source_and_content
函數用于將檢索結果格式化為源和內容的標準格式:
def format_knowledge_to_source_and_content(result: Union[str, List[dict]]) -> List[dict]:knowledge = []if isinstance(result, str):# 字符串格式處理result = f'{result}'.strip()try:docs = json5.loads(result)except Exception:print_traceback()knowledge.append({'source': '上傳的文檔', 'content': result})return knowledgeelse:docs = resulttry:# 文檔列表格式處理_tmp_knowledge = []assert isinstance(docs, list)for doc in docs:url, snippets = doc['url'], doc['text']assert isinstance(snippets, list)_tmp_knowledge.append({'source': f'[文件]({get_basename_from_url(url)})','content': '\n\n...\n\n'.join(snippets)})knowledge.extend(_tmp_knowledge)except Exception:print_traceback()knowledge.append({'source': '上傳的文檔', 'content': result})return knowledge
該函數支持兩種輸入格式:
- 字符串格式:嘗試解析為JSON,失敗則作為整體內容
- 文檔列表格式:包含URL和文本片段的字典列表
輸出為統一的[{'source': '來源', 'content': '內容'}, ...]
格式。
2.3 核心運行方法
_run
方法是Assistant類的核心方法,負責處理用戶查詢:
def _run(self,messages: List[Message],lang: Literal['en', 'zh'] = 'en',knowledge: str = '',**kwargs) -> Iterator[List[Message]]:"""Q&A with RAG and tool use abilities."""new_messages = self._prepend_knowledge_prompt(messages=messages, lang=lang, knowledge=knowledge, **kwargs)return super()._run(messages=new_messages, lang=lang, **kwargs)
該方法首先調用_prepend_knowledge_prompt
方法添加知識提示,然后調用父類的_run
方法處理消息。
2.4 知識提示預處理
_prepend_knowledge_prompt
方法用于在消息前添加知識提示:
def _prepend_knowledge_prompt(self,messages: List[Message],lang: Literal['en', 'zh'] = 'en',knowledge: str = '',**kwargs) -> List[Message]:messages = copy.deepcopy(messages)if not knowledge:# 從文件中檢索知識*_, last = self.mem.run(messages=messages, lang=lang, **kwargs)knowledge = last[-1][CONTENT]logger.debug(f'Retrieved knowledge of type `{type(knowledge).__name__}`:\n{knowledge}')if knowledge:knowledge = format_knowledge_to_source_and_content(knowledge)logger.debug(f'Formatted knowledge into type `{type(knowledge).__name__}`:\n{knowledge}')else:knowledge = []snippets = []for k in knowledge:snippets.append(KNOWLEDGE_SNIPPET[lang].format(source=k['source'], content=k['content']))knowledge_prompt = ''if snippets:knowledge_prompt = KNOWLEDGE_TEMPLATE[lang].format(knowledge='\n\n'.join(snippets))if knowledge_prompt:if messages[0][ROLE] == SYSTEM:messages[0][CONTENT] += '\n\n' + knowledge_promptelse:messages = [Message(role=SYSTEM, content=knowledge_prompt)] + messagesreturn messages
處理流程:
- 如果未提供知識,則從文件中檢索
- 格式化知識為標準格式
- 將知識片段格式化為多語言模板
- 將格式化的知識添加到系統消息中
2.5 輔助函數
get_current_date_str
函數用于獲取當前日期的字符串表示:
def get_current_date_str(lang: Literal['en', 'zh'] = 'en',hours_from_utc: Optional[int] = None,
) -> str:# 獲取當前時間if hours_from_utc is None:cur_time = datetime.datetime.now()else:cur_time = datetime.datetime.utcnow() + datetime.timedelta(hours=hours_from_utc)# 根據語言格式化日期字符串if lang == 'en':date_str = 'Current date: ' + cur_time.strftime('%A, %B %d, %Y')elif lang == 'zh':cur_time = cur_time.timetuple()date_str = f'當前時間:{cur_time.tm_year}年{cur_time.tm_mon}月{cur_time.tm_mday}日,星期'date_str += ['一', '二', '三', '四', '五', '六', '日'][cur_time.tm_wday]date_str += '。'else:raise NotImplementedErrorreturn date_str
該函數支持中英文兩種格式,可以指定UTC時差。
3. BasicDocQA類分析
3.1 類定義與初始化
BasicDocQA
類繼承自Assistant
類,專門用于文檔問答:
class BasicDocQA(Assistant):"""This is an agent for doc QA."""def __init__(self,function_list: Optional[List[Union[str, Dict, BaseTool]]] = None,llm: Optional[Union[Dict, BaseChatModel]] = None,system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE,name: Optional[str] = DEFAULT_NAME,description: Optional[str] = DEFAULT_DESC,files: Optional[List[str]] = None,rag_cfg: Optional[Dict] = None):super().__init__(function_list=function_list,llm=llm,system_message=system_message,name=name,description=description,files=files,rag_cfg=rag_cfg)
默認名稱和描述:
DEFAULT_NAME = 'Basic DocQA'
DEFAULT_DESC = '可以根據問題,檢索出知識庫中的某個相關細節來回答。適用于需要定位到具體位置的問題,例如"介紹表1"等類型的問題'
3.2 提示模板
BasicDocQA
類使用特定的提示模板,支持中英文:
PROMPT_TEMPLATE_ZH = """請充分理解以下參考資料內容,組織出滿足用戶提問的條理清晰的回復。
#參考資料:
{ref_doc}"""PROMPT_TEMPLATE_EN = """Please fully understand the content of the following reference materials and organize a clear response that meets the user's questions.
# Reference materials:
{ref_doc}"""PROMPT_TEMPLATE = {'zh': PROMPT_TEMPLATE_ZH,'en': PROMPT_TEMPLATE_EN,
}
3.3 核心運行方法
BasicDocQA
類重寫了_run
方法,使用不同的文檔問答提示:
def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]:"""This agent using different doc qa prompt with Assistant"""# 使用Memory agent進行數據管理*_, last = self.mem.run(messages=messages, **kwargs)knowledge = last[-1][CONTENT]messages = copy.deepcopy(messages)system_prompt = PROMPT_TEMPLATE[lang].format(ref_doc=knowledge)if messages[0][ROLE] == SYSTEM:messages[0][CONTENT] += '\n\n' + system_promptelse:messages.insert(0, Message(SYSTEM, system_prompt))response = self._call_llm(messages=messages)return response
處理流程:
- 使用
mem.run
檢索相關知識 - 使用特定的文檔問答提示模板
- 將格式化的提示添加到系統消息中
- 調用語言模型生成回復
4. 兩個類的比較
4.1 相同點
- 都繼承自Agent基類,具備Agent的基本能力
- 都集成了RAG能力,可以檢索知識庫
- 都支持中英文兩種語言
- 都通過添加系統提示來引導模型回答
4.2 區別點
-
用途不同:
Assistant
類是通用助手,集成了RAG和函數調用能力BasicDocQA
類專注于文檔問答,適用于需要定位具體細節的問題
-
提示模板不同:
Assistant
類使用知識庫模板,強調知識來源BasicDocQA
類使用參考資料模板,強調對參考資料的理解和組織
-
處理流程不同:
Assistant
類先格式化知識,再添加到系統消息BasicDocQA
類直接將知識作為參考資料添加到系統消息
5. 工作流程分析
5.1 Assistant類工作流程
- 初始化Assistant實例,配置參數
- 接收用戶消息
- 如果未提供知識,從文件中檢索相關知識
- 格式化知識為標準格式(源和內容)
- 將格式化的知識添加到系統消息中
- 調用語言模型生成回復
5.2 BasicDocQA類工作流程
- 初始化BasicDocQA實例,配置參數
- 接收用戶消息
- 從文件中檢索相關知識
- 使用特定的文檔問答提示模板
- 將格式化的提示添加到系統消息中
- 調用語言模型生成回復
6. 實際應用場景
6.1 Assistant類適用場景
- 通用問答系統
- 需要調用外部函數的場景
- 多種知識源集成的場景
- 需要展示知識來源的場景
6.2 BasicDocQA類適用場景
- 特定文檔問答
- 需要定位文檔中具體細節的場景
- 專注于文檔內容理解和組織的場景
- 例如:“介紹表1”、"第三章說了什么"等具體位置問題
7. 總結
Qwen-Agent框架中的Assistant
類和BasicDocQA
類展示了框架在文件相關操作中的靈活性和強大能力。Assistant
類作為通用助手,集成了RAG能力和函數調用能力,可以處理各種復雜任務;而BasicDocQA
類專注于文檔問答,適用于需要定位具體細節的問題。
兩個類都通過添加系統提示來引導模型回答,但使用了不同的提示模板和處理流程。這種設計使得框架可以根據不同場景靈活配置,提供最適合的回答。
在實際應用中,開發者可以根據具體需求選擇合適的類,或者基于這些類進行擴展,構建更加專業和高效的智能助手系統。