DC娱乐网

NET+AI | Agent 产品开发实战 | 02. 开发一个 A...

本课时定位上一课我们讲的是产品起点:想法可以很大,但起点一定要低。这一课开始,我们进入 Agent 开发本身。但注意,这
本课时定位

上一课我们讲的是产品起点:

想法可以很大,但起点一定要低。

这一课开始,我们进入 Agent 开发本身。

但注意,这一课依然不是要把所有概念一次性讲完。

一开始真不用学太多。

你只需要先搞明白 5 个东西:

IChatClient

Prompt

Function Calling

Skills

Agent

这几个点串起来,就足够支撑你做出第一个可运行 Agent。

不要一上来就被这些词吓住:

Multi-Agent

Workflow

Orchestration

Harness

Memory

MCP

Planner

Evaluator

Guardrails

Context Engineering

这些以后都会遇到。

但不是第一天都要懂。

第一天最重要的是建立一个最小知识闭环:

这一课的目标不是“懂完 Agent”。

而是先知道:

一个最小 Agent 到底由哪些东西组成,它们各自解决什么问题。

一、先别急着学一堆 Agent 概念

很多人学 Agent,一开始就会陷入概念焦虑。

今天看到一个新框架。

明天看到一个新协议。

后天又看到一个新词:

AutoGPT

ReAct

Plan-and-Execute

Function Calling

Tool Calling

MCP

Agent Runtime

Agent Memory

Agent Workflow

Agent Harness

Multi-Agent Collaboration

看起来都很重要。

也确实重要。

但问题是:

你还没有做出第一个能跑通的 Agent,就开始试图理解完整 Agent 世界观。

这会让学习变得很重。

更糟的是,你可能会误以为:

不把这些概念都学完,就不能开始做 Agent。

其实不是。

开发第一个 Agent,只需要先回答几个非常朴素的问题:

我的 .NET 程序怎么先连上一个可用模型?

我要让模型扮演什么角色?

模型什么时候需要调用外部能力?

这些外部能力怎么暴露给模型?

当某个能力复用多次时,怎么沉淀下来?

对应到技术上,就是这一课的 5 个关键词。

二、IChatClient:先把模型连接起来

在讲 Prompt 之前,我更建议先做一件很朴素的事:

先让你的 .NET 程序真的连上一个模型。

否则后面讲再多 Agent、工具、Skills,都会停留在概念层。

在 .NET 里,我非常建议一开始就熟悉 IChatClient这个抽象。

它来自 Microsoft.Extensions.AI,作用是把模型调用统一成一个比较稳定的接口。

你可以先不用太关心底层是:

DeepSeek

Azure OpenAI

OpenAI

GitHub Models

Ollama

其他兼容 OpenAI 协议的模型服务

对业务代码来说,最好先只关心一件事:

我有一个 IChatClient,可以给它消息,它返回模型响应。

用 DeepSeek 作为第一例

对国内开发者来说,DeepSeek 很适合作为入门示例。

原因很简单:

它容易申请和使用

成本相对友好

支持 OpenAI 兼容接口

很多现有 SDK 和框架可以复用 OpenAI 调用方式

所谓“兼容 OpenAI 接口”,可以先简单理解成:

代码仍然使用 OpenAI 风格的客户端,只是把 endpoint 换成 DeepSeek,把 model 换成 DeepSeek 的模型名。

例如最小代码可以这样写:

这段代码里有几个点很重要:

OpenAIClient

仍然是 OpenAI 风格客户端

Endpoint

改成了 DeepSeek 的地址

deepseek-chat

是 DeepSeek 的通用对话模型

.AsIChatClient

把具体客户端转换成 MEAI 的统一抽象

如果你要用 DeepSeek 的推理模型,通常只需要把模型名换成 deepseek-reasoner。

当然,“OpenAI 兼容”不代表所有高级参数、响应细节、工具行为都完全一致。

但对入门阶段来说,先跑通普通对话调用已经够了。

