From 01a4190a28e12df434abff00083c2e2e262a4590 Mon Sep 17 00:00:00 2001 From: lingyuzeng Date: Wed, 27 Aug 2025 16:14:23 +0800 Subject: [PATCH] first add --- .gitignore | 2 + README.md | 46 ++++++ qwen3_coder_with_qwen_agent.py | 250 +++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 qwen3_coder_with_qwen_agent.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f55c65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv/ +Qwen-Agent/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2821e4 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +## Agent + +自建基于llama-box启动qwen3-code-flash-1M 的 AI Agent 代码仓库 + + +### llama-box 启动参数 + +注意: + 添加 --enable-reasoning:这个参数对 Qwen3 模型的工具调用非常重要,能够提升推理能力。 + 添加 --jinja:这个参数用于加载 Jinja 模板,Qwen3 模型需要使用 Jinja 模板进行推理。、 + +启动参数参考: + +```bash +llama-box \ + --host 0.0.0.0 \ + --port 8080 \ + --model /Volumes/long990max/gpustack_data/unsloth/Qwen3-Coder-30B-A3B-Instruct-1M-GGUF/Qwen3-Coder-30B-A3B-Instruct-1M-UD-Q4_K_XL.gguf \ + --chat-template chatml \ + --jinja \ + --enable-reasoning \ + --flash-attn \ + --cache-type-k q4_0 \ + --cache-type-v q4_0 \ + --ctx-size 262144 \ + --gpu-layers 49 \ + --threads 12 \ + --threads-batch 16 \ + --threads-http 16 \ + --batch-size 1024 \ + --ubatch-size 1024 \ + --defrag-thold -1 \ + --no-context-shift +``` + +### 安装环境 + +```bash +uv venv --managed-python -p 3.12 --seed .venv +source .venv/bin/activate +git clone https://github.com/hotwa/Qwen-Agent +cd Qwen-Agent +uv pip install -e ./"[gui,rag,code_interpreter,mcp]" +cd .. +python qwen3_coder_with_qwen_agent.py +``` \ No newline at end of file diff --git a/qwen3_coder_with_qwen_agent.py b/qwen3_coder_with_qwen_agent.py new file mode 100644 index 0000000..ba3f5fc --- /dev/null +++ b/qwen3_coder_with_qwen_agent.py @@ -0,0 +1,250 @@ +""" +Qwen3-Coder 使用 Qwen-Agent 框架测试示例 + +这个示例演示了如何使用 Qwen-Agent 框架调用 qwen3-coder 模型进行工具调用测试。 + +工具配置说明: +1. MCP工具:通过'mcpServers'配置,需要安装对应的MCP服务器 + - time: 获取当前时间的MCP工具 + - fetch: 网络请求工具 + - filesystem: 文件系统操作工具 + 这些工具需要额外安装对应的MCP服务器才能使用 + +2. 原生工具:Qwen-Agent内置的工具,直接可用 + - 'code_interpreter': 代码解释器,可以执行Python代码 + - 'image_gen': 图像生成工具 + - 'web_search': 网络搜索工具 + - 'calculator'或'python_executor': Python代码执行器(可作为计算器) + +环境变量说明: +- MAX_LLM_CALL_PER_RUN: 控制LLM调用的最大轮数(不是工具调用次数) + 这个限制是针对整个对话的LLM调用轮数,包括初始调用和后续基于工具结果的调用 +- MAX_TOOL_CALLS_PER_TURN: 控制每轮LLM输出中可触发的工具调用数量 + 这个限制是针对单次LLM响应中可以触发的工具数量,防止模型一次性触发过多工具 +""" + +import os +# 设置环境变量以控制工具调用行为 +# MAX_LLM_CALL_PER_RUN: 控制LLM调用的最大轮数(不是工具调用次数) +# MAX_TOOL_CALLS_PER_TURN: 控制每轮LLM输出中可触发的工具调用数量 +os.environ["MAX_LLM_CALL_PER_RUN"] = "8" +os.environ["MAX_TOOL_CALLS_PER_TURN"] = "8" + +import sys +import traceback +from pathlib import Path + +# 添加项目路径到Python路径 +project_root = Path(__file__).parent.parent.parent +sys.path.insert(0, str(project_root)) + +try: + from qwen_agent.agents import Assistant + from qwen_agent.gui import WebUI + from qwen_agent.utils.output_beautify import typewriter_print +except ImportError as e: + print(f"❌ 导入失败: {e}") + print("请确保已安装 qwen-agent: pip install qwen-agent") + sys.exit(1) + + +def init_agent_service(): + """初始化Agent服务""" + # 配置API参数,使用OpenAI兼容模式 + llm_cfg = { + 'model': 'qwen3-coder-flash-1M', + 'model_server': 'https://ai.jmsu.top/v1', + 'api_key': 'gpustack_96d105073565a038_23d7fe2768b4b27f9d92ab4661452ade', + 'generate_cfg': { + # 使用API的原生工具调用接口 + 'temperature': 0.7, + 'top_p': 0.8, + 'fncall_prompt_type': 'qwen', # 对 Qwen3 通常更稳 + }, + } + + # 配置工具 + tools = [ + { + 'mcpServers': { # 指定MCP配置 + 'time': { + 'command': 'uvx', + 'args': ['mcp-server-time', '--local-timezone=Asia/Shanghai'] + }, + 'fetch': { + 'command': 'uvx', + 'args': ['mcp-server-fetch'] + }, + 'filesystem': { + 'command': 'npx', + 'args': [ + '-y', + '@modelcontextprotocol/server-filesystem', + '~/Desktop/' + ] + }, + } + }, + # 添加一些原生工具示例 + 'code_interpreter', # 代码解释器 + 'web_search', # 网络搜索 + ] + + # 创建Agent + bot = Assistant( + llm=llm_cfg, + function_list=tools, + name='Qwen3-Coder Tool-calling Demo', + description="I'm a demo using the Qwen3-Coder tool calling. Welcome to add and play with your own tools!" + ) + + return bot + + +def test(query: str = '现在几点了?'): + """测试函数""" + print("=== Qwen3-Coder 使用 Qwen-Agent 测试 ===\n") + + # 初始化Agent + bot = init_agent_service() + + # 进行对话 + messages = [{'role': 'user', 'content': query}] + + print(f"💬 用户: {query}") + print("🤖 助手: ", end='', flush=True) + + # 缓存最终回复内容 + final_response = "" + current_tool_call = "" + in_tool_call = False + + try: + for response in bot.run(messages=messages): + # 处理响应 + if response: + for msg in response: + if msg['role'] == 'assistant': + if msg.get('content'): + # 检查是否包含工具调用标记 + if '[TOOL_CALL]' in msg['content']: + in_tool_call = True + current_tool_call = msg['content'] + # 清空之前的输出,准备显示工具调用 + print(f"\r🤖 助手: {msg['content']}", end='', flush=True) + elif in_tool_call: + # 继续工具调用信息 + current_tool_call += msg['content'] + print(msg['content'], end='', flush=True) + else: + # 普通回复内容 + final_response += msg['content'] + # 不再实时打印,等待工具调用完成后统一显示 + # print(msg['content'], end='', flush=True) + elif msg['role'] == 'function': + # 工具响应,显示工具结果 + in_tool_call = False + print(f"\n[TOOL_RESPONSE] {msg.get('name', 'unknown')}") + print(msg.get('content', '')) + # 重置最终回复内容,准备接收新的回复 + final_response = "" + + # 输出最终结果 + if final_response: + print("\r🤖 助手: ", end='', flush=True) + print(final_response, end='', flush=True) + print() # 输出完成后换行 + + except Exception as e: + print(f"\n❌ 调用失败: {e}") + import traceback + traceback.print_exc() + + +def app_tui(): + """命令行交互模式""" + print("=== Qwen3-Coder 使用 Qwen-Agent 命令行交互模式 ===\n") + + # 初始化Agent + bot = init_agent_service() + + # 进行对话 + messages = [] + while True: + query = input('user question: ') + if query.lower() in ['quit', 'exit', '退出']: + print("👋 再见!") + break + + messages.append({'role': 'user', 'content': query}) + response = [] + response_plain_text = '' + print("🤖 助手: ", end='', flush=True) + try: + for response in bot.run(messages=messages): + # 添加调试信息 + # print(f"[DEBUG] 收到响应: {type(response)}", file=sys.stderr) + + # 使用 typewriter_print 实现流式输出 + if response: + try: + response_plain_text = typewriter_print(response, response_plain_text) + except Exception as e: + # 如果typewriter_print出错,尝试直接打印 + # print(f"[WARNING] typewriter_print出错: {e}", file=sys.stderr) + if isinstance(response, list): + for item in response: + if isinstance(item, dict) and 'content' in item: + print(item['content'], end='', flush=True) + elif isinstance(item, dict) and 'message' in item: + print(item['message'], end='', flush=True) + print() # 输出完成后换行 + messages.extend(response) + except Exception as e: + print(f"\n❌ 调用失败: {e}") + import traceback + traceback.print_exc() + + +def app_gui(): + """图形界面模式""" + # 初始化Agent + bot = init_agent_service() + chatbot_config = { + 'prompt.suggestions': [ + '现在几点了?', + '请用Python写一个计算斐波那契数列的函数', + '帮我搜索一下Python装饰器的用法' + ] + } + WebUI( + bot, + chatbot_config=chatbot_config, + ).run() + + +def main(): + """主函数""" + print("🚀 Qwen3-Coder 使用 Qwen-Agent 测试示例") + print("=" * 50) + + print("\n请选择运行模式:") + print("1. 简单测试 - 固定问题") + print("2. 命令行交互模式") + print("3. 图形界面模式") + + choice = input("\n请输入选项 (1-3): ").strip() + + if choice == "1": + test() + elif choice == "2": + app_tui() + elif choice == "3": + app_gui() + else: + print("❌ 无效选项,运行默认测试") + test() + + +if __name__ == '__main__': + main() \ No newline at end of file