feat: 提交

main
old-tom 5 months ago
parent 56741d8670
commit 7b7433e4a5

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<enabledExtensions>
<entry key="MermaidLanguageExtension" value="false" />
<entry key="PlantUMLLanguageExtension" value="false" />
</enabledExtensions>
</component>
</project>

@ -0,0 +1,59 @@
# LLM function calling 示例
## 功能介绍
## 安装
1. clone 本项目
```shell
git clone http://1.14.96.249:3000/old-tom/llmFunctionCallDemo.git
```
2. 创建虚拟环境
+ 这里使用的是pipenv,可以换成uv或者conda
+ python 版本为 3.10或以上稳定版即可
```shell
cd llmFunctionCallDemo clone的代码目录
```
```shell
pipenv install --python 3.10
```
3. 安装依赖
```shell
pip install -r requirements.txt
```
4. 向量库部署和初始化 (docker)
```shell
docker run --name marqo -it --privileged -p 8882:8882 --add-host host.docker.internal:host-gateway marqoai/marqo:latest
```
初始化:执行[vector_db.py](vector_db.py) create_and_set_index()方法
测试:执行[vector_db.py](vector_db.py) query_vector_db() 方法,参数为任意字符串
5. 配置文件
[env.toml](env.toml)
```toml
[base]
# 多轮对话历史存储类型memory内存
history_chat_store = 'memory'
# 相似度阈值
similarity_threshold = 0.93
# dev
dev = true
####### 模型配置 #######
[siliconflow]
# 硅基流动
# 密钥
api_key = ''
# 模型名称
model = ''
# API地址
base_url = ''
# 最大token数
max_tokens = 4096
# 温度系数
temperature = 0.6
# 是否流式返回
streaming = true
```
## TestCase
## TODO

@ -3,7 +3,10 @@
history_chat_store = 'memory'
# 相似度阈值
similarity_threshold = 0.93
# debug模式 (不适用于流式输出)
debug = false
# 详细输出 (不适用于流式输出)
verbose = false
####### 模型配置 #######
[siliconflow]
# 硅基流动