后面我们会演示 Function Calling。这里先提前提醒一句:DeepSeek 兼容 OpenAI 接口,适合演示基础对话和很多常见调用方式;但具体到工具调用、并发工具调用、流式工具调用等高级能力时,要以当前模型和 SDK 的实际支持为准。

如果某个兼容模型在工具调用上表现不稳定,可以先用 OpenAI 或 Azure OpenAI 验证机制,再回到兼容模型上做适配。

为什么先讲 IChatClient

因为它会帮你把两件事分开:

业务上:这个 Agent 要完成什么任务

技术上:底层到底调用哪个模型

今天你可以先用 DeepSeek。

明天你可以换成 Azure OpenAI。

后天你可以接 GitHub Models 或本地 Ollama。

只要业务代码尽量围绕 IChatClient写,后面切换模型供应商就不会那么痛。

实际项目里当然还会有:

模型配置

API Key

Endpoint

ChatOptions

工具注册

流式响应

错误处理

观测日志

但第一天不要被这些细节压住。

先建立一个最小认知:

IChatClient 是模型能力进入 .NET 应用的统一入口。

三、Prompt:再把任务说清楚

模型连上以后,下一步才是 Prompt。

Prompt 解决的是“怎么让模型按你的产品目标工作”。

很多人一开始会把 Prompt 当成一句自然语言指令:

请帮我生成一篇文章。

这当然也是 Prompt。

但对 Agent 产品来说,Prompt 更像是一个行为契约。

它至少要说清楚:

你是谁

你服务谁

你负责什么

你不负责什么

你应该如何提问

你应该如何输出

什么时候应该调用工具

什么情况下应该停下来等用户确认

继续沿用前面的写作助手例子。

如果它只是这样写:

你是一个写作助手,请帮助用户写文章。

这太宽了。

模型会不知道该直接写文章,还是先问问题,还是生成标题,还是开始调研。

更好的方向是:

你是一个写作助手。你的任务是先识别用户的写作意图:他想写什么、写给谁看、想解决什么问题。如果只是写作目标、读者、角度不清楚,就先提问;如果涉及不确定事实、近期趋势、外部资料或你不清楚的背景,再使用联网搜索工具补充信息。不要为了显得主动而乱搜,也不要直接生成完整正文。

这里最关键的不是文字变长了。

而是边界变清楚了。

落到 .NET 代码里,Prompt 通常不会只是一段孤立字符串,而是会变成一组带角色的消息:

然后把这组消息交给刚才创建好的 IChatClient:

这段代码看起来很简单,但它已经体现了 Agent Prompt 的基本结构:

System

消息定义角色、任务和边界

User

消息提供当前输入

约束写在 Prompt 里,减少模型乱跑

Agent 的 Prompt,不是为了显得高级。

而是为了减少模型自由发挥的空间。

Prompt 的第一原则:先限制,再发挥

很多人写 Prompt,喜欢强调“你要做什么”。

但 Agent Prompt 更重要的是写清楚“你现在不要做什么”。

比如:

不要直接生成完整文章

不要在用户确认前进入下一阶段

不要编造外部事实

不要把临时偏好写成长期记忆

不要把工具调用结果当成绝对真理

这听起来很啰嗦。

但这正是 Agent 产品化和普通聊天机器人的区别。

普通聊天机器人追求回答得像样。

Agent 追求在一个受控流程里持续推进任务。

四、Function Calling:让 Agent 不只是会说话

如果一个 Agent 只能输出文本,它很容易变成一个“会说话的聊天机器人”。

但产品里的 Agent,通常需要做事。

比如 AI WritingFlow 里,一个 Agent 可能需要:

搜索资料

抓取网页

保存写作简报

读取用户偏好

写入文章草稿

生成封面图

更新工作流状态

这些能力不能只靠 Prompt。

你需要把外部能力暴露给模型。

这就是 Function Calling 或 Tool Calling 的价值。

你可以把它理解成:

