goatchain 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.md +173 -236
  2. package/README.zh.md +430 -0
  3. package/dist/index.d.ts +2958 -1802
  4. package/dist/index.js +270 -136
  5. package/package.json +13 -8
package/README.zh.md ADDED
@@ -0,0 +1,430 @@
1
+ # GoatChain 🐐
2
+
3
+ > 轻量级、可扩展的 TypeScript SDK,用于构建具有流式支持、工具调用和中间件模式的 AI 代理。
4
+
5
+ [![npm version](https://badge.fury.io/js/goatchain.svg)](https://badge.fury.io/js/goatchain)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ [English Documentation](../README.md)
9
+
10
+ ## ✨ 特性
11
+
12
+ - **🔄 代理循环** - 自动工具调用循环,可配置最大迭代次数
13
+ - **📡 流式优先** - 实时流式响应,包含详细事件
14
+ - **🧅 中间件模式** - Koa 风格的洋葱模型,支持可扩展钩子
15
+ - **🔧 工具系统** - 易于使用的工具注册和执行
16
+ - **💾 状态管理** - 两级状态存储(代理 + 会话级别)
17
+ - **📸 快照/恢复** - 代理和会话的完整持久化支持
18
+ - **🎯 TypeScript 原生** - 完整的类型安全和全面的类型导出
19
+
20
+ ## 📦 安装
21
+
22
+ ```bash
23
+ pnpm add goatchain
24
+ ```
25
+
26
+ ## 🚀 快速开始
27
+
28
+ 最简单的 Agent Loop 示例:
29
+
30
+ ```typescript
31
+ import process from 'node:process'
32
+ import { Agent, createModel, createOpenAIAdapter } from 'goatchain'
33
+
34
+ // 创建模型
35
+ const model = createModel({
36
+ adapter: createOpenAIAdapter({
37
+ defaultModelId: 'gpt-4o',
38
+ apiKey: process.env.OPENAI_API_KEY!,
39
+ }),
40
+ })
41
+
42
+ // 创建 Agent
43
+ const agent = new Agent({
44
+ name: 'Simple Assistant',
45
+ systemPrompt: 'You are a helpful assistant.',
46
+ model,
47
+ })
48
+
49
+ // 流式处理响应
50
+ const session = await agent.createSession()
51
+ session.send('Hello!')
52
+ for await (const event of session.receive()) {
53
+ if (event.type === 'text_delta') {
54
+ process.stdout.write(event.delta)
55
+ } else if (event.type === 'done') {
56
+ console.log('\n完成:', event.stopReason)
57
+ }
58
+ }
59
+ ```
60
+
61
+ 📖 **详细文档**: 查看 [docs/getting-started.md](./docs/getting-started.md) 了解更多示例和完整指南。
62
+
63
+ ## 🧰 CLI
64
+
65
+ 全局安装 `goatchain-cli` 后(或在此仓库中使用 `pnpm -s cli`),运行:
66
+
67
+ ```bash
68
+ goatchain
69
+ ```
70
+
71
+ 常用选项:
72
+
73
+ - `-k, --api-key <key>`(或设置 `OPENAI_API_KEY`)
74
+ - `-m, --model <id>`
75
+ - `--base-url <url>`
76
+ - `--max-tokens <n>`
77
+ - `--temperature <n>`
78
+
79
+ 命令:
80
+
81
+ - `/help` 帮助
82
+ - `/model <id>` 切换模型 ID(OpenAI)
83
+ - `/plan [on|off|toggle]` 切换计划模式(计划 → 确认 → 执行)
84
+ - `/approvals` 选择审批模式(交互式)
85
+ - `/set <k> <v>` 设置请求参数(例如 `temperature`、`maxTokens`)
86
+ - `/unset <k>` 清除请求参数
87
+ - `/params` 显示当前请求参数
88
+ - `/base-url <url>` 设置基础 URL
89
+ - `/api-key <key>` 设置 API 密钥(不显示)
90
+ - `/web-search-key <key>` 设置 WebSearch 工具的 Serper API 密钥(不显示)
91
+ - `/tools` 列出启用的工具(Read/Write/Edit/Glob/Grep/WebSearch*)
92
+ - `/sessions` 列出并选择保存的会话
93
+ - `/use <sessionId>` 恢复已保存的会话(打印最近历史)
94
+ - `/save` 持久化当前配置/会话
95
+ - `/status` 显示当前模型/会话信息
96
+ - `/new` 开始新对话(清除历史)
97
+
98
+ 需要环境变量 `OPENAI_API_KEY`。
99
+
100
+ 网络搜索(可选):
101
+
102
+ - 设置 `SERPER_API_KEY`(或 `GOATCHAIN_SERPER_API_KEY`)以启用内置的 `WebSearch` 工具获取最新信息,如天气
103
+ - 在交互模式下,可以运行 `/web-search-key <key>` 将密钥持久化到工作区配置中
104
+ - 也可以在 `./.goatchain/config.json` 中设置(工作区范围,gitignore):
105
+ - `{"tools":{"webSearch":{"apiKey":"...","apiEndpoint":"...","numResults":10}}}`
106
+
107
+ 计划模式(交互式规划):
108
+
109
+ - 使用 `/plan on` 启用,进入计划优先的工作流程:
110
+ 1. 代理探索代码库并研究您的请求
111
+ 2. 代理可能使用 `AskUserQuestion` 工具询问澄清问题以了解您的偏好(例如库选择、架构决策)
112
+ 3. 代理使用 `TodoPlan` 创建结构化计划(3-8 步)
113
+ 4. 您审查并批准计划
114
+ 5. 代理执行批准的计划
115
+ - 规划期间,文件修改被阻止(只读阶段)
116
+ - 设置 `GOATCHAIN_PLAN_MODE=1` 或在 `.goatchain/config.json` 中配置以默认启用
117
+
118
+ 本地持久化(工作区范围):
119
+
120
+ - 配置和会话保存在 `./.goatchain/` 下(自动创建)
121
+ - `.goatchain/` 被 gitignore 以避免意外提交密钥
122
+
123
+ DeepSeek 思考模式兼容性:
124
+
125
+ - 一些 OpenAI 兼容的网关(例如 DeepSeek 思考模式)要求包含 `tool_calls` 的助手消息上存在 `reasoning_content`(可能拒绝空字符串)。GoatChain 会在可用时附加累积的思考内容
126
+ - 如果您通过 GoatChain 无法从 `baseUrl`/`modelId` 检测到的代理使用 DeepSeek,可以显式启用:
127
+ - 交互式:运行 `/settings` 并切换"交错思考 (Interleaved Thinking)"
128
+ - 配置文件:在 `./.goatchain/config.json` 中设置 `openai.compat.interleavedThinking=true`
129
+
130
+ ## 🏗️ 架构
131
+
132
+ ```mermaid
133
+ classDiagram
134
+ direction TB
135
+
136
+ class Agent {
137
+ +id: string
138
+ +name: string
139
+ +systemPrompt: string
140
+ +model: ModelClient
141
+ +tools: ToolRegistry
142
+ +stateStore: StateStore
143
+ +sessionManager: BaseSessionManager
144
+ +stats: AgentStats
145
+ +use(middleware): this
146
+ +createSession(options): BaseSession
147
+ +resumeSession(sessionId, options): BaseSession
148
+ +setModel(modelOrRef): void
149
+ }
150
+
151
+ class ModelClient {
152
+ <<interface>>
153
+ +modelId: string
154
+ +stream(request): AsyncIterable~ModelStreamEvent~
155
+ +run?(request): Promise~ModelRunResult~
156
+ }
157
+
158
+ class StateStore {
159
+ <<interface>>
160
+ +savePoint: string
161
+ +deleteOnComplete: boolean
162
+ +saveCheckpoint(checkpoint): Promise~void~
163
+ +loadCheckpoint(sessionId): Promise~AgentLoopCheckpoint~
164
+ +deleteCheckpoint(sessionId): Promise~void~
165
+ +listCheckpoints(): Promise~AgentLoopCheckpoint[]~
166
+ }
167
+
168
+ class BaseTool {
169
+ <<abstract>>
170
+ +name: string
171
+ +description: string
172
+ +parameters: JSONSchema
173
+ +execute(args, ctx?): Promise~unknown~
174
+ }
175
+
176
+ class ToolRegistry {
177
+ +register(tool): void
178
+ +unregister(name): boolean
179
+ +get(name): BaseTool
180
+ +list(): BaseTool[]
181
+ +toOpenAIFormat(): OpenAITool[]
182
+ }
183
+
184
+ class BaseSession {
185
+ <<abstract>>
186
+ +id: string
187
+ +status: SessionStatus
188
+ +messages: Message[]
189
+ +usage: Usage
190
+ +configOverride: SessionConfigOverride
191
+ +addMessage(message): void
192
+ +save(): Promise~void~
193
+ +toSnapshot(): SessionSnapshot
194
+ +restoreFromSnapshot(snapshot): void
195
+ }
196
+
197
+ class BaseSessionManager {
198
+ <<abstract>>
199
+ +create(sessionId?): Promise~BaseSession~
200
+ +get(sessionId): Promise~BaseSession~
201
+ +list(): Promise~BaseSession[]~
202
+ +destroy(sessionId): Promise~void~
203
+ }
204
+
205
+ class Middleware {
206
+ <<function>>
207
+ (ctx: AgentLoopState, next: NextFunction) => Promise~void~
208
+ }
209
+
210
+ class AgentLoopState {
211
+ +sessionId: string
212
+ +messages: Message[]
213
+ +iteration: number
214
+ +pendingToolCalls: ToolCallWithResult[]
215
+ +currentResponse: string
216
+ +shouldContinue: boolean
217
+ +usage: Usage
218
+ }
219
+
220
+ Agent --> ModelClient : uses
221
+ Agent --> ToolRegistry : uses
222
+ Agent --> StateStore : uses
223
+ Agent --> BaseSessionManager : uses
224
+ Agent ..> Middleware : applies
225
+ Agent ..> AgentLoopState : manages
226
+ ToolRegistry --> BaseTool : contains
227
+ BaseSessionManager --> BaseSession : manages
228
+ ```
229
+
230
+ ## 🧅 中间件模式
231
+
232
+ GoatChain 使用 Koa 风格的洋葱模型作为中间件。每个中间件围绕核心执行进行包装:
233
+
234
+ ```
235
+ outer:before → inner:before → exec (model.stream) → inner:after → outer:after
236
+ ```
237
+
238
+ ### 命名中间件
239
+
240
+ 中间件可以有名字,方便管理和移除:
241
+
242
+ ```typescript
243
+ // 添加命名中间件(推荐)
244
+ agent.use(async (state, next) => {
245
+ const start = Date.now()
246
+ console.log(`[${state.iteration}] 模型调用前`)
247
+ const nextState = await next(state)
248
+ console.log(`[${state.iteration}] 模型调用后 (${Date.now() - start}ms)`)
249
+ return nextState
250
+ }, 'logging')
251
+
252
+ // 通过名字移除
253
+ agent.removeMiddleware('logging')
254
+
255
+ // 查看所有中间件名字
256
+ console.log(agent.middlewareNames) // ['logging', 'compression', ...]
257
+
258
+ // 使用 unsubscribe 函数
259
+ const unsubscribe = agent.use(middleware, 'temp')
260
+ unsubscribe() // 移除中间件
261
+ ```
262
+
263
+ ### 内置中间件的默认名字
264
+
265
+ GoatChain 的内置中间件工厂函数会自动提供默认名字:
266
+
267
+ ```typescript
268
+ // 计划模式中间件 - 自动命名为 'plan-mode'
269
+ agent.use(createPlanModeMiddleware())
270
+
271
+ // 上下文压缩中间件 - 自动命名为 'context-compression'
272
+ agent.use(createContextCompressionMiddleware({
273
+ maxTokens: 128000,
274
+ }))
275
+
276
+ // 查看所有中间件
277
+ console.log(agent.middlewareNames)
278
+ // 输出: ['plan-mode', 'context-compression']
279
+
280
+ // 通过默认名字移除
281
+ agent.removeMiddleware('plan-mode')
282
+
283
+ // 或者自定义名字
284
+ agent.use(createPlanModeMiddleware({ name: 'my-plan' }))
285
+ agent.use(createContextCompressionMiddleware({
286
+ maxTokens: 128000,
287
+ }), 'my-compression') // 覆盖默认名字
288
+ ```
289
+
290
+ ### 中间件示例
291
+
292
+ ```typescript
293
+ // 日志中间件
294
+ agent.use(async (state, next) => {
295
+ const start = Date.now()
296
+ console.log(`[${state.iteration}] 模型调用前`)
297
+
298
+ const nextState = await next(state) // 执行模型流
299
+
300
+ console.log(`[${state.iteration}] 模型调用后 (${Date.now() - start}ms)`)
301
+ return nextState
302
+ }, 'logging')
303
+
304
+ // 错误处理中间件
305
+ agent.use(async (state, next) => {
306
+ try {
307
+ return await next(state)
308
+ }
309
+ catch (error) {
310
+ state.shouldContinue = false
311
+ state.stopReason = 'error'
312
+ state.error = error
313
+ return state
314
+ }
315
+ }, 'error-handler')
316
+
317
+ // 速率限制中间件
318
+ agent.use(async (state, next) => {
319
+ await rateLimiter.acquire()
320
+ return next(state)
321
+ }, 'rate-limiter')
322
+ ```
323
+
324
+ ## 📡 事件类型
325
+
326
+ 会话接收流发出以下事件:
327
+
328
+ | 事件 | 描述 |
329
+ | ----------------- | ------------------------------------- |
330
+ | `iteration_start` | 循环迭代开始 |
331
+ | `text_delta` | LLM 的部分文本响应 |
332
+ | `thinking_start` | 思考阶段开始(如果支持) |
333
+ | `thinking_delta` | 思考内容增量(如果支持) |
334
+ | `thinking_end` | 思考阶段结束(如果支持) |
335
+ | `tool_call_start` | 工具调用开始 |
336
+ | `tool_call_delta` | 工具调用参数增量 |
337
+ | `tool_call_end` | 工具调用完成 |
338
+ | `tool_result` | 工具执行完成 |
339
+ | `iteration_end` | 循环迭代结束(包括使用量) |
340
+ | `done` | 流完成(包括使用量) |
341
+ | `error` | 发生错误 |
342
+
343
+ ```typescript
344
+ interface AgentEvent {
345
+ type:
346
+ | 'text_delta'
347
+ | 'tool_call_start'
348
+ | 'tool_call_delta'
349
+ | 'tool_call_end'
350
+ | 'tool_result'
351
+ | 'thinking_start'
352
+ | 'thinking_delta'
353
+ | 'thinking_end'
354
+ | 'error'
355
+ | 'done'
356
+ | 'iteration_start'
357
+ | 'iteration_end'
358
+ // ... 事件特定字段
359
+ }
360
+ ```
361
+
362
+ `iteration_end` 和 `done` 事件包括可选的 `usage: Usage`,包含累积的令牌计数。
363
+
364
+ ## 💾 检查点与恢复
365
+
366
+ 内置检查点支持,用于恢复中断的代理执行:
367
+
368
+ ```typescript
369
+ import { Agent, FileStateStore } from 'goatchain'
370
+
371
+ // 创建带配置的状态存储
372
+ const stateStore = new FileStateStore({
373
+ dir: './checkpoints',
374
+ savePoint: 'before', // 每次迭代前保存
375
+ deleteOnComplete: true, // 成功完成后清理
376
+ })
377
+
378
+ // 提供 stateStore 时,代理自动保存检查点
379
+ const agent = new Agent({
380
+ name: 'MyAgent',
381
+ systemPrompt: 'You are helpful.',
382
+ model,
383
+ stateStore,
384
+ })
385
+
386
+ // 运行代理 - 检查点自动保存
387
+ const session = await agent.createSession()
388
+ session.send('Hello')
389
+ for await (const event of session.receive()) {
390
+ console.log(event)
391
+ }
392
+
393
+ // 如果中断,从检查点恢复
394
+ const checkpoint = await stateStore.loadCheckpoint(session.id)
395
+ if (checkpoint) {
396
+ const resumed = await agent.resumeSession(session.id)
397
+ for await (const event of resumed.receive()) {
398
+ console.log(event)
399
+ }
400
+ }
401
+ ```
402
+
403
+ 可用的状态存储:
404
+
405
+ - `FileStateStore` - 基于文件的持久化
406
+ - `InMemoryStateStore` - 内存中(用于测试)
407
+
408
+ ## 📖 API 参考
409
+
410
+ ### Agent
411
+
412
+ | 方法 | 描述 |
413
+ | ------------------------------- | ----------------------- |
414
+ | `constructor(options)` | 创建新代理 |
415
+ | `use(middleware)` | 添加中间件 |
416
+ | `createSession(options)` | 创建会话 |
417
+ | `resumeSession(sessionId, options)` | 恢复会话 |
418
+ | `setModel(modelOrRef)` | 运行时切换/固定模型 |
419
+
420
+ ### Session
421
+
422
+ | 方法 | 描述 |
423
+ | ----------------------- | -------------------------------------------- |
424
+ | `send(input, options?)` | 将输入排队到会话 |
425
+ | `receive(options?)` | 流式事件,如果存在检查点则从中恢复 |
426
+ | `messages` | 对话历史 |
427
+
428
+ ## 📄 许可证
429
+
430
+ MIT © [Simon He](https://github.com/Simon-He95)