mirror of
https://github.com/RYDE-WORK/Langchain-Chatchat.git
synced 2026-01-19 13:23:16 +08:00
Agent大更新合并 (#1666)
* 更新上agent提示词代码 * 更新部分文档,修复了issue中提到的bge匹配超过1 的bug * 按需修改 * 解决了部分最新用户用依赖的bug,加了两个工具,移除google工具 * Agent大幅度优化 1. 修改了UI界面 (1)高亮所有没有进行agent对齐的模型, (2)优化输出体验和逻辑,使用markdown 2. 降低天气工具使用门槛 3. 依赖更新 (1) vllm 更新到0.2.0,增加了一些参数 (2) torch 建议更新到2.1 (3)pydantic不要更新到1.10.12
This commit is contained in:
parent
387b4cb967
commit
2c8fc95f7a
@ -2,8 +2,8 @@ langchain>=0.0.302
|
||||
fschat[model_worker]==0.2.30
|
||||
openai
|
||||
sentence_transformers
|
||||
transformers==4.33.3
|
||||
torch>=2.0.1
|
||||
transformers>=4.34
|
||||
torch>=2.0.1 # 推荐2.1
|
||||
torchvision
|
||||
torchaudio
|
||||
fastapi>=0.103.2
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
langchain>=0.0.302
|
||||
fschat[model_worker]==0.2.30
|
||||
fschat[model_worker]>=0.2.30
|
||||
openai
|
||||
sentence_transformers
|
||||
transformers>=4.33.3
|
||||
transformers>=4.34
|
||||
torch>=2.0.1
|
||||
torchvision
|
||||
torchaudio
|
||||
|
||||
@ -20,7 +20,7 @@ class Status:
|
||||
agent_action: int = 4
|
||||
agent_finish: int = 5
|
||||
error: int = 6
|
||||
make_tool: int = 7
|
||||
tool_finish: int = 7
|
||||
|
||||
|
||||
class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
@ -29,11 +29,19 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
self.queue = asyncio.Queue()
|
||||
self.done = asyncio.Event()
|
||||
self.cur_tool = {}
|
||||
self.out = True
|
||||
|
||||
async def on_tool_start(self, serialized: Dict[str, Any], input_str: str, *, run_id: UUID,
|
||||
parent_run_id: UUID | None = None, tags: List[str] | None = None,
|
||||
metadata: Dict[str, Any] | None = None, **kwargs: Any) -> None:
|
||||
|
||||
# 对于截断不能自理的大模型,我来帮他截断
|
||||
stop_words = ["Observation:", "Thought","\"","(", "\n","\t"]
|
||||
for stop_word in stop_words:
|
||||
index = input_str.find(stop_word)
|
||||
if index != -1:
|
||||
input_str = input_str[:index]
|
||||
break
|
||||
|
||||
self.cur_tool = {
|
||||
"tool_name": serialized["name"],
|
||||
"input_str": input_str,
|
||||
@ -44,13 +52,13 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
"final_answer": "",
|
||||
"error": "",
|
||||
}
|
||||
# print("\nInput Str:",self.cur_tool["input_str"])
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
|
||||
async def on_tool_end(self, output: str, *, run_id: UUID, parent_run_id: UUID | None = None,
|
||||
tags: List[str] | None = None, **kwargs: Any) -> None:
|
||||
self.out = True
|
||||
self.cur_tool.update(
|
||||
status=Status.agent_finish,
|
||||
status=Status.tool_finish,
|
||||
output_str=output.replace("Answer:", ""),
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
@ -65,19 +73,11 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
|
||||
async def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||
if token:
|
||||
if "Action" in token:
|
||||
self.out = False
|
||||
self.cur_tool.update(
|
||||
status=Status.running,
|
||||
llm_token="\n\n",
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
if self.out:
|
||||
self.cur_tool.update(
|
||||
self.cur_tool.update(
|
||||
status=Status.running,
|
||||
llm_token=token,
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
|
||||
async def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None:
|
||||
self.cur_tool.update(
|
||||
@ -87,15 +87,13 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
|
||||
async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
||||
self.out = True
|
||||
self.cur_tool.update(
|
||||
status=Status.complete,
|
||||
llm_token="",
|
||||
llm_token="\n",
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
|
||||
async def on_llm_error(self, error: Exception | KeyboardInterrupt, **kwargs: Any) -> None:
|
||||
self.out = True
|
||||
self.cur_tool.update(
|
||||
status=Status.error,
|
||||
error=str(error),
|
||||
@ -107,4 +105,10 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler):
|
||||
tags: Optional[List[str]] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
# 返回最终答案
|
||||
self.cur_tool.update(
|
||||
status=Status.agent_finish,
|
||||
final_answer=finish.return_values["output"],
|
||||
)
|
||||
self.queue.put_nowait(dumps(self.cur_tool))
|
||||
self.cur_tool = {}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
from __future__ import annotations
|
||||
from langchain.agents import Tool, AgentOutputParser
|
||||
from langchain.prompts import StringPromptTemplate
|
||||
from typing import List, Union
|
||||
from typing import List, Union, Tuple, Dict
|
||||
from langchain.schema import AgentAction, AgentFinish
|
||||
import re
|
||||
from configs.model_config import LLM_MODEL, TEMPERATURE, HISTORY_LEN
|
||||
|
||||
begin = False
|
||||
class CustomPromptTemplate(StringPromptTemplate):
|
||||
# The template to use
|
||||
template: str
|
||||
@ -19,40 +21,78 @@ class CustomPromptTemplate(StringPromptTemplate):
|
||||
for action, observation in intermediate_steps:
|
||||
thoughts += action.log
|
||||
thoughts += f"\nObservation: {observation}\nThought: "
|
||||
# Set the agent_scratchpad variable to that value
|
||||
# Set the agent_scratchpad variable to that value
|
||||
kwargs["agent_scratchpad"] = thoughts
|
||||
# Create a tools variable from the list of tools provided
|
||||
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
|
||||
# Create a list of tool names for the tools provided
|
||||
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
|
||||
# Return the formatted templatepr
|
||||
# print( self.template.format(**kwargs), end="\n\n")
|
||||
return self.template.format(**kwargs)
|
||||
class CustomOutputParser(AgentOutputParser):
|
||||
|
||||
def parse(self, llm_output: str) -> AgentFinish | AgentAction | str:
|
||||
|
||||
class CustomOutputParser(AgentOutputParser):
|
||||
begin: bool = False
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.begin = True
|
||||
|
||||
def parse(self, llm_output: str) -> AgentFinish | tuple[dict[str, str], str] | AgentAction:
|
||||
# Check if agent should finish
|
||||
support_agent = ["gpt","Qwen","qwen-api","baichuan-api"]
|
||||
if not any(agent in LLM_MODEL for agent in support_agent) and self.begin:
|
||||
self.begin = False
|
||||
stop_words = ["Observation:"]
|
||||
min_index = len(llm_output)
|
||||
for stop_word in stop_words:
|
||||
index = llm_output.find(stop_word)
|
||||
if index != -1 and index < min_index:
|
||||
min_index = index
|
||||
llm_output = llm_output[:min_index]
|
||||
|
||||
if "Final Answer:" in llm_output:
|
||||
output = llm_output.split("Final Answer:", 1)[-1].strip()
|
||||
self.begin = True
|
||||
return AgentFinish(
|
||||
# Return values is generally always a dictionary with a single `output` key
|
||||
# It is not recommended to try anything else at the moment :)
|
||||
return_values={"output": llm_output.replace("Final Answer:", "").strip()},
|
||||
# return_values={"output": llm_output.replace("Final Answer:", "").strip()},
|
||||
return_values={"output": output},
|
||||
log=llm_output,
|
||||
)
|
||||
|
||||
# Parse out the action and action input
|
||||
regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
||||
match = re.search(regex, llm_output, re.DOTALL)
|
||||
if not match:
|
||||
parts = llm_output.split("Action:")
|
||||
if len(parts) < 2:
|
||||
return AgentFinish(
|
||||
return_values={"output": f"调用agent失败: `{llm_output}`"},
|
||||
log=llm_output,
|
||||
)
|
||||
action = match.group(1).strip()
|
||||
action_input = match.group(2)
|
||||
|
||||
action = parts[1].split("Action Input:")[0].strip()
|
||||
action_input = parts[1].split("Action Input:")[1].strip()
|
||||
|
||||
# 原来的正则化检查方式
|
||||
# regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
||||
# print("llm_output",llm_output)
|
||||
# match = re.search(regex, llm_output, re.DOTALL)
|
||||
# print("match",match)
|
||||
# if not match:
|
||||
# return AgentFinish(
|
||||
# return_values={"output": f"调用agent失败: `{llm_output}`"},
|
||||
# log=llm_output,
|
||||
# )
|
||||
# action = match.group(1).strip()
|
||||
# action_input = match.group(2)
|
||||
|
||||
# Return the action and action input
|
||||
|
||||
try:
|
||||
ans = AgentAction(
|
||||
tool=action,
|
||||
tool_input=action_input.strip(" ").strip('"'),
|
||||
log=llm_output
|
||||
tool=action,
|
||||
tool_input=action_input.strip(" ").strip('"'),
|
||||
log=llm_output
|
||||
)
|
||||
return ans
|
||||
except:
|
||||
@ -60,6 +100,3 @@ class CustomOutputParser(AgentOutputParser):
|
||||
return_values={"output": f"调用agent失败: `{llm_output}`"},
|
||||
log=llm_output,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
## 单独运行的时候需要添加
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.chains import LLMMathChain
|
||||
from server.utils import wrap_done, get_ChatOpenAI
|
||||
from server.utils import get_ChatOpenAI
|
||||
from configs.model_config import LLM_MODEL, TEMPERATURE
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.callbacks.manager import CallbackManagerForToolRun
|
||||
|
||||
_PROMPT_TEMPLATE = """将数学问题翻译成可以使用Python的numexpr库执行的表达式。使用运行此代码的输出来回答问题。
|
||||
_PROMPT_TEMPLATE = """
|
||||
将数学问题翻译成可以使用Python的numexpr库执行的表达式。使用运行此代码的输出来回答问题。
|
||||
问题: ${{包含数学问题的问题。}}
|
||||
```text
|
||||
${{解决问题的单行数学表达式}}
|
||||
@ -68,3 +71,8 @@ def calculate(query: str):
|
||||
llm_math = LLMMathChain.from_llm(model, verbose=True, prompt=PROMPT)
|
||||
ans = llm_math.run(query)
|
||||
return ans
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = calculate("2的三次方")
|
||||
print("答案:",result)
|
||||
|
||||
|
||||
@ -20,12 +20,12 @@ tools = [
|
||||
Tool.from_function(
|
||||
func=translate,
|
||||
name="翻译工具",
|
||||
description="翻译各种语言"
|
||||
description="如果你无法访问互联网,并且需要翻译各种语言,应该使用这个工具"
|
||||
),
|
||||
Tool.from_function(
|
||||
func=weathercheck,
|
||||
name="天气查询工具",
|
||||
description="查询天气",
|
||||
description="如果你无法访问互联网,并需要查询中国各地未来24小时的天气,你应该使用这个工具,每轮对话仅能使用一次",
|
||||
),
|
||||
Tool.from_function(
|
||||
func=shell,
|
||||
@ -35,12 +35,12 @@ tools = [
|
||||
Tool.from_function(
|
||||
func=search_knowledge,
|
||||
name="知识库查询工具",
|
||||
description="使用西交利物浦大学大数据专业的本专业数据库来解答问题",
|
||||
description="访问知识库来获取答案",
|
||||
),
|
||||
Tool.from_function(
|
||||
func=search_internet,
|
||||
name="互联网查询工具",
|
||||
description="访问Bing互联网来解答问题",
|
||||
description="如果你无法访问互联网,这个工具可以帮助你访问Bing互联网来解答问题",
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.chains import LLMChain
|
||||
## 单独运行的时候需要添加
|
||||
import sys
|
||||
import os
|
||||
|
||||
from server.utils import get_ChatOpenAI
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.chains import LLMChain
|
||||
from server.utils import get_ChatOpenAI
|
||||
from langchain.chains.llm_math.prompt import PROMPT
|
||||
from configs.model_config import LLM_MODEL,TEMPERATURE
|
||||
|
||||
@ -16,25 +15,12 @@ _PROMPT_TEMPLATE = '''
|
||||
2. 无论提供的是陈述句或疑问句,只进行翻译
|
||||
3. 不添加与原文无关的内容
|
||||
|
||||
原文: ${{用户需要翻译的原文和目标语言}}
|
||||
{question}
|
||||
```output
|
||||
${{翻译结果}}
|
||||
```
|
||||
答案: ${{答案}}
|
||||
问题: ${{用户需要翻译的原文和目标语言}}
|
||||
答案: 你翻译结果
|
||||
|
||||
现在,这是我的问题:
|
||||
问题: {question}
|
||||
|
||||
以下是两个例子
|
||||
问题: 翻译13成英语
|
||||
```text
|
||||
13 英语
|
||||
```output
|
||||
thirteen
|
||||
以下是两个例子
|
||||
问题: 翻译 我爱你 成法语
|
||||
```text
|
||||
13 法语
|
||||
```output
|
||||
Je t'aime.
|
||||
'''
|
||||
|
||||
PROMPT = PromptTemplate(
|
||||
@ -51,5 +37,8 @@ def translate(query: str):
|
||||
)
|
||||
llm_translate = LLMChain(llm=model, prompt=PROMPT)
|
||||
ans = llm_translate.run(query)
|
||||
return ans
|
||||
|
||||
return ans
|
||||
if __name__ == "__main__":
|
||||
result = translate("Can Love remember the question and the answer? 这句话如何诗意的翻译成中文")
|
||||
print("答案:",result)
|
||||
@ -1,10 +1,12 @@
|
||||
## 使用和风天气API查询天气
|
||||
## 使用和风天气API查询天气,这个模型仅仅对免费的API进行了适配
|
||||
## 这个模型的提示词非常复杂,我们推荐使用GPT4模型进行运行
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
## 单独运行的时候需要添加
|
||||
import sys
|
||||
import os
|
||||
# sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
|
||||
from server.utils import get_ChatOpenAI
|
||||
@ -26,9 +28,71 @@ from langchain.schema.language_model import BaseLanguageModel
|
||||
import requests
|
||||
from typing import List, Any, Optional
|
||||
from configs.model_config import LLM_MODEL, TEMPERATURE
|
||||
from datetime import datetime
|
||||
from langchain.prompts import PromptTemplate
|
||||
|
||||
|
||||
## 使用和风天气API查询天气
|
||||
KEY = ""
|
||||
KEY = "ac880e5a877042809ac7ffdd19d95b0d"
|
||||
|
||||
|
||||
|
||||
_PROMPT_TEMPLATE = """
|
||||
用户会提出一个关于天气的问题,你的目标是拆分出用户问题中的区,市 并按照我提供的工具回答。
|
||||
例如 用户提出的问题是: 上海浦东未来1小时天气情况?
|
||||
则 提取的市和区是: 上海 浦东
|
||||
如果用户提出的问题是: 上海未来1小时天气情况?
|
||||
则 提取的市和区是: 上海 None
|
||||
请注意以下内容:
|
||||
1. 如果你没有找到区的内容,则一定要使用 None 替代,否则程序无法运行
|
||||
2. 如果用户没有指定市 则直接返回缺少信息
|
||||
|
||||
问题: ${{用户的问题}}
|
||||
|
||||
你的回答格式应该按照下面的内容,请注意,格式内的```text 等标记都必须输出,这是我用来提取答案的标记。
|
||||
```text
|
||||
|
||||
${{拆分的市和区,中间用空格隔开}}
|
||||
```
|
||||
... weathercheck(市 区)...
|
||||
```output
|
||||
|
||||
${{提取后的答案}}
|
||||
```
|
||||
答案: ${{答案}}
|
||||
|
||||
|
||||
|
||||
这是一个例子:
|
||||
问题: 上海浦东未来1小时天气情况?
|
||||
|
||||
|
||||
```text
|
||||
上海 浦东
|
||||
```
|
||||
...weathercheck(上海 浦东)...
|
||||
|
||||
```output
|
||||
预报时间: 1小时后
|
||||
具体时间: 今天 18:00
|
||||
温度: 24°C
|
||||
天气: 多云
|
||||
风向: 西南风
|
||||
风速: 7级
|
||||
湿度: 88%
|
||||
降水概率: 16%
|
||||
|
||||
Answer: 上海浦东一小时后的天气是多云。
|
||||
|
||||
现在,这是我的问题:
|
||||
|
||||
问题: {question}
|
||||
"""
|
||||
PROMPT = PromptTemplate(
|
||||
input_variables=["question"],
|
||||
template=_PROMPT_TEMPLATE,
|
||||
)
|
||||
|
||||
|
||||
def get_city_info(location, adm, key):
|
||||
base_url = 'https://geoapi.qweather.com/v2/city/lookup?'
|
||||
@ -38,12 +102,9 @@ def get_city_info(location, adm, key):
|
||||
return data
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def format_weather_data(data):
|
||||
def format_weather_data(data,place):
|
||||
hourly_forecast = data['hourly']
|
||||
formatted_data = ''
|
||||
formatted_data = f"\n 这是查询到的关于{place}未来24小时的天气信息: \n"
|
||||
for forecast in hourly_forecast:
|
||||
# 将预报时间转换为datetime对象
|
||||
forecast_time = datetime.strptime(forecast['fxTime'], '%Y-%m-%dT%H:%M%z')
|
||||
@ -71,12 +132,11 @@ def format_weather_data(data):
|
||||
elif hours_diff >= 24:
|
||||
# 如果超过24小时,转换为天数
|
||||
days_diff = hours_diff // 24
|
||||
hours_diff_str = str(int(days_diff)) + '天后'
|
||||
hours_diff_str = str(int(days_diff)) + '天'
|
||||
else:
|
||||
hours_diff_str = str(int(hours_diff)) + '小时后'
|
||||
hours_diff_str = str(int(hours_diff)) + '小时'
|
||||
# 将预报时间和当前时间的差值添加到输出中
|
||||
formatted_data += '预报时间: ' + hours_diff_str + '\n'
|
||||
formatted_data += '具体时间: ' + forecast_time_str + '\n'
|
||||
formatted_data += '预报时间: ' + forecast_time_str + ' 距离现在有: ' + hours_diff_str + '\n'
|
||||
formatted_data += '温度: ' + forecast['temp'] + '°C\n'
|
||||
formatted_data += '天气: ' + forecast['text'] + '\n'
|
||||
formatted_data += '风向: ' + forecast['windDir'] + '\n'
|
||||
@ -84,53 +144,54 @@ def format_weather_data(data):
|
||||
formatted_data += '湿度: ' + forecast['humidity'] + '%\n'
|
||||
formatted_data += '降水概率: ' + forecast['pop'] + '%\n'
|
||||
# formatted_data += '降水量: ' + forecast['precip'] + 'mm\n'
|
||||
formatted_data += '\n\n'
|
||||
formatted_data += '\n'
|
||||
return formatted_data
|
||||
|
||||
|
||||
def get_weather(key, location_id, time: str = "24"):
|
||||
if time:
|
||||
url = "https://devapi.qweather.com/v7/weather/" + time + "h?"
|
||||
else:
|
||||
time = "3" # 免费订阅只能查看3天的天气
|
||||
url = "https://devapi.qweather.com/v7/weather/" + time + "d?"
|
||||
def get_weather(key, location_id,place):
|
||||
url = "https://devapi.qweather.com/v7/weather/24h?"
|
||||
params = {
|
||||
'location': location_id,
|
||||
'key': key,
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
data = response.json()
|
||||
return format_weather_data(data)
|
||||
return format_weather_data(data,place)
|
||||
|
||||
|
||||
def split_query(query):
|
||||
parts = query.split()
|
||||
location = parts[0] if parts[0] != 'None' else parts[1]
|
||||
adm = parts[1]
|
||||
time = parts[2]
|
||||
return location, adm, time
|
||||
adm = parts[0]
|
||||
location = parts[1] if parts[1] != 'None' else adm
|
||||
return location, adm
|
||||
|
||||
|
||||
def weather(query):
|
||||
location, adm, time = split_query(query)
|
||||
location, adm= split_query(query)
|
||||
key = KEY
|
||||
if time != "None" and int(time) > 24:
|
||||
return "只能查看24小时内的天气,无法回答"
|
||||
if time == "None":
|
||||
time = "24" # 免费的版本只能24小时内的天气
|
||||
if key == "":
|
||||
return "请先在代码中填入和风天气API Key"
|
||||
city_info = get_city_info(location=location, adm=adm, key=key)
|
||||
location_id = city_info['location'][0]['id']
|
||||
weather_data = get_weather(key=key, location_id=location_id, time=time)
|
||||
return weather_data
|
||||
|
||||
try:
|
||||
city_info = get_city_info(location=location, adm=adm, key=key)
|
||||
location_id = city_info['location'][0]['id']
|
||||
place = adm + "市" + location + "区"
|
||||
|
||||
weather_data = get_weather(key=key, location_id=location_id,place=place)
|
||||
return weather_data + "以上是查询到的天气信息,请你查收\n"
|
||||
except KeyError:
|
||||
try:
|
||||
city_info = get_city_info(location=adm, adm=adm, key=key)
|
||||
location_id = city_info['location'][0]['id']
|
||||
place = adm + "市"
|
||||
weather_data = get_weather(key=key, location_id=location_id,place=place)
|
||||
return weather_data + "重要提醒:用户提供的市和区中,区的信息不存在,或者出现错别字,因此该信息是关于市的天气,请你查收\n"
|
||||
except KeyError:
|
||||
return "输入的地区不存在,无法提供天气预报"
|
||||
class LLMWeatherChain(Chain):
|
||||
llm_chain: LLMChain
|
||||
llm: Optional[BaseLanguageModel] = None
|
||||
"""[Deprecated] LLM wrapper to use."""
|
||||
prompt: BasePromptTemplate
|
||||
prompt: BasePromptTemplate = PROMPT
|
||||
"""[Deprecated] Prompt to use to translate to python if necessary."""
|
||||
input_key: str = "question" #: :meta private:
|
||||
output_key: str = "answer" #: :meta private:
|
||||
@ -175,7 +236,8 @@ class LLMWeatherChain(Chain):
|
||||
output = weather(expression)
|
||||
except Exception as e:
|
||||
output = "输入的信息有误,请再次尝试"
|
||||
# raise ValueError(f"错误: {expression},输入的信息不对")
|
||||
return {self.output_key: output}
|
||||
raise ValueError(f"错误: {expression},输入的信息不对")
|
||||
|
||||
return output
|
||||
|
||||
@ -198,7 +260,8 @@ class LLMWeatherChain(Chain):
|
||||
elif "Answer:" in llm_output:
|
||||
answer = "Answer: " + llm_output.split("Answer:")[-1]
|
||||
else:
|
||||
raise ValueError(f"unknown format from LLM: {llm_output}")
|
||||
return {self.output_key: f"输入的格式不对: {llm_output},应该输入 (市 区)的组合"}
|
||||
# raise ValueError(f"unknown format from LLM: {llm_output}")
|
||||
return {self.output_key: answer}
|
||||
|
||||
async def _aprocess_llm_result(
|
||||
@ -259,92 +322,13 @@ class LLMWeatherChain(Chain):
|
||||
def from_llm(
|
||||
cls,
|
||||
llm: BaseLanguageModel,
|
||||
prompt: BasePromptTemplate,
|
||||
prompt: BasePromptTemplate = PROMPT,
|
||||
**kwargs: Any,
|
||||
) -> LLMWeatherChain:
|
||||
llm_chain = LLMChain(llm=llm, prompt=prompt)
|
||||
return cls(llm_chain=llm_chain, **kwargs)
|
||||
|
||||
|
||||
from langchain.prompts import PromptTemplate
|
||||
|
||||
_PROMPT_TEMPLATE = """用户将会向您咨询天气问题,您不需要自己回答天气问题,而是将用户提问的信息提取出来区,市和时间三个元素后使用我为你编写好的工具进行查询并返回结果,格式为 区+市+时间 每个元素用空格隔开。如果缺少信息,则用 None 代替。
|
||||
问题: ${{用户的问题}}
|
||||
|
||||
```text
|
||||
|
||||
${{拆分的区,市和时间}}
|
||||
```
|
||||
|
||||
... weather(提取后的关键字,用空格隔开)...
|
||||
```output
|
||||
|
||||
${{提取后的答案}}
|
||||
```
|
||||
答案: ${{答案}}
|
||||
|
||||
这是两个例子:
|
||||
问题: 上海浦东未来1小时天气情况?
|
||||
|
||||
```text
|
||||
浦东 上海 1
|
||||
```
|
||||
...weather(浦东 上海 1)...
|
||||
|
||||
```output
|
||||
|
||||
预报时间: 1小时后
|
||||
具体时间: 今天 18:00
|
||||
温度: 24°C
|
||||
天气: 多云
|
||||
风向: 西南风
|
||||
风速: 7级
|
||||
湿度: 88%
|
||||
降水概率: 16%
|
||||
|
||||
Answer:
|
||||
预报时间: 1小时后
|
||||
具体时间: 今天 18:00
|
||||
温度: 24°C
|
||||
天气: 多云
|
||||
风向: 西南风
|
||||
风速: 7级
|
||||
湿度: 88%
|
||||
降水概率: 16%
|
||||
|
||||
问题: 北京市朝阳区未来24小时天气如何?
|
||||
```text
|
||||
|
||||
朝阳 北京 24
|
||||
```
|
||||
...weather(朝阳 北京 24)...
|
||||
```output
|
||||
预报时间: 23小时后
|
||||
具体时间: 明天 17:00
|
||||
温度: 26°C
|
||||
天气: 霾
|
||||
风向: 西南风
|
||||
风速: 11级
|
||||
湿度: 65%
|
||||
降水概率: 20%
|
||||
Answer:
|
||||
预报时间: 23小时后
|
||||
具体时间: 明天 17:00
|
||||
温度: 26°C
|
||||
天气: 霾
|
||||
风向: 西南风
|
||||
风速: 11级
|
||||
湿度: 65%
|
||||
降水概率: 20%
|
||||
|
||||
现在,这是我的问题:
|
||||
问题: {question}
|
||||
"""
|
||||
PROMPT = PromptTemplate(
|
||||
input_variables=["question"],
|
||||
template=_PROMPT_TEMPLATE,
|
||||
)
|
||||
|
||||
|
||||
def weathercheck(query: str):
|
||||
model = get_ChatOpenAI(
|
||||
@ -357,9 +341,4 @@ def weathercheck(query: str):
|
||||
return ans
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
## 检测api是否能正确返回
|
||||
query = "上海浦东未来1小时天气情况"
|
||||
# ans = weathercheck(query)
|
||||
ans = weather("浦东 上海 1")
|
||||
print(ans)
|
||||
result = weathercheck("苏州工姑苏区今晚热不热?")
|
||||
@ -53,13 +53,11 @@ async def agent_chat(query: str = Body(..., description="用户输入", examples
|
||||
agent = LLMSingleActionAgent(
|
||||
llm_chain=llm_chain,
|
||||
output_parser=output_parser,
|
||||
stop=["Observation:", "Observation:\n", "<|im_end|>"], # Qwen模型中使用这个
|
||||
# stop=["Observation:", "Observation:\n"], # 其他模型,注意模板
|
||||
stop=["\nObservation:", "Observation:", "<|im_end|>"], # Qwen模型中使用这个
|
||||
allowed_tools=tool_names,
|
||||
)
|
||||
# 把history转成agent的memory
|
||||
memory = ConversationBufferWindowMemory(k=HISTORY_LEN * 2)
|
||||
|
||||
for message in history:
|
||||
# 检查消息的角色
|
||||
if message.role == 'user':
|
||||
@ -74,29 +72,41 @@ async def agent_chat(query: str = Body(..., description="用户输入", examples
|
||||
memory=memory,
|
||||
)
|
||||
input_msg = History(role="user", content="{{ input }}").to_msg_template(False)
|
||||
task = asyncio.create_task(wrap_done(
|
||||
agent_executor.acall(query, callbacks=[callback], include_run_info=True),
|
||||
callback.done),
|
||||
)
|
||||
while True:
|
||||
try:
|
||||
task = asyncio.create_task(wrap_done(
|
||||
agent_executor.acall(query, callbacks=[callback], include_run_info=True),
|
||||
callback.done))
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if stream:
|
||||
async for chunk in callback.aiter():
|
||||
tools_use = []
|
||||
# Use server-sent-events to stream the response
|
||||
data = json.loads(chunk)
|
||||
if data["status"] == Status.error:
|
||||
tools_use.append("工具调用失败:\n" + data["error"])
|
||||
yield json.dumps({"tools": tools_use}, ensure_ascii=False)
|
||||
yield json.dumps({"answer": "(工具调用失败,请查看工具栏报错) \n\n"}, ensure_ascii=False)
|
||||
if data["status"] == Status.start or data["status"] == Status.complete:
|
||||
continue
|
||||
if data["status"] == Status.agent_action:
|
||||
yield json.dumps({"answer": "(正在使用工具,请注意工具栏变化) \n\n"}, ensure_ascii=False)
|
||||
if data["status"] == Status.agent_finish:
|
||||
if data["status"] == Status.error:
|
||||
tools_use.append("工具名称: " + data["tool_name"])
|
||||
tools_use.append("工具状态: " + "调用失败")
|
||||
tools_use.append("错误信息: " + data["error"])
|
||||
tools_use.append("重新开始尝试")
|
||||
tools_use.append("\n```\n")
|
||||
yield json.dumps({"tools": tools_use}, ensure_ascii=False)
|
||||
if data["status"] == Status.agent_action:
|
||||
yield json.dumps({"answer": "\n\n```\n\n"}, ensure_ascii=False)
|
||||
if data["status"] == Status.tool_finish:
|
||||
tools_use.append("工具名称: " + data["tool_name"])
|
||||
tools_use.append("工具状态: " + "调用成功")
|
||||
tools_use.append("工具输入: " + data["input_str"])
|
||||
tools_use.append("工具输出: " + data["output_str"])
|
||||
tools_use.append("\n```\n")
|
||||
yield json.dumps({"tools": tools_use}, ensure_ascii=False)
|
||||
yield json.dumps({"answer": data["llm_token"]}, ensure_ascii=False)
|
||||
if data["status"] == Status.agent_finish:
|
||||
yield json.dumps({"final_answer": data["final_answer"]}, ensure_ascii=False)
|
||||
else:
|
||||
yield json.dumps({"answer": data["llm_token"]}, ensure_ascii=False)
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
@ -44,7 +44,8 @@ class ApiModelWorker(BaseModelWorker):
|
||||
|
||||
def count_token(self, params):
|
||||
# TODO:需要完善
|
||||
print("count token")
|
||||
# print("count token")
|
||||
print("\n\n\n")
|
||||
print(params)
|
||||
prompt = params["prompt"]
|
||||
return {"count": len(str(prompt)), "error_code": 0}
|
||||
|
||||
@ -26,10 +26,10 @@ class ChatGLMWorker(ApiModelWorker):
|
||||
# 这里的是chatglm api的模板,其它API的conv_template需要定制
|
||||
self.conv = conv.Conversation(
|
||||
name=self.model_names[0],
|
||||
system_message="你是一个聪明、对人类有帮助的人工智能,你可以对人类提出的问题给出有用、详细、礼貌的回答。",
|
||||
system_message="你是一个聪明的助手,请根据用户的提示来完成任务",
|
||||
messages=[],
|
||||
roles=["Human", "Assistant"],
|
||||
sep="\n### ",
|
||||
sep="\n###",
|
||||
stop_str="###",
|
||||
)
|
||||
|
||||
@ -57,7 +57,7 @@ class ChatGLMWorker(ApiModelWorker):
|
||||
def get_embeddings(self, params):
|
||||
# TODO: 支持embeddings
|
||||
print("embedding")
|
||||
print(params)
|
||||
# print(params)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -158,7 +158,7 @@ def create_model_worker_app(log_level: str = "INFO", **kwargs) -> FastAPI:
|
||||
else:
|
||||
from fastchat.serve.model_worker import app, GptqConfig, AWQConfig, ModelWorker
|
||||
args.gpus = "0" # GPU的编号,如果有多个GPU,可以设置为"0,1,2,3"
|
||||
args.max_gpu_memory = "20GiB"
|
||||
args.max_gpu_memory = "22GiB"
|
||||
args.num_gpus = 1 # model worker的切分是model并行,这里填写显卡的数量
|
||||
|
||||
args.load_8bit = False
|
||||
@ -170,7 +170,7 @@ def create_model_worker_app(log_level: str = "INFO", **kwargs) -> FastAPI:
|
||||
args.awq_ckpt = None
|
||||
args.awq_wbits = 16
|
||||
args.awq_groupsize = -1
|
||||
args.model_names = []
|
||||
args.model_names = [""]
|
||||
args.conv_template = None
|
||||
args.limit_worker_concurrency = 5
|
||||
args.stream_interval = 2
|
||||
|
||||
@ -7,7 +7,6 @@ import os
|
||||
from configs import LLM_MODEL, TEMPERATURE
|
||||
from server.utils import get_model_worker_config
|
||||
from typing import List, Dict
|
||||
|
||||
chat_box = ChatBox(
|
||||
assistant_avatar=os.path.join(
|
||||
"img",
|
||||
@ -16,6 +15,9 @@ chat_box = ChatBox(
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_messages_history(history_len: int, content_in_expander: bool = False) -> List[Dict]:
|
||||
'''
|
||||
返回消息历史。
|
||||
@ -104,6 +106,8 @@ def dialogue_page(api: ApiRequest):
|
||||
temperature = st.slider("Temperature:", 0.0, 1.0, TEMPERATURE, 0.05)
|
||||
|
||||
history_len = st.number_input("历史对话轮数:", 0, 20, HISTORY_LEN)
|
||||
LLM_MODEL_WEBUI = llm_model
|
||||
TEMPERATURE_WEBUI = temperature
|
||||
|
||||
def on_kb_change():
|
||||
st.toast(f"已加载知识库: {st.session_state.selected_kb}")
|
||||
@ -155,9 +159,17 @@ def dialogue_page(api: ApiRequest):
|
||||
|
||||
elif dialogue_mode == "自定义Agent问答":
|
||||
chat_box.ai_say([
|
||||
f"正在思考和寻找工具 ...",])
|
||||
f"正在思考...",
|
||||
Markdown("...", in_expander=True, title="思考过程", state="complete"),
|
||||
])
|
||||
text = ""
|
||||
element_index = 0
|
||||
ans = ""
|
||||
support_agent = ["gpt", "Qwen", "qwen-api", "baichuan-api"] # 目前支持agent的模型
|
||||
if not any(agent in llm_model for agent in support_agent):
|
||||
ans += "正在思考... \n\n <span style='color:red'>改模型并没有进行Agent对齐,无法正常使用Agent功能!</span>\n\n\n<span style='color:red'>请更换 GPT4或Qwen-14B等支持Agent的模型获得更好的体验! </span> \n\n\n"
|
||||
chat_box.update_msg(ans, element_index=0, streaming=False)
|
||||
|
||||
|
||||
for d in api.agent_chat(prompt,
|
||||
history=history,
|
||||
model=llm_model,
|
||||
@ -169,14 +181,17 @@ def dialogue_page(api: ApiRequest):
|
||||
if error_msg := check_error_msg(d): # check whether error occured
|
||||
st.error(error_msg)
|
||||
|
||||
elif chunk := d.get("final_answer"):
|
||||
ans += chunk
|
||||
chat_box.update_msg(ans, element_index=0)
|
||||
elif chunk := d.get("answer"):
|
||||
text += chunk
|
||||
chat_box.update_msg(text, element_index=0)
|
||||
chat_box.update_msg(text, element_index=1)
|
||||
elif chunk := d.get("tools"):
|
||||
element_index += 1
|
||||
chat_box.insert_msg(Markdown("...", in_expander=True, title="使用工具...", state="complete"))
|
||||
chat_box.update_msg("\n\n".join(d.get("tools", [])), element_index=element_index, streaming=False)
|
||||
chat_box.update_msg(text, element_index=0, streaming=False)
|
||||
text += "\n\n".join(d.get("tools", []))
|
||||
chat_box.update_msg(text, element_index=1)
|
||||
chat_box.update_msg(ans, element_index=0, streaming=False)
|
||||
chat_box.update_msg(text, element_index=1, streaming=False)
|
||||
elif dialogue_mode == "知识库问答":
|
||||
chat_box.ai_say([
|
||||
f"正在查询知识库 `{selected_kb}` ...",
|
||||
|
||||
@ -250,7 +250,7 @@ class ApiRequest:
|
||||
logger.error(f'{e.__class__.__name__}: {msg}',
|
||||
exc_info=e if log_verbose else None)
|
||||
else:
|
||||
print(chunk, end="", flush=True)
|
||||
# print(chunk, end="", flush=True)
|
||||
yield chunk
|
||||
except httpx.ConnectError as e:
|
||||
msg = f"无法连接API服务器,请确认 ‘api.py’ 已正常启动。({e})"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user