👤 用户
你从头把这个页面完整的生成一遍
🤖 DeepSeek 助手

DeepSeek 从头完整生成的 CmdCode 智能体循环页面,仅保留 OpenCode Go 渠道,使用 deepseek-v4-flash 模型。页面包含完整的智能体循环(Agent Loop)、工具调度器(Tool Executor)和提示词系统(Prompt System)三大核心模块。

完整源码

以下是在单个网页中实现智能体循环的完整 HTML 源码(约 625 行),所有请求通过 OpenCode Go API 代理:

CmdCode DeepSeek V4 Flash · OpenCode Go · 完整源码
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CmdCode · DeepSeek V4 Flash (OpenCode Go)</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; background: #f1f5f9; min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 16px; } .container { width: 100%; max-width: 900px; height: 95vh; background: #ffffff; border-radius: 24px; box-shadow: 0 30px 60px rgba(0,0,0,0.1); display: flex; flex-direction: column; overflow: hidden; } .header { background: #0f172a; color: #f8fafc; padding: 20px 24px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; border-bottom: 1px solid #1e293b; } .header-left h1 { font-size: 1.6rem; font-weight: 700; letter-spacing: -0.5px; background: linear-gradient(to right, #60a5fa, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .header-left .subtitle { font-size: 0.8rem; color: #94a3b8; margin-top: 4px; } .api-badge { background: #1e293b; padding: 8px 16px; border-radius: 40px; display: flex; align-items: center; gap: 10px; font-size: 0.85rem; color: #cbd5e1; border: 1px solid #334155; } .api-badge span:first-child { font-weight: 600; color: #e2e8f0; white-space: nowrap; } .api-badge input { background: transparent; border: none; color: white; outline: none; font-size: 0.8rem; width: 180px; padding: 4px 2px; border-bottom: 1px solid #475569; } .api-badge input:focus { border-bottom-color: #60a5fa; } .api-badge button { background: #2563eb; border: none; color: white; font-weight: 600; border-radius: 20px; padding: 6px 14px; cursor: pointer; font-size: 0.75rem; } .api-badge button:hover { background: #1d4ed8; } .key-status { font-size: 0.75rem; margin-left: 4px; } .chat-area { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 16px; background: #f8fafc; } .message { display: flex; gap: 10px; animation: fadeIn 0.3s ease; } .message.user { justify-content: flex-end; } .message .bubble { max-width: 80%; padding: 12px 18px; border-radius: 18px; line-height: 1.5; font-size: 0.9rem; word-break: break-word; white-space: pre-wrap; } .message.assistant .bubble { background: #ffffff; border: 1px solid #e2e8f0; color: #0f172a; border-bottom-left-radius: 4px; } .message.user .bubble { background: #2563eb; color: white; border-bottom-right-radius: 4px; } .message.tool .bubble { background: #f1f5f9; color: #475569; font-size: 0.8rem; border-radius: 12px; border: 1px dashed #cbd5e1; max-width: 90%; text-align: center; } .input-area { padding: 16px 24px; background: #ffffff; border-top: 1px solid #e2e8f0; display: flex; gap: 10px; align-items: flex-end; } .input-area textarea { flex: 1; border: 1px solid #e2e8f0; border-radius: 24px; padding: 12px 18px; resize: none; font-size: 0.9rem; outline: none; font-family: inherit; transition: border 0.2s; max-height: 80px; background: #f8fafc; } .input-area textarea:focus { border-color: #2563eb; background: #ffffff; } .input-area button { background: #2563eb; color: white; border: none; border-radius: 30px; padding: 12px 24px; font-weight: 600; cursor: pointer; font-size: 0.9rem; transition: 0.2s; white-space: nowrap; } .input-area button:hover { background: #1d4ed8; } .input-area button:disabled { background: #94a3b8; cursor: not-allowed; } .thinking { display: flex; align-items: center; gap: 8px; color: #64748b; font-style: italic; padding: 8px 4px; } .thinking::after { content: '...'; animation: dots 1.2s steps(4,end) infinite; } @keyframes dots { 0%,20%{content:''} 40%{content:'.'} 60%{content:'..'} 80%,100%{content:'...'} } @keyframes fadeIn { from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:translateY(0)} } </style> </head> <body> <div class="container"> <div class="header"> <div class="header-left"> <h1>⚡ CmdCode 智能体循环</h1> <div class="subtitle">模型 · deepseek-v4-flash (OpenCode Go)</div> </div> <div class="api-badge"> <span>🔑 OpenCode Go Key</span> <input type="password" id="apiKeyInput" placeholder="粘贴 API Key..."> <button id="saveKeyBtn">保存</button> <span class="key-status" id="keyStatus">⚪</span> </div> </div> <div class="chat-area" id="chatArea"> <div class="message assistant"> <div class="bubble">👋 你好!当前使用 <strong>DeepSeek V4 Flash</strong> (OpenCode Go) 模型。<br>请先输入你的 API Key...</div> </div> </div> <div class="input-area"> <textarea id="userInput" rows="1" placeholder="输入你的指令..."></textarea> <button id="sendBtn">发送</button> </div> </div> <script> (function() { const BASE_URL = 'https://opencode.ai/zen/go/v1'; const MODEL_NAME = 'deepseek-v4-flash'; const STORAGE_KEY = 'cmdcode_opencode_go_key'; // 工具定义(tools/definitions) const TOOLS = [ { type: "function", function: { name: "get_current_time", description: "获取当前时间", parameters: { type: "object", properties: {} } } }, { type: "function", function: { name: "calculator", description: "安全计算数学表达式", parameters: { type: "object", properties: { expression: { type: "string", description: "数学表达式" } }, required: ["expression"] } } }, { type: "function", function: { name: "echo", description: "回显文本", parameters: { type: "object", properties: { text: { type: "string", description: "要回显的文本" } }, required: ["text"] } } } ]; // 工具处理函数 const toolHandlers = { get_current_time: async () => new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false }), calculator: async ({ expression }) => { if (!/^[0-9+\-*/().\s]+$/.test(expression)) return '错误:非法字符'; return String(Function('"use strict"; return (' + expression + ')')()); }, echo: async ({ text }) => '回声: ' + text }; // Tool Executor class ToolExecutor { constructor(h) { this.handlers = h; } async execute(toolCalls) { const results = []; for (const call of toolCalls) { const { id, function: { name, arguments: a } } = call; let args = {}; try { args = JSON.parse(a||'{}'); } catch(e) { args={}; } const h = this.handlers[name]; if (!h) { results.push({ tool_call_id: id, role: 'tool', content: '未知工具: '+name }); continue; } try { results.push({ tool_call_id: id, role: 'tool', content: String(await h(args)) }); } catch(e) { results.push({ tool_call_id: id, role: 'tool', content: '执行异常: '+e.message }); } } return results; } } // SessionManager(Agent Loop) class SessionManager { constructor(llm, executor, systemPrompt) { this.llmCall = llm; this.toolExecutor = executor; this.messages = [{ role: 'system', content: systemPrompt }]; } pushMessage(m) { this.messages.push(m); } async run(userInput) { this.pushMessage({ role: 'user', content: userInput }); let loopCount = 0; while (loopCount < 5) { loopCount++; const response = await this.llmCall(this.messages); this.pushMessage(response); if (response.tool_calls && response.tool_calls.length > 0) { const results = await this.toolExecutor.execute(response.tool_calls); for (const r of results) this.pushMessage(r); continue; } return response; } return { role: 'assistant', content: '(已达到最大推理步数)' }; } } // OpenCode Go API 调用 function createLLM(apiKey) { return async (messages) => { const resp = await fetch(BASE_URL+'/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+apiKey }, body: JSON.stringify({ model: MODEL_NAME, messages, tools: TOOLS, temperature: 0.1 }) }); if (!resp.ok) throw new Error('API 错误 ('+resp.status+'): '+await resp.text()); const data = await resp.json(); const msg = data.choices[0].message; return { role: 'assistant', content: msg.content || null, tool_calls: msg.tool_calls || undefined }; }; } // UI const $ = id => document.getElementById(id); const chatArea = $('chatArea'), userInput = $('userInput'), sendBtn = $('sendBtn'); const apiKeyInput = $('apiKeyInput'), saveKeyBtn = $('saveKeyBtn'), keyStatus = $('keyStatus'); let isProcessing = false; let savedKey = localStorage.getItem(STORAGE_KEY) || ''; if (savedKey) { apiKeyInput.value = savedKey; keyStatus.textContent = '✅'; } saveKeyBtn.addEventListener('click', () => { const v = apiKeyInput.value.trim(); if (!v) return alert('请输入 API Key'); localStorage.setItem(STORAGE_KEY, v); keyStatus.textContent = '✅'; alert('API Key 已保存'); }); function addMessage(role, content, type) { const d = document.createElement('div'); d.className = 'message ' + (type||role); const b = document.createElement('div'); b.className = 'bubble'; if (type==='tool') b.textContent = '🔧 ' + content; else if (role==='assistant' && content) b.innerHTML = content.replace(/\n/g,'<br>'); else b.textContent = content; d.appendChild(b); chatArea.appendChild(d); chatArea.scrollTop = chatArea.scrollHeight; return d; } function addThinking() { const d=document.createElement('div'); d.className='thinking'; d.textContent='思考中'; chatArea.appendChild(d); chatArea.scrollTop=chatArea.scrollHeight; return d; } async function handleSend() { if (isProcessing) return; const text = userInput.value.trim(); if (!text) return; const apiKey = localStorage.getItem(STORAGE_KEY); if (!apiKey) { alert('请先输入并保存 OpenCode Go 的 API Key'); return; } isProcessing = true; sendBtn.disabled = true; userInput.disabled = true; addMessage('user', text); userInput.value = ''; userInput.style.height = 'auto'; const thinkingEl = addThinking(); try { const systemPrompt = '你是 CmdCode 智能助手,具备工具调用能力。可使用:get_current_time(获取时间)、calculator(计算)、echo(回显)。请使用中文回复。'; const executor = new ToolExecutor(toolHandlers); const llm = createLLM(apiKey); const session = new SessionManager(llm, executor, systemPrompt); const orig = session.llmCall; session.llmCall = async (msgs) => { const r = await orig(msgs); if (r.tool_calls) r.tool_calls.forEach(tc => addMessage('tool', '调用工具: '+tc.function.name+'('+tc.function.arguments+')', 'tool')); return r; }; const final = await session.run(text); if (thinkingEl && thinkingEl.parentNode) thinkingEl.parentNode.removeChild(thinkingEl); addMessage('assistant', final.content || '(无文本回复)'); } catch(err) { if (thinkingEl && thinkingEl.parentNode) thinkingEl.parentNode.removeChild(thinkingEl); addMessage('tool', '❌ 请求失败: '+err.message, 'tool'); } finally { isProcessing = false; sendBtn.disabled = false; userInput.disabled = false; userInput.focus(); } } sendBtn.addEventListener('click', handleSend); userInput.addEventListener('keydown', e => { if (e.key==='Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }); userInput.addEventListener('input', () => { userInput.style.height='auto'; userInput.style.height=Math.min(userInput.scrollHeight,80)+'px'; }); })(); </script> </body> </html>