@ -23,7 +23,7 @@ PROMPT_TEMPLATE = {
1. 北卡口入境摄像头出场1号通道
2. 北卡口出口道路监控
3. 北卡口入境摄像头出场2号通道
您需要选择哪个选项请回复选项前的数字
您需要选择哪个选项请回复选项前的数字\n
6. 如果工具返回单个结果不要有多余输出及思考过程强制格式为正在执行xxx操作,请等待完成
7. 在任何情况下都不要修改或扩展提供的工具参数
@ -44,13 +44,13 @@ PROMPT_TEMPLATE = {
1.根据指令和提供的工具描述选择最合适的工具,并仔细阅读工具参数说明
2.你需要根据工具参数说明进行参数校验评估用户输入的参数是否满足条件如果不满足需要返回参数校验错误信息
3.在任何情况下都不要修改或扩展提供的工具参数
4.如果用户的指令是任意表示序号的数字或者语句比如1第一第1个第二个你需要回顾上一轮对话用户指令并推断出本轮用户指令
4.如果用户的指令是任意表示序号的数字或者语句比如1第一第1个第二个你需要回顾上一轮对话用户指令并推断出本轮用户指令
例如上一轮对话中的用户指令为打开相机,你的回复是:请确认您要查看的相机具体名称
1. 北卡口入境摄像头出场1号通道
2. 北卡口出口道路监控
3. 北卡口入境摄像头出场2号通道
您需要选择哪个选项请回复选项前的数字
本轮用户指令为1, 你需要推断出本轮用户指令为打开北卡口入境摄像头出场1号通道相机
本轮用户指令为1, 你需要推断出本轮用户指令为打开北卡口入境摄像头出场1号通道相机,并强制调用工具
"""
}
}

@ -18,10 +18,11 @@ from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMess
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.messages import HumanMessage
from log_conf import log
from llmagent.llm_config import base_conf
# debug模式,有更多输出
set_debug(False)
set_verbose(False)
set_debug(base_conf.debug)
set_verbose(base_conf.verbose)
# 默认系统提示词
DEFAULT_SYS_PROMPT = ''
@ -136,7 +137,7 @@ class BaseChatAgent(ABC):
# 总体任务描述及提示词
user_msg = PROMPT_TEMPLATE.get('VOICE_ASSISTANT')['template'].format(user_input=user_input)
messages = [HumanMessage(user_msg)]
llm_with_tools = llm.bind_tools(STRUCT_TOOLS)
llm_with_tools = llm.bind_tools(STRUCT_TOOLS, tool_choice='auto')
# 判断使用哪个工具,需要加提示词让模型判断参数是否符合规则
user_input = PROMPT_TEMPLATE.get('TOOL_CALLER')['template'].format(user_input=user_input)
# 工具chain加入历史对话

@ -27,6 +27,15 @@ class ConfigNotFoundError(Exception):
Exception.__init__(self, msg)
def load_env():
if not os.path.isfile(DEFAULT_CONF_PATH):
raise ConfigNotFoundError(f'模型配置文件{DEFAULT_CONF_NAME}不存在')
return toml.load(DEFAULT_CONF_PATH)
conf = load_env()
class LLMConf(BaseModel):
api_key: str
model: str
@ -38,12 +47,19 @@ class LLMConf(BaseModel):
class LLMConfigLoader(object):
@staticmethod
def load(item_name, conf_path: str = DEFAULT_CONF_PATH) -> LLMConf:
def load(item_name) -> LLMConf:
"""
校验并加载配置
:return:
"""
if not os.path.isfile(conf_path):
raise ConfigNotFoundError(f'模型配置文件{DEFAULT_CONF_NAME}不存在')
conf = toml.load(conf_path)
return LLMConf(**conf[item_name])
class BaseConf(BaseModel):
history_chat_store: str
similarity_threshold: float
debug: bool = False
verbose: bool = False
base_conf = BaseConf(**conf['base'])

@ -11,14 +11,15 @@ from typing import Annotated
from langchain_core.tools import tool
from vector_db import query_vector_db
from log_conf import log
from llmagent.llm_config import base_conf
@tool("play_video", description="播放、查看、打开实时视频")
def play_video(camera_name: Annotated[str, "相机名称,例如南卡口1号相机"]) -> str:
camera_info = query_camera_from_db(camera_name)
log.info('【function】play_video 输入 [{}]', camera_name)
if camera_info:
if len(camera_info) > 1:
# TODO 需要多轮对话,确认相机名称
hit_camera_names = [x['carme_name'] for x in camera_info]
return f"找到以下相机,请选择一个:{hit_camera_names}"
else:
@ -74,7 +75,7 @@ def query_camera_from_db(camera_name: str, top_n: int = 3) -> str:
top_one = rt['hits'][0]
# 相似度评分
score = top_one['_score']
if score > 0.93:
if score > base_conf.similarity_threshold:
return rt['hits'][0:1]
else:
return rt['hits'][0:top_n]

@ -15,7 +15,7 @@ if __name__ == '__main__':
########## 测试 function call #########
# print(dsr.invoke_with_tool_call('播放南卡口相机'))
## [{'name': 'play_video', 'args': {'camera_name': '北卡口1号道相机'}, 'id': 'call_apnb8fiqdkaz313katcs3tjf', 'type': 'tool_call'}]
dsr.multi_with_tool_call_stream('将大屏切换为-1分屏', 1)
# dsr.multi_with_tool_call_stream('将大屏切换为-1分屏', 1)
## [{'name': 'split_screen', 'args': {'split_n': 2}, 'id': 'call_2o7c94f591xag8p6lcyice9q', 'type': 'tool_call'}]
# print(dsr.invoke_with_tool('播放北卡口入境1号道录像,从今天到2025-03-16 02:09:31'))
## 由于大模型没有联网,所以无法判断‘今天’
@ -40,5 +40,5 @@ if __name__ == '__main__':
# dsr.multi_with_stream('我的第一个问题是什么?请直接返回问题,不要有多余输出及思考过程', 1)
########## 测试 多轮对话-相机选择 #########
# dsr.multi_with_tool_call_stream('播放南卡口相机', 1)
# dsr.multi_with_tool_call_stream('1', 1)
dsr.multi_with_tool_call_stream('播放南卡口相机', 1)
dsr.multi_with_tool_call_stream('1', 1)

@ -25,6 +25,7 @@ langchain-core==0.3.45
langchain-openai==0.3.8
langchain-text-splitters==0.3.7
langsmith==0.3.15
loguru==0.7.3
marqo==3.11.0
marshmallow==3.26.1
multidict==6.2.0

@ -619,7 +619,7 @@ def query_vector_db(query):
if __name__ == '__main__':
# create_and_set_index()
rt = query_vector_db('南卡口出境2号相机')
rt = query_vector_db('1')
# TODO 根据 _score字段 取出相似度最高的结果
if rt:
for ele in rt['hits']:

Loading…
Cancel
Save