Engineering note
项目启动与第一条消息:先把最小闭环跑起来
这是「Agent 工程实战」的第 3 篇。专题从一个能聊天、能调工具的 .NET Agent 出发,逐步补齐可靠性、安全、测试与发布能力。
本篇要解决的问题: 从配置、启动到第一轮对话,建立一个可以继续演进的 .NET Agent 起点。
本章定位:本章从
Program.cs开始,讨论一个控制台入口为什么适合原型、又为什么不能一直承载全部职责。你会设计退出命令、取消机制和输入命令层。建议阅读方式:先通读原理,再对照当前项目源码,最后完成本章实践。本章的实战重点是交互契约:先决定用户怎样控制 Agent,再决定代码怎样实现。
本章导读
本章从 Program.cs 开始,讨论一个控制台入口为什么适合原型、又为什么不能一直承载全部职责。你会设计退出命令、取消机制和输入命令层。
本章采用“源码观察 → 概念拆解 → 工程改造 → 实践验证”的顺序。示例中的接口和代码骨架用于说明设计方向,真正提交代码时应结合项目当前状态逐步落地。
3.1 入口代码
当前入口非常直接:
using Boo;
var agent = new BooAgent();
await agent.Start();
Start() 先调用 HandleTools(),然后无限读取控制台输入。只要输入非空,就交给 HandleMessage()。
3.2 为什么要保留简单入口
早期项目不需要一开始就引入复杂依赖注入、Web 服务和数据库。控制台入口有三个优点:
- 能快速验证模型协议;
- 能直接观察工具调用过程;
- 出错时调用链短,容易调试。
但“简单入口”不应变成“所有逻辑都放在入口附近”。随着项目增长,建议把 BooAgent 拆成会话服务、工具注册中心、模型客户端和展示层。
3.3 第一项小改进:退出命令与取消
不要让用户只能强制关闭进程。第一步可以加入:
if (message is "/exit" or "/quit")
break;
下一步用 CancellationTokenSource 响应 Ctrl+C:
using var cancellation = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cancellation.Cancel();
};
之后把 token 从 Start() 传到 HTTP 请求和工具执行方法。
3.4 本章小结
控制台程序适合验证 Agent 的核心机制,但应该从第一天就设计退出、取消和错误提示,否则后续迁移到 Web 时会被迫重写。
3.5 控制台是一个 Adapter
当前 BooAgent 同时承担了三件事:用户界面、工具发现和 Agent 编排。这样做在几十行代码时很舒服,但随着功能增加,控制台会逐渐变成“上帝类”。
可以先不做大规模重构,只要建立一个目标概念:控制台是 Adapter。
public interface IUserInput
{
Task<string?> ReadAsync(CancellationToken cancellationToken);
}
public interface IUserOutput
{
void Write(string text);
void WriteLine(string text);
}
BooAgent 以后只依赖这两个接口,Web、桌面应用或测试就可以提供不同实现。控制台不再决定 Agent 怎么思考,它只负责把输入转换成请求,把事件显示出来。
3.6 输入处理要有命令层
当用户开始使用 Agent,很快会需要 /help、/tools、/clear、/history 等控制命令。如果所有输入都直接发给模型,命令会浪费 token,也会让模型误以为用户真的要执行一个任务。
可以先用一个小的解析器:
public sealed record ConsoleCommand(string Name, string[] Arguments);
public static ConsoleCommand? TryParseCommand(string input)
{
if (!input.StartsWith('/')) return null;
var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
return new ConsoleCommand(parts[0][1..], parts.Skip(1).ToArray());
}
命令层只处理本地控制,不参与模型消息历史。这样 /clear 的含义是清空会话,而不是请模型执行一个名为 clear 的动作。
3.7 本章练习:设计 CLI 交互
为程序设计以下行为,并写出输入输出样例:
/help显示帮助;/tools列出已注册工具;/clear清理当前会话;/exit安全退出;- 空行不发送请求。
完成后再考虑把这些命令接入代码。先写交互契约,能避免后续不断修改用户体验。
3.8 本章产出
本章结束时,控制台应有明确的退出方式、取消提示、控制命令和错误显示。即使 Agent 以后迁移到 Web,这些交互约定仍然可以转化为 API 行为。
单篇实战作业
实践:设计五条 CLI 输入样例,并为每条写出预期状态和输出;先写行为,再写实现。
建议把作业拆成一个独立提交,并在提交说明中写清楚:改动前的行为、改动后的行为、验证命令、尚未解决的风险。本章的实战重点是交互契约:先决定用户怎样控制 Agent,再决定代码怎样实现。
章节复盘
复盘问题:控制台退出、取消、错误和空输入是否有稳定行为?稳定行为比漂亮提示更重要。
本章的完成标准不是把所有设计一次性做完,而是能把它变成项目中的一个明确边界,并为下一章留下可验证的接口。
下一篇:第 4 篇《模型调用与流式输出:正确处理 SSE 和增量事件》
如果你正在把 Agent 放进真实工作流,建议完成本篇的实战作业后再继续:每一步都应留下可验证的代码、测试或运行记录。