老胡茶室
老胡茶室

Groq Code CLI Internal

foxgem

一直以来都打算研究一下 Claude Code 或 Gemini CLI 这类 Coding CLI 的内部机制。但苦于为生计项目奔波,一直没能抽出时间深入研究。

最近,看到有人发推 Groq Code CLI,而它又看起来像是简版的 Claude Code 或 Gemini CLI ,于是乎决定花些时间研究一下。

Not Yet Another Coding CLI

Groq Code CLI 的 README 写得非常详尽,包含了很多细节,在此不再赘述。

唯一需要特别强调的是:Groq Code CLI 的异类。它并没打算成为新的 Claude Code 或 Gemini CLI,而是为开发者提供了一个白标解决方案,让他们能够构建自己的 Coding CLI。

It is a blueprint, a building block, for developers looking to leverage, customize, and extend a CLI to be entirely their own.

架构

Groq Code CLI 的代码仓库非常精简,文件数量不多。在快速浏览之后,我决定让 Gemini CLI 为我生成一个架构图。

graph TD
    subgraph User Interaction
        A[User Terminal]
    end

    subgraph CLI Application
        B[CLI Entry Point 
---
src/core/cli.ts] C[UI
---
Ink/React - src/ui/App.tsx] end subgraph Core Logic D[Agent
---
src/core/agent.ts] E[Groq API] F[Tools
---
src/tools] G[Commands
---
src/commands] end subgraph Data H[Local Settings
---
.groq/] end A -- "groq [options]" --> B B -- "Starts" --> C C -- "User Input/Commands" --> G C -- "User Input" --> D G -- "Executes" --> C D -- "Processes input" --> E E -- "Returns response" --> D D -- "Executes tools" --> F F -- "Returns results" --> D D -- "Updates" --> C G -- "Reads/Writes" --> H

简单吧,😄!

看完之后,就清楚接下来的重点了:AgentTools。至于像 TUI 等其他部分,则不是我关注的重点。

缺省系统提示词

src/core/agent.ts 中,你可以找到 Groq Code CLI 的缺省系统提示词,其内容并不难理解:

You are a coding assistant powered by ${this.model} on Groq. Tools are available to you. Use tools to complete tasks.

CRITICAL: For ANY implementation request (building apps, creating components, writing code), you MUST use tools to create actual files. NEVER provide text-only responses for coding tasks that require implementation.

Use tools to:
- Read and understand files (read_file, list_files, search_files)
- Create, edit, and manage files (create_file, edit_file, list_files, read_file, delete_file)
- Execute commands (execute_command)
- Search for information (search_files)
- Help you understand the codebase before answering the user's question

IMPLEMENTATION TASK RULES:
- When asked to "build", "create", "implement", or "make" anything: USE TOOLS TO CREATE FILES
- Start immediately with create_file or list_files - NO text explanations first
- Create actual working code, not example snippets
- Build incrementally: create core files first, then add features
- NEVER respond with "here's how you could do it" - DO IT with tools

FILE OPERATION DECISION TREE:
- ALWAYS check if file exists FIRST using list_files or read_file
- Need to modify existing content? → read_file first, then edit_file (never create_file)
- Need to create something new? → list_files to check existence first, then create_file
- File exists but want to replace completely? → create_file with overwrite=true
- Unsure if file exists? → list_files or read_file to check first
- MANDATORY: read_file before any edit_file operation

IMPORTANT TOOL USAGE RULES:
  - Always use "file_path" parameter for file operations, never "path"
  - Check tool schemas carefully before calling functions
  - Required parameters are listed in the "required" array
  - Text matching in edit_file must be EXACT (including whitespace)
  - NEVER prefix tool names with "repo_browser."

COMMAND EXECUTION SAFETY:
  - Only use execute_command for commands that COMPLETE QUICKLY (tests, builds, short scripts)
  - NEVER run commands that start long-running processes (servers, daemons, web apps)
  - Examples of AVOIDED commands: "flask app.py", "npm start", "python -m http.server"
  - Examples of SAFE commands: "python test_script.py", "npm test", "ls -la", "git status"
  - If a long-running command is needed to complete the task, provide it to the user at the end of the response, not as a tool call, with a description of what it's for.

IMPORTANT: When creating files, keep them focused and reasonably sized. For large applications:
1. Start with a simple, minimal version first
2. Create separate files for different components
3. Build incrementally rather than generating massive files at once

Be direct and efficient.

Don't generate markdown tables.

When asked about your identity, you should identify yourself as a coding assistant running on the ${this.model} model via Groq.

Agent Loop 和 Tool Execution

在同一个文件中,你可以找到著名的 agent loop。详见下图:

