feat(Agent): 新增维基百科搜索和温度查询工具并实现web界面
- 添加search_wikipedia和get_current_temperature工具函数 - 实现基于Streamlit的web交互界面 - 更新requirements.txt添加相关依赖 - 修复PROMPT_TEMPLATE变量名拼写错误 - 移除不再使用的工具函数 - 添加web界面截图到文档
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -1,4 +1,55 @@
|
||||
json
|
||||
openai
|
||||
datetime
|
||||
pprint
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
62
docs/chapter7/Agent/web_demo.py
Normal file
62
docs/chapter7/Agent/web_demo.py
Normal file
@@ -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})
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
14
docs/chapter7/RAG/requirements.txt
Normal file
14
docs/chapter7/RAG/requirements.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
openai
|
||||
zhipuai
|
||||
numpy
|
||||
python-dotenv
|
||||
torch
|
||||
torchvision
|
||||
torchaudio
|
||||
transformers
|
||||
tqdm
|
||||
PyPDF2
|
||||
markdown
|
||||
html2text
|
||||
tiktoken
|
||||
beautifulsoup4
|
||||
@@ -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 会自动处理。
|
||||
|
||||
<div align='center'>
|
||||
<img src="https://raw.githubusercontent.com/datawhalechina/happy-llm/main/docs/images/7-images/7-3-streamlit-demo.png" alt="alt text" width="80%">
|
||||
<p>图7.10 Agent 工作流程</p>
|
||||
</div>
|
||||
|
||||
**示例交互:**
|
||||
|
||||
```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
|
||||
|
||||
BIN
docs/images/7-images/7-3-streamlit-demo.png
Normal file
BIN
docs/images/7-images/7-3-streamlit-demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
Reference in New Issue
Block a user