use tracing::warn;
use zai_rs::client::ZaiClient;
use zai_rs::model::{ChatCompletion, chat_base_response::ChatCompletionResponse, *};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
if std::env::var_os("RUST_LOG").is_some() {
let _ = tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.try_init();
}
let model = GLM4_5_flash {};
let weather_func = Function::new(
"get_weather",
"Get current weather for a city",
serde_json::json!({
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"],
"additionalProperties": false
}),
);
let tools = Tools::Function {
function: weather_func,
};
let zai_client = ZaiClient::from_env()?;
let user_text = "你是谁,帮为查找深圳今天的天气";
let mut request = ChatCompletion::new(model, TextMessage::user(user_text))
.with_thinking(ThinkingType::disabled())
.with_temperature(0.7)
.with_top_p(0.9)
.with_max_tokens(512)
.add_tool(tools);
let body: ChatCompletionResponse = request.send_via(&zai_client).await?;
let v = serde_json::to_value(&body).expect("Failed to serialize response to JSON");
println!(
"{}",
serde_json::to_string_pretty(&v).expect("Failed to format JSON")
);
if let Some((id, name, arguments)) = parse_first_tool_call(&v) {
println!("提取到的 tool_call -> name: {name}, arguments: {arguments}");
let result = handle_tool_call(&name, &arguments)
.unwrap_or_else(|| serde_json::json!({"ok": false, "error": "no_result"}));
println!(
"模拟函数返回结果: {}",
serde_json::to_string_pretty(&result).expect("Failed to format JSON")
);
let tool_msg = TextMessage::tool_with_id(
serde_json::to_string(&result).expect("Failed to serialize tool result"),
id,
);
request = request.add_messages(tool_msg).with_max_tokens(512);
let body2: ChatCompletionResponse = request.send_via(&zai_client).await?;
let v2 = serde_json::to_value(&body2).expect("Failed to serialize response to JSON");
println!(
"继续对话返回: {}",
serde_json::to_string_pretty(&v2).expect("Failed to format JSON")
);
} else {
println!("未发现 tool_calls");
}
Ok(())
}
fn parse_first_tool_call(v: &serde_json::Value) -> Option<(String, String, String)> {
let tool_calls = v.pointer("/choices/0/message/tool_calls")?.as_array()?;
let tc0 = tool_calls.first()?;
let id = tc0.get("id")?.as_str()?.to_string();
let func = tc0.get("function")?;
let name = func.get("name")?.as_str()?.to_string();
let arguments = func.get("arguments")?.as_str()?.to_string();
Some((id, name, arguments))
}
fn handle_tool_call(name: &str, arguments: &str) -> Option<serde_json::Value> {
match name {
"get_weather" => {
let parsed: serde_json::Value = match serde_json::from_str(arguments) {
Ok(v) => v,
Err(err) => {
warn!(error = %err, arguments, "解析 arguments 失败");
return Some(serde_json::json!({
"ok": false,
"error": "invalid_arguments",
"raw": arguments,
}));
},
};
let city = parsed
.get("city")
.and_then(|v| v.as_str())
.unwrap_or("未知城市");
Some(serde_json::json!({
"ok": true,
"name": name,
"request": { "city": city },
"result": {
"city": city,
"condition": "晴",
"temperature_c": 28,
"humidity": 0.65,
"tips": format!("{} 现在户外紫外线较强,注意防晒。", city),
},
"source": "mock",
}))
},
_ => {
Some(serde_json::json!({
"ok": false,
"error": "unknown_tool",
"name": name,
"raw_arguments": arguments,
}))
},
}
}