diff --git a/docs/chapter7/Agent/demo.py b/docs/chapter7/Agent/demo.py index efb3c94..92f7355 100644 --- a/docs/chapter7/Agent/demo.py +++ b/docs/chapter7/Agent/demo.py @@ -1,5 +1,5 @@ from src.core import Agent -from src.tools import add, count_letter_in_string, compare, get_current_datetime +from src.tools import add, count_letter_in_string, compare, get_current_datetime, search_wikipedia, get_current_temperature from openai import OpenAI @@ -13,7 +13,7 @@ if __name__ == "__main__": agent = Agent( client=client, model="Qwen/Qwen2.5-32B-Instruct", - tools=[get_current_datetime, add, compare, count_letter_in_string], + tools=[get_current_datetime, search_wikipedia, get_current_temperature], ) while True: diff --git a/docs/chapter7/Agent/requirements.txt b/docs/chapter7/Agent/requirements.txt index cf56b10..067ceb1 100644 --- a/docs/chapter7/Agent/requirements.txt +++ b/docs/chapter7/Agent/requirements.txt @@ -1,4 +1,55 @@ -json -openai -datetime -pprint \ No newline at end of file +altair==5.5.0 +annotated-types==0.7.0 +anyio==4.9.0 +attrs==25.3.0 +beautifulsoup4==4.13.4 +blinker==1.9.0 +cachetools==6.1.0 +certifi==2025.6.15 +charset-normalizer==3.4.2 +click==8.2.1 +datetime==5.5 +distro==1.9.0 +gitdb==4.0.12 +gitpython==3.1.44 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +jinja2==3.1.6 +jiter==0.10.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +markupsafe==3.0.2 +narwhals==1.43.1 +numpy==2.3.0 +openai==1.88.0 +packaging==25.0 +pandas==2.3.0 +pillow==11.2.1 +protobuf==6.31.1 +pyarrow==20.0.0 +pydantic==2.11.7 +pydantic-core==2.33.2 +pydeck==0.9.1 +python-dateutil==2.9.0.post0 +pytz==2025.2 +referencing==0.36.2 +requests==2.32.4 +rpds-py==0.25.1 +setuptools==80.9.0 +six==1.17.0 +smmap==5.0.2 +sniffio==1.3.1 +soupsieve==2.7 +streamlit==1.46.0 +tenacity==9.1.2 +toml==0.10.2 +tornado==6.5.1 +tqdm==4.67.1 +typing-extensions==4.14.0 +typing-inspection==0.4.1 +tzdata==2025.2 +urllib3==2.5.0 +wikipedia==1.4.0 +zope-interface==7.2 diff --git a/docs/chapter7/Agent/src/core.py b/docs/chapter7/Agent/src/core.py index 44bbbe3..b0a6815 100644 --- a/docs/chapter7/Agent/src/core.py +++ b/docs/chapter7/Agent/src/core.py @@ -2,11 +2,11 @@ from openai import OpenAI import json from typing import List, Dict, Any from src.utils import function_to_json -from src.tools import get_current_datetime, add, compare, count_letter_in_string +from src.tools import get_current_datetime, add, compare, count_letter_in_string, search_wikipedia, get_current_temperature import pprint -SYSREM_PROMPT = """ +SYSTEM_PROMPT = """ 你是一个叫不要葱姜蒜的人工智能助手。你的输出应该与用户的语言保持一致。 当用户的问题需要调用工具时,你可以从提供的工具列表中调用适当的工具函数。 """ @@ -17,7 +17,7 @@ class Agent: self.tools = tools self.model = model self.messages = [ - {"role": "system", "content": SYSREM_PROMPT}, + {"role": "system", "content": SYSTEM_PROMPT}, ] self.verbose = verbose diff --git a/docs/chapter7/Agent/src/tools.py b/docs/chapter7/Agent/src/tools.py index d0734c1..a9d98d3 100644 --- a/docs/chapter7/Agent/src/tools.py +++ b/docs/chapter7/Agent/src/tools.py @@ -1,12 +1,14 @@ -from datetime import datetime +import datetime +import wikipedia +import requests # 获取当前日期和时间 def get_current_datetime() -> str: """ - 获取当前日期和时间。 + 获取真实的当前日期和时间。 :return: 当前日期和时间的字符串表示。 """ - current_datetime = datetime.now() + current_datetime = datetime.datetime.now() formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S") return formatted_datetime @@ -54,3 +56,76 @@ def count_letter_in_string(a: str, b: str): count = string.count(letter) return(f"The letter '{letter}' appears {count} times in the string.") + +def search_wikipedia(query: str) -> str: + """ + 在维基百科中搜索指定查询的前三个页面摘要。 + :param query: 要搜索的查询字符串。 + :return: 包含前三个页面摘要的字符串。 + """ + page_titles = wikipedia.search(query) + summaries = [] + for page_title in page_titles[: 3]: # 取前三个页面标题 + try: + # 使用 wikipedia 模块的 page 函数,获取指定标题的维基百科页面对象。 + wiki_page = wikipedia.page(title=page_title, auto_suggest=False) + # 获取页面摘要 + summaries.append(f"页面: {page_title}\n摘要: {wiki_page.summary}") + except ( + wikipedia.exceptions.PageError, + wikipedia.exceptions.DisambiguationError, + ): + pass + if not summaries: + return "维基百科没有搜索到合适的结果" + return "\n\n".join(summaries) + + +def get_current_temperature(latitude: float, longitude: float) -> str: + """ + 获取指定经纬度位置的当前温度。 + :param latitude: 纬度坐标。 + :param longitude: 经度坐标。 + :return: 当前温度的字符串表示。 + """ + + # Open Meteo API 的URL + open_meteo_url = "https://api.open-meteo.com/v1/forecast" + + # 请求参数 + params = { + 'latitude': latitude, + 'longitude': longitude, + 'hourly': 'temperature_2m', + 'forecast_days': 1, + } + + # 发送 API 请求 + response = requests.get(open_meteo_url, params=params) + + # 检查响应状态码 + if response.status_code == 200: + # 解析 JSON 响应 + results = response.json() + else: + # 处理请求失败的情况 + raise Exception(f"API Request failed with status code: {response.status_code}") + + # 获取当前 UTC 时间 + current_utc_time = datetime.datetime.now(datetime.UTC) + + # 将时间字符串转换为 datetime 对象 + time_list = [datetime.datetime.fromisoformat(time_str).replace(tzinfo=datetime.timezone.utc) for time_str in + results['hourly']['time']] + + # 获取温度列表 + temperature_list = results['hourly']['temperature_2m'] + + # 找到最接近当前时间的索引 + closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time)) + + # 获取当前温度 + current_temperature = temperature_list[closest_time_index] + + # 返回当前温度的字符串形式 + return f'现在温度是 {current_temperature}°C' diff --git a/docs/chapter7/Agent/web_demo.py b/docs/chapter7/Agent/web_demo.py new file mode 100644 index 0000000..ced1be7 --- /dev/null +++ b/docs/chapter7/Agent/web_demo.py @@ -0,0 +1,62 @@ +import streamlit as st +from src.core import Agent +from src.tools import add, count_letter_in_string, compare, get_current_datetime, search_wikipedia, get_current_temperature +from openai import OpenAI + +# --- 页面配置 --- +st.set_page_config( + page_title="Tiny Agent Demo", # 页面标题 + page_icon="🤖", # 页面图标 + layout="centered", # 页面布局 + initial_sidebar_state="auto", # 侧边栏初始状态 +) + +# --- OpenAI客户端初始化 --- +client = OpenAI( + api_key="sk-quovvfgjdmmrvwiljusggiwvxfiekzicwjgtdvpfqhpmbpqu", + base_url="https://api.siliconflow.cn/v1", +) + +# --- Agent初始化 --- +@st.cache_resource +def load_agent(): + """创建并缓存Agent实例。""" + return Agent( + client=client, + model="Qwen/Qwen2.5-32B-Instruct", # 使用的模型 + tools=[get_current_datetime, search_wikipedia, get_current_temperature], # Agent可以使用的工具 + ) + +agent = load_agent() # 加载Agent + +# --- UI组件 --- +st.title("🤖 Happy-LLM Tiny Agent") # 设置页面标题 +st.markdown("""欢迎来到 Tiny Agent web 界面! + +在下方输入您的提示,查看 Agent 的实际操作。 +""") # 显示Markdown格式的欢迎信息 + +# 初始化聊天记录 +if "messages" not in st.session_state: + st.session_state.messages = [] + +# 在应用重新运行时显示历史聊天记录 +for message in st.session_state.messages: + with st.chat_message(message["role"]): + st.markdown(message["content"]) + +# 响应用户输入 +if prompt := st.chat_input("我能为您做些什么?"): + # 在聊天消息容器中显示用户消息 + st.chat_message("user").markdown(prompt) + # 将用户消息添加到聊天记录中 + st.session_state.messages.append({"role": "user", "content": prompt}) + + with st.spinner('思考中...'): + response = agent.get_completion(prompt) # 获取Agent的响应 + + # 在聊天消息容器中显示助手响应 + with st.chat_message("assistant"): + st.markdown(response) + # 将助手响应添加到聊天记录中 + st.session_state.messages.append({"role": "assistant", "content": response}) \ No newline at end of file diff --git a/docs/chapter7/RAG/Embeddings.py b/docs/chapter7/RAG/Embeddings.py index 4a1f277..ededb35 100644 --- a/docs/chapter7/RAG/Embeddings.py +++ b/docs/chapter7/RAG/Embeddings.py @@ -13,7 +13,6 @@ from copy import copy from typing import Dict, List, Optional, Tuple, Union import numpy as np -os.environ['CURL_CA_BUNDLE'] = '' from dotenv import load_dotenv, find_dotenv _ = load_dotenv(find_dotenv()) diff --git a/docs/chapter7/RAG/LLM.py b/docs/chapter7/RAG/LLM.py index ceda6f9..43b3ead 100644 --- a/docs/chapter7/RAG/LLM.py +++ b/docs/chapter7/RAG/LLM.py @@ -11,7 +11,7 @@ import os from typing import Dict, List, Optional, Tuple, Union PROMPT_TEMPLATE = dict( - RAG_PROMPT_TEMPALTE="""使用以上下文来回答用户的问题。如果你不知道答案,就说你不知道。总是使用中文回答。 + RAG_PROMPT_TEMPLATE="""使用以上下文来回答用户的问题。如果你不知道答案,就说你不知道。总是使用中文回答。 问题: {question} 可参考的上下文: ··· @@ -19,7 +19,7 @@ PROMPT_TEMPLATE = dict( ··· 如果给定的上下文无法让你做出回答,请回答数据库中没有这个内容,你不知道。 有用的回答:""", - InternLM_PROMPT_TEMPALTE="""先对上下文进行内容总结,再使用上下文来回答用户的问题。如果你不知道答案,就说你不知道。总是使用中文回答。 + InternLM_PROMPT_TEMPLATE="""先对上下文进行内容总结,再使用上下文来回答用户的问题。如果你不知道答案,就说你不知道。总是使用中文回答。 问题: {question} 可参考的上下文: ··· @@ -65,7 +65,7 @@ class InternLMChat(BaseModel): self.load_model() def chat(self, prompt: str, history: List = [], content: str='') -> str: - prompt = PROMPT_TEMPLATE['InternLM_PROMPT_TEMPALTE'].format(question=prompt, context=content) + prompt = PROMPT_TEMPLATE['InternLM_PROMPT_TEMPLATE'].format(question=prompt, context=content) response, history = self.model.chat(self.tokenizer, prompt, history) return response diff --git a/docs/chapter7/RAG/requirements.txt b/docs/chapter7/RAG/requirements.txt new file mode 100644 index 0000000..db3dd3e --- /dev/null +++ b/docs/chapter7/RAG/requirements.txt @@ -0,0 +1,14 @@ +openai +zhipuai +numpy +python-dotenv +torch +torchvision +torchaudio +transformers +tqdm +PyPDF2 +markdown +html2text +tiktoken +beautifulsoup4 \ No newline at end of file diff --git a/docs/chapter7/第七章 大模型应用.md b/docs/chapter7/第七章 大模型应用.md index 4280780..db45cc6 100644 --- a/docs/chapter7/第七章 大模型应用.md +++ b/docs/chapter7/第七章 大模型应用.md @@ -17,7 +17,6 @@ 2. **工具使用评测集**: - **BFCL V2**:用于评测模型在复杂工具使用任务中的表现,特别是在执行多步骤操作时的正确性和效率。这些任务通常涉及与数据库交互或执行特定指令,以模拟实际工具使用场景。 - - **Nexus**:用于测试模型在多步骤操作中的工具使用能力,主要评估其在多任务操作中的协调性和任务管理能力,如进行文件操作、数据整合等复杂流程。 3. **数学评测集**: - **GSM8K**:GSM8K是一个包含小学数学问题的数据集,用于测试模型的数学推理和逻辑分析能力。具体任务包括算术运算、简单方程求解、数字推理等。GSM8K中的问题虽然看似简单,但模型需要理解问题语义并进行正确的数学运算,体现了逻辑推理和语言理解的双重挑战。 @@ -481,29 +480,6 @@ def get_current_datetime() -> str: formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S") return formatted_datetime -def add(a: float, b: float): - """ - 计算两个浮点数的和。 - :param a: 第一个浮点数。 - :param b: 第二个浮点数。 - :return: 两个浮点数的和。 - """ - return str(a + b) - -def compare(a: float, b: float): - """ - 比较两个浮点数的大小。 - :param a: 第一个浮点数。 - :param b: 第二个浮点数。 - :return: 比较结果的字符串表示。 - """ - if a > b: - return f'{a} is greater than {b}' - elif a < b: - return f'{b} is greater than {a}' - else: - return f'{a} is equal to {b}' - def count_letter_in_string(a: str, b: str): """ 统计字符串中某个字母的出现次数。 @@ -513,6 +489,28 @@ def count_letter_in_string(a: str, b: str): """ return str(a.count(b)) +def search_wikipedia(query: str) -> str: + """ + 在维基百科中搜索指定查询的前三个页面摘要。 + :param query: 要搜索的查询字符串。 + :return: 包含前三个页面摘要的字符串。 + """ + page_titles = wikipedia.search(query) + summaries = [] + for page_title in page_titles[: 3]: # 取前三个页面标题 + try: + # 使用 wikipedia 模块的 page 函数,获取指定标题的维基百科页面对象。 + wiki_page = wikipedia.page(title=page_title, auto_suggest=False) + # 获取页面摘要 + summaries.append(f"页面: {page_title}\n摘要: {wiki_page.summary}") + except ( + wikipedia.exceptions.PageError, + wikipedia.exceptions.DisambiguationError, + ): + pass + if not summaries: + return "维基百科没有搜索到合适的结果" + return "\n\n".join(summaries) # ... (可能还有其他工具函数) ``` @@ -552,7 +550,7 @@ from utils import function_to_json # 导入定义好的工具函数 from tools import get_current_datetime, add, compare, count_letter_in_string -SYSREM_PROMPT = """ +SYSTEM_PROMPT = """ 你是一个叫不要葱姜蒜的人工智能助手。你的输出应该与用户的语言保持一致。 当用户的问题需要调用工具时,你可以从提供的工具列表中调用适当的工具函数。 """ @@ -669,6 +667,11 @@ if __name__ == "__main__": 运行 `python demo.py` 后,你可以开始提问。如果问题需要调用工具,Agent 会自动处理。 +
+ alt text +

图7.10 Agent 工作流程

+
+ **示例交互:** ```bash @@ -691,6 +694,10 @@ Assistant: 当前的时间是2025年4月26日17:01:33。不过,我注意到 User: exit ``` +另外,我们也准备了一份可以展示的 Streamlit 应用,可以运行在本地,展示 Agent 的功能。`streamlit run web_demo.py` 来运行,以下为 Agent 运行效果。 + + + **参考文献** [1] Hugging Face. (2023). *Open LLM Leaderboard: 开源大语言模型基准测试平台*. https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard diff --git a/docs/images/7-images/7-3-streamlit-demo.png b/docs/images/7-images/7-3-streamlit-demo.png new file mode 100644 index 0000000..af33f26 Binary files /dev/null and b/docs/images/7-images/7-3-streamlit-demo.png differ