Build AI Agents with Chinese LLMs: Function Calling and Tool Use (2026)

August 13, 2026 · 20 min read

AI agents — systems that can reason, plan, and execute actions using external tools — are transforming how businesses automate workflows. Chinese LLMs now offer function calling capabilities rivaling GPT-4 at a fraction of the cost. This guide walks you through building production-ready agents with DeepSeek, GLM-4, and Qwen via TokenEase.

What Makes a Good AI Agent?

A production AI agent needs four core capabilities:

  1. Tool Definition — The LLM understands available functions and their parameters
  2. Function Calling — The model decides when and how to invoke tools
  3. Loop Control — The agent iterates until the task is complete
  4. Memory Management — Context is maintained across multi-step workflows
ModelFunction CallingParallel ToolsTool ChoiceCost per 1M tokens
GPT-4oNativeYesauto/required/none$5.00 / $15.00
DeepSeek-V4NativeYesauto/required$0.50 / $2.00
GLM-4NativeYesauto$0.70 / $2.10
Qwen-MaxNativeYesauto$0.50 / $2.00
Kimi-K2NativeYesauto$0.30 / $1.20

Step 1: Define Your Tools

Tools are defined as JSON schemas that the LLM can understand:

# Tool definitions for a data analysis agent
tools = [
    {
        "type": "function",
        "function": {
            "name": "query_database",
            "description": "Execute a SQL query against the analytics database",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "SQL SELECT query to execute"
                    },
                    "limit": {
                        "type": "integer",
                        "description": "Maximum rows to return",
                        "default": 100
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "Send an email to a specified recipient",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {
                        "type": "string",
                        "description": "Email address of recipient"
                    },
                    "subject": {
                        "type": "string",
                        "description": "Email subject line"
                    },
                    "body": {
                        "type": "string",
                        "description": "Email body content"
                    }
                },
                "required": ["to", "subject", "body"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "generate_chart",
            "description": "Create a chart from data and return as base64 PNG",
            "parameters": {
                "type": "object",
                "properties": {
                    "chart_type": {
                        "type": "string",
                        "enum": ["bar", "line", "pie", "scatter"],
                        "description": "Type of chart to generate"
                    },
                    "data": {
                        "type": "string",
                        "description": "JSON array of data points"
                    },
                    "title": {
                        "type": "string",
                        "description": "Chart title"
                    }
                },
                "required": ["chart_type", "data", "title"]
            }
        }
    }
]
Best Practice: Write descriptions as if explaining to a junior developer. The LLM uses descriptions to decide which tool to call. Vague descriptions lead to wrong tool selection.

Step 2: Basic Function Calling with TokenEase

import requests
import json

TOKEN = "your_tokenease_api_key"
BASE_URL = "https://tokenease.io/v1"

def chat_with_tools(messages, tools=None, model="deepseek-v4"):
    """Send chat completion request with tool support"""
    payload = {
        "model": model,
        "messages": messages,
        "temperature": 0.3
    }
    if tools:
        payload["tools"] = tools
        payload["tool_choice"] = "auto"
    
    response = requests.post(
        f"{BASE_URL}/chat/completions",
        headers={"Authorization": f"Bearer {TOKEN}"},
        json=payload
    )
    return response.json()

# Example: Ask the agent to analyze sales data
messages = [
    {"role": "system", "content": "You are a data analysis assistant. Use available tools to help users."},
    {"role": "user", "content": "What were our top 5 products by revenue last month?"}
]

response = chat_with_tools(messages, tools=tools)
print(json.dumps(response, indent=2))

Step 3: Handling Tool Calls and Building the Loop

class AIAgent:
    def __init__(self, model="deepseek-v4", max_iterations=10):
        self.model = model
        self.max_iterations = max_iterations
        self.messages = []
        self.tool_registry = {}
    
    def register_tool(self, name, func):
        """Register a Python function as a tool"""
        self.tool_registry[name] = func
    
    def execute_tool(self, tool_call):
        """Execute a tool call and return the result"""
        function_name = tool_call["function"]["name"]
        arguments = json.loads(tool_call["function"]["arguments"])
        
        if function_name not in self.tool_registry:
            return {"error": f"Unknown tool: {function_name}"}
        
        try:
            result = self.tool_registry[function_name](**arguments)
            return {"result": result}
        except Exception as e:
            return {"error": str(e)}
    
    def run(self, user_input, tools=None):
        """Main agent loop"""
        self.messages.append({"role": "user", "content": user_input})
        
        for i in range(self.max_iterations):
            # Get LLM response
            response = chat_with_tools(self.messages, tools, self.model)
            message = response["choices"][0]["message"]
            
            # Add assistant message to history
            self.messages.append(message)
            
            # Check if tool calls were made
            tool_calls = message.get("tool_calls", [])
            
            if not tool_calls:
                # No tool calls — return the final answer
                return message["content"]
            
            # Execute each tool call
            for tool_call in tool_calls:
                result = self.execute_tool(tool_call)
                
                # Add tool result to messages
                self.messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call["id"],
                    "content": json.dumps(result)
                })
        
        return "Max iterations reached without completing the task."

