找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3169

积分

1

好友

440

主题
发表于 昨天 19:36 | 查看: 0| 回复: 0

该系列文章基于 github.com/shareAI-lab/learn-claude-code 写就,该仓库以大道至简的风格剖析了Claude Code的核心原理。为了便于.NET开发者学习,我已将代码基于.NET 10的 dotnet file 重写,源码已上传至GitHub(地址见文末)。

C# 实现简版 Claude Code 宣传图

本文是《Learn Claude Code (C# 版)》系列的第一篇,对应源码文件 v0_bash_agent.cs。我们将从一个极简的起点,探究智能体开发的本质。

核心问题:Agent 的本质是什么?

在构建了各种复杂的智能体系统之后,我们不妨回归起点,追问一个根本问题:Agent 的本质究竟是什么?

答案可能比想象中更简单:一个工具 + 一个循环 = 完整的 Agent 能力

为什么 Bash 就足够了?

Unix 哲学中有句名言:“一切皆文件,一切皆可管道”。而 Bash 就是打开这个世界的钥匙。几乎你能想到的所有操作,都可以通过它完成:

你需要 Bash 命令示例
读取文件 cat, head, tail, grep
写入文件 echo '...' > file, cat << 'EOF' > file
搜索 find, grep, rg, ls
执行程序 python, npm, dotnet, 任何命令
子代理 dotnet run v0_bash_agent.cs “task”

最后一行是关键洞察:通过 bash 调用程序自身,即可实现子代理机制。这为后续的递归任务分解铺平了道路。

代码解析:极简工具与核心循环

唯一的工具定义

整个 Agent 只定义了一个工具:bash。工具定义的目的就是告诉模型“你可以做什么”。

var bashTool = new Tool
{
    Name = “bash”,
    Description = “”“
        执行 shell 命令。常用模式:
        - 读取: cat/head/tail, grep/find/rg/ls, wc -l
        - 写入: echo 'content' > file, sed -i 's/old/new/g' file
        - 子代理: dotnet run v0_bash_agent.cs '任务描述'
        ”“”,
    InputSchema = new InputSchema
    {
        Type = “object”,
        Properties = new Dictionary<string, JsonElement>
        {
            [“command”] = JsonDocument.Parse(“{“type”: “string”}”).RootElement
        },
        Required = [“command”]
    }
};

注意 Description 字段不仅描述了功能,还教授了模型使用模式,这对于引导模型正确思考至关重要。

核心 Agent 循环

所有智能体的核心都可以归结为下面这个循环,它清晰展现了模型与环境的交互过程:

async Task<string> ChatAsync(string prompt, List<Message>? history = null)
{
    history ??= [];
    history.Add(prompt.AsUserMessage());

    while (true)
    {
        // 1. 调用模型
        var response = await client.CreateMessageAsync(modelId, history, tools);

        // 2. 添加助手消息到历史
        history.Add(response.AsRequestMessage());

        // 3. 如果没有工具调用,完成
        if (response.StopReason != StopReason.ToolUse)
            return ExtractText(response);

        // 4. 执行工具并添加结果
        var results = await ExecuteToolsAsync(response);
        history.Add(results);
    }
}

这个循环模式可以概括为四步:

  1. 调用模型:将当前对话历史和可用工具列表传给模型。
  2. 检查意图:模型返回的 StopReason.ToolUse 标志意味着它想调用工具。
  3. 执行工具:运行模型请求的具体命令(如 cat file.txt)。
  4. 反馈与迭代:将工具执行结果作为新消息加入历史,然后回到第1步,直到模型认为任务完成并输出最终答案。

子代理机制:递归的优雅

v0 版本最精妙的设计之一,就是通过 Bash 命令实现了子代理。这本质上是一种递归调用:

主 Agent
  |-- bash: dotnet run v0_bash_agent.cs “分析架构”
       |-- 子 Agent (隔离进程,干净历史)
            |-- bash: find . -name “*.cs”
            |-- bash: cat src/Program.cs
            |-- 通过 stdout 返回摘要

进程隔离 = 上下文隔离。每个子进程都拥有独立的对话历史,避免了探索性任务产生的中间细节污染主任务的思考上下文。这是一个简单却极其强大的设计。

系统提示的艺术

好的系统提示是引导模型行为的关键。v0 的系统提示做了以下几件事:

var systemPrompt = $”““
    你是一个位于 {workDir} 的 CLI agent。使用 bash 命令解决问题。

    规则:
    - 行动优先,简短解释。
    - 读取文件: cat, grep, find, rg, ls, head, tail
    - 写入文件: echo ‘…’ > file, sed -i, 或 cat << ‘EOF’ > file
    - 子代理: 对于复杂子任务,生成子代理:
      dotnet run v0_bash_agent.cs “探索 src/ 并总结架构”

    何时使用子代理:
    - 任务需要读取很多文件(隔离探索过程)
    - 任务独立且自包含
    - 你想避免用中间细节污染当前对话
    ”“”;
  1. 定义身份:明确告诉模型“你是一个 CLI agent”。
  2. 提供上下文:告知当前工作目录。
  3. 教授模式:具体说明如何使用 bash 工具进行读写。
  4. 引导策略:给出何时应该启动子代理的指导原则,这是高级任务规划的基础。

关键洞察

  1. 极简主义的力量:v0 证明了一个反直觉的观点——一个工具可以做一切。不需要专门的 read_filewrite_file 工具,catecho 就足够了。但代价是模型需要消耗更多 Token 来“思考”如何用 Bash 组合出所需操作。

  2. 模型即决策者:我们的代码只做两件事:提供工具(Bash)和运行循环。所有核心决策——调用什么命令、以什么顺序调用、何时停止——都交由模型完成。

  3. 递归的优雅:子代理并非新概念,它只是递归思想的应用。v0_bash_agent.cs 调用自身,就像函数递归调用一样自然且强大。

运行示例

你可以通过以下方式体验这个极简 Agent:

交互式模式

dotnet run v0_bash_agent.cs

>> 列出当前目录的 cs 文件
$ ls *.cs
v0_bash_agent.cs
v1_basic_agent.cs
...

>> 分析 v0_bash_agent.cs 的结构
$ head -50 v0_bash_agent.cs
...

子代理模式(任务分解)

dotnet run v0_bash_agent.cs “探索 src/ 并总结架构”

从 v0 到 v1:极简之后的实用化思考

v0 成功地证明了最简设计的可行性,但在实际应用中存在局限:

  1. 效率:用 cat 命令读文件,模型需要解析输出,比专用 read_file 工具消耗更多 Token,响应更慢。
  2. 安全:没有路径检查和限制,命令理论上可以逃逸出设定的工作目录。
  3. 可读性:对模型而言,echo ‘content’ > file 的意图不如 write_file 工具直观明确。

因此,在后续的 v1 版本中,我们将引入 read_filewrite_file 等专用工具,在保持核心循环不变的前提下,优化性能、安全性和易用性。这也是一个典型的 开源实战 项目迭代思路。

总结

v0 版本传达的核心哲学是:Bash 就是一切。一个工具 + 一个循环 = 完整的 Agent。

这或许不是最实用的工程实现,但它像一把手术刀,精准地揭示了智能体开发的本质:模型提供了80%的“智能”(决策与规划),而代码只负责20%的工具提供与循环调度。理解这一点,是构建更复杂 C#/.Net 智能体系统的基础。

本文涉及的 C# 版完整源码已开源,你可以在 云栈社区 的相关板块或通过原文链接找到项目地址,进行更深入的探索和实践。




上一篇:Java Lambda 表达式实战:用 Optional.orElseGet 实现延迟调用优化性能
下一篇:揭秘推理模型内部机制:思想社会、人格分工与强化学习如何提升AI推理能力
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-2 22:20 , Processed in 0.282850 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表