我把一些函数注册给模型,模型在需要时可以选择调用它们。

比如概念上:

在 Microsoft.Extensions.AI里,可以把普通 C# 方法包装成模型可调用的工具。

下面是一个最小示例:

这里有几个关键点:

[Description]

不是装饰品,它会帮助模型理解工具用途和参数含义

UseFunctionInvocation

让工具调用可以被自动执行

ChatOptions.Tools

控制这个 Agent 当前能看到哪些工具

Prompt 里要写清楚“什么时候调用工具,什么时候不要调用”

这一段演示的是 MEAI 层的直接模型调用:你仍然在直接使用IChatClient.GetResponseAsync(...),只是给这个模型调用增加了工具能力。

后面第六章会演示 MAF 层的 Agent 封装:同样基于IChatClient,但会用chatClient.AsAIAgent(...)创建一个真正的AIAgent,再通过RunAsync(...)执行任务。

可以先这样理解:

IChatClient

是模型调用底座

GetResponseAsync(...)

适合理解最小模型调用和工具调用

AIAgent.RunAsync(...)

适合把提示词、工具、Skills、运行时上下文组织成一个角色来执行

模型看到这个工具以后,就可以在合适的时候调用它。

但这里有一个很重要的提醒:

工具不是越多越好。

第一版 Agent 的工具越多,模型越容易选错。

你也越难判断问题出在哪里。

所以早期工具设计要尽量小:

一个工具只做一件事

工具名字要清楚

参数要少

返回结果要可解释

不要把复杂业务流程塞进一个万能工具

工具的第一原则:让模型少猜

坏工具通常长这样:

它什么都能做。

也意味着模型不知道它到底该什么时候用。

好工具更像这样:

它们边界更窄。

模型更容易判断。

人也更容易调试。

五、Skills:先认识可复用的专业能力

如果说 Tool 解决的是“能做事”。

那 Skills 解决的是“会做得更像样”。

Tool 更像函数。

Skill 更像方法、经验、流程和领域规则。

换句话说:

Tool 偏执行:模型决定调用一个外部函数,拿到结果再继续生成

Skill 偏方法论:告诉 Agent 如何更专业地完成某类任务

Tool 解决“做什么动作”

Skill 解决“按什么方法做得更好”

继续沿用前面的写作助手例子。

WebSearchAsync是一个 Tool。

它解决的是:

当模型需要外部资料时,怎么真的去查一下。

但写作过程中还有另一类能力,并不适合做成函数。

比如:

什么时候应该在文章里加图

什么内容适合用流程图

什么内容适合用时序图

Mermaid 语法怎么写才不容易错

图表应该如何服务文章表达,而不是为了画图而画图

这些不是“执行一个动作”。

它们更像写作方法、表达经验和领域规则。

这类能力就更适合沉淀成 Skill。

比如可以引入一个现成的 Mermaid 图表 Skill:

https://www.skills.sh/softaworks/agent-toolkit/mermaid-diagrams

这个 Skill 的目标是帮助 Agent 创建专业的软件图表,使用 Mermaid 的文本语法生成 flowchart、sequence diagram、class diagram 等图表。

对写作助手来说,它的价值很直接:

当用户要写一篇技术文章时,Agent 不只是生成文字,还能判断哪些地方适合插入 Mermaid 图表。

比如:

解释 AI Coding 工作流时,用流程图

解释 Agent 调用工具时,用时序图

解释系统架构时,用组件图或 flowchart

解释状态流转时,用 state diagram

这比把所有 Mermaid 语法规则都塞进 Prompt 里更好维护。

你可以先通过 skills.sh 安装这个 Skill:

安装后,假设它位于本地 skills/mermaid-diagrams目录。

真实路径取决于你的 skills CLI 配置和项目结构,代码里按实际路径调整即可。

这一节先只需要理解:Skill 是一个可以独立维护、可以被 Agent 复用的能力包。

它通常包含:

任务目标

使用场景

输入要求

输出格式

示例