# Initialize agent
agent = AIAgent(model="deepseek-v4")

# Register actual tool implementations
import sqlite3

def query_database(query, limit=100):
    conn = sqlite3.connect("analytics.db")
    cursor = conn.cursor()
    cursor.execute(query + f" LIMIT {limit}")
    rows = cursor.fetchall()
    columns = [description[0] for description in cursor.description]
    conn.close()
    return {"columns": columns, "rows": rows}

def send_email(to, subject, body):
    # Integration with your email service
    return {"status": "sent", "to": to, "subject": subject}

agent.register_tool("query_database", query_database)
agent.register_tool("send_email", send_email)

# Run the agent
result = agent.run(
    "What were our top 5 products by revenue last month? Send the results to manager@company.com",
    tools=tools
)
print(result)

Step 4: Multi-Step Reasoning with Planning

For complex tasks, add a planning layer before execution:

class PlanningAgent(AIAgent):
    def plan(self, user_input):
        """Generate a step-by-step plan"""
        plan_prompt = f"""Given the user request, create a step-by-step plan.
Available tools: query_database, send_email, generate_chart

User request: {user_input}

Respond with a JSON array of steps:
[{{"step": 1, "action": "tool_name", "reason": "why"}}, ...]"""
        
        response = chat_with_tools(
            [{"role": "user", "content": plan_prompt}],
            model=self.model
        )
        
        plan_text = response["choices"][0]["message"]["content"]
        # Extract JSON from response
        try:
            plan = json.loads(plan_text[plan_text.find("["):plan_text.rfind("]")+1])
            return plan
        except:
            return [{"step": 1, "action": "direct_response", "reason": "Simple query"}]
    
    def run_with_plan(self, user_input, tools=None):
        """Execute with planning"""
        plan = self.plan(user_input)
        print(f"Plan: {json.dumps(plan, indent=2)}")
        
        # Execute each step
        for step in plan:
            if step["action"] == "direct_response":
                return self.run(user_input, tools)
            else:
                # Execute specific tool step
                pass
        
        return self.run(user_input, tools)

# Example: Complex multi-step task
complex_task = """
1. Query last month's sales data
2. Identify top 5 products
3. Generate a bar chart of their revenue
4. Email the chart to the sales team
"""
agent = PlanningAgent(model="glm-4")
result = agent.run_with_plan(complex_task, tools=tools)

Step 5: Adding Memory and Context

class StatefulAgent(PlanningAgent):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.memory = []  # Short-term memory
        self.facts = {}   # Extracted facts
    
    def extract_facts(self, text):
        """Extract key facts from conversations"""
        prompt = f"Extract key facts from this text as JSON key-value pairs:\n{text}"
        response = chat_with_tools([{"role": "user", "content": prompt}], model=self.model)
        try:
            facts = json.loads(response["choices"][0]["message"]["content"])
            self.facts.update(facts)
        except:
            pass
    
    def get_context(self):
        """Build context from memory and facts"""
        context = "Known facts:\n"
        for k, v in self.facts.items():
            context += f"- {k}: {v}\n"
        context += f"\nRecent conversation:\n"
        for msg in self.memory[-5:]:  # Last 5 interactions
            context += f"{msg['role']}: {msg['content']}\n"
        return context
    
    def run(self, user_input, tools=None):
        # Enhance user input with context
        context = self.get_context()
        enhanced_input = f"Context: {context}\n\nCurrent request: {user_input}"
        
        result = super().run(enhanced_input, tools)
        
        # Store interaction in memory
        self.memory.append({"role": "user", "content": user_input})
        self.memory.append({"role": "assistant", "content": result})
        
        # Extract facts
        self.extract_facts(result)
        
        return result

# Conversation continuity
agent = StatefulAgent(model="deepseek-v4")
agent.run("My company is Acme Corp and I'm the CTO", tools=tools)
agent.run("What's our server uptime?", tools=tools)  # Knows "our" = Acme Corp