源码架构分析

该 HTML 源码完整实现了 CmdCode 项目的三大核心模块:

模块行数描述
工具定义 ~10 3 个工具(get_current_time / calculator / echo)的 JSON Schema,符合 OpenAI 工具调用格式
工具执行器 ~25 ToolExecutor 类遍历工具调用列表,解析参数,执行 handler,统一返回格式
智能体循环 ~25 SessionManager 类实现 LLM ↔ 工具调用的循环,最大 5 步
API 层 ~20 createLLM 函数封装 OpenCode Go 的 /chat/completions 调用
UI 交互 ~80 消息渲染、API Key 管理、键盘事件、思考动画

与之前演示页面的核心区别

维度之前(cmdcode-mini-loop)此页面
API 供应商 DeepSeek 直连 / 模拟模式 OpenCode Go(聚合供应商)
模型 deepseek-chat / mock deepseek-v4-flash
UI 风格 渐变紫蓝 + 毛玻璃 深色极简 + 蓝色主题
密钥输入 顶部隐藏式输入 Header 内嵌式徽章组件

关于 OpenCode Go

OpenCode Go 是 Hermes Agent 内置的模型 API 聚合供应商。其端点 https://opencode.ai/zen/go/v1 兼容 OpenAI API 格式,支持多模型(包括 deepseek-v4-flash、kimi-k2.5、glm-5.1、minimax-m2.5 等)。只需一个 API Key 即可访问所有模型,自动处理负载均衡和限流。

使用方法

  1. 打开页面,在 Header 中粘贴 OpenCode Go API Key 并点击保存
  2. 在输入框中输入指令(如"现在几点?"、"计算 15*7+3")
  3. 观察智能体循环的运行过程:用户输入 → LLM 推理 → 工具调用 → 工具结果 → LLM 最终回复