当前位置: 首页 > news >正文

理解大模型的function call ,思维链COT和MCP 协议

在大模型中,function call 是指模型调用外部功能或工具以完成特定任务的过程。这种机制使得模型不仅能生成文本,还能执行特定的操作,如生成图像、获取数据或进行计算。

关键特点

  1. 功能扩展:通过调用外部函数,模型可以实现更复杂的功能,比如生成图像、访问数据库或进行API请求。

  2. 参数传递:在调用函数时,通常需要传递一些参数,以便函数能够正确执行所需的任务。

  3. 响应处理:函数执行后,返回的结果可以被模型进一步处理或直接返回给用户。

代码步骤

数据库初始化 (init_database)
  • 创建一个 SQLite 数据库(products.db),包含一个 products 表,字段有:id(ID)、name(名称)、price(价格)和 description(描述)。
  • 用示例数据填充表,例如 iPhone 14、Galaxy S23、MacBook Pro。
  • search_product_in_db 函数用于按产品名称查询数据库。
函数定义 (function_definitions)
  • 以 JSON 格式定义可用函数的 schema:
{"name": "search_product","description": "根据名称在数据库中搜索产品","parameters": {"type": "object","properties": {"product_name": {"type": "string", "description": "要搜索的产品名称"}},"required": ["product_name"]}
}

这个 schema 提供给模型,让它知道可以“调用”哪些函数以及需要的参数。

模型调用 (call_model)
  • 构建一个提示(prompt),包含:
    • 系统消息(包含函数定义)。
    • 用户查询(例如“告诉我关于 iPhone 14 的信息”)。
    • 指令:分析查询,判断是否需要数据库搜索,若需要则调用对应函数,返回单一 JSON 对象(包含 role 和 content)。
  • 模型生成响应后,代码解析出其中的 JSON。
函数调用处理 (process_query)
  • 解析模型的 JSON 响应。
  • 如果响应中包含 function_call 字段,则执行指定的函数(例如 search_product)及其参数。
  • 返回人类可读的格式化结果,或者如果没有函数调用,则返回原始内容。

函数调用工作原理

这里的“函数调用”机制类似于现代 AI 助手(例如 ChatGPT 的工具集成)的工作方式。流程如下:

  1. 用户查询:例如“告诉我关于 iPhone 14 的信息”。
  2. 提示构建
  3. call_model 函数构造的提示类似:
    [System] 你是一个有用的助手,可以使用以下函数:
    [{"name": "search_product", "description": "根据名称在数据库中搜索产品", "parameters": {...}}]
    [User] 告诉我关于 iPhone 14 的信息
    分析查询,判断是否需要数据库搜索。如果需要,调用相应函数。
    以单一 JSON 对象返回,包含 "role" 和 "content" 字段。

模型响应

  • 模型分析查询,决定需要产品信息。
  • 生成类似以下的 JSON 响应:
    {"role": "assistant","content": {"function_call": {"name": "search_product","arguments": {"product_name": "iPhone 14"}}}
    }

处理响应

  • process_query 检查 content 是否包含 function_call。
  • 如果有,提取函数名(search_product)和参数(product_name: "iPhone 14")。
  • 调用 search_product_in_db("iPhone 14"),查询数据库。

全部代码

import json
import sqlite3
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import re
import logging# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('function_call.log'), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)# 数据库初始化
def init_database():logger.info("Initializing database")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute('''CREATE TABLE IF NOT EXISTS products(id INTEGER PRIMARY KEY, name TEXT, price REAL, description TEXT)''')sample_data = [(1, "iPhone 14", 799.99, "Latest Apple smartphone with A16 chip"),(2, "Galaxy S23", 699.99, "Samsung flagship with Snapdragon 8 Gen 2"),(3, "MacBook Pro", 1299.99, "Apple laptop with M2 chip")]c.executemany('INSERT OR IGNORE INTO products VALUES (?,?,?,?)', sample_data)conn.commit()conn.close()logger.info("Database initialized successfully")# 数据库搜索函数
def search_product_in_db(product_name):logger.info(f"Searching database for product: {product_name}")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute("SELECT * FROM products WHERE name LIKE ?", ('%' + product_name + '%',))result = c.fetchone()conn.close()if result:product_info = {"id": result[0], "name": result[1], "price": result[2], "description": result[3]}logger.info(f"Found product: {product_info}")return product_infologger.info(f"No product found for: {product_name}")return None# Function call schema
function_definitions = [{"name": "search_product","description": "Search for a product in the database by name","parameters": {"type": "object","properties": {"product_name": {"type": "string", "description": "The name of the product to search for"}},"required": ["product_name"]}}
]# 加载模型和tokenizer
model_path = "./base_model/qwq_32b"
logger.info(f"Loading model from {model_path}")
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path)
model.eval()
logger.info("Model and tokenizer loaded successfully")# 调用模型的函数
def call_model(prompt, functions):logger.info(f"Calling model with prompt: {prompt}")try:input_text = f"""[System] You are a helpful assistant with access to these functions:
{json.dumps(functions, indent=2)}
[User] {prompt}
Analyze the query and determine if a database search is needed. If yes, call the appropriate function. 
Respond with a single JSON object containing "role" and "content" fields. Do not include any text outside the JSON."""logger.debug(f"Full input text: {input_text}")inputs = tokenizer(input_text, return_tensors="pt")with torch.no_grad():outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.7, do_sample=True)response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)logger.info(f"Raw model response: {response_text}")# 尝试提取并修复JSONjson_match = re.search(r'\{.*\}', response_text, re.DOTALL)if json_match:json_str = json_match.group(0)try:parsed_json = json.loads(json_str)logger.info(f"Extracted JSON: {parsed_json}")return parsed_jsonexcept json.JSONDecodeError as e:logger.warning(f"JSON parsing failed: {e}. Attempting to fix: {json_str}")# 尝试修复常见JSON错误(缺少逗号、未闭合等)json_str = json_str.replace("'", '"')  # 单引号转双引号if not json_str.endswith('}'):json_str += '}'try:parsed_json = json.loads(json_str)logger.info(f"Fixed JSON: {parsed_json}")return parsed_jsonexcept json.JSONDecodeError as e:logger.error(f"Failed to fix JSON: {e}")else:logger.warning(f"No JSON found in response: {response_text}")return {"role": "assistant", "content": f"Error: Invalid response format - {response_text}"}except Exception as e:logger.error(f"Error calling model: {e}")return None# 主处理逻辑
def process_query(user_query):logger.info(f"Processing query: {user_query}")prompt = f"User query: {user_query}"response = call_model(prompt, function_definitions)print('response', response)if response is None:logger.error("Model response is None")return "Sorry, there was an error processing your request."logger.info(f"Processed model response: {response}")content = response.get("content")print('content', content)if isinstance(content, dict) and "function_call" in content:function_call = content["function_call"]logger.info(f"Executing function call: {function_call}")if function_call["name"] == "search_product":print('function_call', function_call)params = function_call["arguments"]if isinstance(params, str):try:params = json.loads(params)except json.JSONDecodeError as e:logger.error(f"Failed to parse arguments: {e}")return "Error: Invalid function arguments"product_name = params["product_name"]result = search_product_in_db(product_name)if result:return f"Found product: {result['name']}\nPrice: ${result['price']}\nDescription: {result['description']}"else:return f"No product found matching '{product_name}'"return content if isinstance(content, str) else json.dumps(content)# 主函数
def main():init_database()user_query = "Tell me about iPhone 14"print("User query:", user_query)print("\nResponse:")print(process_query(user_query))if __name__ == "__main__":main()

运行控制台

