初始化项目,由ModelHub XC社区提供模型

Model: ewinregirgojr/MiniCPM5-1B-Agentic-DPO-LoRA
Source: Original Platform
This commit is contained in:
ModelHub XC
2026-07-17 22:25:17 +08:00
commit 368aa7ed79
11 changed files with 654414 additions and 0 deletions

38
.gitattributes vendored Normal file
View File

@@ -0,0 +1,38 @@
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
minicpm5-agentic-fp16.gguf filter=lfs diff=lfs merge=lfs -text
minicpm5-agentic-Q8_0.gguf filter=lfs diff=lfs merge=lfs -text
minicpm5-agentic-Q4_K_M.gguf filter=lfs diff=lfs merge=lfs -text

185
README.md Normal file
View File

@@ -0,0 +1,185 @@
---
language:
- en
license: apache-2.0
tags:
- minicpm
- tool-use
- function-calling
- lora
- gguf
- agent
datasets:
- Salesforce/xlam-function-calling-60k
base_model: openbmb/MiniCPM5-1B
---
# MiniCPM5-1B-Agentic-LoRA
[Better Finetune Repo V2 CLICK HERE](https://huggingface.co/ewinregirgojr/MiniCPM5-1B-Agentic-Tooluse-QLoRA-v2)
> **TL;DR:** This is a 1-Billion parameter autonomous agent model fine-tuned for Python dictionary tool execution. It was fine-tuned using QLoRA on a single 16GB T4 GPU using the Salesforce xLAM function-calling dataset. **Note:** This model does NOT use a standard chat template; it uses a custom raw string format with `<user>`, `<tools>`, and `<calls>` tags.
This is a fine-tuned version of the **MiniCPM5-1B** model, specifically optimized for **agentic tool use and function calling**.
This model was trained on a Google Colab T4 GPU using **Supervised Fine-Tuning (SFT)** with QLoRA on the `Salesforce/xlam-function-calling-60k` dataset to master tool usage.
> **🌱 Potential for Improvement:** Due to the strict 3-hour GPU quota limits on Google Colab, this model was trained on a randomly sampled **subset of only 10,000 examples** from the full 60k xLAM dataset. Consequently, this model has significant room for improvement! Re-training for multiple epochs on the full 60,000-sample dataset would likely yield a dramatic increase in tool-calling precision.
The repository includes the merged PyTorch weights as well as highly optimized **GGUF (FP16, Q8_0, Q4_K_M)** files for fast CPU/Edge inference.
## 🤝 Credits & Acknowledgements
This model is built on top of [MiniCPM5-1B](https://huggingface.co/openbmb/MiniCPM5-1B), developed by the **OpenBMB** team. We extend our immense gratitude to the original creators for releasing their powerful, highly efficient model under the Apache 2.0 license, which made this fine-tuning and agentic research project possible.
## 🚀 Model Details
- **Base Model**: `openbmb/MiniCPM5-1B`
- **Training Dataset**: `Salesforce/xlam-function-calling-60k` (10k subset used due to GPU limits)
- **Training Method**: SFT + QLoRA
- **Hardware Used**: NVIDIA T4 (Google Colab, 12.7GB System RAM)
- **Primary Use Case**: API calling and tool execution.
## 📝 Prompt Format (CRITICAL)
**DO NOT** use `tokenizer.apply_chat_template()` with this model. This model was trained on raw strings with a very specific, custom XML-like schema format for structuring the input.
**CRITICAL WARNING ABOUT JSON & NEWLINES:**
1. The model outputs **Python dictionary literals** (which use single quotes `'`), NOT valid JSON (which strictly requires double quotes `"`). You must use `ast.literal_eval()` instead of `json.loads()` to parse the output!
2. Do **NOT** put newlines inside the tags. The dictionaries must start immediately after the tags.
3. The tool definition schema MUST match the xLAM dataset schema (nested parameters dict).
4. The output function calls will use the key `arguments`, not `parameters`.
You must format your prompt EXACTLY like this (including the newlines):
```text
<user>Where can I find live giveaways for beta access and games?</user>
<tools>{'name': 'live_giveaways_by_type', 'description': 'Retrieve live giveaways based on the specified type.', 'parameters': {'type': {'description': 'The type of giveaways to retrieve (e.g., game, loot, beta).', 'type': 'str', 'default': 'game'}}}</tools>
<calls>
```
The model will then generate the corresponding tool calls inside the `<calls>` block as Python dictionary literals.
---
## 💻 Usage (Hugging Face `transformers`)
```python
import torch
import ast
from transformers import AutoTokenizer, AutoModelForCausalLM
model_id = "ewinregirgojr/MiniCPM5-1B-Agentic-DPO-LoRA"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
# Because the LoRA adapters were merged permanently into the base model weights,
# you can load this repository directly as a standalone CausalLM without needing the peft library!
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float16,
device_map="auto",
trust_remote_code=True
)
# You MUST construct the prompt as a raw string! Notice the lack of newlines inside <tools>.
# And you MUST use the xLAM nested-parameter schema for your tools!
prompt = """<user>Where can I find live giveaways for beta access and games?</user>
<tools>{'name': 'live_giveaways_by_type', 'description': 'Retrieve live giveaways based on the specified type.', 'parameters': {'type': {'description': 'The type of giveaways to retrieve (e.g., game, loot, beta).', 'type': 'str', 'default': 'game'}}}</tools>
<calls>"""
# If you are on a CPU, remove `.to("cuda")` below
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# CRITICAL: You must pass eos_token_id to generate() so it knows when to stop!
outputs = model.generate(
**inputs,
max_new_tokens=250,
temperature=0.1,
eos_token_id=tokenizer.eos_token_id
)
# Decode the output
response_text = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
print(response_text.strip())
# To parse the output into a Python dict, DO NOT use json.loads()! Use ast.literal_eval()
# parsed_tool = ast.literal_eval(response_text.strip())
```
---
## 🏎️ Usage (Llama.cpp Python via GGUF)
For ultra-fast inference on edge devices or CPUs, use the provided Q8 or Q4 GGUF files.
```python
# Install with GPU support (if applicable)
# CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python huggingface_hub
import ast
from llama_cpp import Llama
from huggingface_hub import hf_hub_download
# Download the Q8 Quantized Model
model_path = hf_hub_download(
repo_id="ewinregirgojr/MiniCPM5-1B-Agentic-DPO-LoRA",
filename="minicpm5-agentic-Q8_0.gguf"
)
# Load Model
llm = Llama(
model_path=model_path,
n_ctx=4096,
n_gpu_layers=-1, # Set to 0 if you are running strictly on CPU
verbose=False
)
prompt = """<user>Where can I find live giveaways for beta access and games?</user>
<tools>{'name': 'live_giveaways_by_type', 'description': 'Retrieve live giveaways based on the specified type.', 'parameters': {'type': {'description': 'The type of giveaways to retrieve (e.g., game, loot, beta).', 'type': 'str', 'default': 'game'}}}</tools>
<calls>"""
# Generate
response = llm(
prompt,
max_tokens=512,
temperature=0.1,
echo=False,
stop=["}"] # <--- CRITICAL: Forces the model to stop looping!
)
response_text = response['choices'][0]['text'].strip()
# Append the closing brace since the stop string is excluded from the output
response_text += "}"
print(response_text)
# parsed_tool = ast.literal_eval(response_text)
```
## ❓ Frequently Asked Questions (FAQ) for Developers
**Q: Can I run this model on a pure CPU without a GPU?**
**A:** Yes! The best way to run this model on a CPU is by downloading the GGUF files (`Q4_K_M` or `Q8_0`) and using `llama.cpp`. In the Python code above, simply set `n_gpu_layers=0` to force all computation onto your CPU. If you are using the `transformers` library instead, make sure you don't call `.to("cuda")` on your inputs.
**Q: Because the LoRA was merged into the model, does that mean I can't fine-tune it anymore?**
**A:** No, you can absolutely still fine-tune it! When a LoRA is merged, its weights are mathematically added into the base model's weights permanently. This means this model (`ewinregirgojr/MiniCPM5-1B-Agentic-DPO-LoRA`) can now be treated as a brand new base model. If you want to improve it (for example, by training it on the remaining 50,000 xLAM samples), you can simply load this model as your base, attach a brand new LoRA adapter to it, train it, and merge it again.
**Q: Can I use `tokenizer.apply_chat_template()` with this model?**
**A:** No. This model was fine-tuned using a custom raw string structure (`<user>`, `<tools>`, `<calls>`). Using the standard Hugging Face chat template will format the prompt incorrectly and break the tool-calling behavior.
**Q: Why do I get a `JSONDecodeError` when I use `json.loads()` on the output?**
**A:** Because of the way the training data was concatenated in Python, the model outputs **Python dictionary strings** (which use single quotes `'`), rather than valid JSON (which requires double quotes `"`). You must use `ast.literal_eval(output)` to parse the generated tool calls.
**Q: Should I use `skip_special_tokens=True` or `False`?**
**A:** You should use `skip_special_tokens=True`. The `<user>`, `<tools>`, and `<calls>` tags were treated as normal text during training and were never added to the tokenizer's special tokens list. Therefore, it is completely safe to skip special tokens—this will remove the `[EOS]` token at the end of the generation without deleting your tool tags.
**Q: How do I format the `<tools>` and `<calls>` tags?**
**A:** The dictionaries must start immediately after the opening tags and end immediately before the closing tags, with **no newlines** in between. For example: `<tools>{'name': 'my_tool'}</tools>`.
## 🛠️ Build Pipeline Details
For developers wishing to replicate this build on severely RAM-constrained environments like Google Colab T4 (12.7GB RAM):
1. **Model Loading:** The LoRA weights were merged permanently into the base model on the CPU using `device_map="cpu"` and `safe_merge=True` to prevent VRAM and system RAM fragmentation.
2. **GGUF Conversion:** Hugging Face weights were converted to `FP16 GGUF` directly on disk.
3. **Quantization:** `llama.cpp` was compiled using `cmake --target llama-quantize` to strictly build the quantization binary in under 60 seconds, bypassing the full project build to prevent OS-level out-of-memory (`cc1plus` killed) errors.

179
chat_template.jinja Normal file
View File

@@ -0,0 +1,179 @@
{{- bos_token }}{%- if tools %}
{%- set tool_definitions %}
{{- "# Tools\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
{%- for tool in tools %}
{{- "\n" }}
{{- tool | tojson(ensure_ascii=False) }}
{%- endfor %}
{{- '\n</tools>\n\nTool usage guidelines:\n- You may call zero or more functions. If no function calls are needed, just answer normally and do not include any <function ... </function>.\n- When calling a function, return an XML object within <function ... </function> using:\n<function name="function-name"><param name="param-name">param-value</param></function>\n- param-value may be multi-line. If it contains <, & or newline characters, wrap it in a CDATA block: <param name="param-name"><![CDATA[...multi-line value...]]></param>' }}
{%- endset %}
{{- '<|im_start|>system\n' }}
{%- if messages[0].role == 'system' %}
{%- if '<tool_def_sep>' in messages[0].content %}
{{- messages[0].content.replace('<tool_def_sep>', tool_definitions) }}
{%- else %}
{{- messages[0].content + '\n\n' + tool_definitions }}
{%- endif %}
{%- else %}
{{- tool_definitions.lstrip() }}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- else %}
{%- if messages[0].role == 'system' %}
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
{%- for message in messages[::-1] %}
{%- set index = (messages|length - 1) - loop.index0 %}
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
{%- set ns.multi_step_tool = false %}
{%- set ns.last_query_index = index %}
{%- endif %}
{%- endfor %}
{%- for message in messages %}
{%- if message.content is string %}
{%- set content = message.content %}
{%- else %}
{%- set content = '' %}
{%- endif %}
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
{%- elif message.role == "assistant" %}
{%- set reasoning_content = '' %}
{%- if message.reasoning_content is string %}
{%- set reasoning_content = message.reasoning_content %}
{%- else %}
{%- if '</think>' in content %}
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
{%- endif %}
{%- endif %}
{%- if message.tool_calls %}
{%- set content_parts = content.split('<tool_sep>') %}
{%- set processed_content = content_parts[0] %}
{%- set tool_calls_count = message.tool_calls|length %}
{%- set tool_sep_count = content_parts|length - 1 %}
{%- set min_count = [tool_calls_count, tool_sep_count]|min %}
{%- for i in range(1, content_parts|length) %}
{%- set tool_index = i - 1 %}
{%- if tool_index < tool_calls_count %}
{%- set tool_call = message.tool_calls[tool_index] %}
{%- if tool_call.function %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{%- set single_tool_xml %}
{{- '<function name="' ~ tool_call.name ~ '">' }}
{%- if tool_call.arguments %}
{%- set args_dict = tool_call.arguments %}
{%- for param_name, param_value in args_dict.items() %}
{{- '<param name="' ~ param_name ~ '">' }}
{%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
{{- '<![CDATA[' + param_value + ']]>' }}
{%- else %}
{{- param_value }}
{%- endif %}
{{- '</param>' }}
{%- endfor %}
{%- endif %}
{{- '</function>' }}
{%- endset %}
{%- set processed_content = processed_content + single_tool_xml + content_parts[i] %}
{%- else %}
{%- set processed_content = processed_content + content_parts[i] %}
{%- endif %}
{%- endfor %}
{%- if tool_calls_count > tool_sep_count %}
{%- for remaining_index in range(tool_sep_count, tool_calls_count) %}
{%- set tool_call = message.tool_calls[remaining_index] %}
{%- if tool_call.function %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{%- set remaining_tool_xml %}
{{- '<function name="' ~ tool_call.name ~ '">' }}
{%- if tool_call.arguments %}
{%- set args_dict = tool_call.arguments %}
{%- for param_name, param_value in args_dict.items() %}
{{- '<param name="' ~ param_name ~ '">' }}
{%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
{{- '<![CDATA[' + param_value + ']]>' }}
{%- else %}
{{- param_value }}
{%- endif %}
{{- '</param>' }}
{%- endfor %}
{%- endif %}
{{- '</function>' }}
{%- endset %}
{%- set processed_content = processed_content + remaining_tool_xml %}
{%- endfor %}
{%- endif %}
{%- set content = processed_content %}
{%- endif %}
{%- if loop.index0 > ns.last_query_index %}
{%- if reasoning_content %}
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
{%- else %}
{{- '<|im_start|>' + message.role + '\n' + content }}
{%- endif %}
{%- else %}
{{- '<|im_start|>' + message.role + '\n' + content }}
{%- endif %}
{%- if message.tool_calls and not has_tool_sep %}
{%- for tool_call in message.tool_calls %}
{%- if (loop.first and content) or (not loop.first) %}
{{- '\n' }}
{%- endif %}
{%- if tool_call.function %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{{- '<function name="' ~ tool_call.name ~ '">' }}
{%- if tool_call.arguments %}
{%- set args_dict = tool_call.arguments %}
{%- for param_name, param_value in args_dict.items() %}
{{- '<param name="' ~ param_name ~ '">' }}
{%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
{{- '<![CDATA[' + param_value + ']]>' }}
{%- else %}
{{- param_value }}
{%- endif %}
{{- '</param>' }}
{%- endfor %}
{%- endif %}
{{- '</function>' }}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- elif message.role == "tool" %}
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
{{- '<|im_start|>user' }}
{%- endif %}
{{- '\n<tool_response>\n' }}
{%- if message.content is string %}
{{- content }}
{%- else %}
{{- message.content | tojson(ensure_ascii=False) }}
{%- endif %}
{{- '\n</tool_response>' }}
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
{{- '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|im_start|>assistant\n' }}
{%- if enable_thinking is defined %}
{%- if enable_thinking is false %}
{{- '<think>\n\n</think>\n\n' }}
{%- elif enable_thinking is true %}
{{- '<think>\n' }}
{%- endif %}
{%- endif %}
{%- endif %}

35
config.json Normal file
View File

@@ -0,0 +1,35 @@
{
"architectures": [
"LlamaForCausalLM"
],
"attention_bias": false,
"attention_dropout": 0.0,
"bos_token_id": 0,
"dtype": "float16",
"eos_token_id": [
1,
130073
],
"head_dim": 128,
"hidden_act": "silu",
"hidden_size": 1536,
"initializer_range": 0.02,
"intermediate_size": 4608,
"max_position_embeddings": 131072,
"mlp_bias": false,
"model_type": "llama",
"num_attention_heads": 16,
"num_hidden_layers": 24,
"num_key_value_heads": 2,
"pad_token_id": 1,
"pretraining_tp": 1,
"rms_norm_eps": 1e-06,
"rope_parameters": {
"rope_theta": 5000000,
"rope_type": "default"
},
"tie_word_embeddings": false,
"transformers_version": "5.13.0",
"use_cache": true,
"vocab_size": 130560
}

1
generation_config.json Normal file
View File

@@ -0,0 +1 @@
{"_from_model_config": true, "bos_token_id": 0, "do_sample": true, "eos_token_id": [1, 130073, 114], "pad_token_id": 1, "temperature": 0.9, "top_p": 0.95, "transformers_version": "5.13.0"}

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f45b6e7793cc0469dab9462b1d590b2148f6c3df68272f90a14524b865a61e18
size 688065856

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:df295781eb01546b69e6fee65263c92cb18e0baad112fc858d19b487b2a29545
size 1153529152

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a536f8caa7f9c123214741a67afb454b47bb95a77afd3dcca0d695b29e502468
size 2166551872

3
model.safetensors Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ea54dda4e2d67f49ac922f5a8235e58aa81f43bf3b8b526ecf1027529163f41e
size 2161290720

653947
tokenizer.json Normal file

File diff suppressed because it is too large Load Diff

17
tokenizer_config.json Normal file
View File

@@ -0,0 +1,17 @@
{
"add_prefix_space": null,
"backend": "tokenizers",
"bos_token": "<s>",
"clean_up_tokenization_spaces": false,
"eos_token": "</s>",
"is_local": true,
"legacy": true,
"local_files_only": false,
"model_max_length": 1000000000000000019884624838656,
"pad_token": "</s>",
"sp_model_kwargs": {},
"spaces_between_special_tokens": false,
"tokenizer_class": "TokenizersBackend",
"unk_token": "<unk>",
"use_default_system_prompt": false
}