use agent_base::{AgentResult, ChatMessage, LlmClient, OpenAiClient, StreamChunk};
use futures_util::StreamExt;
async fn test_summarize(
client: &OpenAiClient,
label: &str,
enable_thinking: bool,
) -> AgentResult<()> {
println!("\n{}", "=".repeat(70));
println!("{} — thinking={}", label, enable_thinking);
println!("{}", "=".repeat(70));
let messages = vec![
ChatMessage::system("你是运维工程师助手,回复简洁直接。"),
ChatMessage::user("看下磁盘空间"),
ChatMessage::Assistant {
content: None,
reasoning_content: None,
tool_calls: Some(vec![agent_base::ToolCallMessage {
id: "call_001".to_string(),
name: "execute_command".to_string(),
arguments: r#"{"command":"df -h","target_host":"10.0.0.1"}"#.to_string(),
}]),
},
ChatMessage::Tool {
tool_call_id: "call_001".to_string(),
content: "Filesystem Size Used Avail Use% Mounted on\n/dev/vda3 40G 8.0G 30G 22% /\ntmpfs 1.8G 0 1.8G 0% /dev/shm\n/dev/vda2 197M 6.3M 191M 4% /boot/efi".to_string(),
},
];
let reasoning = agent_base::ReasoningConfig {
enabled: Some(enable_thinking),
budget_tokens: None,
effort: None,
};
let mut stream = client
.chat_stream(
&messages,
&[] as &[serde_json::Value],
Some(&reasoning),
None,
)
.await?;
let mut thought = String::new();
let mut text = String::new();
let mut tool_calls = 0;
let mut has_stop = false;
while let Some(chunk) = stream.next().await {
match chunk? {
StreamChunk::Thought(t) => {
thought.push_str(&t);
}
StreamChunk::Text(t) => {
text.push_str(&t);
}
StreamChunk::ToolCall(_) => {
tool_calls += 1;
}
StreamChunk::Stop => {
has_stop = true;
}
StreamChunk::Usage(u) => {
println!(
" 📊 prompt={}, completion={}",
u.prompt_tokens.unwrap_or(0),
u.completion_tokens.unwrap_or(0)
);
}
}
}
println!(" 💭 思考: {} 字符", thought.len());
if !thought.is_empty() {
let preview: String = thought.chars().take(200).collect();
println!(" {}", preview);
}
println!(" 📝 回复: {} 字符", text.len());
if !text.is_empty() {
let preview: String = text.chars().take(300).collect();
println!(" {}", preview);
}
println!(" 🔧 工具调用: {} | 🏁 Stop: {}", tool_calls, has_stop);
if text.is_empty() && tool_calls == 0 {
println!(" ⚠️ 空响应!react_loop 会重试");
} else if text.is_empty() && thought.len() > 0 {
println!(" ⚠️ 有思考无正文(思考内容应该作为兜底输出)");
} else {
println!(" ✅ 正常");
}
Ok(())
}
#[tokio::main]
async fn main() -> AgentResult<()> {
dotenvy::dotenv().ok();
let api_key = std::env::var("DASHSCOPE_API_KEY")
.or_else(|_| std::env::var("OPENAI_API_KEY"))
.map_err(|_| agent_base::AgentError::internal("API key 未设置"))?;
let base_url = std::env::var("DASHSCOPE_BASE_URL")
.or_else(|_| std::env::var("OPENAI_BASE_URL"))
.unwrap_or_else(|_| "https://dashscope.aliyuncs.com/compatible-mode/v1".to_string());
let models = vec![
("qwen-flash", "千问 Flash"),
("qwen-plus", "千问 Plus"),
("qwen3.7-max", "千问 Max"),
];
for (model_id, model_label) in &models {
let client = OpenAiClient::new(
api_key.clone(),
model_id.to_string(),
Some(base_url.clone()),
);
test_summarize(&client, model_label, false).await?;
test_summarize(&client, model_label, true).await?;
}
Ok(())
}