查看日志报错

2025-03-18 09:14:46,533 - ERROR - Failed to fix JSON: Extra data: line 17 column 1 (char 364)
2025-03-18 09:14:46,533 - INFO - Processed model response: {'role': 'assistant', 'content': 'Error: Invalid response format - [System] You are a helpful assistant with access to these functions:\n[\n  {\n    "name": "search_product",\n    "description": "Search for a product in the database by name",\n    "parameters": {\n      "type": "object",\n      "properties": {\n        "product_name": {\n          "type": "string",\n          "description": "The name of the product to search for"\n        }\n      },\n      "required": [\n        "product_name"\n      ]\n    }\n  }\n]\n[User] User query: Tell me about iPhone 14\nAnalyze the query and determine if a database search is needed. If yes, call the appropriate function. \nRespond with a single JSON object containing "role" and "content" fields. Do not include any text outside the JSON. OK, the user is asking about the iPhone 14. I need to figure out if I should use the search_product function. The function\'s purpose is to search for a product by name, so that\'s exactly what I need here. The parameter required is product_name, which in this case is "iPhone 14". I should call the function with that parameter. Let me make sure there\'s no other part of the query that needs handling, but it seems straightforward. Alright, the response should be a JSON object with the role and the content including the function call.\n\n{\n  "role": "function",\n  "content": {\n    "name": "search_product",\n    "arguments": {\n      "product_name": "iPhone 14"\n    }\n  }\n}\n\n\n{\n  "role": "function",\n  "content": {\n    "name": "search_product",\n    "arguments": {\n      "product_name": "iPhone 14"\n    }\n  }\n}'}

修改的重点

  1. 提示优化
    • 在提示中明确添加:Do not include any text outside the JSON, including explanations or repeated objects.,以约束模型只生成单一 JSON 对象。
    • 这减少了模型生成多余推理过程或重复 JSON 的可能性。
  2. JSON 提取逻辑改进
    • 使用 re.findall(r'\{[^{}]*?(?:\{[^{}]*?\}[^{}]*?)*\}', response_text, re.DOTALL) 查找所有完整的 JSON 对象。
    • 取最后一个匹配项(json_matches[-1]),避免提取不完整的片段或重复的对象。
    • 原来的 re.search 只匹配第一个 {...},可能导致截断或匹配错误。

修改后的 call_model方法