sequenceDiagram
    participant User
    participant ChatUI as "Chat UI (React/Ink)"
    participant Agent as "Agent Core (agent.ts)"
    participant Groq
    participant Tools as "Tool Executor (tools.ts)"

    User->>ChatUI: Enters message
    ChatUI->>Agent: sendMessage(userInput)
    Agent->>Groq: client.chat.completions.create()
    Groq-->>Agent: Response (with tool_calls)

    alt Tool requires approval
        Agent->>ChatUI: onToolApproval() callback
        ChatUI->>User: Show PendingToolApproval UI
        User->>ChatUI: Approves/Rejects tool
        ChatUI-->>Agent: Resolves promise with approval
    end

    alt Tool Approved
        Agent->>Tools: executeTool(toolName, toolArgs)
        Tools-->>Agent: Tool Result (success/failure)
        Agent->>Groq: client.chat.completions.create() (with tool result)
        Groq-->>Agent: Final Response (text)
        Agent->>ChatUI: onFinalMessage() callback
        ChatUI->>User: Display final message
    else Tool Rejected
        Agent->>ChatUI: onToolEnd() with rejection
        ChatUI->>User: Display tool canceled message
    else No Tool Call
        Groq-->>Agent: Final Response (text)
        Agent->>ChatUI: onFinalMessage() callback
        ChatUI->>User: Display final message
    end

从中,还可以看到 Tool Execution 的机制:

  1. Groq 返回一个包含 tool_calls 的响应,它只是一组待执行工具的定义,类型为 any[]
  2. 实际执行由 src/core/tools.ts 文件中的 executeTool 完成。

复杂请求的任务管理

与许多其他 coding agent 一样,Groq Code CLI 也用任务管理系统来处理复杂的请求。这通过它在 src/tools/tools.ts 中定义的任务管理工具来实现:

// Tool Registry: maps tool names to functions
export const TOOL_REGISTRY = {
  ...
  create_tasks: createTasks,
  update_tasks: updateTasks,
};

整个流程可以看作是一种 tool execution:

sequenceDiagram
    participant User
    participant ChatUI as "Chat UI"
    participant Agent as "Agent Core"
    participant Groq
    participant Tools

    User->>ChatUI: Enters complex request (e.g., "build a login page")
    ChatUI->>Agent: sendMessage(userInput)
    Agent->>Groq: client.chat.completions.create()

    Groq-->>Agent: Response (tool_calls: create_tasks)
    Agent->>Tools: executeTool('create_tasks', tasks)
    Tools-->>Agent: Tool Result (success, task list)
    Agent->>ChatUI: onToolEnd() callback (displays task list)
    ChatUI-->>User: Show created task list

    Agent->>Groq: client.chat.completions.create() (with task list result)
    Groq-->>Agent: Response (tool_calls: execute first task, e.g., create_file)
    Agent->>Tools: executeTool('create_file', ...)
    Tools-->>Agent: Tool Result (success)
    Agent->>ChatUI: onToolEnd() callback

    Agent->>Groq: client.chat.completions.create() (with tool result)
    Groq-->>Agent: Response (tool_calls: update_tasks)
    Agent->>Tools: executeTool('update_tasks', {id: "1", status: "completed"})
    Tools-->>Agent: Tool Result (success, updated list)
    Agent->>ChatUI: onToolEnd() callback (updates task list)
    ChatUI-->>User: Show updated task list (task 1 is done)

    Note over Agent, Groq: Loop continues for remaining tasks...

    Groq-->>Agent: Final Response (text)
    Agent->>ChatUI: onFinalMessage() callback
    ChatUI->>User: Display final message

注意:对于这些工具,不需要用户确认,因为它们是安全工具:

// Safe tools that can be auto-executed without approval
export const SAFE_TOOLS = [
  'read_file',
  'list_files',
  'search_files',
  'create_tasks',
  'update_tasks'
];

Never Be Blocked

我敢保证你一定遇到过这样的情况:在使用 VS Code 的 GitHub Copilot 或其他工具时,agent 陷入低效循环,无法产生任何有价值的结果。此时,agent 会问是否继续。

Groq Code CLI 也有类似设计:

while (true) { // Outer loop for iteration reset
  while (iteration < maxIterations) {
    // Check for interruption before each iteration
    if (this.isInterrupted) {
      debugLog('Chat loop interrupted by user');
      this.currentAbortController = null;
      return;
    }
...

流程图如下:

graph TD
    A[Start] --> B{Agent Processing Loop};
    B -- "iteration < 50" --> C[API Call & Tool Execution];
    C --> D[iteration++];
    D --> B;
    B -- "iteration >= 50" --> E{Max Iterations Reached};
    E --> F[Pause Agent & Prompt User];
    F --> G{Continue?};
    G -- "User selects Yes" --> H[Reset iteration = 0];
    H --> B;
    G -- "User selects No" --> I[Stop Processing];
    I --> J[End];

写到最后

若只是对 agent 相关逻辑感兴趣,以上就是全部内容。如果对 TUI 感兴趣,则可以研究一下 ink