大语言模型应用层学习笔记
前言
经过快速的发展,人工智能已经不再遥不可及,就在我们的身边触手可及。特别是2026年,特殊的年份。喜欢看国外科幻片的人一定对人工智能很熟悉。一幕幕神奇的场面,或已经发生,或在不久的将来得以实现。大语言模型应用是人工智能的一个分支。揭开大语音模型应用神秘的面纱,发现技术的发展是一步一个脚印。大语言模型应用实现接地气,在各领域的知识上进行构建。让人觉得更加亲近。
大语言模型本质是基于海量文本数据训练的自回归 Transformer,通过学习词语间的概率分布,依据上文预测下一个最合理的 token,从而生成连贯文本。大语言模型应用有个专有名称是 Agent。Agent 是具备感知、规划、调用工具并循环迭代,自主完成复杂目标的智能体。模型底座、工具、知识库等工具构建大语言模型应用。
- 大语言模型领域的名词概念
| 名称 | 中文 | 解释 | 场景 |
|---|---|---|---|
| 基础概念 | |||
| LLM(Large Language Model) | 大语言模型 | 基于 Transformer 架构、海量文本预训练的生成式 AI 模型,能理解、生成人类自然语言 | 聊天机器人、文案生成、代码编写、问答系统、文档总结 |
| ReAct(Reason + Act) | 思考 - 行动智能体范式 | 交替执行思考 (Reason)、调用工具行动 (Act),循环迭代获取信息再回答,解决复杂推理任务 | 需要多轮搜索、多工具调用的复杂问答、数学计算、数据调研 Agent |
| OODA | 观察 - 判断 - 决策 - 行动循环 | Agent 决策循环框架:Observe (观察环境)→Orient (分析信息)→Decide (制定计划)→Act (执行动作),循环迭代 | 自主规划类 Agent、自动化运维机器人、长期多步骤复杂任务智能体 |
| Reasoning | 逻辑推理 | 模型链式思考、逻辑推导、计算、因果分析,解决复杂、多步骤问题(CoT 核心) | 数学解题、逻辑分析、复杂问题拆解、法律 / 医疗专业问答 |
| Planning | 任务规划 | Agent 自主拆解复杂目标,生成分步执行计划,按顺序调度工具与子任务 | 项目自动化、长流程办公任务、多工具协同复杂任务 |
| Agent Loop | 智能体自主思考循环 | Agent 核心执行逻辑:接收任务→思考→调用工具→更新记忆→再次判断,直到任务完成 | 一切自主智能体、自动化工作流、多步骤复杂任务处理 |
| Multi-Agent | 多智能体协同系统 | 多个独立 Agent 分工、通信、协作完成超复杂综合任务(一个负责搜索、一个编码、一个审核) | AI 研发团队模拟、完整自动化业务流水线、复杂项目自主执行系统 |
| SubAgent | 子智能体 / 附属智能体 | 主 Agent 拆分出的专项小 Agent,各司其职(搜索子 Agent、代码子 Agent、总结子 Agent) | 大型复杂任务拆解、专项能力分离、分工协作自动化 |
| Agent | 智能体 | 指能够感知环境、自主决策并执行动作以实现特定目标的智能实体 | - |
| 推理部署层 | |||
| Ollama | 本地大模型一键运行工具 | 开箱即用本地 LLM 管理工具,一键拉取、运行、量化各类开源模型,提供 API 接口 | 个人本地离线 AI、本地开发调试、客户端离线 Agent、局域网私有大模型服务 |
| vLLM | 高性能大模型推理引擎 | 基于 PagedAttention 的高吞吐推理框架,大幅提升并发、降低显存占用,做服务部署 | 云端 API 服务、企业高并发私有模型、多模型批量推理、生产级 LLM 服务 |
| llama.cpp | 纯 C/C++ 跨平台轻量化推理库 | 无 GPU 依赖,CPU / 移动端可运行量化 GGUF 模型,极致轻量化本地推理底层库 | 低配置电脑离线推理、嵌入式 / 手机端 AI、极简 CLI 模型程序、本地轻量化 Agent 底层 |
| OpenClaw | 开源工具调用专用基座模型 | 专门优化 Function Calling、多工具调用的开源 LLM,擅长复杂工具链式调用 | 离线本地 Agent、私有工具自动化、无联网本地函数调用场景 |
| Hermes Agent | Hermes 开源对话大模型 | Mistral/Llama 基座微调的对话模型,强指令遵循、Function Calling、多轮对话能力 | 本地离线聊天机器人、轻量化私有问答、本地智能体基座 |
| 基础能力层 | |||
| SSE(Server-Sent Events) | 服务器推送事件 | 单向长连接流式传输协议,服务端持续分段输出文本,前端实时逐字展示 AI 输出。 | 网页对话实时打字效果、流式问答、日志实时推送、大模型输出分片返回 |
| Stream | 流式推理 / 分片输出 | 模型不等待完整结果生成,逐 Token 分段返回文本,降低首字延迟 | 在线聊天页面、语音实时转写、实时代码补全、长文本边生成边展示 |
| LLM API | 大模型服务接口 | 标准化 HTTP 接口,程序通过网络请求调用云端 / 本地大模型推理服务 | 应用接入 AI、前后端 AI 功能、第三方 AI 工具集成、多服务统一调用入口 |
| ASR(Automatic Speech Recognition) | 自动语音识别 | 语音转文字,将人声音频识别为文本,输入给 LLM | 语音对话 AI、实时会议转写、语音助手、音频文档解析 |
| VAD(Voice Activity Detection) | 语音活动检测 | 识别音频中人声起止,区分静音与人声片段,节省推理算力 | 实时语音通话 AI、低功耗语音设备、语音对话打断检测 |
| TTS(Text To Speech) | 文本转语音 | 将 LLM 输出文字合成自然人声音频 | 语音播报、AI 数字人、语音对话机器人、有声文档朗读 |
| KV Cache(Key-Value Cache) | 键值缓存,LLM 核心推理优化技术 | 缓存上下文 Transformer 注意力计算 KV 向量,避免重复计算历史 Token,大幅加速生成 | 所有在线 LLM 服务、长对话场景、高并发推理、超长上下文文档问答 |
| 工具技能层 | |||
| Tool Use | 工具调用能力(广义 Function Calling) | 模型主动调用外部工具、接口、脚本扩展自身能力,弥补静态知识库局限 | 联网、文件、数据库、计算器、第三方 API 接入 AI |
| Function Calling | 工具 / 函数调用能力 | 大模型识别用户需求后,自动格式化参数调用外部自定义工具、接口,获取实时数据再整合回答 | 联网搜索、查数据库、调用计算器、操作本地文件、API 接口自动化 |
| MCP(Model Context Protocol) | 模型上下文协议 | 标准化协议,统一工具、本地文件、数据库与大模型的上下文交互规范,实现模型跨资源调用 | 本地 Agent、客户端 AI 工具、IDE 智能助手、多数据源对接大模型 |
| A2A(Agent to Agent) | 智能体到智能体协议 | ||
| ANP(Agent Network Protocol) | 智能体网络协议 | ||
| AG-UI(Agent-User Interface Protocol) | 智能体-用户交互协议 | ||
| DID(Decentralized Identifier) | 去中心化标识符 | ||
| Skills | AI 技能集 | 封装好的可复用工具函数集合,Agent 可按需加载启用(搜索、文件读写、计算、绘图都属于 Skill) | 模块化 Agent 开发、按需扩展 AI 能力、企业定制私有工具库 |
| SDD (Spec-Driven Development) | 规则驱动开发 | 专为AI Agent 自动编码、大模型工程落地诞生的标准化开发范式,是解决「Vibe Coding(氛围感即兴编码)」缺陷的核心方法论 | AI 代码智能体、多 Agent 协作系统、长期复杂项目迭代、Agent 工具开发、大模型工程交付流水线 |
| 其他 | |||
| Human-in-the-loop | 人工介入 |
| 名称 | 中文 | 解释 | 场景 |
|---|---|---|---|
| 知识库 | |||
| RAG(Retrieval‑Augmented Generation) | 检索增强生成 | 从外部知识库检索相关资料,再把检索内容 + 用户问题一起交给大模型生成答案。解决大模型幻觉、知识过时、私有数据无法访问三大痛点 | 企业知识库问答、文档助手、客服机器人、内部资料查询 |
| Chunking | 分块 / 切片 | 把长文档切成小段文本。策略:固定长度切块、语义切块、标题分层切块 | |
| Metadata | 元数据 | 文档来源、页码、时间、标签,用于溯源、过滤检索结果 | |
| Embedding | 嵌入模型 | 把文本转换成高维向量 | text‑embedding、BGE、m3e |
| Vector DB | 向量库 / 向量数据库 | 存储、快速搜索向量 | Chroma、FAISS、Milvus、Pinecone、Qdrant |
| Index | 向量索引 | 向量库构建的加速检索结构 | IVF‑Flat、HNSW |
| - | Query 向量化 | 用户问题同样转为向量 | |
| - | 相似度检索 / 语义搜索 | 向量库匹配最相似的文本块,Top‑K 取出 K 条结果 | |
| Rerank | 重排序 | 用 Reranker 模型对初次检索结果二次打分,提升精度;解决语义检索召回不准 | |
| Dense | 稠密检索 | ||
| BM25(Best Matching 25) | 最佳匹配 25,经典词法稀疏检索算法 | ||
| Hybrid Search | 混合检索 | 向量检索 + 关键词检索 (BM25),结果合并,召回效果更强 | |
| Retrieval Filter | 检索过滤 | 通过元数据限定文档范围,缩小检索域 | |
| Jev(TypeSafe AI Jev) | Jev 判定模型,System One 判定模型 | ||
| Context Window | 上下文窗口拼接 | 检索片段 + 用户 Query + 系统提示词合并送入 LLM | |
| Context Compression | 上下文压缩 | 精简检索出来的冗余内容,节省 token | |
| Citation | 引用溯源 / 引用标注 | 输出答案附带来源页码、文档名,可校验真实性 | |
| Agentic‑RAG | 智能体检索 | Agent 自主判断是否需要检索、循环检索、迭代追问,RAG 是 Agent 最常用工具 |
本地部署模型
以 ollama 为例
-
下载并安装 ollama
-
通过 ollama 下载模型
### 下载模型,以 qwen3.5 9b 为例,模型未下载的情况下 ollama 会先下载模型 ollama run qwen3.5:9b -
判断模型是否安装成功
### 列举大模型信息 ollama show qwen3.5:9b
展示结果如下,表示模型安装成功
-
运行 qwen3.5:9b 模型
### 运行大模型 ollama run qwen3.5:9b
-
与大模型对话
- 输入问题
- 回答问题
- 使用 ollama cli 运行 llm 的时候,ollama 会运行一套 llm api,基于这一点我们可以用这套 api 做一些验证
-
验证 llm api 正常运行
-
验证 SSE 能力
- 输入
- 输出
-
验证 Stream 能力
- 输入
- 输出
-
验证 tool_call 能力
- 输入
- 输出
- 替换模型,使用生图模型生图
- 输入
- 输出
搭建模型工作流
使用 dify 平台作为载体进行工作流搭建。选择 dify 的原因是 dify 面向技术人员专业化表达、可视化操作可以先屏蔽技术细节快速实现效果。
搭建一个工作流
搭建一个《说一个笑话》的工作流
输入:小明
结果:
输入:杨梅梅
结果:
输入:李雷
结果:
构建一个知识库
构建知识库并搭建一个人工智能领域问答机器人。使用模型拆解用户问题,然后到《人工智能概论》书籍搜集答案,最终交由大模型汇总回复
Agent 工程
Harness Engineering
中文释义:驾驭工程 / 基座工程
详细定义:指为大模型智能体(Agent)构建运行环境、约束机制、验证闭环与反馈系统的系统工程实践
-
Harness Engineering 演变
Harness Engineering 不是凭空提出而是从 Prompt Engineering、Context Engineering 中逐步演进而来。Harness 的翻译是马具/驾驭,意思是通过约束让大模型可以正确的运行。不再是精心设计提示词、构建上下文资料,而是通过设计一套规范来驾驭大模型。
Prompt Engineering:
-
中文释义:提示词工程
-
详细定义:针对单次模型调用,设计、组织、调优指令文本(System Prompt、用户指令、Few‑shot 示例、输出格式约束),引导大模型输出符合预期结果的技术学科
Context Engineering:
-
中文释义:上下文工程
-
详细定义:大模型每一轮推理时,筛选、组装、维护进入上下文窗口的全部 Token 信息集合;决定「哪些信息在此时应该交给模型,哪些应该丢弃」,管理模型完整信息运行环境的工程实践
-
SDD (Spec-Driven Development)
SDD 是 Harness Engineering 在编码场景的实现,常见的 SDD 有 OpenSpec、Spec-kit。SDD 会在开始执行任务之前与用户先制定规范,然后再让模型执行。
-
OpenSpec: https://github\.com/Fission\-AI/OpenSpec
-
Spec-kit: https://github\.github\.com/spec\-kit/
-
Loop Engineering
中文释义:循环 / 单智能体闭环工程
详细定义:聚焦单个 Agent 自主迭代闭环:规划→执行→观测校验→纠错重试→判断终止,核心解决 AI 自我反思、自动纠错、任务自推进,代表框架 ReAct、Self-Refine
Loop 工程类型:
- 回合制循环,由用户提示触发。
智能体在一个回合内收集上下文、执行操作并检查结果,然后由人工审核输出并撰写下一个提示。当需求仍在形成阶段,且每次输出都会影响下一次提问内容时,使用此模式。
- 目标导向循环,由包含成功标准和预算限制的 /goal 命令触发,例如“将首页Lighthouse评分提升至90分,最多尝试5次”。
当智能体尝试停止时,评估模型会判断目标是否达成,若未完成则将其重新投入工作。当结果可衡量,但执行路径无需人工干预时使用此方式。
- 基于时间的循环,由定时器触发。
定时器触发后,代理执行固定指令,例如“检查 PR,修复 CI”,然后等待下一次触发。/loop 命令在本地运行,而 /schedule 则将其移至云端,确保笔记本电脑关闭后任务仍能继续。 适用于事先已知任务内容、仅需重复执行时间安排的周期性工作。
- 主动式循环,由事件或计划触发,且无需人工在场。
一个例行程序监控某个通道,当有事项需要处理时便启动工作流。该工作流依次运行分类代理、修复代理以及评审员,后者以对抗性方式评估工作成果,直到任务完成。适用于持续性职责场景,即无法预知具体输入内容,但可以确定会有任务到来的情况。
每种类型都比前一种多移交一项任务:
-
轮次制:两项任务均由人类承担
-
目标制:自动化检查环节
-
时间制:自动化触发机制
-
主动式:同时实现自动化,并在运行时动态决定工作流结构
Graph Engineering
中文释义:图 / 多智能体拓扑工程
详细定义:用显式状态图(节点 + 边 + 全局状态) 管控多 Agent、并行分支、人工审批、子流程嵌套,把流程拓扑固化为可执行状态机,代表框架 LangGraph、MetaGPT
Graph Engineering 以上图为例,把 Agent 任务抽象成状态图,节点是角色 Agent,边代表流转逻辑,共享状态存储全部上下文信息。相比于 Loop Engineering 是单智能体仅有一条边指向自己,Graph Engineering 是多智能体的相互协作。
节点(Node)类型(不同角色):
- Researcher 调研 Agent
负责搜集信息、整理素材,输出调研笔记,写入共享状态。
- Writer 写作 Agent
读取调研笔记,产出初稿 draft;收到反馈后负责修订内容。
- Reviewer 评审 Agent
对产出的草稿做审核、校验,输出评审意见 verdict。
主流智能体实现
Openclaw
Openclaw 的推广引起了一阵 AI 潮流,领养小龙虾成为一个时髦。小龙虾是一个个人智能助手。用户通过任一终端与网关连接,与大模型对话完成各项任务。Openclaw 主要包含网关架构、渠道管理、Agent Runtime、会话管理、Memory 管理、skills管理等模块。实现一套插件机制提供能力扩展。
整体设计
运行流程图
OpenClaw 架构图
渠道及渠道实现
| Channel | 协议/库 | 特性 |
|---|---|---|
| Baileys(WhatsApp Web 协议) | QR 登录、媒体支持、群组提及网关 | |
| Telegram | grammY(Bot API) | 流式传输、Webhook 支持 |
| Discord | discord.js | 原生命令、DM 策略 |
| Slack | Bolt | DM 配对策略、频道白名单 |
| Signal | signal-cli | 需本地安装 signal-cli |
| iMessage | imsg CLI | 仅 macOS |
| Google Chat | Chat API | 扩展渠道 |
| Matrix、Teams | 扩展插件 | 社区支持 |
网关架构
网关架构使用的是 Websocket 协议实现端到端的长连接。采用了命令模式、分发器、中间件等设计模式。
- 请求上下文
// Context ==> Request State
interface RequestContext {
requestId: string;
connectionId: string;
user?: User;
signal: AbortSignal;
logger: Logger;
metadata: {
ip?: string;
userAgent?: string;
};
services: {
room: RoomService;
chat: ChatService;
user: UserService;
};
}
// ConnectionState
interface ConnectionState {
connectionId: string;
userId?: string;
rooms: Set<string>;
subscriptions: Set<string>;
}
// GlobalState
interface GlobalState {
rooms: Map<string, Room>;
connections: Map<string, Connection>;
sessions: Map<string, Session>;
}
- 路由/分发器
// 路由
interface Route<T = unknown, R = unknown> {
auth?: boolean;
permission?: string;
validate?: (
params: unknown
) => T;
handler: (
ctx: Context,
params: T
) => Promise<R>;
}
// 触发器
class Dispatcher {
private routes = new Map<
string,
Route
>();
register<T, R>(
method: string,
route: Route<T, R>
) {
if (this.routes.has(method)) {
throw new Error(
`Duplicate method: ${method}`
);
}
this.routes.set(method, route);
}
async dispatch(
ctx: Context,
request: Request
) {
const route =
this.routes.get(request.method);
if (!route) {
throw new MethodNotFoundError();
}
if (route.auth && !ctx.user) {
throw new UnauthorizedError();
}
if (route.permission) {
checkPermission(
ctx.user,
route.permission
);
}
const params = route.validate
? route.validate(request.params)
: request.params;
return route.handler(ctx, params);
}
}
- 调用示例
// 事件注册
dispatcher.register(
"chat.send",
{
auth: true,
permission: "chat:write",
validate: params => {
if (
!params ||
typeof params !== "object"
) {
throw new Error(
"Invalid params"
);
}
return params;
},
handler: async (ctx, params) => {
return chatService.send(
ctx.user!.id,
params
);
}
}
);
// 时间监听,事件触发器
ws.on("message", async raw => {
try {
const request = JSON.parse(raw.toString());
const result = await dispatcher.dispatch(
ctx,
request
);
ws.send(JSON.stringify({
id: request.id,
success: true,
result
}));
} catch (error) {
ws.send(JSON.stringify({
id: request.id,
success: false,
error: {
code: "INTERNAL_ERROR",
message: error.message
}
}));
}
});
插件机制
插件机制实现了插件发现、插件加载、插件注册等能力。避免过度加载插件。插件支持渠道、工具、模型等几个模块的契约。支持用户针对这些模块扩展。
- 基础类型
// plugin/types.ts
export type PluginStatus =
| "discovered"
| "registering"
| "registered"
| "starting"
| "active"
| "stopping"
| "stopped"
| "failed";
export interface PluginManifest {
id: string;
name: string;
version: string;
description?: string;
/**
* 插件依赖。
* 这里约定依赖项必须先于当前插件完成注册。
*/
dependencies?: string[];
}
export interface PluginContext {
pluginId: string;
/**
* 一次业务执行的关联 ID。
* 不同请求不应该共享同一个可变 Context。
*/
requestId?: string;
/**
* 只读的请求级元数据。
*/
metadata?: Readonly<Record<string, unknown>>;
/**
* 日志接口由宿主提供。
*/
logger: Logger;
}
export interface Logger {
info(message: string, data?: unknown): void;
warn(message: string, data?: unknown): void;
error(message: string, error?: unknown): void;
}
export interface PluginService {
id: string;
start(context: PluginContext): void | Promise<void>;
stop?(): void | Promise<void>;
}
export interface PluginTool {
name: string;
description: string;
execute(
params: unknown,
context: PluginContext,
): unknown | Promise<unknown>;
}
export interface PluginHook {
event: string;
handler(
payload: unknown,
context: PluginContext,
): void | Promise<void>;
}
export interface PluginAPI {
readonly pluginId: string;
readonly context: PluginContext;
registerTool(tool: PluginTool): void;
registerHook(hook: PluginHook): void;
registerService(service: PluginService): void;
}
export interface PluginDefinition {
manifest: PluginManifest;
register(api: PluginAPI): void | Promise<void>;
}
- 插件注册能力
// plugin registry
import type {
PluginHook,
PluginManifest,
PluginService,
PluginStatus,
PluginTool,
} from "./types.js";
export interface PluginRecord {
manifest: PluginManifest;
status: PluginStatus;
error?: unknown;
}
export interface Owned<T> {
pluginId: string;
value: T;
}
export class PluginRegistry {
private plugins = new Map<string, PluginRecord>();
private tools = new Map<string, Owned<PluginTool>>();
private hooks: Owned<PluginHook>[] = [];
private services = new Map<string, Owned<PluginService>>();
addPlugin(record: PluginRecord): void {
const id = record.manifest.id;
if (this.plugins.has(id)) {
throw new Error(`Plugin already exists: ${id}`);
}
this.plugins.set(id, record);
}
getPlugin(id: string): PluginRecord | undefined {
return this.plugins.get(id);
}
setStatus(
id: string,
status: PluginStatus,
error?: unknown,
): void {
const record = this.plugins.get(id);
if (!record) {
throw new Error(`Unknown plugin: ${id}`);
}
record.status = status;
record.error = error;
}
registerTool(pluginId: string, tool: PluginTool): void {
if (this.tools.has(tool.name)) {
throw new Error(`Tool already exists: ${tool.name}`);
}
this.tools.set(tool.name, {
pluginId,
value: tool,
});
}
registerHook(pluginId: string, hook: PluginHook): void {
this.hooks.push({
pluginId,
value: hook,
});
}
registerService(
pluginId: string,
service: PluginService,
): void {
if (this.services.has(service.id)) {
throw new Error(`Service already exists: ${service.id}`);
}
this.services.set(service.id, {
pluginId,
value: service,
});
}
getTool(name: string): Owned<PluginTool> | undefined {
return this.tools.get(name);
}
getHooks(event: string): Owned<PluginHook>[] {
return this.hooks.filter(
(item) => item.value.event === event,
);
}
getServices(pluginId: string): Owned<PluginService>[] {
return [...this.services.values()].filter(
(item) => item.pluginId === pluginId,
);
}
/**
* 撤销某个插件拥有的所有注册项。
*/
removePluginContributions(pluginId: string): void {
for (const [name, item] of this.tools) {
if (item.pluginId === pluginId) {
this.tools.delete(name);
}
}
for (let i = this.hooks.length - 1; i >= 0; i--) {
if (this.hooks[i].pluginId === pluginId) {
this.hooks.splice(i, 1);
}
}
for (const [id, item] of this.services) {
if (item.pluginId === pluginId) {
this.services.delete(id);
}
}
}
removePluginRecord(pluginId: string): void {
this.plugins.delete(pluginId);
}
}
- 插件 API
// plugin/plugin-api.ts
import type {
Logger,
PluginAPI,
PluginContext,
PluginHook,
PluginService,
PluginTool,
} from "./types.js";
import { PluginRegistry } from "./registry.js";
export class PluginAPIImpl implements PluginAPI {
constructor(
readonly pluginId: string,
readonly context: PluginContext,
private readonly registry: PluginRegistry,
private readonly onRegistration: (
rollback: () => void,
) => void,
) {}
registerTool(tool: PluginTool): void {
this.registry.registerTool(this.pluginId, tool);
this.onRegistration(() => {
// 具体回滚由注册表按插件所有权完成。
this.registry.removePluginContributions(this.pluginId);
});
}
registerHook(hook: PluginHook): void {
this.registry.registerHook(this.pluginId, hook);
this.onRegistration(() => {
this.registry.removePluginContributions(this.pluginId);
});
}
registerService(service: PluginService): void {
this.registry.registerService(this.pluginId, service);
this.onRegistration(() => {
this.registry.removePluginContributions(this.pluginId);
});
}
}
- 插件管理
// plugin/plugin-manager.ts
import type {
Logger,
PluginContext,
PluginDefinition,
PluginManifest,
PluginService,
} from "./types.js";
import { PluginRegistry } from "./registry.js";
import { PluginAPIImpl } from "./plugin-api.js";
interface PluginInstance {
definition: PluginDefinition;
context: PluginContext;
services: PluginService[];
}
export class PluginManager {
readonly registry = new PluginRegistry();
private instances = new Map<string, PluginInstance>();
constructor(private readonly logger: Logger) {}
async load(definition: PluginDefinition): Promise<void> {
const { manifest } = definition;
const { id } = manifest;
if (this.instances.has(id)) {
throw new Error(`Plugin already loaded: ${id}`);
}
this.assertDependencies(manifest);
const context: PluginContext = {
pluginId: id,
logger: this.logger,
};
this.registry.addPlugin({
manifest,
status: "registering",
});
const api = new PluginAPIImpl(
id,
context,
this.registry,
);
try {
// 第一阶段:注册能力。
await definition.register(api);
this.registry.setStatus(id, "registered");
const instance: PluginInstance = {
definition,
context,
services: this.registry
.getServices(id)
.map((item) => item.value),
};
this.instances.set(id, instance);
// 第二阶段:启动后台服务。
this.registry.setStatus(id, "starting");
for (const service of instance.services) {
await service.start(context);
}
this.registry.setStatus(id, "active");
this.logger.info(`Plugin loaded: ${id}`);
} catch (error) {
this.registry.setStatus(id, "failed", error);
await this.rollback(id);
this.logger.error(
`Plugin load failed: ${id}`,
error,
);
throw error;
}
}
async unload(id: string): Promise<void> {
const instance = this.instances.get(id);
if (!instance) {
return;
}
this.registry.setStatus(id, "stopping");
const errors: unknown[] = [];
// 先停止后台服务,避免卸载后继续产生工作。
for (const service of [...instance.services].reverse()) {
try {
await service.stop?.();
} catch (error) {
errors.push(error);
}
}
// 无论停止是否成功,都撤销该插件的注册项。
this.registry.removePluginContributions(id);
this.instances.delete(id);
if (errors.length > 0) {
const error = new AggregateError(
errors,
`Plugin cleanup failed: ${id}`,
);
this.registry.setStatus(id, "failed", error);
throw error;
}
this.registry.setStatus(id, "stopped");
this.logger.info(`Plugin unloaded: ${id}`);
}
private async rollback(id: string): Promise<void> {
const instance = this.instances.get(id);
if (instance) {
for (const service of [...instance.services].reverse()) {
try {
await service.stop?.();
} catch (error) {
this.logger.error(
`Plugin rollback cleanup failed: ${id}`,
error,
);
}
}
}
this.instances.delete(id);
this.registry.removePluginContributions(id);
}
private assertDependencies(
manifest: PluginManifest,
): void {
for (const dependency of manifest.dependencies ?? []) {
const record = this.registry.getPlugin(dependency);
if (!record || record.status !== "active") {
throw new Error(
`Plugin ${manifest.id} requires active dependency: ${dependency}`,
);
}
}
}
}
- 工具注册
// plugin/tool-executor.ts
import type { PluginContext } from "./types.js";
import { PluginRegistry } from "./registry.js";
export class ToolExecutor {
constructor(
private readonly registry: PluginRegistry,
private readonly logger: PluginContext["logger"],
) {}
async execute(
name: string,
params: unknown,
requestId?: string,
): Promise<unknown> {
const registered = this.registry.getTool(name);
if (!registered) {
throw new Error(`Tool not found: ${name}`);
}
const plugin = this.registry.getPlugin(
registered.pluginId,
);
if (!plugin || plugin.status !== "active") {
throw new Error(
`Plugin is not active: ${registered.pluginId}`,
);
}
const context: PluginContext = {
pluginId: registered.pluginId,
requestId,
logger: this.logger,
};
return registered.value.execute(params, context);
}
}
- Hook bus
// plugin/hook-bus.ts
import type {
PluginContext,
PluginHook,
} from "./types.js";
import { PluginRegistry } from "./registry.js";
export class HookBus {
constructor(
private readonly registry: PluginRegistry,
private readonly logger: PluginContext["logger"],
) {}
async emit(
event: string,
payload: unknown,
): Promise<void> {
const hooks = this.registry.getHooks(event);
for (const registered of hooks) {
const plugin = this.registry.getPlugin(
registered.pluginId,
);
if (!plugin || plugin.status !== "active") {
continue;
}
const context: PluginContext = {
pluginId: registered.pluginId,
logger: this.logger,
};
try {
await registered.value.handler(
payload,
context,
);
} catch (error) {
// 单个 Hook 失败不阻断其他订阅者。
this.logger.error(
`Hook failed: ${event}, plugin: ${registered.pluginId}`,
error,
);
}
}
}
}
- 插件示例
// plugins/logger/index.ts
import type { PluginDefinition } from "../../plugin/types.js";
export const loggerPlugin: PluginDefinition = {
manifest: {
id: "logger",
name: "Logger Plugin",
version: "1.0.0",
},
register(api) {
api.registerHook({
event: "app.started",
handler(_payload, context) {
context.logger.info(
"Application started",
{
pluginId: context.pluginId,
},
);
},
});
},
};
Agent Turn 实现
Agent Turn 指的使用一轮 Agent 调用,可能是一次大模型调用,也可能是多次。因为设计思路的差异,Agent Turn 会有不同,但是与 LLM 的交互不同 Agent 间会有差异。
- 类型定义
// contracts.ts
import type {
Message,
ModelResponse,
ToolCall,
TurnContext,
} from "./types";
export interface ModelAdapter {
generate(input: {
messages: Message[];
tools: ToolDefinition[];
context: TurnContext;
}): Promise<ModelResponse>;
}
export interface ToolDefinition {
name: string;
description: string;
execute(
args: Record<string, unknown>,
context: TurnContext,
): Promise<string>;
}
export interface ToolRegistry {
get(name: string): ToolDefinition | undefined;
list(): ToolDefinition[];
}
export interface SessionStore {
getMessages(sessionId: string): Promise<Message[]>;
appendMessages(
sessionId: string,
messages: Message[],
): Promise<void>;
}
export interface EventBus {
emit(event: import("./types").TurnEvent): void;
}
export interface AgentHook {
beforeTurn?(context: TurnContext): Promise<void>;
beforeToolCall?(
call: ToolCall,
context: TurnContext,
): Promise<void>;
afterToolCall?(
call: ToolCall,
result: string,
context: TurnContext,
): Promise<void>;
afterTurn?(
result: string,
context: TurnContext,
): Promise<void>;
}
- 控制队列
// keyed-queue.ts
export class KeyedQueue {
private tails = new Map<string, Promise<void>>();
async run<T>(
key: string,
task: () => Promise<T>,
): Promise<T> {
const previous =
this.tails.get(key) ?? Promise.resolve();
let release!: () => void;
const current = new Promise<void>((resolve) => {
release = resolve;
});
this.tails.set(key, current);
// 前一个任务无论成功还是失败,都允许后一个任务继续。
await previous;
try {
return await task();
} finally {
release();
// 只清理当前队列尾节点,避免误删后续任务。
if (this.tails.get(key) === current) {
this.tails.delete(key);
}
}
}
}
- Agent Runtime 实现
// 在 Message 类型中增加可选字段
export interface Message {
role: "system" | "user" | "assistant" | "tool";
content: string;
toolCallId?: string;
toolName?: string;
toolCalls?: ToolCall[];
}
// agent-runtime.ts
import { randomUUID } from "node:crypto";
import type {
AgentHook,
ModelAdapter,
SessionStore,
ToolRegistry,
} from "./contracts";
import type {
Message,
ToolCall,
TurnContext,
TurnEvent,
TurnResult,
} from "./types";
import { KeyedQueue } from "./keyed-queue";
import { HookManager } from "./hook-manager";
interface AgentRuntimeOptions {
model: ModelAdapter;
sessions: SessionStore;
tools: ToolRegistry;
emit: (event: TurnEvent) => void;
maxIterations?: number;
timeoutMs?: number;
}
export class AgentRuntime {
private queue = new KeyedQueue();
private hooks = new HookManager();
private readonly maxIterations: number;
private readonly timeoutMs: number;
constructor(
private readonly options: AgentRuntimeOptions,
) {
this.maxIterations =
options.maxIterations ?? 10;
this.timeoutMs =
options.timeoutMs ?? 60_000;
}
registerHook(hook: AgentHook): void {
this.hooks.register(hook);
}
async run(input: {
sessionId: string;
agentId: string;
message: string;
signal?: AbortSignal;
}): Promise<TurnResult> {
return this.queue.run(
input.sessionId,
async () => this.runTurn(input),
);
}
private async runTurn(input: {
sessionId: string;
agentId: string;
message: string;
signal?: AbortSignal;
}): Promise<TurnResult> {
const runId = randomUUID();
const controller = new AbortController();
const context: TurnContext = {
runId,
sessionId: input.sessionId,
agentId: input.agentId,
signal: controller.signal,
metadata: {},
startedAt: Date.now(),
};
const abortFromCaller = () => {
controller.abort(input.signal?.reason);
};
if (input.signal?.aborted) {
abortFromCaller();
} else {
input.signal?.addEventListener(
"abort",
abortFromCaller,
{ once: true },
);
}
const timeout = setTimeout(() => {
controller.abort(
new Error("Agent turn timed out"),
);
}, this.timeoutMs);
const emit = (
event: Omit<
TurnEvent,
"runId" | "sessionId" | "timestamp"
>,
) => {
this.options.emit({
...event,
runId,
sessionId: input.sessionId,
timestamp: Date.now(),
});
};
let terminalPublished = false;
const publishTerminal = (
phase: "end" | "error" | "aborted",
) => {
if (terminalPublished) return;
terminalPublished = true;
emit({
type: "lifecycle",
phase,
});
};
const assertNotAborted = () => {
if (controller.signal.aborted) {
throw controller.signal.reason ??
new Error("Agent turn aborted");
}
};
try {
emit({
type: "lifecycle",
phase: "start",
});
assertNotAborted();
await this.hooks.beforeTurn(context);
assertNotAborted();
const messages =
await this.options.sessions.getMessages(
input.sessionId,
);
messages.push({
role: "user",
content: input.message,
});
const systemMessage: Message = {
role: "system",
content:
"You are a helpful assistant. " +
"Use available tools when necessary.",
};
const prompt = [
systemMessage,
...messages,
];
let finalContent = "";
for (
let iteration = 0;
iteration < this.maxIterations;
iteration++
) {
assertNotAborted();
const response =
await this.options.model.generate({
messages: prompt,
tools: this.options.tools.list(),
context,
});
assertNotAborted();
// 模型给出最终回复,结束本轮。
if (response.type === "final") {
finalContent = response.content;
prompt.push({
role: "assistant",
content: finalContent,
});
emit({
type: "assistant",
content: finalContent,
});
break;
}
// 模型请求调用工具。
prompt.push({
role: "assistant",
content: "",
toolCalls: response.calls,
});
for (const call of response.calls) {
assertNotAborted();
const result = await this.executeTool(
call,
context,
assertNotAborted,
emit,
);
prompt.push({
role: "tool",
content: result,
toolCallId: call.id,
toolName: call.name,
});
}
// 下一轮将工具结果一起交给模型。
}
assertNotAborted();
if (!finalContent) {
throw new Error(
`Agent exceeded ${this.maxIterations} iterations ` +
"without producing a final answer",
);
}
emit({
type: "lifecycle",
phase: "finishing",
});
// 保存本轮新增的消息,而非把全部历史重复写入。
const newMessages = prompt.slice(messages.length);
await this.options.sessions.appendMessages(
input.sessionId,
newMessages,
);
await this.hooks.afterTurn(
finalContent,
context,
);
publishTerminal("end");
return {
runId,
sessionId: input.sessionId,
status: "completed",
content: finalContent,
};
} catch (error) {
const aborted = controller.signal.aborted;
publishTerminal(
aborted ? "aborted" : "error",
);
throw error;
} finally {
clearTimeout(timeout);
input.signal?.removeEventListener(
"abort",
abortFromCaller,
);
}
}
private async executeTool(
call: ToolCall,
context: TurnContext,
assertNotAborted: () => void,
emit: (
event: Omit<
TurnEvent,
"runId" | "sessionId" | "timestamp"
>,
) => void,
): Promise<string> {
assertNotAborted();
const tool = this.options.tools.get(call.name);
if (!tool) {
throw new Error(
`Tool not found: ${call.name}`,
);
}
await this.hooks.beforeToolCall(
call,
context,
);
assertNotAborted();
emit({
type: "tool",
phase: "start",
toolCallId: call.id,
toolName: call.name,
});
try {
const result = await tool.execute(
call.arguments,
context,
);
assertNotAborted();
await this.hooks.afterToolCall(
call,
result,
context,
);
emit({
type: "tool",
phase: "end",
toolCallId: call.id,
toolName: call.name,
content: result,
});
return result;
} catch (error) {
emit({
type: "tool",
phase: "error",
toolCallId: call.id,
toolName: call.name,
});
throw error;
}
}
}
Deepseek-Harness
Deepseek Harness 的口号是 “Everything is a Plugin”。我认为 Deepseek Harness 是可以实现自进化的。想象一下:一个乐高智能机器人,他具备普通智能机器人的能力外还具备生产乐高模块能力,生产的乐高模块可以用于改造自己。
整体设计
Deepseek Harness 整体架构
Fiber 生命周期
Fiber 实现
- 基础类型
type Cleanup = () => void | Promise<void>;
type Effect = () => Cleanup | void | Promise<Cleanup | void>;
type FiberState =
| "PENDING"
| "LOADING"
| "ACTIVE"
| "FAILED"
| "UNLOADING"
| "DISPOSED";
interface Plugin {
name: string;
apply(ctx: PluginContext): void | Promise<void>;
}
interface EffectRecord {
label: string;
cleanup: Cleanup;
disposed: boolean;
}
- 插件上下文
class PluginContext {
constructor(
readonly fiber: Fiber,
) {}
effect(
execute: Effect,
label = "anonymous",
): () => Promise<void> {
return this.fiber.effect(execute, label);
}
plugin(plugin: Plugin): Fiber {
return this.fiber.mount(plugin);
}
get state(): FiberState {
return this.fiber.state;
}
}
- Fiber 实现
class Fiber {
state: FiberState = "PENDING";
readonly ctx = new PluginContext(this);
private effects: EffectRecord[] = [];
private children: Fiber[] = [];
private disposePromise?: Promise<void>;
constructor(
readonly plugin: Plugin,
readonly parent?: Fiber,
) {}
async load(): Promise<void> {
if (this.state !== "PENDING") {
throw new Error(
`Cannot load fiber in state ${this.state}`,
);
}
this.state = "LOADING";
try {
await this.plugin.apply(this.ctx);
this.state = "ACTIVE";
} catch (error) {
this.state = "FAILED";
try {
await this.disposeResources();
} catch (cleanupError) {
throw new AggregateError(
[error, cleanupError],
`Plugin ${this.plugin.name} failed to load and cleanup`,
);
}
throw error;
}
}
effect(
execute: Effect,
label = "anonymous",
): () => Promise<void> {
if (
this.state !== "LOADING" &&
this.state !== "ACTIVE"
) {
throw new Error(
`Cannot register effect in state ${this.state}`,
);
}
let record: EffectRecord | undefined;
const dispose = async (): Promise<void> => {
if (!record || record.disposed) {
return;
}
record.disposed = true;
await record.cleanup();
};
const register = async () => {
const cleanup = await execute();
record = {
label,
cleanup: cleanup ?? (() => {}),
disposed: false,
};
this.effects.push(record);
};
// execute may be asynchronous, so registration must
// finish before the plugin starts using the resource.
//
// For this compact implementation, async registration
// is exposed separately below.
throw new Error(
"Use effectAsync() for asynchronous effect acquisition",
);
}
async effectAsync(
execute: Effect,
label = "anonymous",
): Promise<() => Promise<void>> {
if (
this.state !== "LOADING" &&
this.state !== "ACTIVE"
) {
throw new Error(
`Cannot register effect in state ${this.state}`,
);
}
const cleanup = await execute();
const record: EffectRecord = {
label,
cleanup: cleanup ?? (() => {}),
disposed: false,
};
this.effects.push(record);
return async () => {
if (record.disposed) {
return;
}
record.disposed = true;
await record.cleanup();
};
}
mount(plugin: Plugin): Fiber {
if (
this.state !== "LOADING" &&
this.state !== "ACTIVE"
) {
throw new Error(
`Cannot mount child in state ${this.state}`,
);
}
const child = new Fiber(plugin, this);
this.children.push(child);
return child;
}
async startChild(child: Fiber): Promise<void> {
if (!this.children.includes(child)) {
throw new Error("Child does not belong to this fiber");
}
await child.load();
}
dispose(): Promise<void> {
if (this.disposePromise) {
return this.disposePromise;
}
this.disposePromise = this.disposeInternal();
return this.disposePromise;
}
private async disposeInternal(): Promise<void> {
if (this.state === "DISPOSED") {
return;
}
this.state = "UNLOADING";
try {
await this.disposeResources();
} finally {
this.state = "DISPOSED";
}
}
private async disposeResources(): Promise<void> {
const errors: unknown[] = [];
// Dispose children in reverse mounting order.
for (const child of [...this.children].reverse()) {
try {
await child.dispose();
} catch (error) {
errors.push(error);
}
}
// Dispose effects in reverse registration order.
for (const effect of [...this.effects].reverse()) {
if (effect.disposed) {
continue;
}
effect.disposed = true;
try {
await effect.cleanup();
} catch (error) {
errors.push(
new Error(
`Effect "${effect.label}" cleanup failed`,
{ cause: error },
),
);
}
}
this.children = [];
this.effects = [];
if (errors.length > 0) {
throw new AggregateError(
errors,
`Failed to dispose plugin ${this.plugin.name}`,
);
}
}
}
- 使用示例
const loggerPlugin: Plugin = {
name: "logger",
async apply(ctx) {
console.log("logger loading");
await ctx.fiber.effectAsync(
() => {
console.log("logger resource acquired");
return () => {
console.log("logger resource released");
};
},
"logger-resource",
);
},
};
Agent Turn 实现
- 基础类型
type MessageRole =
| "system"
| "user"
| "assistant"
| "tool";
interface ToolCall {
id: string;
name: string;
arguments: unknown;
}
interface Message {
role: MessageRole;
content: string;
toolCalls?: ToolCall[];
toolCallId?: string;
}
interface ModelResponse {
content: string;
toolCalls: ToolCall[];
}
interface LLM {
generate(
messages: Message[],
tools: ToolDefinition[],
signal?: AbortSignal,
): Promise<ModelResponse>;
}
- 工具上下文
interface ToolContext {
turnId: string;
step: number;
signal?: AbortSignal;
}
interface ToolDefinition {
name: string;
description: string;
execute(
args: unknown,
context: ToolContext,
): Promise<unknown>;
}
class ToolRegistry {
private tools = new Map<
string,
ToolDefinition
>();
register(tool: ToolDefinition): () => void {
if (this.tools.has(tool.name)) {
throw new Error(
`Tool already exists: ${tool.name}`,
);
}
this.tools.set(tool.name, tool);
return () => {
if (this.tools.get(tool.name) === tool) {
this.tools.delete(tool.name);
}
};
}
get(name: string): ToolDefinition {
const tool = this.tools.get(name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
return tool;
}
list(): ToolDefinition[] {
return [...this.tools.values()];
}
}
- Session log 实现
type SessionEvent =
| {
type: "turn/start";
turnId: string;
}
| {
type: "step/start";
turnId: string;
step: number;
}
| {
type: "user/message";
turnId: string;
message: string;
}
| {
type: "assistant/message";
turnId: string;
content: string;
}
| {
type: "tool/call";
turnId: string;
toolCall: ToolCall;
}
| {
type: "tool/result";
turnId: string;
toolCallId: string;
result: unknown;
}
| {
type: "step/end";
turnId: string;
step: number;
}
| {
type: "turn/end";
turnId: string;
reason: "completed" | "max_steps" | "aborted";
};
class SessionLog {
private events: SessionEvent[] = [];
append(event: SessionEvent): void {
this.events.push(event);
}
read(): readonly SessionEvent[] {
return this.events;
}
}
- Agent Runtime 实现
interface TurnOptions {
maxSteps?: number;
signal?: AbortSignal;
}
interface TurnResult {
turnId: string;
output: string;
steps: number;
reason: "completed" | "max_steps" | "aborted";
}
class AgentRuntime {
constructor(
private llm: LLM,
private tools: ToolRegistry,
private session: SessionLog,
) {}
async runTurn(
input: string,
options: TurnOptions = {},
): Promise<TurnResult> {
const turnId = crypto.randomUUID();
const maxSteps = options.maxSteps ?? 10;
const signal = options.signal;
const messages: Message[] = [
{
role: "system",
content:
"You are a helpful agent. Use tools when necessary.",
},
{
role: "user",
content: input,
},
];
this.session.append({
type: "turn/start",
turnId,
});
this.session.append({
type: "user/message",
turnId,
message: input,
});
let output = "";
let completedSteps = 0;
let reason: TurnResult["reason"] = "max_steps";
try {
for (
let step = 1;
step <= maxSteps;
step++
) {
signal?.throwIfAborted();
this.session.append({
type: "step/start",
turnId,
step,
});
const response = await this.llm.generate(
messages,
this.tools.list(),
signal,
);
signal?.throwIfAborted();
const assistantMessage: Message = {
role: "assistant",
content: response.content,
toolCalls: response.toolCalls,
};
messages.push(assistantMessage);
output = response.content;
this.session.append({
type: "assistant/message",
turnId,
content: response.content,
});
// No tool calls means the model has finished.
if (response.toolCalls.length === 0) {
completedSteps = step;
this.session.append({
type: "step/end",
turnId,
step,
});
reason = "completed";
break;
}
// Execute tool calls produced by the model.
for (const toolCall of response.toolCalls) {
signal?.throwIfAborted();
this.session.append({
type: "tool/call",
turnId,
toolCall,
});
let result: unknown;
try {
const tool = this.tools.get(
toolCall.name,
);
result = await tool.execute(
toolCall.arguments,
{
turnId,
step,
signal,
},
);
} catch (error) {
// Convert tool errors into model-visible results.
result = {
error:
error instanceof Error
? error.message
: String(error),
};
}
this.session.append({
type: "tool/result",
turnId,
toolCallId: toolCall.id,
result,
});
messages.push({
role: "tool",
content: JSON.stringify(result),
toolCallId: toolCall.id,
});
}
this.session.append({
type: "step/end",
turnId,
step,
});
completedSteps = step;
}
} catch (error) {
if (signal?.aborted) {
reason = "aborted";
} else {
throw error;
}
} finally {
this.session.append({
type: "turn/end",
turnId,
reason,
});
}
return {
turnId,
output,
steps: completedSteps,
reason,
};
}
}