Step 6: Error Handling and Recovery

class RobustAgent(StatefulAgent):
    def execute_tool(self, tool_call):
        """Execute with retry and error handling"""
        function_name = tool_call["function"]["name"]
        arguments = json.loads(tool_call["function"]["arguments"])
        
        max_retries = 3
        for attempt in range(max_retries):
            try:
                result = self.tool_registry[function_name](**arguments)
                return {"status": "success", "result": result}
            except Exception as e:
                if attempt < max_retries - 1:
                    # Ask LLM to fix arguments
                    fix_prompt = f"""Tool '{function_name}' failed with: {str(e)}
Arguments: {json.dumps(arguments)}
Please provide corrected arguments as JSON."""
                    
                    fix_response = chat_with_tools(
                        [{"role": "user", "content": fix_prompt}],
                        model=self.model
                    )
                    try:
                        new_args = json.loads(fix_response["choices"][0]["message"]["content"])
                        arguments = new_args
                    except:
                        pass
                else:
                    return {"status": "error", "error": str(e), "attempts": attempt + 1}
    
    def run(self, user_input, tools=None):
        """Run with comprehensive error handling"""
        try:
            result = super().run(user_input, tools)
            return result
        except Exception as e:
            fallback_prompt = f"""The agent encountered an error: {str(e)}
User request: {user_input}
Please provide a helpful response or ask clarifying questions."""
            
            response = chat_with_tools(
                [{"role": "user", "content": fallback_prompt}],
                model=self.model
            )
            return response["choices"][0]["message"]["content"]

# Production-ready agent
agent = RobustAgent(model="glm-4", max_iterations=15)
agent.register_tool("query_database", query_database)
agent.register_tool("send_email", send_email)

Advanced: Parallel Tool Execution

DeepSeek-V4 and GLM-4 support calling multiple tools in parallel:

def execute_parallel(agent, tool_calls):
    """Execute independent tool calls in parallel"""
    from concurrent.futures import ThreadPoolExecutor
    
    def execute_single(tc):
        return agent.execute_tool(tc)
    
    with ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(execute_single, tool_calls))
    
    return results

# When the LLM returns multiple tool_calls, execute them in parallel
if len(tool_calls) > 1:
    results = execute_parallel(agent, tool_calls)
else:
    results = [agent.execute_tool(tool_calls[0])]

Security Best Practices

def validate_sql_query(query):
    """Prevent destructive SQL operations"""
    forbidden = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "CREATE"]
    upper_query = query.upper()
    for word in forbidden:
        if word in upper_query:
            return False, f"Forbidden operation: {word}"
    if not upper_query.strip().startswith("SELECT"):
        return False, "Only SELECT queries allowed"
    return True, "Valid"

# Wrap database tool with validation
def safe_query_database(query, limit=100):
    is_valid, msg = validate_sql_query(query)
    if not is_valid:
        return {"error": msg}
    return query_database(query, limit)

Cost Optimization for Agents

StrategySavingsImplementation
Tool result caching30-50%Cache repeated queries
Model cascading60%Use Kimi for planning, DeepSeek for execution
Early termination20%Stop when answer is sufficient
Batch tool calls15%Combine independent queries

Real-World Example: Customer Support Agent

# Complete customer support agent
cs_tools = [
    {
        "type": "function",
        "function": {
            "name": "search_knowledge_base",
            "description": "Search help articles for solutions",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "check_order_status",
            "description": "Look up order by ID",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {"type": "string"}
                },
                "required": ["order_id"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "create_ticket",
            "description": "Escalate to human support",
            "parameters": {
                "type": "object",
                "properties": {
                    "issue": {"type": "string"},
                    "priority": {"type": "string", "enum": ["low", "medium", "high"]}
                },
                "required": ["issue", "priority"]
            }
        }
    }
]

# Agent handles:
# "Where is my order ORD-12345?" → check_order_status
# "How do I reset my password?" → search_knowledge_base
# "I was charged twice" → create_ticket(high) + check_order_status

Next Steps

Start building your AI agent today with TokenEase:

  1. Get your API key (free $1 credit)
  2. Start with DeepSeek-V4 for reliable function calling
  3. Build your tool registry incrementally
  4. Add planning and memory for complex workflows

For more on production patterns, see our guides on failover and load balancing and A/B testing AI models.

Last updated: August 2026. Function calling capabilities vary by model version.