禁止事项

质量检查标准

这比把所有规则都塞进一个超长 Prompt 更可维护。

这也是 Skills 和普通 Prompt 的区别。

Prompt 更像当前 Agent 的角色说明。

Skill 更像可以复用、可以组合、可以替换的专业能力包。

但它能帮你先理解一个关键思想:

Skill 是可以被多个 Agent 复用的领域规则和操作方法,不一定一开始就要做成复杂运行时。

Skills 的第一原则:等重复出现后再沉淀

不要第一天就写一堆 Skills。

因为你还不知道哪些能力真的会重复。

更稳的方式是:

先在 Prompt 里写清楚规则

连续几次发现同类规则重复出现

再把它抽出来变成 Skill

后续多个 Agent 共享这个 Skill

这和代码抽象很像。

不是看起来能复用就抽象。

而是重复真实出现后再抽象。

六、Agent:把模型、提示词、工具和 Skills 组织起来

有了 IChatClient、Prompt、Function Calling 和 Skills,还不等于你有了一个清晰的 Agent。

Agent 更像是把这些东西组织起来后的“角色单元”。

一个最小 Agent 通常包含:

名字

目标

系统提示词

可用工具

可用技能

运行时约束

输入输出约定

在 .NET 里,不需要自己先造一个 AgentDefinition类型来理解 Agent。

更自然的方式,是直接用 Microsoft.Extensions.AI提供模型与工具抽象,再用 Microsoft Agent Framework 把它包装成AIAgent。

这一节我们把前面的 WebSearchAsyncTool 和mermaid-diagramsSkill 一起接进同一个写作 Agent。

下面这个例子不是让你一开始就照搬完整工程结构,而是为了看清楚 Tool、Skill 和 Agent 在 .NET 里的组合关系。

这里的关系很清楚:

chatClient来自Microsoft.Extensions.AI,负责统一模型调用AIFunctionFactory.Create(...)

来自 MEAI,负责把 C# 方法变成 Tool

AgentSkillsProvider

负责把 Skill 提供给 Agent

AIContextProviders

是 Agent 获取额外上下文能力的入口

chatClient.AsAIAgent(...)

来自 Microsoft Agent Framework,负责创建真正的 Agent

agent.RunAsync(...)

才是对这个 Agent 发起一次任务执行

这比一开始自己定义一套 Agent 元数据模型更适合入门。

先用框架最原生的方式跑通,再根据产品复杂度决定要不要做自己的注册、路由、配置和装配层。

以 AI WritingFlow 为例,Clarification Agent 和 Topic Agent 不应该只是两个不同 Prompt。

它们应该是两个职责不同的角色。

Clarification Agent 关注:

用户想写什么

为什么写

给谁看

想表达什么观点

还缺哪些信息

Topic Agent 关注:

哪些选题方向更值得展开

哪些方向有读者痛点

哪些方向更适合当前作者

哪些方向可形成文章结构

这两个 Agent 的目标不同,工具可能相似,但行为边界不一样。

如果你不把 Agent 职责写清楚,后面就会出现几个问题:

Clarification Agent 忍不住开始写正文

Topic Agent 忍不住开始润色标题

Draft Agent 忍不住重新选题

Polish Agent 忍不住重写整篇文章

这些不是模型“坏”。

这是角色边界不清。

Agent 的第一原则:少即是多

第一版不要急着拆很多 Agent。

一个 Agent 能解决的问题,就先用一个 Agent。

当你真的发现:

目标不同

输入输出不同

工具集合不同

用户交互方式不同

失败处理方式不同

再考虑拆成第二个 Agent。

不要因为“多 Agent 听起来更高级”就拆。

拆 Agent 的目的不是炫技。

而是降低单个 Agent 的认知负担。

七、把 5 个概念串起来

现在我们把这一课的 5 个关键词串起来。

一个最小 Agent 可以这样理解:

用一句话说:

IChatClient 先把模型能力接入 .NET,Prompt 定义行为边界,Function Calling 让模型能调用外部工具,Skills 沉淀可复用专业能力,Agent 最后把这些组织成一个清晰角色。

这就是第一个 Agent 的最小知识闭环。

你不需要一开始就懂完整 Agent 架构。

你只要先能用这 5 个概念解释清楚一个 Agent 是怎么跑起来的,就已经可以开始做第一版了。

八、AI WritingFlow 的第二课结论

如果回到 AI WritingFlow,第二课的结论很明确:

第一版不需要一口气做完完整写作工作流。

也不需要一开始就设计多 Agent 协作系统。

我们只需要先做出一个很小的 Agent:

用户输入一个模糊写作想法,Agent 先根据 Prompt 识别写作意图,通过 IChatClient 调用模型;当涉及不确定事实、近期趋势或外部资料时,模型可以调用 WebSearchAsync补充信息;当文章内容适合可视化表达时,Agent 可以参考mermaid-diagramsSkill 生成合适的 Mermaid 图表,最后输出一个更清晰的写作角度或小节规划。

这里面已经包含了最小 Agent 所需的核心能力:

IChatClient:完成模型调用

Prompt:定义写作助手的行为边界

Function Calling:连接联网搜索等外部能力

Skills:沉淀 Mermaid 图表写作这类可复用专业能力

Agent:把模型、提示词、工具和 Skills 组织成一个明确角色

这就够了。

第一版不要追求完整。

先追求跑通。

跑通以后,再看真实问题在哪里。

九、课堂练习

请基于你自己的 Agent 产品想法,完成下面 5 个问题。

练习 1:给你的第一个 Agent 起名字

不要叫 MainAgent。

名字要体现职责。

示例:

Writing Assistant Agent

Topic Discovery Agent

Research Assistant

Draft Coach

Code Review Agent

练习 2:写一句 Agent 目标

模板:

这个 Agent 的目标是帮助【目标用户】完成【一个明确任务】,并产出【一个可见结果】。

示例:

Writing Assistant Agent 的目标是帮助内容创作者识别写作意图,必要时联网补充资料,并产出可继续展开的文章角度。

练习 3:写 3 条“不做什么”

示例:

不直接生成完整文章

不在用户确认前进入选题阶段

不编造外部事实

练习 4:列出最多 3 个工具

第一版最多 3 个。

示例:

搜索资料

抓取网页内容

读取用户偏好

如果你列出了 10 个工具,说明你的第一版可能太大了。

练习 5:判断是否需要 Skill

问自己:

这个能力是否已经重复出现?是否值得多个 Agent 复用?

如果答案是否,那就先不要抽成 Skill。

先放在 Prompt 里。

十、本课小结

这一课我们只建立了 Agent 的最小知识闭环。

你需要记住 5 件事:

IChatClient 是模型能力进入 .NET 应用的统一入口

Prompt 不是一句指令,而是 Agent 的行为契约

Function Calling 让 Agent 从“会说话”变成“能做事”

Skills 是等稳定能力重复出现后,再沉淀出来的复用机制

Agent 是把模型、Prompt、工具、Skills 和约束组织起来的角色单元

下一课我们会进入技术选型:

为什么对 AI WritingFlow 来说,.NET Minimal API、Microsoft Agent Framework、EF Core、SQLite、React、Tailwind、shadcn/ui 是一套够快、够稳、也适合 AI Coding 协作的组合。

但在进入技术选型前,先别急。

请先把你的第一个 Agent 定义清楚。

一个职责清楚的小 Agent,比一个边界模糊的大系统更有价值。

课后作业

请写一页纸,不超过 600 字,包含:

第一个 Agent 的名字

这个 Agent 的一句话目标

这个 Agent 的 Prompt 边界:做什么 / 不做什么

第一版最多 3 个工具

暂时不做的 5 个高级能力

如果你写完以后发现这个 Agent 仍然什么都想做,那就继续砍。

砍到它只负责一个最小闭环为止。