def call_model(prompt, functions):logger.info(f"Calling model with prompt: {prompt}")try:# 修改后的提示,强调只返回 JSONinput_text = f"""[System] You are a helpful assistant with access to these functions:
{json.dumps(functions, indent=2)}
[User] {prompt}
Analyze the query and determine if a database search is needed. If yes, call the appropriate function.
Respond with a single JSON object containing "role" and "content" fields. Do not include any text outside the JSON, including explanations or repeated objects."""logger.debug(f"Full input text: {input_text}")inputs = tokenizer(input_text, return_tensors="pt")with torch.no_grad():outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.7, do_sample=True)response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)logger.info(f"Raw model response: {response_text}")# 改进 JSON 提取逻辑# 查找最后一个完整的 JSON 对象,避免重复或不完整匹配json_matches = re.findall(r'\{[^{}]*?(?:\{[^{}]*?\}[^{}]*?)*\}', response_text, re.DOTALL)if json_matches:json_str = json_matches[-1]  # 取最后一个完整的 JSONtry:parsed_json = json.loads(json_str)logger.info(f"Extracted JSON: {parsed_json}")return parsed_jsonexcept json.JSONDecodeError as e:logger.warning(f"JSON parsing failed: {e}. Attempting to fix: {json_str}")# 修复常见 JSON 错误json_str = json_str.replace("'", '"')  # 单引号转双引号if not json_str.endswith('}'):json_str += '}'try:parsed_json = json.loads(json_str)logger.info(f"Fixed JSON: {parsed_json}")return parsed_jsonexcept json.JSONDecodeError as e:logger.error(f"Failed to fix JSON: {e}")else:logger.warning(f"No valid JSON found in response: {response_text}")return {"role": "assistant", "content": f"Error: Invalid response format - {response_text}"}except Exception as e:logger.error(f"Error calling model: {e}")return None

给点调试意见

  1. 运行修改后的代码,输入查询 "Tell me about iPhone 14"。
  2. 检查日志文件 function_call.log,确认:
    • Raw model response 是否只包含单一 JSON。
    • Extracted JSON 是否正确解析。
  3. 如果模型仍生成多余文本,可能需要进一步微调模型或调整生成参数(例如降低 temperature 或设置 stop 标记)。

json 格式还需要修改下

修改的重点

  1. 不再假设响应一定包含 role 和 content,而是直接检查是否包含 name 和 arguments。如果检测到这种格式(例如 {'name': 'search_product', 'arguments': {'product_name': 'iPhone 14'}}),将其视为函数调用。
  2. 检查 arguments 是否为字符串(以防模型返回 JSON 字符串),如果是则尝试解析为字典。从 arguments 中提取 product_name,并确保其存在。
  3. 如果响应中没有 name 和 arguments,但有 content 字段,则尝试按旧格式处理(保持兼容性)。如果格式完全不匹配,记录错误并返回提示。
  4. 增加了对无效响应格式的检查和日志记录,确保问题可追溯。
def process_query(user_query):logger.info(f"Processing query: {user_query}")prompt = f"User query: {user_query}"response = call_model(prompt, function_definitions)print('response', response)if response is None:logger.error("Model response is None")return "Sorry, there was an error processing your request."logger.info(f"Processed model response: {response}")# 检查响应是否为字典类型if not isinstance(response, dict):logger.error(f"Invalid response format: {response}")return "Error: Invalid response format from model"# 直接检查是否包含 'name' 和 'arguments'(新格式)if 'name' in response and 'arguments' in response:function_name = response['name']params = response['arguments']logger.info(f"Executing function call: {function_name} with arguments: {params}")if function_name == "search_product":# 确保 params 是字典,如果是字符串则尝试解析if isinstance(params, str):try:params = json.loads(params)except json.JSONDecodeError as e:logger.error(f"Failed to parse arguments: {e}")return "Error: Invalid function arguments"product_name = params.get("product_name")if not product_name:logger.error("Missing product_name in arguments")return "Error: Missing product_name in arguments"result = search_product_in_db(product_name)if result:return f"Found product: {result['name']}\nPrice: ${result['price']}\nDescription: {result['description']}"else:return f"No product found matching '{product_name}'"# 如果响应中没有 'name' 和 'arguments',假设是普通文本内容content = response.get("content")if content:return content if isinstance(content, str) else json.dumps(content)# 如果格式仍然不匹配,返回错误logger.error(f"Unrecognized response format: {response}")return "Error: Unrecognized response format"

再次运行

增加Cot

在现有代码的基础上,我将增加一个新的 function call,用于实现“思维链条”(Chain of Thought, CoT),并通过组织智能体协同工作来增强查询处理的逻辑性。这个新功能将模拟一个智能体分解复杂查询、逐步推理并调用其他函数的过程。我们将添加一个名为 reason_step_by_step 的函数,让模型以结构化的方式逐步分析问题。

设计思路

  1. 新函数 reason_step_by_step
    • 功能:接收用户查询,分解为多个推理步骤,并决定是否需要调用其他函数(如 search_product)。
    • 输出:返回一个 JSON 对象,包含每一步的推理和可能的函数调用。
    • 参数:query(用户查询)。
  2. 智能体协同工作
    • 一个“推理智能体”负责分解问题并生成思维链条。
    • 如果需要数据查询,则调用“搜索智能体”(search_product)。
    • 最终由 process_query 整合结果。
  3. 代码修改
    • 在 function_definitions 中添加 reason_step_by_step。
    • 修改 call_model 以支持多函数调用。
    • 更新 process_query 以处理嵌套的函数调用和思维链条。

定义function call schema

function_definitions = [{"name": "search_product","description": "Search for a product in the database by name","parameters": {"type": "object","properties": {"product_name": {"type": "string", "description": "The name of the product to search for"}},"required": ["product_name"]}},{"name": "reason_step_by_step","description": "Analyze a query step-by-step and determine actions, potentially calling other functions","parameters": {"type": "object","properties": {"query": {"type": "string", "description": "The user query to analyze"}},"required": ["query"]}}
]

把提示词换成中文

system_part = "[System] 你是一个有用的助手,可以使用以下功能:\n"functions_part = json.dumps(functions, indent=2) + "\n"user_part = f"[User] {prompt}\n"instructions = ("分析查询并确定下一步行动。对于复杂查询(例如比较),使用 \"reason_step_by_step\" 将其分解为多个步骤。\n""以单个 JSON 对象响应,包含 \"role\" 和 \"content\" 字段:\n""- 对于函数调用,使用 \"role\": \"function\", \"content\": {\"name\": \"<函数名>\", \"arguments\": {...}}。\n""- 对于多步骤计划,使用 \"role\": \"assistant\", \"content\": {\"steps\": [{\"description\": \"...\", \"function_call\": {...}}, ...]}。\n""- 对于直接回答,使用 \"role\": \"assistant\", \"content\": \"<字符串>\"。\n""不要返回函数定义或不完整的 JSON。不要在 JSON 之外包含任何文本。")input_text = system_part + functions_part + user_part + instructionslogger.debug(f"Full input text: {input_text}")inputs = tokenizer(input_text, return_tensors="pt")

call_model 的默认回退机制确保复杂查询始终通过 reason_step_by_step 分解:

return {"role": "function","content": {"name": "reason_step_by_step","arguments": {"query": prompt.split("User query: ")[1] if "User query: " in prompt else prompt}}
}

更新代码

import json
import sqlite3
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import re
import logging# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('function_call.log'), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)# 数据库初始化
def init_database():logger.info("Initializing database")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute('''CREATE TABLE IF NOT EXISTS products(id INTEGER PRIMARY KEY, name TEXT, price REAL, description TEXT)''')sample_data = [(1, "iPhone 14", 799.99, "Latest Apple smartphone with A16 chip"),(2, "Galaxy S23", 699.99, "Samsung flagship with Snapdragon 8 Gen 2"),(3, "MacBook Pro", 1299.99, "Apple laptop with M2 chip")]c.executemany('INSERT OR IGNORE INTO products VALUES (?,?,?,?)', sample_data)conn.commit()conn.close()logger.info("Database initialized successfully")# 数据库搜索函数
def search_product_in_db(product_name):logger.info(f"Searching database for product: {product_name}")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute("SELECT * FROM products WHERE name LIKE ?", ('%' + product_name + '%',))result = c.fetchone()conn.close()if result:product_info = {"id": result[0], "name": result[1], "price": result[2], "description": result[3]}logger.info(f"Found product: {product_info}")return product_infologger.info(f"No product found for: {product_name}")return None# Function call schema
function_definitions = [{"name": "search_product","description": "Search for a product in the database by name","parameters": {"type": "object","properties": {"product_name": {"type": "string", "description": "The name of the product to search for"}},"required": ["product_name"]}},{"name": "reason_step_by_step","description": "Analyze a query step-by-step and determine actions, potentially calling other functions","parameters": {"type": "object","properties": {"query": {"type": "string", "description": "The user query to analyze"}},"required": ["query"]}}
]# 加载模型和tokenizer
model_path = "./base_model/qwq_32b"
logger.info(f"Loading model from {model_path}")
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path)
model.eval()
logger.info("Model and tokenizer loaded successfully")# 调用模型的函数
def call_model(prompt, functions):logger.info(f"Calling model with prompt: {prompt}")try:input_text = f"""[System] You are a helpful assistant with access to these functions:
{json.dumps(functions, indent=2)}
[User] {prompt}
Analyze the query and determine the next action. If a function call is needed, specify it.
Respond with a single JSON object containing "role" and "content" fields. 
- For function calls, use "role": "function" and "content" with "name" and "arguments".
- For direct responses, use "role": "assistant" and "content" as a string or object.
Do not include any text outside the JSON."""logger.debug(f"Full input text: {input_text}")inputs = tokenizer(input_text, return_tensors="pt")with torch.no_grad():outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.7, do_sample=True)response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)logger.info(f"Raw model response: {response_text}")json_matches = re.findall(r'\{[^{}]*?(?:\{[^{}]*?\}[^{}]*?)*\}', response_text, re.DOTALL)if json_matches:json_str = json_matches[-1]try:parsed_json = json.loads(json_str)logger.info(f"Extracted JSON: {parsed_json}")# 兼容旧格式:如果缺少 role 和 content,转换为标准格式if "role" not in parsed_json and "name" in parsed_json:parsed_json = {"role": "function","content": {"name": parsed_json["name"],"arguments": parsed_json["arguments"]}}return parsed_jsonexcept json.JSONDecodeError as e:logger.warning(f"JSON parsing failed: {e}. Attempting to fix: {json_str}")json_str = json_str.replace("'", '"')if not json_str.endswith('}'):json_str += '}'try:parsed_json = json.loads(json_str)logger.info(f"Fixed JSON: {parsed_json}")if "role" not in parsed_json and "name" in parsed_json:parsed_json = {"role": "function","content": {"name": parsed_json["name"],"arguments": parsed_json["arguments"]}}return parsed_jsonexcept json.JSONDecodeError as e:logger.error(f"Failed to fix JSON: {e}")else:logger.warning(f"No valid JSON found in response: {response_text}")return {"role": "assistant", "content": f"Error: Invalid response format - {response_text}"}except Exception as e:logger.error(f"Error calling model: {e}")return None# 主处理逻辑
def process_query(user_query):logger.info(f"Processing query: {user_query}")prompt = f"User query: {user_query}"# 首先调用 reason_step_by_step 来分解查询initial_response = call_model(prompt, function_definitions)print('initial_response', initial_response)if initial_response is None:logger.error("Initial model response is None")return "Sorry, there was an error processing your request."logger.info(f"Initial model response: {initial_response}")# 检查响应格式if not isinstance(initial_response, dict):logger.error(f"Invalid initial response format: {initial_response}")return "Error: Invalid response format from model"# 处理推理智能体的响应def handle_response(response):# 兼容旧格式:如果缺少 role 和 content,直接处理if 'role' not in response or 'content' not in response:if 'name' in response and 'arguments' in response:response = {"role": "function","content": {"name": response["name"],"arguments": response["arguments"]}}else:logger.error(f"Invalid response structure: {response}")return "Error: Invalid response structure"role = response['role']content = response['content']if role == "function":if isinstance(content, dict) and 'name' in content and 'arguments' in content:function_name = content['name']params = content['arguments']if isinstance(params, str):try:params = json.loads(params)except json.JSONDecodeError as e:logger.error(f"Failed to parse arguments: {e}")return "Error: Invalid function arguments"logger.info(f"Executing function: {function_name} with arguments: {params}")if function_name == "search_product":product_name = params.get("product_name")if not product_name:logger.error("Missing product_name in arguments")return "Error: Missing product_name in arguments"result = search_product_in_db(product_name)if result:return f"Found product: {result['name']}\nPrice: ${result['price']}\nDescription: {result['description']}"else:return f"No product found matching '{product_name}'"elif function_name == "reason_step_by_step":query = params.get("query")if not query:logger.error("Missing query in arguments")return "Error: Missing query in arguments"# 递归调用模型,处理思维链条step_response = call_model(f"Step-by-step reasoning for: {query}", function_definitions)return handle_response(step_response)elif role == "assistant":if isinstance(content, str):return contentelif isinstance(content, dict) and 'steps' in content:# 处理思维链条的步骤final_output = []for step in content.get('steps', []):if 'function_call' in step:func_response = {"role": "function","content": step['function_call']}result = handle_response(func_response)final_output.append(f"Step: {step.get('description', 'Unknown step')}\nResult: {result}")else:final_output.append(f"Step: {step.get('description', 'Unknown step')}")return "\n\n".join(final_output)logger.error(f"Unrecognized response format: {response}")return "Error: Unrecognized response format"return handle_response(initial_response)# 主函数
def main():init_database()user_query = "Compare iPhone 14 and Galaxy S23"print("User query:", user_query)print("\nResponse:")print(process_query(user_query))if __name__ == "__main__":main()

完全依赖 reason_step_by_step:让模型负责分解比较任务为多个步骤,避免在代码中硬编码后续逻辑。增强思维链逻辑:确保 process_query 只执行模型返回的步骤,而不是主动发起额外的查询。

修复效果

  • 错误解决:模型回复被转换为标准格式,避免 Invalid response structure 错误。
  • 兼容性:即使模型未严格遵循提示要求,代码也能处理不完整响应。
  • 添加后续查询逻辑:合并两次查询的结果,生成完整的比较。

运行结果

贴一下Cot 整个提示词和响应输出

[User] Step-by-step reasoning for: Compare iPhone 14 and Galaxy S23
分析查询并确定下一步行动。对于复杂查询(例如比较),使用 "reason_step_by_step" 将其分解为多个步骤。
以单个 JSON 对象响应,包含 "role" 和 "content" 字段:
- 对于函数调用,使用 "role": "function", "content": {"name": "<函数名>", "arguments": {...}}。
- 对于多步骤计划,使用 "role": "assistant", "content": {"steps": [{"description": "...", "function_call": {...}}, ...]}。
- 对于直接回答,使用 "role": "assistant", "content": "<字符串>"。
不要返回函数定义或不完整的 JSON。不要在 JSON 之外包含任何文本。 [ {"role": "assistant", "content": {"steps": [
  {"description": "Search for iPhone 14 product details", "function_call": {"name": "search_product", "arguments": {"product_name": "iPhone 14"}}},
  {"description": "Search for Galaxy S23 product details", "function_call": {"name": "search_product", "arguments": {"product_name": "Galaxy S23"}}},
  {"description": "Compare the two products based on retrieved data", "function_call": {"name": "reason_step_by_step", "arguments": {"query": "Compare iPhone 14 and Galaxy S23 using the retrieved product data"}}}
]}}]

[System] 用户请求比较iPhone 14和Galaxy S23。按照计划,我需要先分别查找这两个产品的信息,然后进行比较分析。现在,我需要执行第一步,即搜索iPhone 14的产品信息。根据之前的指示,应该调用search_product函数并传入product_name参数。确保JSON格式正确,没有多余内容。 [ {"role": "function", "content": {"name": "search_product", "arguments": {"product_name": "iPhone 14"}}} ]

[System] 现在需要处理iPhone 14的搜索结果。假设返回的数据包含关键参数如显示屏尺寸、电池容量、摄像头规格和起始价格。例如:
{
  "product": "iPhone 14",
  "display": "6.1英寸",
  "battery": "3,279 mAh",
  "camera": "双摄系统(12MP主摄 + 12MP超广角)",
  "price": "$799"
}
接下来应该继续搜索Galaxy S23的信息,对吗? [ {"role": "function", "content": {"name": "search_product", "arguments": {"product_name": "Galaxy S23"}}} ]

[System] Galaxy S23的搜索结果返回如下:
{
  "product": "Galaxy S23",
  "display": "6.1英寸",
  "battery": "3,900 mAh",
  "camera": "三摄系统(50MP主摄 + 12MP超广角 + 10MP长焦)",
  "price": "$799"
}
现在需要比较这两个产品的参数。根据步骤计划,接下来要调用reason_step
2025-03-18 12:07:57,025 - INFO - Extracted JSON: {'product': 'Galaxy S23', 'display': '6.1英寸', 'battery': '3,900 mAh', 'camera': '三摄系统(50MP主摄 + 12MP超广角 + 10MP长焦)', 'price': '$799'}

使用MCP 协议

协议要求

使用 MCP(Model Communication Protocol,一种假设的协议,类似于 OpenAI 的函数调用协议)协议,我们需要调整代码的结构,使其符合 MCP 的规范。MCP 协议通常要求模型以标准化的 JSON 格式与外部工具交互,明确定义函数调用和响应格式。假设 MCP 协议类似于以下要求:

  • 请求格式:模型接收包含系统提示、用户输入和可用函数的结构化输入。
  • 响应格式:模型返回 JSON,包含 role(如 "function" 或 "assistant")和 content(函数调用或直接回答)。
  • 函数调用:模型通过 function_call 字段指定要调用的函数及其参数。
  • 多步骤支持:支持思维链(Chain of Thought)分解任务为多个步骤。

改造的重点

  1. MCP 协议适配
    • 输入:使用 messages 列表传递对话历史,包含 role("system", "user", "function")和 content。
    • 输出:模型返回的 JSON 格式调整为:
      • 函数调用:{"role": "function", "content": {"function_call": {"name": "...", "arguments": {...}}}}
      • 多步骤计划:{"role": "assistant", "content": {"steps": [...]}}
      • 直接回答:{"role": "assistant", "content": "..."}
    • call_model 将消息格式化为字符串,并解析模型返回的 JSON。
  2. 对话历史
    • process_query 和 handle_response 使用 conversation_history(即 messages)跟踪对话状态。
    • 函数执行结果以 {"role": "function", "content": "..."} 的形式加入历史。
  3. 函数调用处理
    • handle_response 识别 "function_call" 字段,执行对应的函数(如 search_product 或 reason_step_by_step)。
    • reason_step_by_step 通过递归调用分解任务,符合思维链逻辑。
  4. 输出格式:比较结果使用中文格式(如 "比较结果:\n- iPhone 14\n 价格: $799.99\n 描述: ...")。

附加代码实现

import json
import sqlite3
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import re
import logging
from typing import Dict, Callable, Any, Optional# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('function_call.log'), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)# 明确指定使用 CPU
device = torch.device("cpu")
logger.info(f"Using device: {device}")# 数据库初始化
def init_database():logger.info("Initializing database")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute('''CREATE TABLE IF NOT EXISTS products(id INTEGER PRIMARY KEY, name TEXT, price REAL, description TEXT)''')sample_data = [(1, "iPhone 14", 799.99, "Latest Apple smartphone with A16 chip"),(2, "Galaxy S23", 699.99, "Samsung flagship with Snapdragon 8 Gen 2"),(3, "MacBook Pro", 1299.99, "Apple laptop with M2 chip")]c.executemany('INSERT OR IGNORE INTO products VALUES (?,?,?,?)', sample_data)conn.commit()conn.close()logger.info("Database initialized successfully")# 数据库搜索函数
def search_product_in_db(product_name: str) -> Dict[str, Any]:logger.info(f"Searching database for product: {product_name}")conn = sqlite3.connect('products.db')c = conn.cursor()c.execute("SELECT * FROM products WHERE name LIKE ?", ('%' + product_name + '%',))result = c.fetchone()conn.close()if result:product_info = {"id": result[0], "name": result[1], "price": result[2], "description": result[3]}logger.info(f"Found product: {product_info}")return product_infologger.info(f"No product found for: {product_name}")return None# MCP 函数定义
function_definitions = [{"name": "search_product","description": "在数据库中按名称搜索产品","parameters": {"type": "object","properties": {"product_name": {"type": "string", "description": "要搜索的产品名称"}},"required": ["product_name"]}},{"name": "reason_step_by_step","description": "逐步分析查询并确定行动,可调用其他函数","parameters": {"type": "object","properties": {"query": {"type": "string", "description": "要分析的用户查询"}},"required": ["query"]}}
]# 加载模型和 tokenizer
model_path = "./base_model/qwq_32b"
logger.info(f"Loading model from {model_path}")
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path)
model.eval()
logger.info("Model and tokenizer loaded successfully")# 函数处理逻辑映射表
def search_product_handler(params: Dict[str, Any], db_func: Callable) -> Dict[str, Any]:product_name = params.get("product_name")if not product_name:raise ValueError("Missing product_name in arguments")result = db_func(product_name)if result:return {"name": result["name"], "price": result["price"], "description": result["description"]}return {"error": f"No product found matching '{product_name}'"}def reason_step_by_step_handler(params: Dict[str, Any], conversation_history: list, call_model_func: Callable,handle_response_func: Callable) -> Any:query = params.get("query")if not query:raise ValueError("Missing query in arguments")conversation_history.append({"role": "user", "content": f"Step-by-step reasoning for: {query}"})step_response = call_model_func(conversation_history, function_definitions)return handle_response_func(step_response, conversation_history)# 函数映射表,包含每个函数的处理逻辑和所需参数
FUNCTION_HANDLERS = {"search_product": {"handler": search_product_handler,"args": ["params", "db_func"]},"reason_step_by_step": {"handler": reason_step_by_step_handler,"args": ["params", "conversation_history", "call_model_func", "handle_response_func"]}
}# 通用的函数执行逻辑
def execute_function(function_name: str, params: Dict[str, Any], conversation_history: list, db_func: Callable,call_model_func: Callable, handle_response_func: Callable) -> Any:func_info = FUNCTION_HANDLERS.get(function_name)if not func_info:logger.error(f"Unknown function: {function_name}")return {"error": f"Function '{function_name}' not supported"}handler = func_info["handler"]required_args = func_info["args"]args_map = {"params": params,"db_func": db_func,"conversation_history": conversation_history,"call_model_func": call_model_func,"handle_response_func": handle_response_func}# 动态选择参数call_args = [args_map[arg] for arg in required_args]try:result = handler(*call_args)if function_name != "reason_step_by_step":  # reason_step_by_step 不需要额外记录conversation_history.append({"role": "function","content": json.dumps({"name": function_name, "result": result}, ensure_ascii=False)})return resultexcept ValueError as e:logger.error(f"Function execution error: {str(e)}")return {"error": str(e)}except Exception as e:logger.error(f"Unexpected error in function {function_name}: {str(e)}")return {"error": f"Unexpected error: {str(e)}"}# MCP 请求生成和模型调用
def call_model(messages: list, functions: list) -> Dict[str, Any]:logger.info(f"Calling model with messages: {json.dumps(messages, ensure_ascii=False)}")try:last_message = messages[-1]if last_message.get("role") == "function" and "function_call" in last_message.get("content", {}):return last_messageinput_text = ""for msg in messages:role = msg["role"]content = msg["content"]if role == "system":input_text += f"[System] {content}\n"elif role == "user":input_text += f"[User] {content}\n"elif role == "function":input_text += f"[Function] {content}\n"input_text += "可用函数:\n" + json.dumps(functions, indent=2, ensure_ascii=False) + "\n"input_text += ("根据用户查询,生成一个符合 MCP 协议的 JSON 响应,包含 \"role\" 和 \"content\" 字段。\n""任务:对于复杂查询(如比较多个产品),分解为多个步骤,使用 \"search_product\" 获取每个产品信息,然后返回步骤计划。\n""响应格式:\n""- 函数调用:{\"role\": \"function\", \"content\": {\"function_call\": {\"name\": \"<函数名>\", \"arguments\": {...}}}}。\n""- 多步骤计划:{\"role\": \"assistant\", \"content\": {\"steps\": [{\"description\": \"...\", \"function_call\": {...}}, ...]}}。\n""- 直接回答:{\"role\": \"assistant\", \"content\": \"具体回答文本\"}。\n""确保 JSON 完整且可执行,仅在 [Assistant] 后输出响应,不要包含参数定义或提示中的示例文本。\n")logger.debug(f"Full input text: {input_text}")inputs = tokenizer(input_text, return_tensors="pt")with torch.no_grad():outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.7, do_sample=True)response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)logger.info(f"Raw model response: {response_text}")assistant_start = response_text.find("[Assistant]")if assistant_start != -1:assistant_text = response_text[assistant_start + len("[Assistant]"):].strip()json_match = re.search(r'\{.*\}', assistant_text, re.DOTALL)if json_match:json_str = json_match.group(0)try:parsed_json = json.loads(json_str)logger.info(f"Extracted JSON: {parsed_json}")if "role" in parsed_json and "content" in parsed_json:return parsed_jsonexcept json.JSONDecodeError as e:logger.warning(f"Failed to parse JSON from [Assistant]: {e}")logger.warning(f"No valid JSON found in [Assistant] response: {response_text}")query = messages[-1]["content"].split("User query: ")[1] if "User query: " in messages[-1]["content"] else \messages[-1]["content"]return {"role": "function","content": {"function_call": {"name": "reason_step_by_step", "arguments": {"query": query}}}}except Exception as e:logger.error(f"Error calling model: {e}")return None# 处理响应
def handle_response(response: Dict[str, Any], conversation_history: list) -> str:if 'role' not in response or 'content' not in response:logger.error(f"Invalid response structure: {response}")return "Error: Invalid response structure"role = response['role']content = response['content']if role == "function":if isinstance(content, dict) and "function_call" in content:function_call = content["function_call"]function_name = function_call.get("name")params = function_call.get("arguments", {})if isinstance(params, str):try:params = json.loads(params)except json.JSONDecodeError as e:logger.error(f"Failed to parse arguments: {e}")return "Error: Invalid function arguments"logger.info(f"Executing function: {function_name} with arguments: {params}")return execute_function(function_name, params, conversation_history, search_product_in_db, call_model,handle_response)elif role == "assistant":if isinstance(content, str):return contentelif isinstance(content, dict) and 'steps' in content:results = []for step in content.get('steps', []):if 'function_call' in step:func_response = {"role": "function", "content": {"function_call": step['function_call']}}result = handle_response(func_response, conversation_history)results.append({"step": step.get('description', 'Unknown step'), "result": result})else:results.append({"step": step.get('description', 'Unknown step'), "result": None})user_query = conversation_history[-1]["content"] if conversation_history else ""if "compare" in user_query.lower():comparison = "比较结果:\n"for res in results:if isinstance(res['result'], dict) and "name" in res['result']:comparison += f"- {res['result']['name']}\n  价格: ${res['result']['price']}\n  描述: {res['result']['description']}\n\n"else:comparison += f"{res['step']}:\n- {res['result']}\n\n"return comparison.strip()return "\n".join([f"步骤: {r['step']}\n结果: {r['result']}" for r in results])logger.error(f"Unrecognized response format: {response}")return "Error: Unrecognized response format"# 主处理逻辑
def process_query(user_query: str) -> str:logger.info(f"Processing query: {user_query}")messages = [{"role": "system", "content": "你是一个有用的助手,支持 MCP 协议。"},{"role": "user", "content": f"User query: {user_query}"}]initial_response = call_model(messages, function_definitions)print('initial_response', initial_response)if initial_response is None or isinstance(initial_response, str) and "Error" in initial_response:logger.error(f"Initial response error: {initial_response}")return initial_response if initial_response else "Sorry, there was an error processing your request."logger.info(f"Initial model response: {initial_response}")if not isinstance(initial_response, dict):logger.error(f"Invalid initial response format: {initial_response}")return "Error: Invalid response format from model"return handle_response(initial_response, messages)# 主函数
def main():init_database()user_query = "Compare iPhone 14 and Galaxy S23"print("User query:", user_query)print("\nResponse:")print(process_query(user_query))if __name__ == "__main__":main()

函数映射表(FUNCTION_HANDLERS)

  • 将 search_product 和 reason_step_by_step 的处理逻辑封装为独立函数,并通过字典映射:
    FUNCTION_HANDLERS = {"search_product": search_product_handler,"reason_step_by_step": reason_step_by_step_handler
    }
  • 新增函数只需在映射表中添加处理函数,无需修改核心逻辑。

抽象出参数验证、错误处理和结果记录:

def execute_function(function_name: str, params: Dict[str, Any], ...):handler = FUNCTION_HANDLERS.get(function_name)result = handler(params, ...)

在 execute_function 中根据 args 动态选择参数

required_args = func_info["args"]
args_map = {"params": params,"db_func": db_func,"conversation_history": conversation_history,"call_model_func": call_model_func,"handle_response_func": handle_response_func
}
call_args = [args_map[arg] for arg in required_args]
result = handler(*call_args)

大致流程是这么个着

流程:

  • role == "function" → 提取 function_name="search_product" 和 params={"product_name": "iPhone 14"}。
  • 调用 execute_function → 返回 {"name": "iPhone 14", "price": 799.99, "description": "Latest Apple smartphone with A16 chip"}。
  • 预期输出:{'name': 'iPhone 14', 'price': 799.99, 'description': 'Latest Apple smartphone with A16 chip'}。

    MCP 修改的特点

    • 标准化:函数调用使用 "function_call" 字段,符合协议规范。
    • 对话性:通过 messages 维护上下文,支持多轮交互。
    • 思维链:reason_step_by_step 负责任务分解,代码只执行模型指定的步骤。

    handle_response 方法是整个代码中处理模型响应或函数调用结果的核心逻辑。它接收模型返回的响应(response)和对话历史(conversation_history),并根据响应的结构(role 和 content)执行相应的操作,最终返回字符串形式的处理结果。以下是对这个方法的逐步解释:

    运行结果

    通过这个例子基本能明白function call 和MCP协议

      相关文章:

      理解大模型的function call ,思维链COT和MCP 协议

      在大模型中&#xff0c;function call 是指模型调用外部功能或工具以完成特定任务的过程。这种机制使得模型不仅能生成文本&#xff0c;还能执行特定的操作&#xff0c;如生成图像、获取数据或进行计算。 关键特点 功能扩展&#xff1a;通过调用外部函数&#xff0c;模型可以实…...

      K8S学习之基础三十三:K8S之监控Prometheus部署程序版

      部署 Prometheus 通常包括以下步骤&#xff1a; 1. 下载 Prometheus 首先&#xff0c;从 Prometheus 官方网站 下载适用于你操作系统的最新版本。 bash 复制 wget https://github.com/prometheus/prometheus/releases/download/v2.30.0/prometheus-2.30.0.linux-amd64.tar…...

      c语言笔记 结构体指针运用

      目录 1.结构体指针与结构体变量 2.结构体指针与结构体数组 c语言其实有时候基本知识还是一样只是说换了一个名称但是所表示的含义是一样的。 结构体指针是指针的一种类型&#xff0c;可以指向结构体变量或者结构体数组&#xff0c;下面我们来探究一下结构体指针跟结构体变量的…...

      科普类——双目立体视觉与 RGBD 相机的简单对比

      双目立体视觉与 RGBD 相机生成的深度图在原理、性能和应用场景上有显著差异。以下是两者的详细对比和分析&#xff1a; 1. 原理差异 (1) 双目立体视觉 (Stereo Vision) 原理&#xff1a; 通过两个摄像头模拟人眼视差&#xff0c;计算匹配像素点的水平位移&#xff08;视差&…...

      为什么要用linux?

      使用 Linux 有许多独特的优势&#xff0c;尤其适合技术爱好者、开发者和企业用户。以下是 选择 Linux 的主要理由&#xff0c;涵盖不同场景的需求&#xff1a; --- 1. 开源与自由 &#x1f193; - 完全免费&#xff1a;无需支付系统或软件授权费用&#xff0c;节省成本。 - 开放…...

      Linux系统管理与编程05:网络管理番外篇

      兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 0.安装VMware workstation(以下简称VW)、MobaXterm和CentOS7.x minimal版 CentOS7.x minimal安装时选择网卡连接为nat&#xff0c;过程参照我的博客&#xff08;略&#xff09;。 1.…...

      (2025|ICLR|华南理工,任务对齐,缓解灾难性遗忘,底层模型冻结和训练早停)语言模型持续学习中的虚假遗忘

      Spurious Forgetting in Continual Learning of Language Models 目录 1. 引言 2. 动机&#xff1a;关于虚假遗忘的初步实验 3. 深入探讨虚假遗忘 3.1 受控实验设置 3.2 从性能角度分析 3.3 从损失景观角度分析 3.4 从模型权重角度分析 3.5 从特征角度分析 3.6 结论 …...

      RabbitMQ 集群降配

      这里写自定义目录标题 摘要检查状态1. 检查 RabbitMQ 服务状态2. 检查 RabbitMQ 端口监听3. 检查 RabbitMQ 管理插件是否启用4. 检查开机自启状态5. 确认集群高可用性6. 检查使用该集群的服务是否做了断开重连 实操1. 负载均衡配置2. 逐个节点降配&#xff08;滚动操作&#xf…...

      Git——分布式版本控制工具使用教程

      本文主要介绍两种版本控制工具——SVN和Git的概念&#xff0c;接着会讲到Git的安装&#xff0c;Git常用的命令&#xff0c;以及怎么在Vscode中使用Git。帮助新手小白快速上手Git。如果想直接上手用Vscode操作远程仓库则直接看7和9即可&#xff01; 目录 1. SVN和Git介绍 1.1 …...

      在 Offset Explorer 中配置多节点 Kafka 集群的详细指南

      一、是否需要配置 Zookeeper&#xff1f; Kafka 集群的 Zookeeper 依赖性与版本及运行模式相关&#xff1a; Kafka 版本是否需要 Zookeeper说明0.11.x 及更早版本✅ 必须配置Kafka 完全依赖 Zookeeper 管理元数据2.8 及以下版本✅ 必须配置Kafka 依赖外置或内置的 Zookeeper …...

      nvm 安装某个node.js版本后不能使用或者报错,或不能使用npm的问题

      安装了nvm之后发现不能使用某个版本的node.js&#xff0c;报错之后&#xff0c;不能使用npm这个命令。可以这样解决&#xff1a; 1、再node.js官网直接下载node.js 的压缩包。 找到nvm的安装目录 2、直接将文件夹解压到这个安装目录中修改一下名字即可。...

      C++20 中的同步输出流:`std::basic_osyncstream` 深入解析与应用实践

      文章目录 一、std::basic_osyncstream 的背景与动机二、std::basic_osyncstream 的基本原理三、std::basic_osyncstream 的使用方法&#xff08;一&#xff09;基本用法&#xff08;二&#xff09;多线程环境下的使用&#xff08;三&#xff09;与文件流的结合 四、std::basic_…...

      美摄接入DeepSeek等大模型,用多模态融合重构视频创作新边界!

      今年以来&#xff0c;DeepSeek凭借其强大的深度推理分析能力&#xff0c;在AI领域掀起新的热潮。美摄科技快速响应市场需求&#xff0c;迅速接入以DeepSeek、通义千问、商汤、文心一言为代表的大模型&#xff0c;为企业视频创作生产带来全新体验。 传统视频创作面临着同质化、…...

      Git 使用笔记

      参考链接&#xff1a; 创建版本库 - Git教程 - 廖雪峰的官方网站 Git使用教程,最详细&#xff0c;最傻瓜&#xff0c;最浅显&#xff0c;真正手把手教 - 知乎 命令使用 cd f: 切换目录到 F 盘 cd gitCxl 切换目录到 gitCxl 文件夹 mkdir gitCxl 创建新文件…...

      C#:深入理解Thread.Sleep与Task.Delay

      1.核心区别概述 特性Thread.SleepTask.Delay阻塞类型同步阻塞当前线程异步非阻塞&#xff0c;释放线程适用场景同步代码中的简单延时异步编程中的非阻塞等待资源消耗占用线程资源&#xff08;线程挂起&#xff09;不占用线程&#xff08;通过计时器回调&#xff09;精度依赖操…...

      基于Redis实现共享token登录

      文章目录 1.集群下session共享存在的问题2.基于Redis实现共享session存储业务流程图3.具体登录的代码实现3.1 引入redis数据库3.2 发送验证码到前端3.2 登录注册功能实现3.2刷新token有效期(LoginIntereceptor)3.3 MvcConfig配置4.拦截器优化4.1增加RefreshTokenInterceptor 4.…...

      【Linux我做主】浅谈Shell及其原理

      浅谈Linux中的Shell及其原理 Linux中Shell的运行原理github地址前言一、Linux内核与Shell的关系1.1 操作系统核心1.2 用户与内核的隔离 二、Shell的演进与核心机制2.1 发展历程2.2 核心功能解析2.3 shell的工作流程1. 用户输入命令2. 解析器拆分指令3. 扩展器处理动态内容变量替…...

      vulhub/Billu_b0x靶机----练习攻略

      1.Billu_b0x靶场下载链接&#xff1a; https://download.vulnhub.com/billu/Billu_b0x.zip 2.下载后&#xff0c;解压出ova文件&#xff0c;直接拖至VMware中&#xff0c;重命名和选择存储位置&#xff0c;点击导入&#xff0c;报错点击重试即可。修改网卡为NAT模式。 打开靶…...

      【华为OD-E卷 -121 消消乐游戏 100分(python、java、c++、js、c)】

      【华为OD-E卷 - 消消乐游戏 100分(python、java、c++、js、c)】 题目 游戏规则:输入一个只包含英文字母的字符串,字符串中的两个字母如果相邻且相同,就可以消除。 在字符串上反复执行消除的动作,直到无法继续消除为止,此时游戏结束。 输出最终得到的字符串长度 输入描…...

      Qt之自定义界面组件 一

      通过qt中的painter绘图事件绘制一个电池电量图的变化。效果如下图 创建一个基于界面widget工程&#xff0c;在wdiget界面添加一个widget界面,将添加的widget界面的类提升为Tbattery.在Tbattery类中重写painEvent电池电量代码 文件目录结构 主要部分代码 //Tbattery.cpp #inc…...

      破解验证码新利器:基于百度OCR与captcha-killer-modified插件的免费调用教程

      破解验证码新利器&#xff1a;基于百度OCR与captcha-killer-modified插件的免费调用教程 引言 免责声明&#xff1a; 本文提供的信息仅供参考&#xff0c;不承担因操作产生的任何损失。读者需自行判断内容适用性&#xff0c;并遵守法律法规。作者不鼓励非法行为&#xff0c;保…...

      1-1 MATLAB深度极限学习机

      本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏目录&#xff0c;查看更多内容。 参考[1]魏洁.深度极限学习机的研究与应用[D].太原理工大学[2023-10-14].DOI:CNKI:CDMD:2.1016.714596. 目录 0.引言 1.ELM-AE实现 2.DE…...

      函数的介绍

      1.函数的概念 在C语言中也有函数的概念&#xff0c;有些翻译为&#xff1a;子程序&#xff0c;这种翻译更为准确。C语言的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。 C语言的程序其实是有无数个小的函数组合而成的&#xff0c;也可以…...

      4.3--入门知识扫盲,IPv4的头部报文解析,数据报分片,地址分类(包你看一遍全部记住)

      IPv4协议&#xff1a;网络世界的快递包裹指南&#xff08;附拆箱说明书&#xff09; “IPv4就像一张明信片&#xff0c;既要写清楚地址&#xff0c;又要控制大小别超重” —— 某网络工程师的桌面铭牌 一、IPv4报头&#xff1a;快递面单的终极艺术 1.1 报头结构图&#xff08;…...

      Linux生成自签名证书

      一、安装OpenSSL 首先确保机器已安装OpenSSL工具OpenSSL的安装可参考&#xff1a;源码编译OpenSSL 二、生成私钥​​​​​​ openssl genpkey -algorithm RSA -out server.key 三、创建证书签署请求(CSR) openssl req -new -key server.key -out server.csr 四、生成自签…...

      烽火HG680-KA_海思HI3798MV310_安卓9.0_U盘强刷固件包及注意点说明

      之前发布过这个固件包&#xff0c;关于烽火HG680-KA&#xff0f;HG680-KB_海思HI3798MV310_安卓9.0_U盘强刷固件包详细说明一下&#xff0c;汇总总结一些常遇到的情况&#xff0c;这次固件会分开发布&#xff0c;以免混淆。 上一个帖子地址&#xff1a;https://blog.csdn.net/…...

      Vue3 核心特性解析:Suspense 与 Teleport 原理深度剖析

      Vue3 核心特性解析&#xff1a;Suspense 与 Teleport 原理深度剖析 一、Teleport&#xff1a;突破组件层级的时空传送 1.1 实现原理图解 #mermaid-svg-75dTmiektg1XNS13 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-s…...

      Linux Vim 寄存器 | 从基础分类到高级应用

      注&#xff1a;本文为 “vim 寄存器” 相关文章合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;略作重排。 未整理去重&#xff0c;如有内容异常&#xff0c;请看原文。 Registers 寄存器 Learning Vim registers is like learning algebra for the first ti…...

      为什么TCP需要三次握手?一次不行吗?

      文章目录 1. 三次握手的过程2. 为什么需要三次握手&#xff1f;3. 握手过程中每一步的具体作用4. 简单比喻5. 为什么是三次握手&#xff0c;而不是两次或四次&#xff1f;6. 三次握手中的序列号有什么作用&#xff1f;7. 总结 1. 三次握手的过程 三次握手是建立 TCP 连接的过程…...

      OpenGL ES 入门指南:从基础到实战

      引言&#xff1a;为什么需要 OpenGL ES&#xff1f; 在当今的嵌入式设备&#xff08;如智能手机、汽车仪表盘、智能家居中控屏&#xff09;中&#xff0c;流畅的图形渲染能力是用户体验的核心。OpenGL ES&#xff08;OpenGL for Embedded Systems&#xff09; 作为行业标准&am…...

      台达PLC转太网转换的教程案例(台达DVP系列)

      产品介绍 台达DVP-PLC自投身工业自动化市场以来&#xff0c;始终致力于创新发展&#xff0c;不断推陈出新。其产品紧密贴合市场需求与行业工艺&#xff0c;凭借卓越性能与丰富功能&#xff0c;深受用户青睐。不仅推出了高效的程序与编辑工具&#xff0c;显著提升了主机执行速度…...

      Ubuntu24.10编译Android12源码并运行于模拟器中

      效果如下&#xff1a; 初始化环境&#xff1a; 运行lunch弹出对应目标 生成模拟器版本镜像 镜像生成成功 生成模拟器启动镜像 编译注意事项: 24.10版本&#xff1a; sudo apt install curl curl -sSL https://gerrit-googlesource.proxy.ustclug.org/git-repo//master/r…...

      Java面试易忽略知识点

      1. CompletableFuture中thenApply()与thenCompose()的区别 考察点&#xff1a;组合式异步编程 解析&#xff1a; ​**thenApply()**&#xff1a;接收前序任务结果&#xff0c;返回普通对象&#xff08;同步转换&#xff09;&#xff0c;适用简单数据处理。​**thenCompose()*…...

      C#通过SignalR直接返回流式响应内容

      1、背景 实现流式响应基本上分为两大技术方案&#xff1a;&#xff08;1&#xff09;基于HTTP的Stream处理&#xff1b;&#xff08;2&#xff09;基于socket的连接。前者的实现方式有&#xff1a;《C#通过API接口返回流式响应内容—SSE方式》和《C#通过API接口返回流式响应内…...

      【排序算法对比】快速排序、归并排序、堆排序

      排序算法对比&#xff1a;快速排序、归并排序、堆排序 1. 快速排序&#xff08;Quick Sort&#xff09; 原理 快速排序采用 分治法&#xff08;Divide and Conquer&#xff09;&#xff0c;通过选取基准值&#xff08;pivot&#xff09;&#xff0c;将数组划分为 小于基准值…...

      YOLO11改进-模块-引入空间带状注意力机制(Spatial Strip Attention,SSA)增强模型对空间信息处理能力的重要模块

      在图像相关任务中&#xff0c;传统卷积神经网络&#xff08;CNN&#xff09;在处理空间信息时&#xff0c;卷积核的感受野有限&#xff0c;难以有效捕捉长距离的空间依赖关系。而自注意力机制虽然能建模长距离依赖&#xff0c;但计算复杂度较高。为了在高效计算的同时更好地捕捉…...

      C++内存分配方式

      文章目录 1、静态内存分配2、栈内存分配3、堆内存分配4、内存池分配5、placement new语法工作原理示例 placement new应用场景 在C 中&#xff0c;内存分配主要有以下几种方式&#xff1a; 1、静态内存分配 特点&#xff1a;在编译时就确定了内存的分配和释放&#xff0c;内存…...

      【经验】Orin系列Ubuntu远程桌面:VNC、NoMachine、URDC

      1、VNC 1.1 Ubuntu端 1)安装VNC服务器 sudo apt install tigervnc-standalone-server2)安装xfce4 桌面 xfce4 用资源较GNOME ,KDE较少。适合老机器,轻量级桌面。与windows界面环境类似。 sudo apt install xfce4 xfce4-goodies也可以使用其它的桌面系统,可以使用如下命…...

      【RabbitMQ】RabbitMQ消息的重复消费问题如何解决?

      可以从消息队列和消费者两方面入手&#xff0c;确保消息处理的幂等性和可靠性。 1.消息重复消费的原因 1.1消息队列的机制 消息确认失败&#xff1a; 消费者处理完消息后&#xff0c;未正确发送确认(ACK)给RabbitMQ&#xff0c;导致消息被重新投递。消息重试机制&#xff1a…...

      Python、MATLAB和PPT完成数学建模竞赛中的地图绘制

      参加数学建模比赛时&#xff0c;很多题目——诸如统计类、数据挖掘类、环保类、建议类的题目总会涉及到地理相关的情景&#xff0c;往往要求我们制作与地图相关的可视化内容。如下图&#xff0c;这是21年亚太赛的那道塞罕坝的题目&#xff0c;期间涉及到温度、降水和森林覆盖率…...

      Git 分支删除操作指南(含本地与远程)

      &#x1f680; Git 分支删除操作指南&#xff08;含本地与远程&#xff09; 在多人协作的开发过程中&#xff0c;定期清理已合并的临时分支&#xff08;如 feature/*、bugfix/*、hotfix/* 等&#xff09;可以保持仓库整洁&#xff0c;避免混乱。 &#x1f4cc; 分支命名规范回…...

      视频推拉流EasyDSS点播平台云端录像播放异常的问题排查与解决

      视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户反馈&#xff0c;项目现…...

      v-model+computed实现父子组件数据传递和同步

      v-modelcomputed实现父子组件数据传递和同步 1. 父组件2. 子组件说明总结 1. 父组件 <template><div><span>父子组件传值&#xff1a;{{countRef}}<my-counter v-modelcount></my-counter></span></div> </template> <scr…...

      一键秒连WiFi智能设备,uni-app全栈式物联开发指南。

      如何使用 uni-app 框架实现通过 WiFi 连接设备并进行命令交互的硬件开发。为了方便理解和实践&#xff0c;我们将提供相应的源代码示例&#xff0c;帮助开发者快速上手。 1. 硬件准备 在开始之前&#xff0c;请确保你已经准备好以下硬件设备&#xff1a; 支持 WiFi 连接的设备…...

      关于Docker是否被淘汰虚拟机实现连接虚拟专用网络Ubuntu 22.04 LTS部署Harbor仓库全流程

      1.今天的第一个主题&#xff1a; 第一个主题是关于Docker是否真的被K8S弃用&#xff0c;还是可以继续兼容&#xff0c;因为我们知道在去年的时候&#xff0c;由于不可控的原因&#xff0c;docker的所有国内镜像源都被Ban了&#xff0c;再加上K8S自从V1.20之后&#xff0c;宣布…...

      【C++】动态规划从入门到精通

      一、动态规划基础概念详解 什么是动态规划 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是一种通过将复杂问题分解为重叠子问题&#xff0c;并存储子问题解以避免重复计算的优化算法。它适用于具有以下两个关键性质的问题&#xff1a; 最优子结构&…...

      【专栏预告】《VR 360°全景视频开发:从GoPro到Unity VR眼镜应用实战》

      【专栏预告】每周天12:00更新&#xff0c;欢迎指导与交流。 专栏地址&#xff1a;《VR 360全景视频开发&#xff1a;从GoPro到Unity VR眼镜应用实战》 前言 随着VR技术的不断发展&#xff0c;360全景视频的需求也在逐年增长。尤其是在VR眼镜端&#xff0c;360全景视频带来了…...

      【leetcode100】搜索插入位置

      1、题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2…...

      Java面试黄金宝典3

      1. 什么是 NIO 原理 缓冲区&#xff08;Buffer&#xff09;&#xff1a; 它是一个线性的、有限的基本数据元素序列&#xff0c;本质上是一块内存区域&#xff0c;被包装成了一个对象&#xff0c;以便于进行高效的数据读写操作。不同类型的基本数据都有对应的Buffer子类&#xf…...

      vue3 报错 Could not find a declaration file for module ‘/App.vue‘

      vue3 报错 Could not find a declaration file for module /App.vue.app.vue路径.js implicitly has an any type 问题描述原因分析&#xff1a;解决方案&#xff1a; 问题描述 Could not find a declaration file for module /App.vue.app.vue路径.js implicitly has an any …...