大模型流式对话返回demo
·
代码
可直接调用
from typing import Dict, Any
import requests
from langchain_ollama import ChatOllama
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
import re
import json
class DeepSeekAgent:
def __init__(self):
self.llm = ChatOllama(
model="deepseek-r1:1.5b",
temperature=0.7,
base_url="http://localhost:11434"
)
self.tools = {
"get_weather": self.get_weather,
"send_email": self.send_email,
}
# 修改模板,明确使用 {input} 变量
self.prompt = ChatPromptTemplate.from_template("""
你是一个人工智能助手,能够完全准确地根据根据用户需要的格式进行回答。
用户输入: {input}
""")
self.chain = self.prompt | self.llm | StrOutputParser()
def call_deepseek(self, prompt: str) -> str:
# """调用DeepSeek API获取响应"""
# headers = {"Authorization": f"Bearer {self.api_key}"}
# data = {
# "model": "deepseek-chat",
# "messages": [{"role": "user", "content": prompt}]
# }
# response = requests.post(self.llm_api, json=data, headers=headers)
# return response.json()["choices"][0]["message"]["content"]
return self.chain.invoke({"input": prompt})
def get_weather(self, location: str) -> str:
"""模拟天气查询工具"""
# 实际可调用天气API(如OpenWeatherMap)
return f"Weather in {location}: Sunny, 25°C"
def send_email(self, recipient: str, subject: str) -> str:
"""模拟发邮件工具"""
# 实际可调用SMTP或邮件API
return f"Email sent to {recipient} with subject: '{subject}'"
# def parse_instruction(self, user_input: str) -> Dict[str, Any]:
# """解析用户指令,决定调用哪个工具"""
# prompt = f"""
# 用户指令: {user_input}
# 请判断是否需要调用工具,并返回JSON格式,例如:
# {{"tool": "get_weather", "args": {{"location": "Beijing"}}}}
# 如果无需工具,返回{{"tool": null}}。
# """
# llm_response = self.call_deepseek(prompt)
# return eval(llm_response) # 实际应使用安全解析(如json.loads)
def parse_instruction(self, user_input):
# 保持原来的提示词模板不变
prompt = f"""
用户指令: {user_input}
请严格遵循以下格式:
请判断是否需要调用工具(你有如下工具可以使用:当用户询问天气如何,或者你需要知道天气如何的时候,查询天气工具(get_weather)),并返回JSON格式
只返回一个JSON对象,不加任何额外解释。
例如:
{{"tool": "get_weather", "args": {{"location": "Beijing"}}}}
如果无需工具,返回:
{{"tool": null}}
"""
# 获取大语言模型的响应
llm_response = self.call_deepseek(prompt)
# 添加调试信息:打印完整响应
print(f"大模型完整响应: {llm_response}")
"""直接从含<think>的响应中提取JSON对象"""
# 先移除<think>标签
no_think = re.sub(r'<think>[\s\S]*?</think>', '', llm_response, flags=re.DOTALL)
print(f"去除think标签响应: {no_think}")
# 方法1:严格提取JSON部分
# 查找第一个{和最后一个}的位置
start = no_think.find('{')
end = no_think.rfind('}') + 1
if start == -1 or end == 0:
raise ValueError("响应中未找到有效的JSON结构")
json_match = no_think[start:end]
print(f"大括号标签响应: {json_match}")
# 验证大括号是否匹配
if json_match.count('{') != json_match.count('}'):
raise ValueError("大括号不匹配,JSON结构不完整")
# 最终清理
json_match = json_match.strip()
print("最终提取的JSON:", repr(json_match))
if not json_match:
raise ValueError("无法从响应中提取JSON内容")
# 关键修复:彻底清理字符串
json_match = json_match.strip() # 去除首尾空白
json_match = re.sub(r'[\u200b-\u200f\u202a-\u202e]', '', json_match) # 去除零宽字符
json_match = json_match.encode('ascii', 'ignore').decode('ascii') # 去除非ASCII字符
# 调试:打印清理后的JSON字符串
print("清理后的JSON字符串:", repr(json_match))
try:
result = json.loads(json_match)
print("解析成功:", result)
return result
except json.JSONDecodeError as e:
# 详细错误诊断
print(f"JSON解析失败,错误位置:{e.lineno}行{e.colno}列")
print("问题字符的ASCII码:", ord(json_match[e.pos]))
raise ValueError(f"JSON解析失败: {str(e)}\n问题内容: {json_match[e.pos - 10:e.pos + 10]}")
# # 再提取JSON部分
# json_match = re.search(r'\{[\s\S]*?\}', no_think)
# if not json_match:
# raise ValueError("未找到有效的JSON内容")
#
# print(f"调用工具响应: {json.loads(json_match.group())}")
# return json.loads(json_match.group())
# # 使用正则表达式提取JSON部分
# json_match = re.search(r'\{.*\}', llm_response, re.DOTALL)
#
# if not json_match:
# raise ValueError(f"响应中未找到有效的JSON: {llm_response}")
#
# json_str = json_match.group()
#
# # 尝试处理可能存在的内部转义
# json_str = re.sub(r'\\{', '{', json_str)
# json_str = re.sub(r'\\}', '}', json_str)
#
# try:
# # 使用安全的json解析
# result = json.loads(json_str)
# print(f"成功解析JSON: {result}") # 调试用
# return result
# except json.JSONDecodeError as e:
# print(f"JSON解析错误: {e.msg}")
# print(f"尝试解析的内容: {json_str}")
# raise
# # 定位并提取Action行
# action_line = None
# for line in llm_response.split('\n'):
# if line.startswith('Action:'):
# # 移除"Action:"前缀并清理空白
# action_line = line.replace('Action:', '').strip()
# break
#
# # 添加错误检查
# if not action_line:
# raise ValueError("响应中未找到有效的Action行")
#
# # 只对函数调用部分执行eval
# return eval(action_line) # 现在只处理如"get_weather(...)"这样的内容
def run(self, user_input: str) -> str:
"""Agent 主逻辑"""
# 1. 解析指令
action = self.parse_instruction(user_input)
# 2. 执行工具或直接响应
if action["tool"] in self.tools:
print(f"调用工具:", {action["tool"]})
tool_func = self.tools[action["tool"]]
return tool_func(**action["args"])
else:
return self.call_deepseek(user_input) # 直接LLM响应
更多推荐



所有评论(0)