一种适合程序员的 Agent 协作方式的实践
本文使用 AI 辅助生成,本文章亦是本文章所述实践之产物。
背景
生成式 AI 在过去两年间以惊人的速度渗透到各行各业,软件工程领域尤甚。从代码补全到需求分析,从测试生成到运维诊断,几乎每一个环节都能看到 AI 的身影。然而,笔者在尝试将 AI 融入日常工作流程时发现,大多数已有的实践方式都落在两个极端之间。
一个极端是使用现成的第三方客户端或产品。这类方案的便利性毋庸置疑,开箱即用,无需任何工程投入。但问题同样明显:无法快速、灵活地为 AI 提供上下文信息。要么需要频繁补充背景知识,对话效率极低;要么 AI 在不充分的上下文中产生不符合预期的输出,徒增纠错成本。
另一个极端是使用 Agent 集群工具,例如 OpenClaw 等框架。这类方案试图通过编排多个 Agent 来解决复杂问题,但随之而来的是可观测性的缺失------你很难看清每个 Agent 在做什么、为什么这么做。要改善这一点,又需要投入大量的工程成本去搭建监控和日志体系。
之所以会有这样的感受,与程序员这个职业的特殊性有关。一方面,程序员需要向 AI 提供足量的上下文来约束其行为和输出,否则 AI 很容易偏离轨道;另一方面,程序员又不能像管理者那样高高在上地分派任务(或者说"当皇帝",笑)。他更像一个施工小队的队长,必须深入一线,及时发现并纠正 AI 的行为偏差,确保每一份产出都符合当前工作的规范。因此,可观测性和可交互性------能够实时看到 Agent 在做什么,也能随时介入调整------变得至关重要。
总而言之,在真正的通用人工智能到来之前,程序员需要像一个 Agent 一样,介入到多个 AI Agent 的工作流之中。
构思与实践
前提
在展开具体实践之前,先交代三个前提条件。
第一,使用集成开发环境(IDE)作为 Agent 协作的面板。本实践的 IDE 限定为 TRAE,因为它免费,且功能足以满足需求。更重要的是,IDE 本身就是可观测性和可交互性的来源------你能实时看到 Agent 在做什么,也能随时介入调整。
第二,使用 DeepSeek 作为 AI 模型。它足够便宜,即便是 Flash 这样较轻量的模型,产出质量也足够好。
第三,笔者有一个长期保持的习惯:使用单独的 Git 仓库作为日常工作区和知识库。在 AI 时代,这个习惯的好处被显著放大。Git 本身提供了优秀的版本控制和跨设备同步能力,而基于 Git 的语义检索又将"搜索"提升到了新的维度------你不需要记住文件存在哪里,只需要描述你想找什么。二者的结合使这套方案在信息的持久化和召回上胜过了常见的笔记系统。
核心实践
整个体系由三个子系统构成:工具系统 、记忆系统 、Skill 系统。三者各司其职,共同支撑 Agent 的高效协作。
工具系统
工具系统负责执行确定性的任务。笔者选择 .NET 10 的基于文件的应用程序(FBA)作为工具的构建基础。FBA 足够轻量,无需复杂的项目配置,一个 .cs 文件就是一个可执行的工具;而且它有编译缓存,重复执行时的冷启动开销极低。工具在设计时尽量支持管道运算,以便组合使用。
在使用 AI 调用工具的场景下,工具参数的接口设计有一个重要原则:宁可返回过量的内容,也不要增加参数来追求精确的输出。举例来说,假如要写一个获取项目连接字符串的工具,那么应该做一次全量扫描,通过模式匹配返回所有连接字符串,而不是添加一个 --project 参数来指定具体项目。原因很简单:AI 很可能会传递一个错误的参数值,而返回过量的数据至少不会漏掉目标。
核心的工具包括以下几种:
- scheduler:用于定时任务触发,基于 cron 表达式,支持超过一个周期的遗漏补偿。当 Agent 离线期间积压的任务,在恢复后能够自动补齐。
- event-logger:记录用户与 Agent 之间的交互事件,为记忆系统提供原材料。每行日志简洁精炼,仅记录关键信息。
- agent-runner:在 .NET 进程中提供一个 Agent 执行环境,用于加载外部的 Agent 定义并执行。
- vector-manage:向量索引系统,基于 Git 的变更历史做增量构建,实现工作区知识的语义检索。
记忆系统
记忆系统参考了腾讯的 TencentDB-Agent-Memory 设计,结合文件系统实现了一套分层记忆架构,并基于 Git 做增量构建和版本管理。
整个记忆系统围绕一个名为 Copilot 的 Agent 运转。Copilot 扮演个人助理的角色,能够根据用户与 Agent 的交互事件,自动构建和整理记忆。记忆的构建方式参考了 TencentDB-Agent-Memory 的分层设计:
- L0 --- 原始事件:每一条交互日志以 JSONL 格式按日期存储,作为记忆的原材料。
- L1 --- 结构化摘要:按天为单位,从原始事件中提炼关键信息,形成当日的活动摘要。
- L2 --- 上下文场景:将多个相关事件组织成场景块,描述一段完整的工作叙事。例如"SQL 优化 Skill 开发和线上实战"就是一个典型的场景。
- L3 --- 用户画像:从大量场景中蒸馏出用户的编码偏好、行为模式和关注领域,形成持久化的画像。
- L4 --- 持久化事实:原子级的知识节点,记录具体的偏好、规则和关键信息。
记忆的整理和消化由 scheduler 驱动,每 6 小时触发一次整理(L0 → L1 → L2 的流水线处理),每 12 小时触发一次消化(L2 → L3 → L4 的深度蒸馏)。这样的频率既保证了记忆的时效性,又不会过度消耗 API 配额。
Skill 系统
Skill 系统与工具系统不同,它不执行确定性的任务,而是处理那些复杂的、不确定的、需要多步推理的工作。
每个 Skill 是一份结构化的 Markdown 文档,定义了触发条件、使用流程、退出条件和输出标准。其中最具代表性的是 long-task Skill,它的执行流程体现了整个体系的核心理念。
当一个长期任务被触发时,long-task Skill 首先检索记忆系统(用户画像、相关场景、历史事实),同时搜索工作区的向量数据库,汇总出足够的信息作为任务规划的上下文。然后它理解用户的需求,创建一个 Readme.md 概述任务的目标和范围,同时创建一个 Tracking.md 记录进度和状态。
在任务真正开始执行之前,用户和 Agent 的主要工作集中在对这些前置规划的修正上------推敲目标是否清晰、范围是否合理、方案是否可行。这个过程确保了每一次执行都有一个经过审慎思考的上下文,从而产出较高质量的结果。
Skill 和工具的区别在于:工具是确定性的,同样的输入总是产生同样的输出;Skill 则不然,它可能包含多轮对话、多次工具调用、多次人工判断。工具是 Skill 的积木,Skill 是工具的使用者。
目录结构
整个 Copilot 体系以 Copilots/ 为根目录,纳入 Git 仓库管理。以下是精简后的目录结构:
Copilots/
├── agents/ # Agent 定义
│ └── copilot/ # Copilot 个人助理
│ ├── config.json # Agent 配置
│ └── skills/ # Agent 私有 Skill
│ ├── git-skill/
│ ├── memo-facts-skill/
│ ├── memo-persona-skill/
│ ├── memo-pipeline-skill/
│ ├── memo-recall-skill/
│ ├── memo-scenes-skill/
│ └── memo-summary-skill/ # 记忆分层体系(6 个 Skill + 管道编排)
│
├── commands/ # 命令定义(向用户开放的快捷入口)
├── rules/ # 规则文件(编码、写作、工作流等)
│
├── skills/ # 全局 Skill
│ ├── long-running-task-skill/ # 长期任务跟踪
│ ├── task-brainstorm-skill/ # 任务头脑风暴
│ ├── fba-tool-skill/ # FBA 工具编写规范
│ ├── yunshu-sql-optimizer-skill/# SQL 查询优化
│ └── ... # 其他 Skill
│
├── tools/ # 可执行工具
│ ├── copilot-mcp-tool/ # MCP 服务端
│ ├── scheduler-tool.cs # 定时调度器
│ ├── event-logger.cs # 事件日志
│ ├── harness-agent-tool.cs # Agent 执行环境
│ ├── guard-tool.cs # 进程守卫
│ ├── sql-server/ # SQL Server 工具集
│ ├── vector-tool/ # 向量索引工具
│ └── lib/ # 共享工具库
│
└── memories/ # 记忆系统
├── l0.events/ # 原始事件(JSONL)
├── l1.summaries/ # 结构化摘要(JSON)
├── l2.scenes/ # 上下文场景(Markdown)
├── l3.persona/ # 用户画像
└── l4.facts/ # 持久化事实(JSON)
agents/ 与 skills/ 的分离是刻意的设计:agents/copilot/skills/ 中的 Skill(记忆体系相关)仅供 Copilot Agent 自身使用,受权限控制;skills/ 中的全局 Skill 则对所有 Agent 开放。tools/ 目录的扁平化结构便于工具发现和调用,功能相关的工具以子目录分组。
整体架构
下方这张图概括了整体设计。Copilot 个人助理位于中心,对接三个子系统。TRAE 内置 Agent 同样可以调用本体系中的 Agent、Skill 和 Tool,两个 Agent 共享同一套基础设施。
工具之间并非松散堆积,而是有一条清晰的依赖链条。下方这张图展示了各工具之间的调用关系和上下层依赖:
核心的依赖关系可以概括为三层:Hooks 在最底层,负责采集一切原始输入;guard-tool 在中间层,确保常驻进程不会因异常退出而中断;scheduler 在调度层,驱动定时任务按周期执行。其余工具处于运行层------不驻留、不等待,被 MCP 或其他 Agent 按需调用,执行完成就退出。
实践案例
从构思到完成这个工程约莫花了一周时间------严格来说,编码工作几乎全部由 AI 完成,即便此刻你在阅读的这篇文章也不例外。下方选取三个具有代表性的案例,展示这套体系在实际工作中的运作方式。
案例一:设计 SQL 查询优化 Skill
背景:我司产品线基于 SQL Server,经常遇到慢查询问题。虽然已经积累了参数嗅探、快照隔离等知识点的 Wiki 文档,但整个优化流程仍然依赖人工分析,缺乏自动化手段。笔者决定开发一个标准化的 SQL 优化 Skill,让 Agent 能够自动执行从分析到建议的迭代闭环。
第 1 轮 --- 可行性分析 :Agent 首先评估了项目的技术现状,确认 SQL Server 数据库可以直连,存在连接字符串配置(main.json 中的 adoSql* 节点),具备工具开发的基础条件。Agent 输出了一份可行性分析报告,提出了四个 FBA 工具的技术方案:连接列表获取、Schema 读取、查询计划分析、查询执行。笔者确认方向可行,要求进入实施。
第 2 轮 --- 工具开发与快速迭代 :Agent 在一小时内完成了四个 FBA 工具的初版开发。笔者的要求是"先跑通再看",没有追求完美。四个工具涵盖从连接发现到执行验证的完整链路。笔者在这个过程中持续给出调整指令:工具从 Skill 目录迁移到 tools/ 下的独立目录(共享工具放 tools/sql-server/,私有工具放 tools/yunshu-sql-optimizer-skill/),增强 JSON 输出的结构化程度,添加多结果集支持,改进了 AOT 编译配置。
第 3 轮 --- 流程标准化 :Agent 设计了一套七文件的模板体系来规范优化产出。笔者审阅后给出关键意见:模板文件"太过零碎了",要求精简。最终确定为两文件范式------一份面向人的分析报告(optimization-report.md)和一份面向过程的技术记录(tracking.md),采用双环境闭环(Develop 环境验证可行性 + Debugger 环境执行线上变更)。Agent 按照这个要求重新设计了产出模板,并通过一次真实数据验证确认方案落地。
第 4 轮 --- 经验沉淀 :第一次实战暴露了 Sort 算子优化器代价与实际耗时严重脱节的问题。笔者要求补充服务器诊断步骤(sys.dm_os_wait_stats 区分 IO/锁/CPU 瓶颈),增加"读代码"前置环节(理解 SQL 是如何构造的------动态拼接还是 EF Core 生成------决定可改范围),增加可行性校验(区分"可加索引""可改写 SQL""不可修改"三种状态)。Agent 逐项更新了 SKILL.md。这些改进使 Skill 从"能跑"进化到了"可靠"的程度。
案例二:执行 SQL 查询优化 Skill------线上实战
Skill 设计完成后,立即投入了一次真实的线上 SQL 优化实战。这次实战不只是验证了 Skill 的有效性,更重要的是反哺了 Skill 本身的设计:这是一条 OnlineOrder 订单列表的分页查询,涉及 4 张表的 JOIN 加一个标量子查询(计算同物流单号的订单数)。筛选条件通过动态字符串拼接,最终 SELECT 近 50 列(含 nvarchar(MAX) 宽列)。OnlineOrder 表 51 万行的数据量不算大,但查询涉及多级排序(Number ASC, CreateTime DESC, LogisticsNumber ASC, Id ASC),加上近 50 列(含 nvarchar(MAX) 宽列)的 SELECT 负载,线上耗时高达 7,474 毫秒。排序规则硬编码在代码中,且无法使用 CTE 或两阶段拆分。
Agent 遵循刚刚完善的三步前置流程:先读了代码(确认 SQL 构造方式和可改范围),再做服务器诊断(确认 IO 是主要瓶颈),最后备份表结构。核心问题是 Sort------其 IO 代价 5,045 占查询总代价的 99.6%,严重溢出到 tempdb。
优化方案是创建一个与 ORDER BY 完全匹配的复合索引,以 IsDeleted 作为等式前导列,后续列顺序与排序完全一致。这是一个纯索引方案,零代码改动。
Develop 环境验证结果(100 条数据):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| Sort 算子 | 存在(代价 5,323) | 已消除 |
| 总预估代价 | 5,324 | 0.79(-99.98%) |
| 扫描方式 | Clustered Index Scan | Index Seek |
笔者确认后在线上执行,效果显著:
| 查询 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| COUNT | 1,817 ms | 1,499 ms | -18% |
| 数据查询 | 7,474 ms | 1,695 ms | -77% |
| Sort 算子 | 存在 | 已消除 | --- |
数据查询从 7.5 秒降至 1.7 秒,改善了 77%。更重要的是,这次实战验证了双环境闭环流程的有效性:先在 Develop 验证计划变化,再到线上体现真实耗时------Develop 数据量小但能确认执行计划变更,线上才能体现耗时优化的真实幅度。
案例三:用这套体系写一篇博客------本文的诞生
long-task 的工作流程可以概括为三步:用户给出大纲(outline.md),Agent 生成任务说明书(Readme.md)和进度跟踪文件(Tracking.md),然后双方基于这两份文件迭代推进。下面就是本文从构思到产出的完整过程。
第一步:笔者给出了一份大纲。
markdown
一种适合程序员的Agent协作方式的实践
==
注意:本文使用AI辅助生成,本文章亦是本文章所述实践之产物。
# 背景
简述AI在这个时代的快速普及,以致于各行各业都无法忽视。
笔者尝试过多种AI的使用方式,但大多数的实践方式都落在两个极端。
使用现成的第三方客户端或者产品,无法快速的为AI提供上下文信息,要么需要频繁补充上下文信息,要么产出结果不符合预期。
使用OpenClaw这样的Agent集群工具,无法获得高可观测性,或者需要大量的工程成本。
之所以会出现这样的情况,主要是因为程序员这个职业比较特殊。
一方面,需要向AI提供足量的上下文来约束AI的行为和输出,另一方面他不能当老板(或者当皇帝,笑)。
他更像一个施工小队的队长,必须及时、快速的调整AI的行为和输出,以满足当前工作的规范。所以可观测性是非常重要的。
总而言之,在AI尚未成为真正的AI之前,程序员需要像一个Agent一样介入多个Agent的工作。
# 构思与实践
## 前提
使用IDE作为Agent协作的面板,本实践限定IDE为TRAE,因为他免费,功能做的也不错。
使用deepseek作为AI模型,因为它足够便宜,而且flash模型的产出就足够好。
笔者有一个使用单独git仓储作为日常工作区和知识库的习惯,所以这个仓储作为本实践的根目录。
## 核心实践
### 工具系统
使用.NET10的FBA作为工具构建基础,因为它足够轻量,而且有编译缓存。工具实现的时候尽量能够支持管道运算。
工具主要用来快速执行确定性的任务。为了符合AI的使用习惯,工具的参数不能设计的太死板。宁愿返回过量的内容,也不要增加参数来追求精确的输出。
举个例子,
比如我要写一个工具获取项目的连接字符串。那么我宁愿做一次扫描(模式匹配),返回所有的连接字符串,也不要添加一个--project参数,因为AI可能传递一个错误的参数。
核心的工具:
-scheduler 用户定时任务触发,基于corn,并且支持>1周期的遗漏补偿。
-event-logger 用户记录用户和Agent的交互事件
-agent-runner 用于在.NET进程中提供一个Agent执行环境,以便加载外部的Agent定义并执行。
-vector-manage 向量索引系统,基于git做向量增量构建。
### 记忆系统
参考TencentDB-Agent-Memory搭建一个基于文件的记忆系统,基于git做记忆增量构建。
记忆系统定义了一个Copilot的Agent,作为个人助理,能够根据Agent和用户的交互时间,自动构建记忆。
记忆的构建方式是让AI参考TencentDB-Agent-Memory实现的。
本实践基于scheduler工具每6小时触发一次记忆整理,每12小时触发一次记忆消化。
Skill系统主要用来完成复杂的、不确定的任务。
核心的SKILL:
-long-task
该SKILL在执行之前会检索记忆系统,同时检索工作区的向量数据库,返回足够的信息用作头脑风暴上下文。
然后他会理解用户的需求,并指定一个readme.md,概述任务的需求和目标。
同时会创建一个tracking.md文件,用来记录任务的进度和状态。
在任务真正开始之前,用户和agent的主要工作都是根据这些前期规划做修正。最终这份材料会作为任务的执行上下文。从而预期一个较高质量的结果。
# 实践案例
从构思到实践这个工程约莫花了一周的事件,可以说99%的编码工作都是AI完成的,包括这篇博客。
## 让AI设计一个Sql查询优化Skill
<从对话记录中提取这部分的日志,作为案例>
## 执行这个Sql查询优化Skill
<从对话记录中提取这部分的日志,作为案例>
这份大纲本身就是一份粗粒度的"需求文档"------结构有了,方向有了,但占位符和松散的表达意味着它还需要被翻译成可执行的任务。而翻译它的,正是后面的 Readme.md。
第二步:Agent 基于大纲和头脑风暴,生成了 Readme.md。
markdown
# 博客撰写:一种适合程序员的 Agent 协作方式的实践
## 背景与动机
基于已有的博客大纲 outline.md,撰写一篇完整的博客文章,记录笔者在 yunshu-station 项目中构建的 Agent 协作实践体系。
## 目标
- 根据大纲填充所有占位内容,撰写完整博客
- 从工作区的记忆系统、任务记录、事件日志中提取素材,填充实践案例
- 博客语言为规范的现代汉语书面语
## 范围
纳入:
- 根据 outline.md 结构撰写完整博客正文
- 补全「Copilot 目录结构」部分
- 补全「实践案例」部分(SQL 优化 Skill 的设计与执行)
- 输出为独立 markdown 文件
排除:
- 不修改大纲的结构和章节划分
- 不添加大纲未提及的内容
- 不进行发布格式转换
## 技术方案
直接从工作区记忆中提取两次 SQL 优化实战的详细数据(对话轮次、SQL 语句、优化方案、效果数据),按照书面表达要求组织为案例章节。
博客产出物放置于 .temp/blog-way-of-better-cop-with-agent/blog.md。
## 依赖与前置
- outline.md 已存在
- 工作区记忆系统(l2.scenes、l4.facts)包含案例素材
## 风险
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 案例素材不够详细 | 案例部分内容单薄 | 从 Tasks/yunshu-sql-optimizer-skill 和 .temp/ 中补充读取原始数据 |
Readme.md 将大纲中的松散叙述翻译成了可执行的约束:目标量化了,范围划清了,风险有了预案。这是人和 AI 之间的一份"合同"------任何理解偏差在这里就会被发现。
第三步:同步产出 Tracking.md,用于跟踪与回溯。
markdown
# 进度跟踪 --- 博客撰写
## 当前状态
- 阶段:进行中
- 最后更新:2026-07-12 16:20
## 进度日志
### 2026-07-12 --- 任务初始化
- 做了什么:读取大纲,收集工作区记忆(用户画像、SQL 优化实战场景、Copilot 架构重构场景),完成头脑风暴信息收集
- 结果:获取了两次 SQL 优化实战的完整对话记录和效果数据、Copilot 完整目录结构、用户写作偏好
- 阻塞:无
### 2026-07-12 --- 撰写博客正文
- 做了什么:根据大纲撰写完整博客正文,包括背景、构思与实践、目录结构、整体架构图、工具依赖链图、三个实践案例
- 结果:博客已生成至 Wiki
- 阻塞:无
### 2026-07-12 --- 反复打磨
- 做了什么:多轮视角调整、架构图三版迭代、案例内容取舍、全文语言走查、总结精炼
- 结果:博客定稿
- 阻塞:无
### 2026-07-12 --- 归档与工具化
- 做了什么:创建 mermaid-tool.cs,渲染两张高质架构图,归档至 Wiki
- 结果:博客 + 图片 + 工具
- 阻塞:无
## 关键决策记录
| 日期 | 决策 | 原因 |
|------|------|------|
| 2026-07-12 | 移除 Yws#805 案例 | 案例二仅保留一次实战,更聚焦 |
| 2026-07-12 | 新增案例三 | 自指性收尾,增强说服力 |
| 2026-07-12 | mermaid 渲染方案选 mmdc + 系统 Chrome | 避免下载 Chromium,轻量化 |
Tracking.md 是整个流程的"仪表盘"。每一次修改、每一项完成,都在这里记录。任务可以中断、可以跨天、可以中途调整方向,只要打开 Tracking.md,双方立刻回到同一个上下文。
之后就是迭代:初稿生成、视角调整("用户"→"笔者")、架构图三版打磨、内容取舍。整个过程,Readme 和 Tracking 是两条不会偏离的基线------人给方向,Agent 执行,纠偏后继续。
这,就是三个案例中最后一个------也是递归性的自指收尾。用这套体系产出的文章,论证这套体系的设计。
总结
这套实践的核心可以总结为:
- 可观测性------使用 AI 驱动的 IDE 作为可观测性和可交互性的来源,让程序员能够实时看到 Agent 在做什么,随时介入纠偏。
- 基于 Agents 范式的功能扩展------持续为 Agent 新增基于场景的工作流程。
- 基于记忆和语义检索的上下文填充------通过分层记忆与向量索引,让 Agent 在任务启动时自动获取相关背景,无需反复补充。
- 基于 Git 的版本管理------代码、工具、配置、记忆全部纳入版本控制,可追溯、可回滚、可同步。
这套体系的设计初衷并非追求理论上的完美,而是在日常开发中的实用主义选择。它不要求额外的服务器或基础设施,一个 Git 仓库加一个 IDE 就是全部;它不绑定特定的 AI 模型,可以根据成本和效果自由切换。对于同样在日常开发中大量使用 AI 的程序员来说,或许能从中找到一些可借鉴的思路。