@fllf/agent-sdk 0.1.1 → 0.1.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.
- package/CHANGELOG.md +169 -0
- package/README.md +844 -184
- package/dist/agent/Agent.d.ts +16 -5
- package/dist/agent/Agent.d.ts.map +1 -1
- package/dist/agent/Agent.js +283 -2
- package/dist/agent/Agent.js.map +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/types.d.ts +27 -3
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/config/config.d.ts +22 -3
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +194 -47
- package/dist/config/config.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/errors.d.ts +74 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +186 -0
- package/dist/errors.js.map +1 -0
- package/dist/executors/base-executor.d.ts +14 -0
- package/dist/executors/base-executor.d.ts.map +1 -0
- package/dist/executors/base-executor.js +31 -0
- package/dist/executors/base-executor.js.map +1 -0
- package/dist/executors/base.d.ts +36 -5
- package/dist/executors/base.d.ts.map +1 -1
- package/dist/executors/chat-request-builder.d.ts +10 -0
- package/dist/executors/chat-request-builder.d.ts.map +1 -0
- package/dist/executors/chat-request-builder.js +96 -0
- package/dist/executors/chat-request-builder.js.map +1 -0
- package/dist/executors/index.d.ts +4 -1
- package/dist/executors/index.d.ts.map +1 -1
- package/dist/executors/index.js +6 -1
- package/dist/executors/index.js.map +1 -1
- package/dist/executors/rag-executor.js +1 -1
- package/dist/executors/rag-executor.js.map +1 -1
- package/dist/executors/simple-chat-executor.d.ts +4 -2
- package/dist/executors/simple-chat-executor.d.ts.map +1 -1
- package/dist/executors/simple-chat-executor.js +59 -57
- package/dist/executors/simple-chat-executor.js.map +1 -1
- package/dist/executors/tool-calling-executor.d.ts +20 -2
- package/dist/executors/tool-calling-executor.d.ts.map +1 -1
- package/dist/executors/tool-calling-executor.js +189 -91
- package/dist/executors/tool-calling-executor.js.map +1 -1
- package/dist/history/base.d.ts +55 -2
- package/dist/history/base.d.ts.map +1 -1
- package/dist/history/base.js +49 -0
- package/dist/history/base.js.map +1 -1
- package/dist/history/compression.d.ts +49 -0
- package/dist/history/compression.d.ts.map +1 -0
- package/dist/history/compression.js +53 -0
- package/dist/history/compression.js.map +1 -0
- package/dist/history/context-window.d.ts +33 -0
- package/dist/history/context-window.d.ts.map +1 -0
- package/dist/history/context-window.js +68 -0
- package/dist/history/context-window.js.map +1 -0
- package/dist/history/in-memory.d.ts +6 -4
- package/dist/history/in-memory.d.ts.map +1 -1
- package/dist/history/in-memory.js +25 -39
- package/dist/history/in-memory.js.map +1 -1
- package/dist/history/index.d.ts +9 -2
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +18 -1
- package/dist/history/index.js.map +1 -1
- package/dist/history/postgres/index.d.ts +4 -0
- package/dist/history/postgres/index.d.ts.map +1 -0
- package/dist/history/postgres/index.js +20 -0
- package/dist/history/postgres/index.js.map +1 -0
- package/dist/history/postgres/postgres-message-history-store.d.ts +35 -0
- package/dist/history/postgres/postgres-message-history-store.d.ts.map +1 -0
- package/dist/history/postgres/postgres-message-history-store.js +195 -0
- package/dist/history/postgres/postgres-message-history-store.js.map +1 -0
- package/dist/history/postgres/schema.d.ts +18 -0
- package/dist/history/postgres/schema.d.ts.map +1 -0
- package/dist/history/postgres/schema.js +46 -0
- package/dist/history/postgres/schema.js.map +1 -0
- package/dist/history/postgres/sql.d.ts +29 -0
- package/dist/history/postgres/sql.d.ts.map +1 -0
- package/dist/history/postgres/sql.js +102 -0
- package/dist/history/postgres/sql.js.map +1 -0
- package/dist/history/postgres/types.d.ts +20 -0
- package/dist/history/postgres/types.d.ts.map +1 -0
- package/dist/history/postgres/types.js +3 -0
- package/dist/history/postgres/types.js.map +1 -0
- package/dist/history/tool-pairing.d.ts +11 -0
- package/dist/history/tool-pairing.d.ts.map +1 -0
- package/dist/history/tool-pairing.js +52 -0
- package/dist/history/tool-pairing.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/LLM.d.ts +1 -0
- package/dist/llm/LLM.d.ts.map +1 -1
- package/dist/llm/LLM.js +136 -26
- package/dist/llm/LLM.js.map +1 -1
- package/dist/llm/errors.d.ts +11 -1
- package/dist/llm/errors.d.ts.map +1 -1
- package/dist/llm/errors.js +50 -1
- package/dist/llm/errors.js.map +1 -1
- package/dist/llm/factory.d.ts +14 -2
- package/dist/llm/factory.d.ts.map +1 -1
- package/dist/llm/factory.js +48 -9
- package/dist/llm/factory.js.map +1 -1
- package/dist/llm/index.d.ts +8 -3
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +10 -1
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/providers/anthropic.d.ts +14 -0
- package/dist/llm/providers/anthropic.d.ts.map +1 -0
- package/dist/llm/providers/anthropic.js +336 -0
- package/dist/llm/providers/anthropic.js.map +1 -0
- package/dist/llm/providers/openai-compatible.d.ts +7 -0
- package/dist/llm/providers/openai-compatible.d.ts.map +1 -1
- package/dist/llm/providers/openai-compatible.js +151 -47
- package/dist/llm/providers/openai-compatible.js.map +1 -1
- package/dist/llm/stream-accumulator.d.ts +17 -0
- package/dist/llm/stream-accumulator.d.ts.map +1 -0
- package/dist/llm/stream-accumulator.js +83 -0
- package/dist/llm/stream-accumulator.js.map +1 -0
- package/dist/llm/tool-arguments.d.ts +6 -0
- package/dist/llm/tool-arguments.d.ts.map +1 -0
- package/dist/llm/tool-arguments.js +20 -0
- package/dist/llm/tool-arguments.js.map +1 -0
- package/dist/llm/types.d.ts +76 -4
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/messages/content.d.ts +36 -0
- package/dist/messages/content.d.ts.map +1 -0
- package/dist/messages/content.js +70 -0
- package/dist/messages/content.js.map +1 -0
- package/dist/messages/index.d.ts +4 -2
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/messages/index.js +10 -1
- package/dist/messages/index.js.map +1 -1
- package/dist/messages/message.d.ts +11 -9
- package/dist/messages/message.d.ts.map +1 -1
- package/dist/messages/message.js +69 -4
- package/dist/messages/message.js.map +1 -1
- package/dist/messages/types.d.ts +12 -0
- package/dist/messages/types.d.ts.map +1 -1
- package/dist/observability/collecting-observer.d.ts +8 -0
- package/dist/observability/collecting-observer.d.ts.map +1 -0
- package/dist/observability/collecting-observer.js +15 -0
- package/dist/observability/collecting-observer.js.map +1 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +3 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/observer.d.ts.map +1 -1
- package/dist/observability/observer.js +14 -1
- package/dist/observability/observer.js.map +1 -1
- package/dist/observability/types.d.ts +16 -1
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/rag/chunking/auto-chunker.d.ts +7 -0
- package/dist/rag/chunking/auto-chunker.d.ts.map +1 -1
- package/dist/rag/chunking/auto-chunker.js +13 -0
- package/dist/rag/chunking/auto-chunker.js.map +1 -1
- package/dist/rag/chunking/chunker.d.ts.map +1 -1
- package/dist/rag/chunking/chunker.js +29 -5
- package/dist/rag/chunking/chunker.js.map +1 -1
- package/dist/rag/chunking/index.d.ts +2 -0
- package/dist/rag/chunking/index.d.ts.map +1 -1
- package/dist/rag/chunking/index.js +2 -0
- package/dist/rag/chunking/index.js.map +1 -1
- package/dist/rag/chunking/markdown-chunker.d.ts.map +1 -1
- package/dist/rag/chunking/markdown-chunker.js.map +1 -1
- package/dist/rag/chunking/qa-pair-chunker.d.ts +23 -0
- package/dist/rag/chunking/qa-pair-chunker.d.ts.map +1 -0
- package/dist/rag/chunking/qa-pair-chunker.js +162 -0
- package/dist/rag/chunking/qa-pair-chunker.js.map +1 -0
- package/dist/rag/chunking/semantic-chunker.d.ts +19 -0
- package/dist/rag/chunking/semantic-chunker.d.ts.map +1 -0
- package/dist/rag/chunking/semantic-chunker.js +291 -0
- package/dist/rag/chunking/semantic-chunker.js.map +1 -0
- package/dist/rag/embeddings/embedder.d.ts.map +1 -1
- package/dist/rag/embeddings/embedder.js +6 -0
- package/dist/rag/embeddings/embedder.js.map +1 -1
- package/dist/rag/embeddings/openai-compatible-embedder.d.ts +22 -4
- package/dist/rag/embeddings/openai-compatible-embedder.d.ts.map +1 -1
- package/dist/rag/embeddings/openai-compatible-embedder.js +108 -14
- package/dist/rag/embeddings/openai-compatible-embedder.js.map +1 -1
- package/dist/rag/generation/context-builder.d.ts +7 -0
- package/dist/rag/generation/context-builder.d.ts.map +1 -1
- package/dist/rag/generation/context-builder.js +4 -1
- package/dist/rag/generation/context-builder.js.map +1 -1
- package/dist/rag/ingestion/metadata.d.ts +6 -1
- package/dist/rag/ingestion/metadata.d.ts.map +1 -1
- package/dist/rag/ingestion/metadata.js +6 -2
- package/dist/rag/ingestion/metadata.js.map +1 -1
- package/dist/rag/pipeline.d.ts.map +1 -1
- package/dist/rag/pipeline.js +34 -11
- package/dist/rag/pipeline.js.map +1 -1
- package/dist/rag/retrieval/index.d.ts +1 -0
- package/dist/rag/retrieval/index.d.ts.map +1 -1
- package/dist/rag/retrieval/index.js +1 -0
- package/dist/rag/retrieval/index.js.map +1 -1
- package/dist/rag/retrieval/parent-child-expanding-retriever.d.ts +31 -0
- package/dist/rag/retrieval/parent-child-expanding-retriever.d.ts.map +1 -0
- package/dist/rag/retrieval/parent-child-expanding-retriever.js +194 -0
- package/dist/rag/retrieval/parent-child-expanding-retriever.js.map +1 -0
- package/dist/rag/stores/in-memory-keyword-store.d.ts.map +1 -1
- package/dist/rag/stores/in-memory-keyword-store.js +3 -8
- package/dist/rag/stores/in-memory-keyword-store.js.map +1 -1
- package/dist/rag/stores/keyword-tokenizer.d.ts +4 -0
- package/dist/rag/stores/keyword-tokenizer.d.ts.map +1 -0
- package/dist/rag/stores/keyword-tokenizer.js +113 -0
- package/dist/rag/stores/keyword-tokenizer.js.map +1 -0
- package/dist/rag/stores/postgres/pg-vector-store.d.ts.map +1 -1
- package/dist/rag/stores/postgres/pg-vector-store.js +21 -8
- package/dist/rag/stores/postgres/pg-vector-store.js.map +1 -1
- package/dist/rag/stores/postgres/postgres-document-store.d.ts.map +1 -1
- package/dist/rag/stores/postgres/postgres-document-store.js +26 -13
- package/dist/rag/stores/postgres/postgres-document-store.js.map +1 -1
- package/dist/rag/stores/postgres/postgres-keyword-store.d.ts.map +1 -1
- package/dist/rag/stores/postgres/postgres-keyword-store.js +43 -43
- package/dist/rag/stores/postgres/postgres-keyword-store.js.map +1 -1
- package/dist/rag/stores/postgres/sql.d.ts +22 -0
- package/dist/rag/stores/postgres/sql.d.ts.map +1 -1
- package/dist/rag/stores/postgres/sql.js +42 -0
- package/dist/rag/stores/postgres/sql.js.map +1 -1
- package/dist/rag/stores/types.d.ts +5 -0
- package/dist/rag/stores/types.d.ts.map +1 -1
- package/dist/rag/types.d.ts +6 -0
- package/dist/rag/types.d.ts.map +1 -1
- package/dist/testing/agent.d.ts +11 -0
- package/dist/testing/agent.d.ts.map +1 -0
- package/dist/testing/agent.js +45 -0
- package/dist/testing/agent.js.map +1 -0
- package/dist/testing/history-contract.d.ts +12 -0
- package/dist/testing/history-contract.d.ts.map +1 -0
- package/dist/testing/history-contract.js +111 -0
- package/dist/testing/history-contract.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +16 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/models.d.ts +34 -0
- package/dist/testing/models.d.ts.map +1 -0
- package/dist/testing/models.js +74 -0
- package/dist/testing/models.js.map +1 -0
- package/dist/testing/tool-schema.d.ts +6 -0
- package/dist/testing/tool-schema.d.ts.map +1 -0
- package/dist/testing/tool-schema.js +29 -0
- package/dist/testing/tool-schema.js.map +1 -0
- package/dist/testing/tools.d.ts +21 -0
- package/dist/testing/tools.d.ts.map +1 -0
- package/dist/testing/tools.js +43 -0
- package/dist/testing/tools.js.map +1 -0
- package/dist/tools/base.d.ts +38 -7
- package/dist/tools/base.d.ts.map +1 -1
- package/dist/tools/base.js +238 -11
- package/dist/tools/base.js.map +1 -1
- package/dist/tools/builtin/advancedSearchTool.d.ts.map +1 -1
- package/dist/tools/builtin/advancedSearchTool.js +30 -4
- package/dist/tools/builtin/advancedSearchTool.js.map +1 -1
- package/dist/tools/builtin/ragSearchTool.d.ts +6 -3
- package/dist/tools/builtin/ragSearchTool.d.ts.map +1 -1
- package/dist/tools/builtin/ragSearchTool.js +8 -6
- package/dist/tools/builtin/ragSearchTool.js.map +1 -1
- package/dist/tools/executor.d.ts +30 -2
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +83 -15
- package/dist/tools/executor.js.map +1 -1
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/registry.d.ts +15 -3
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +21 -2
- package/dist/tools/registry.js.map +1 -1
- package/package.json +58 -4
package/README.md
CHANGED
|
@@ -1,67 +1,34 @@
|
|
|
1
1
|
# FAgent Agent SDK
|
|
2
2
|
|
|
3
|
-
FAgent
|
|
4
|
-
workflows, and RAG-powered chat systems.
|
|
3
|
+
FAgent Agent SDK 是一个可组合的 TypeScript Agent 运行时基础库,用于在第三方系统中接入:
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
- LLM 对话与流式输出。
|
|
6
|
+
- OpenAI、Anthropic、OpenAI-compatible 本地/网关模型。
|
|
7
|
+
- 工具调用 Agent,含并发工具执行、工具错误回传、工具调用流式事件。
|
|
8
|
+
- 多模态消息内容:文本、图片 URL、图片 base64。
|
|
9
|
+
- 会话级消息历史、上下文窗口截断、可选历史摘要压缩。
|
|
10
|
+
- RAG 文档 ingestion、切块、embedding、检索、rerank、上下文构建、答案生成与引用校验。
|
|
11
|
+
- 内存存储与 Postgres/pgvector 参考存储。
|
|
12
|
+
- 运行时事件、错误类型与下游测试辅助工具。
|
|
13
|
+
|
|
14
|
+
本 SDK 只提供运行时与扩展点;不包含 Web UI、用户体系、鉴权、业务数据库迁移、部署脚本或产品级限流。
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
7
17
|
|
|
8
18
|
```bash
|
|
9
19
|
npm install @fllf/agent-sdk
|
|
10
20
|
```
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- Composable `Agent` runtime.
|
|
19
|
-
- Unified `LLM.chat()` interface.
|
|
20
|
-
- OpenAI and OpenAI-compatible local providers.
|
|
21
|
-
- Streaming through `LLM.stream()`.
|
|
22
|
-
- Timeout, retry, abort, and normalized LLM errors.
|
|
23
|
-
- `generateObject()` structured output with Zod validation.
|
|
24
|
-
- Session-scoped message history.
|
|
25
|
-
- Simple chat, tool-calling, and RAG executors.
|
|
26
|
-
- Zod-driven tool definitions.
|
|
27
|
-
- RAG ingestion, chunking, embedding, retrieval, reranking, context building,
|
|
28
|
-
generation, and verification.
|
|
29
|
-
- In-memory stores for development and tests.
|
|
30
|
-
- Postgres RAG store adapters that work with any compatible `query()` client.
|
|
31
|
-
- Observer events for logging, tracing, and debugging.
|
|
32
|
-
|
|
33
|
-
## Runtime requirements
|
|
34
|
-
|
|
35
|
-
- Node.js 18 or newer.
|
|
36
|
-
- TypeScript is recommended for typed integration.
|
|
37
|
-
- Install optional document conversion dependencies only if you use those
|
|
38
|
-
loaders:
|
|
22
|
+
运行要求:
|
|
23
|
+
|
|
24
|
+
- Node.js 18 或更高版本。
|
|
25
|
+
- 推荐使用 TypeScript。
|
|
26
|
+
- RAG 文档转换按需安装可选依赖:
|
|
39
27
|
- HTML: `turndown`, `turndown-plugin-gfm`
|
|
40
28
|
- DOCX: `mammoth`
|
|
41
29
|
- PDF: `pdf-parse`
|
|
42
30
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
OpenAI:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
OPENAI_API_KEY=...
|
|
49
|
-
OPENAI_MODEL=gpt-4o-mini
|
|
50
|
-
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
OpenAI-compatible local runtime, such as Ollama, LM Studio, vLLM, or a gateway:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
LOCAL_BASE_URL=http://127.0.0.1:11434/v1
|
|
57
|
-
LOCAL_MODEL=llama3
|
|
58
|
-
LOCAL_API_KEY=local-key
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
If no OpenAI key is configured, the default `LLM` auto-detection falls back to
|
|
62
|
-
the local OpenAI-compatible provider.
|
|
63
|
-
|
|
64
|
-
## Basic chat integration
|
|
31
|
+
## 快速开始
|
|
65
32
|
|
|
66
33
|
```ts
|
|
67
34
|
import { Agent } from '@fllf/agent-sdk';
|
|
@@ -81,13 +48,10 @@ const result = await agent.run('How do I reset my password?', {
|
|
|
81
48
|
});
|
|
82
49
|
|
|
83
50
|
console.log(result.output);
|
|
51
|
+
console.log(result.usage);
|
|
84
52
|
```
|
|
85
53
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Your API layer owns authentication, authorization, persistence, rate limits, and
|
|
89
|
-
request validation. A typical chat endpoint can adapt incoming requests to
|
|
90
|
-
`Agent.run()`:
|
|
54
|
+
第三方 HTTP 服务通常只需要把自己的认证、租户、会话和用户输入转换为 `Agent.run()`:
|
|
91
55
|
|
|
92
56
|
```ts
|
|
93
57
|
import { Agent } from '@fllf/agent-sdk';
|
|
@@ -97,10 +61,10 @@ const agent = new Agent({
|
|
|
97
61
|
requireSessionId: true,
|
|
98
62
|
});
|
|
99
63
|
|
|
100
|
-
export async function
|
|
101
|
-
conversationId: string;
|
|
102
|
-
userId: string;
|
|
64
|
+
export async function handleChat(body: {
|
|
103
65
|
tenantId: string;
|
|
66
|
+
userId: string;
|
|
67
|
+
conversationId: string;
|
|
104
68
|
message: string;
|
|
105
69
|
}) {
|
|
106
70
|
const sessionId = `${body.tenantId}:${body.userId}:${body.conversationId}`;
|
|
@@ -110,6 +74,7 @@ export async function handleChatRequest(body: {
|
|
|
110
74
|
metadata: {
|
|
111
75
|
tenantId: body.tenantId,
|
|
112
76
|
userId: body.userId,
|
|
77
|
+
conversationId: body.conversationId,
|
|
113
78
|
},
|
|
114
79
|
});
|
|
115
80
|
|
|
@@ -120,54 +85,269 @@ export async function handleChatRequest(body: {
|
|
|
120
85
|
}
|
|
121
86
|
```
|
|
122
87
|
|
|
123
|
-
|
|
88
|
+
建议第三方系统自己负责:
|
|
89
|
+
|
|
90
|
+
- 登录态、租户边界、权限校验。
|
|
91
|
+
- 外部 API 输入校验与限流。
|
|
92
|
+
- 持久化用户、会话、消息、审计日志。
|
|
93
|
+
- 日志脱敏与成本控制。
|
|
94
|
+
|
|
95
|
+
## 模型与环境变量
|
|
96
|
+
|
|
97
|
+
`LLM` 默认自动探测 provider:
|
|
98
|
+
|
|
99
|
+
1. 存在 `OPENAI_API_KEY` 时使用 `openai`。
|
|
100
|
+
2. 否则存在 `ANTHROPIC_API_KEY` 时使用 `anthropic`。
|
|
101
|
+
3. 否则使用 `local`,即 OpenAI-compatible 本地服务。
|
|
102
|
+
|
|
103
|
+
### OpenAI
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
OPENAI_API_KEY=...
|
|
107
|
+
OPENAI_MODEL=gpt-4o-mini
|
|
108
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
109
|
+
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
官方 OpenAI provider 会把 `maxTokens` 映射为 `max_completion_tokens`,流式请求会启用 usage 回传。
|
|
113
|
+
`OpenAIEmbedder` 也会读取 `OPENAI_API_KEY`、`OPENAI_BASE_URL` 和 `OPENAI_EMBEDDING_MODEL`;
|
|
114
|
+
兼容服务应把 `OPENAI_BASE_URL` 配为 API root,SDK 会请求 `{baseURL}/embeddings`。
|
|
115
|
+
|
|
116
|
+
### Anthropic
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
ANTHROPIC_API_KEY=...
|
|
120
|
+
ANTHROPIC_MODEL=claude-opus-4-8
|
|
121
|
+
ANTHROPIC_BASE_URL=
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Anthropic provider 支持 Messages API、多模态、工具调用、流式输出、结构化输出、reasoning effort、prompt cache usage 字段和 `tool_result.is_error`。
|
|
125
|
+
|
|
126
|
+
### 本地或网关 OpenAI-compatible 服务
|
|
127
|
+
|
|
128
|
+
适用于 Ollama、LM Studio、vLLM 或统一网关:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
LOCAL_BASE_URL=http://127.0.0.1:11434/v1
|
|
132
|
+
LOCAL_MODEL=llama3
|
|
133
|
+
LOCAL_API_KEY=local-key
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### SDK 运行参数
|
|
124
137
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
`Config.fromEnv()` 支持:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
MODEL=
|
|
142
|
+
MODEL_PROVIDER=
|
|
143
|
+
TEMPERATURE=
|
|
144
|
+
MAX_TOKENS=
|
|
145
|
+
MAX_INPUT_TOKENS=
|
|
146
|
+
REQUEST_TIMEOUT_MS=
|
|
147
|
+
MAX_RETRIES=
|
|
148
|
+
RETRY_DELAY_MS=
|
|
149
|
+
RETRY_MAX_DELAY_MS=
|
|
150
|
+
MAX_HISTORY_LENGTH=
|
|
151
|
+
DEBUG=
|
|
152
|
+
LOG_LEVEL=
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
示例:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { Config } from '@fllf/agent-sdk';
|
|
159
|
+
|
|
160
|
+
const config = Config.fromEnv({
|
|
161
|
+
onWarning: (warning) => logger.warn({ warning }, 'invalid SDK env var'),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (config.envWarnings.length > 0) {
|
|
165
|
+
// 按业务策略决定继续启动或失败退出。
|
|
131
166
|
}
|
|
132
167
|
```
|
|
133
168
|
|
|
134
|
-
|
|
169
|
+
`Config.fromEnv({ strict: true })` 在环境变量非法时抛出 `ConfigError`。SDK 不会自动打印非法值。
|
|
170
|
+
|
|
171
|
+
注意:`temperature` 没有默认值;未显式配置时不下发,让模型/provider 使用自身默认值。
|
|
172
|
+
|
|
173
|
+
## Agent 核心用法
|
|
174
|
+
|
|
175
|
+
### 构造参数
|
|
176
|
+
|
|
177
|
+
常用 `Agent` 组件:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import {
|
|
181
|
+
Agent,
|
|
182
|
+
Config,
|
|
183
|
+
InMemoryMessageHistoryStore,
|
|
184
|
+
SimpleChatExecutor,
|
|
185
|
+
} from '@fllf/agent-sdk';
|
|
186
|
+
|
|
187
|
+
const agent = new Agent({
|
|
188
|
+
name: 'agent-name',
|
|
189
|
+
llmConfig: { provider: 'openai', model: 'gpt-4o-mini' },
|
|
190
|
+
config: new Config({
|
|
191
|
+
maxHistoryLength: 100,
|
|
192
|
+
maxInputTokens: 8000,
|
|
193
|
+
requestTimeoutMs: 60_000,
|
|
194
|
+
maxRetries: 2,
|
|
195
|
+
}),
|
|
196
|
+
historyStore: new InMemoryMessageHistoryStore(),
|
|
197
|
+
executor: new SimpleChatExecutor(),
|
|
198
|
+
systemPrompt: 'You are helpful.',
|
|
199
|
+
requireSessionId: true,
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
可替换组件包括:
|
|
204
|
+
|
|
205
|
+
- `llm`: 任意实现 `ChatModel` 的模型适配器。
|
|
206
|
+
- `historyStore`: 任意实现 `MessageHistoryStore` 的历史存储。
|
|
207
|
+
- `executor`: `SimpleChatExecutor`、`ToolCallingExecutor`、`RagExecutor` 或自定义 executor。
|
|
208
|
+
- `tools`: `Tool[]` 或 `ToolRegistry`。
|
|
209
|
+
- `observer`: 运行时事件接收器。
|
|
210
|
+
- `contextCompressor`: 超出 `maxInputTokens` 时的历史压缩器。
|
|
211
|
+
- `hooks`: `beforeRun`、`afterRun`、`onRunError` 生命周期钩子。
|
|
212
|
+
- `runIdFactory`: 自定义 run id 生成器。
|
|
213
|
+
|
|
214
|
+
### run 选项与返回值
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const result = await agent.run('hello', {
|
|
218
|
+
sessionId: 'tenant:user:conversation',
|
|
219
|
+
runId: 'optional-fixed-run-id',
|
|
220
|
+
model: 'gpt-4o-mini',
|
|
221
|
+
temperature: 0.2,
|
|
222
|
+
maxTokens: 1024,
|
|
223
|
+
timeoutMs: 30_000,
|
|
224
|
+
maxRetries: 1,
|
|
225
|
+
systemPrompt: 'Override this run only.',
|
|
226
|
+
signal: abortController.signal,
|
|
227
|
+
metadata: { tenantId: 'tenant_1' },
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`AgentRunOptions` 会在执行前校验;非法数值、空 `runId` 或非普通对象 `metadata` 会抛 `AgentExecutionError`,code 为 `invalid_agent_run_options`。
|
|
232
|
+
|
|
233
|
+
返回值:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
type AgentResult = {
|
|
237
|
+
output: string;
|
|
238
|
+
messages: Message[];
|
|
239
|
+
usage?: TokenUsage;
|
|
240
|
+
raw?: unknown;
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`raw` 由 executor 决定:
|
|
245
|
+
|
|
246
|
+
- simple chat: provider 原始响应。
|
|
247
|
+
- tool calling: 最近一次模型原始响应。
|
|
248
|
+
- RAG: `RagAnswer`。
|
|
249
|
+
|
|
250
|
+
### 会话与历史
|
|
251
|
+
|
|
252
|
+
`Agent` 实例可复用。`sessionId` 决定会话上下文:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
await agent.run('Hi', { sessionId: 'tenant_1:user_1:conv_a' });
|
|
256
|
+
await agent.run('New conversation', { sessionId: 'tenant_1:user_1:conv_b' });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
同一 `sessionId` 的 run/stream/add/clear/get 操作在进程内串行化,避免消息顺序错乱。不同 session 可并发。
|
|
260
|
+
|
|
261
|
+
生产接入建议:
|
|
262
|
+
|
|
263
|
+
- 设置 `requireSessionId: true`。
|
|
264
|
+
- session id 由租户、用户、会话 id 共同组成。
|
|
265
|
+
- 开发/测试可使用内存历史;生产应接入持久化 `MessageHistoryStore`。
|
|
266
|
+
|
|
267
|
+
## 流式输出
|
|
268
|
+
|
|
269
|
+
`Agent.stream()` 返回 `AsyncIterable<AgentStreamEvent>`。
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
const agent = new Agent({ name: 'streaming-chat' });
|
|
135
273
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
274
|
+
for await (const event of agent.stream('Write a short summary.', {
|
|
275
|
+
sessionId: 'tenant_1:user_42:conversation_stream',
|
|
276
|
+
})) {
|
|
277
|
+
if (event.type === 'agent:text_delta') {
|
|
278
|
+
process.stdout.write(event.delta ?? '');
|
|
279
|
+
}
|
|
280
|
+
if (event.type === 'agent:end') {
|
|
281
|
+
console.log('\nfinal:', event.output);
|
|
143
282
|
}
|
|
144
283
|
}
|
|
145
284
|
```
|
|
146
285
|
|
|
147
|
-
|
|
286
|
+
事件类型:
|
|
287
|
+
|
|
288
|
+
- `agent:start`
|
|
289
|
+
- `agent:text_delta`
|
|
290
|
+
- `agent:tool_call`
|
|
291
|
+
- `agent:tool_result`
|
|
292
|
+
- `agent:end`
|
|
293
|
+
- `agent:error`
|
|
294
|
+
|
|
295
|
+
流式行为:
|
|
148
296
|
|
|
149
|
-
`
|
|
297
|
+
- `SimpleChatExecutor` 支持流式,但要求模型实现 `stream()` 并声明 streaming capability。
|
|
298
|
+
- `ToolCallingExecutor` 支持工具调用流式;模型支持流式时聚合 tool call delta,模型不支持流式时会退化为一次非流式 `chat()` 并整体输出。
|
|
299
|
+
- `RagExecutor` 当前只实现非流式 `run()`。
|
|
300
|
+
- 流式成功完成后才提交本轮 user/assistant 消息;中途失败或 abort 时不会提交未完成的 simple-chat 轮次。
|
|
301
|
+
|
|
302
|
+
工具调用流式会额外产生:
|
|
150
303
|
|
|
151
304
|
```ts
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
305
|
+
if (event.type === 'agent:tool_call') {
|
|
306
|
+
console.log(event.toolCall);
|
|
307
|
+
}
|
|
308
|
+
if (event.type === 'agent:tool_result') {
|
|
309
|
+
console.log(event.toolResult?.isError, event.toolResult?.content);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 多模态消息
|
|
314
|
+
|
|
315
|
+
消息内容类型为:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
type MessageContent = string | ContentBlock[];
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
示例:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import { Agent, imageUrlBlock, textBlock } from '@fllf/agent-sdk';
|
|
325
|
+
|
|
326
|
+
await agent.run([
|
|
327
|
+
textBlock('Describe this image.'),
|
|
328
|
+
imageUrlBlock('https://example.test/image.png'),
|
|
329
|
+
], {
|
|
330
|
+
sessionId: 'tenant:user:conv',
|
|
155
331
|
});
|
|
156
332
|
```
|
|
157
333
|
|
|
158
|
-
|
|
159
|
-
|
|
334
|
+
也可使用 base64:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { imageBase64Block } from '@fllf/agent-sdk';
|
|
338
|
+
|
|
339
|
+
const image = imageBase64Block('image/png', base64Data);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Provider 行为:
|
|
160
343
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
systems should provide a persistent `MessageHistoryStore`.
|
|
344
|
+
- OpenAI/OpenAI-compatible:user 多模态映射为 `image_url`;assistant/tool 非文本内容会降级为文本占位。
|
|
345
|
+
- Anthropic:user 图片映射为 Anthropic image block;tool 结果可携带 `is_error`。
|
|
346
|
+
- 不支持 vision 的 provider 会在 capability 检查或 provider 侧失败;第三方系统应按模型能力选择输入。
|
|
165
347
|
|
|
166
|
-
##
|
|
348
|
+
## 工具调用
|
|
167
349
|
|
|
168
|
-
|
|
169
|
-
OpenAI-compatible tool schema and validates model-supplied arguments before
|
|
170
|
-
running the tool.
|
|
350
|
+
工具通过 Zod schema 定义。SDK 会把 schema 转成 provider-neutral tool definition,并在执行前校验模型传入参数。
|
|
171
351
|
|
|
172
352
|
```ts
|
|
173
353
|
import { z } from 'zod';
|
|
@@ -184,6 +364,8 @@ class AddTool extends Tool<typeof AddToolSchema, number> {
|
|
|
184
364
|
name: 'add',
|
|
185
365
|
description: 'Adds two numbers.',
|
|
186
366
|
schema: AddToolSchema,
|
|
367
|
+
timeoutMs: 5000,
|
|
368
|
+
errorMode: 'return_to_model',
|
|
187
369
|
});
|
|
188
370
|
}
|
|
189
371
|
|
|
@@ -194,7 +376,7 @@ class AddTool extends Tool<typeof AddToolSchema, number> {
|
|
|
194
376
|
|
|
195
377
|
const agent = new Agent({
|
|
196
378
|
name: 'tool-agent',
|
|
197
|
-
executor: new ToolCallingExecutor(),
|
|
379
|
+
executor: new ToolCallingExecutor({ maxIterations: 5 }),
|
|
198
380
|
tools: [new AddTool()],
|
|
199
381
|
requireSessionId: true,
|
|
200
382
|
});
|
|
@@ -202,25 +384,252 @@ const agent = new Agent({
|
|
|
202
384
|
const result = await agent.run('Calculate 19 + 23 with the tool.', {
|
|
203
385
|
sessionId: 'tenant_1:user_42:conversation_tools',
|
|
204
386
|
});
|
|
387
|
+
```
|
|
205
388
|
|
|
206
|
-
|
|
389
|
+
工具运行控制:
|
|
390
|
+
|
|
391
|
+
- `timeoutMs`: 工具超时后抛 `ToolTimeoutError`。
|
|
392
|
+
- `AgentRunOptions.signal`: 会传入 `ToolRunContext.signal`。
|
|
393
|
+
- `ToolRunContext`: 包含 `runId`、`agentName`、`sessionId`、`metadata`、`signal`。
|
|
394
|
+
- `canExecute(context)`: 工具级权限策略,返回 `false` 时抛 `ToolPermissionError`。
|
|
395
|
+
- `errorMode`: 默认 `return_to_model`,工具失败会作为 `isError: true` 的工具结果回传给模型;设为 `throw` 会中止本次 Agent run。
|
|
396
|
+
- `serializeResult`: 可自定义工具结果如何转成 `MessageContent`。默认规则:字符串和 content block 原样返回,`undefined` 返回空字符串,其余值 `JSON.stringify()`。
|
|
397
|
+
|
|
398
|
+
工具名必须符合 OpenAI-compatible function name 要求:1-64 个字符,仅字母、数字、下划线或连字符。
|
|
399
|
+
|
|
400
|
+
`ToolRegistry` 默认拒绝重复工具名:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import { ToolRegistry } from '@fllf/agent-sdk';
|
|
404
|
+
|
|
405
|
+
const registry = new ToolRegistry();
|
|
406
|
+
registry.register(tool);
|
|
407
|
+
registry.register(nextTool, { replace: true });
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
`ToolCallingExecutor` 关键行为:
|
|
411
|
+
|
|
412
|
+
- 单轮模型返回多个 tool call 时并发执行,结果顺序保持与模型请求一致。
|
|
413
|
+
- 默认 `onMaxIterations: 'finalize'`:达到迭代上限后追加一轮不带工具的模型请求,让模型给出文字收尾。
|
|
414
|
+
- 可设置 `onMaxIterations: 'throw'` 恢复超限即抛错;已执行轨迹仍会写入 history。
|
|
415
|
+
|
|
416
|
+
## 直接使用 LLM
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
import { LLM } from '@fllf/agent-sdk';
|
|
420
|
+
|
|
421
|
+
const llm = new LLM({
|
|
422
|
+
provider: 'anthropic',
|
|
423
|
+
model: 'claude-opus-4-8',
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const response = await llm.chat({
|
|
427
|
+
messages: [{ role: 'user', content: 'Say hello.' }],
|
|
428
|
+
maxTokens: 256,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
console.log(response.content);
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
流式:
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
for await (const chunk of llm.stream({
|
|
438
|
+
messages: [{ role: 'user', content: 'Stream a short answer.' }],
|
|
439
|
+
})) {
|
|
440
|
+
process.stdout.write(chunk.contentDelta ?? '');
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
结构化输出:
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import { z } from 'zod';
|
|
448
|
+
import { LLM } from '@fllf/agent-sdk';
|
|
449
|
+
|
|
450
|
+
const llm = new LLM();
|
|
451
|
+
|
|
452
|
+
const result = await llm.generateObject({
|
|
453
|
+
messages: [{ role: 'user', content: 'Return a short project summary.' }],
|
|
454
|
+
schema: z.object({
|
|
455
|
+
summary: z.string(),
|
|
456
|
+
confidence: z.number(),
|
|
457
|
+
}),
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
console.log(result.value);
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
高级请求字段:
|
|
464
|
+
|
|
465
|
+
- `tools`: provider-neutral `ToolDefinition[]`。
|
|
466
|
+
- `toolChoice`: `'auto' | 'none' | 'required' | { type: 'tool'; name: string }`。
|
|
467
|
+
- `reasoning: { effort }`: OpenAI 映射为 `reasoning_effort`,Anthropic 映射为 `output_config.effort`。
|
|
468
|
+
- `ChatMessage.cacheControl: 'ephemeral'`: Anthropic prompt cache 断点;其他 provider 忽略。
|
|
469
|
+
- `responseFormat`: JSON schema 结构化输出。
|
|
470
|
+
|
|
471
|
+
`ChatStreamAccumulator` 可把 provider 的 `ChatStreamChunk` 聚合成完整 `ChatResponse`:
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import { ChatStreamAccumulator } from '@fllf/agent-sdk';
|
|
475
|
+
|
|
476
|
+
const acc = new ChatStreamAccumulator();
|
|
477
|
+
for await (const chunk of llm.stream(request)) {
|
|
478
|
+
acc.add(chunk);
|
|
479
|
+
}
|
|
480
|
+
const response = acc.toResponse();
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## 自定义模型 provider
|
|
484
|
+
|
|
485
|
+
全局注册:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import { LLM, ModelProviderFactory } from '@fllf/agent-sdk';
|
|
489
|
+
|
|
490
|
+
ModelProviderFactory.register('tenant-provider', (config) => (
|
|
491
|
+
new TenantProvider(config)
|
|
492
|
+
));
|
|
493
|
+
|
|
494
|
+
const llm = new LLM({ provider: 'tenant-provider' });
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
多租户、测试或插件系统建议使用隔离 registry,避免全局污染:
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
import { LLM, ModelProviderRegistry } from '@fllf/agent-sdk';
|
|
501
|
+
|
|
502
|
+
const registry = new ModelProviderRegistry({
|
|
503
|
+
includeDefaultProviders: false,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
registry.register('tenant-provider', (config) => new TenantProvider(config));
|
|
507
|
+
|
|
508
|
+
const llm = new LLM({
|
|
509
|
+
provider: 'tenant-provider',
|
|
510
|
+
providerRegistry: registry,
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
自定义 provider 需要实现 `ChatModel`,并正确声明 `capabilities()`。SDK 会在调用前校验 tool calling、structured output、streaming 等能力,不支持时抛 `LLMUnsupportedCapabilityError`。
|
|
515
|
+
|
|
516
|
+
## 消息历史与持久化
|
|
517
|
+
|
|
518
|
+
`Message.toJSON()` 是推荐持久化格式;读取时用 `Message.fromJSON(row)` 或 `reviveMessage(row)`。
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
type MessageJSON = {
|
|
522
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
523
|
+
content: string | ContentBlock[];
|
|
524
|
+
timestamp: string;
|
|
525
|
+
metadata: Record<string, unknown>;
|
|
526
|
+
};
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
读取窗口:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
const recent = await history.get({ limit: 20 });
|
|
533
|
+
const before = await history.get({
|
|
534
|
+
before: '2026-01-01T00:00:00.000Z',
|
|
535
|
+
limit: 20,
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
`before` 是排他的时间游标;`limit` 返回过滤后的最近 N 条消息。窗口截断会修复开头孤儿 tool 上下文,避免 provider 400。
|
|
540
|
+
|
|
541
|
+
### Postgres 消息历史
|
|
542
|
+
|
|
543
|
+
适用于任何提供 `query(sql, params)` 的对象,例如 `pg.Pool`。SDK 不依赖 `pg` 包,也不依赖事务 API;原子写路径使用单条 SQL。
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
import {
|
|
547
|
+
createPostgresMessageHistorySchemaSql,
|
|
548
|
+
PostgresMessageHistoryStore,
|
|
549
|
+
} from '@fllf/agent-sdk';
|
|
550
|
+
|
|
551
|
+
await db.query(createPostgresMessageHistorySchemaSql({
|
|
552
|
+
tablePrefix: 'agent_history',
|
|
553
|
+
}));
|
|
554
|
+
|
|
555
|
+
const historyStore = new PostgresMessageHistoryStore({
|
|
556
|
+
db,
|
|
557
|
+
tablePrefix: 'agent_history',
|
|
558
|
+
});
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
多实例共享同一个历史库时,可使用乐观并发:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
const history = historyStore.get(sessionId);
|
|
565
|
+
const version = await history.getVersion?.();
|
|
566
|
+
|
|
567
|
+
await history.addMany(messages, {
|
|
568
|
+
expectedVersion: version,
|
|
569
|
+
});
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
版本冲突抛 `HistoryVersionConflictError`,`retryable: true`。典型处理方式是重新读取历史和版本后重试。
|
|
573
|
+
|
|
574
|
+
## 上下文窗口与历史压缩
|
|
575
|
+
|
|
576
|
+
配置 `maxInputTokens` 后,executor 会从最新消息向前保留,超预算时截断较旧消息。估算器为轻量启发式,不用于计费。
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
import {
|
|
580
|
+
Agent,
|
|
581
|
+
Config,
|
|
582
|
+
SummarizingContextCompressor,
|
|
583
|
+
} from '@fllf/agent-sdk';
|
|
584
|
+
|
|
585
|
+
const agent = new Agent({
|
|
586
|
+
name: 'compressing-agent',
|
|
587
|
+
config: new Config({ maxInputTokens: 8000 }),
|
|
588
|
+
contextCompressor: new SummarizingContextCompressor({
|
|
589
|
+
maxSummaryTokens: 512,
|
|
590
|
+
}),
|
|
591
|
+
});
|
|
207
592
|
```
|
|
208
593
|
|
|
209
|
-
|
|
594
|
+
`SummarizingContextCompressor` 会复用当前 Agent 的 LLM,把被截断的旧消息压缩成一条摘要前缀。摘要失败时自动降级为纯截断,不中断主请求。
|
|
210
595
|
|
|
211
|
-
RAG
|
|
212
|
-
`RagPipeline` through either:
|
|
596
|
+
## RAG 接入
|
|
213
597
|
|
|
214
|
-
|
|
215
|
-
QA.
|
|
216
|
-
- `RagSearchTool`: let a tool-calling agent retrieve evidence only when the
|
|
217
|
-
model decides it needs knowledge-base context.
|
|
598
|
+
RAG 子系统通过 `RagPipeline` 暴露三个核心方法:
|
|
218
599
|
|
|
219
|
-
|
|
600
|
+
- `ingest(input)`: 文档解析、标准化、切块、embedding、写入文档库/向量索引/关键词索引。
|
|
601
|
+
- `retrieve(query, options)`: 只返回可引用证据。
|
|
602
|
+
- `answer(query, options)`: 检索、构建上下文、调用模型生成答案、校验引用。
|
|
220
603
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
604
|
+
常用检索/回答参数:
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
type RetrieveOptions = {
|
|
608
|
+
topK?: number;
|
|
609
|
+
minScore?: number;
|
|
610
|
+
tenantId?: string;
|
|
611
|
+
knowledgeBaseId?: string;
|
|
612
|
+
acl?: string[];
|
|
613
|
+
documentIds?: string[];
|
|
614
|
+
sources?: string[];
|
|
615
|
+
metadataFilter?: Record<string, unknown>;
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
type AnswerOptions = RetrieveOptions & {
|
|
619
|
+
llm?: ChatModel;
|
|
620
|
+
maxContextTokens?: number;
|
|
621
|
+
minFusedScore?: number;
|
|
622
|
+
requireCitations?: boolean;
|
|
623
|
+
systemPrompt?: string;
|
|
624
|
+
metadata?: Record<string, unknown>;
|
|
625
|
+
};
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
`minScore` 作用在具体 retriever 的原始分数尺度上;`minFusedScore` 作用在 context builder 的融合后分数尺度上,两者不要混用。
|
|
629
|
+
|
|
630
|
+
### 构建内存 RAG pipeline
|
|
631
|
+
|
|
632
|
+
适合本地开发、测试或最小 MVP;生产应替换为 Postgres/pgvector 或自有存储。
|
|
224
633
|
|
|
225
634
|
```ts
|
|
226
635
|
import {
|
|
@@ -236,6 +645,7 @@ import {
|
|
|
236
645
|
InMemoryKeywordStore,
|
|
237
646
|
InMemoryVectorStore,
|
|
238
647
|
OpenAIEmbedder,
|
|
648
|
+
ParentChildExpandingRetriever,
|
|
239
649
|
RuleBasedVerifier,
|
|
240
650
|
SparseRetriever,
|
|
241
651
|
} from '@fllf/agent-sdk';
|
|
@@ -258,6 +668,17 @@ const sparseRetriever = new SparseRetriever({
|
|
|
258
668
|
documentStore,
|
|
259
669
|
});
|
|
260
670
|
|
|
671
|
+
const retriever = new ParentChildExpandingRetriever({
|
|
672
|
+
retriever: new HybridRetriever({
|
|
673
|
+
denseRetriever,
|
|
674
|
+
sparseRetriever,
|
|
675
|
+
}),
|
|
676
|
+
documentStore,
|
|
677
|
+
strategy: 'parent_or_neighbors',
|
|
678
|
+
neighborWindow: 1,
|
|
679
|
+
maxExpandedChunkCharacters: 3000,
|
|
680
|
+
});
|
|
681
|
+
|
|
261
682
|
export const ragPipeline = new DefaultRagPipeline({
|
|
262
683
|
loader: new AutoDocumentLoader(),
|
|
263
684
|
normalizer: new DefaultDocumentNormalizer(),
|
|
@@ -266,22 +687,59 @@ export const ragPipeline = new DefaultRagPipeline({
|
|
|
266
687
|
documentStore,
|
|
267
688
|
vectorStore,
|
|
268
689
|
keywordStore,
|
|
269
|
-
retriever
|
|
270
|
-
|
|
271
|
-
sparseRetriever,
|
|
272
|
-
}),
|
|
273
|
-
contextBuilder: new DefaultContextBuilder({
|
|
274
|
-
maxContextTokens: 3000,
|
|
275
|
-
}),
|
|
690
|
+
retriever,
|
|
691
|
+
contextBuilder: new DefaultContextBuilder({ maxContextTokens: 3000 }),
|
|
276
692
|
generator: new DefaultRagGenerator(),
|
|
277
693
|
verifier: new RuleBasedVerifier(),
|
|
278
694
|
});
|
|
279
695
|
```
|
|
280
696
|
|
|
281
|
-
###
|
|
697
|
+
### Embedding
|
|
698
|
+
|
|
699
|
+
OpenAI embedding:
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
import { OpenAIEmbedder } from '@fllf/agent-sdk';
|
|
703
|
+
|
|
704
|
+
const embedder = new OpenAIEmbedder({
|
|
705
|
+
model: 'text-embedding-3-small',
|
|
706
|
+
});
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
OpenAI-compatible embedding provider:
|
|
710
|
+
|
|
711
|
+
```ts
|
|
712
|
+
import {
|
|
713
|
+
InMemoryVectorStore,
|
|
714
|
+
OpenAICompatibleEmbedder,
|
|
715
|
+
} from '@fllf/agent-sdk';
|
|
716
|
+
|
|
717
|
+
const embedder = new OpenAICompatibleEmbedder({
|
|
718
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
719
|
+
baseURL: 'https://gateway.example.test/v1',
|
|
720
|
+
model: 'bge-m3',
|
|
721
|
+
dimensions: 1024,
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
const vectorStore = new InMemoryVectorStore({
|
|
725
|
+
dimensions: embedder.dimensions,
|
|
726
|
+
});
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
未知 embedding 模型必须显式传 `dimensions`。更换 embedding 模型或维度时,应新建向量集合或重新 ingestion,不能混用不同维度/模型的向量。
|
|
282
730
|
|
|
283
|
-
|
|
284
|
-
|
|
731
|
+
可用 `EmbeddingCache` 缓存 embedding:
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
import { EmbeddingCache, OpenAIEmbedder } from '@fllf/agent-sdk';
|
|
735
|
+
|
|
736
|
+
const embedder = new EmbeddingCache({
|
|
737
|
+
embedder: new OpenAIEmbedder(),
|
|
738
|
+
namespace: 'tenant_1:kb_support',
|
|
739
|
+
});
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### Ingest 文档
|
|
285
743
|
|
|
286
744
|
```ts
|
|
287
745
|
await ragPipeline.ingest({
|
|
@@ -294,16 +752,92 @@ await ragPipeline.ingest({
|
|
|
294
752
|
});
|
|
295
753
|
```
|
|
296
754
|
|
|
297
|
-
|
|
755
|
+
关键字段:
|
|
756
|
+
|
|
757
|
+
- `source`: 稳定来源路径、URL 或业务文档 id,会出现在引用中。
|
|
758
|
+
- `tenantId`: 租户边界。
|
|
759
|
+
- `knowledgeBaseId`: 知识库边界。
|
|
760
|
+
- `acl`: 检索阶段的访问控制标签。
|
|
761
|
+
- `metadata`: 文档元数据,可包含 `title`、`mimeType`、`version`、`extra` 等。
|
|
762
|
+
- `replaceExisting`: 使用稳定 document id 清理旧 chunks/vectors/keywords;checksum 相同则跳过重复 ingestion。
|
|
763
|
+
|
|
764
|
+
`AutoDocumentLoader` 根据 `mimeType` 或 `source` 后缀选择 Markdown、HTML、DOCX、PDF、纯文本 loader。HTML/DOCX/PDF 需要安装对应可选依赖。
|
|
765
|
+
|
|
766
|
+
### 切块与检索
|
|
767
|
+
|
|
768
|
+
`AutoChunker` 当前策略:
|
|
769
|
+
|
|
770
|
+
- Markdown 或由 HTML/DOCX/PDF 转换出的 Markdown:`MarkdownChunker`,保留标题、表格、代码等结构。
|
|
771
|
+
- FAQ/QA 风格文本:`QaPairChunker`。
|
|
772
|
+
- 长无结构文本:`ParentChildChunker`,小块召回、大块提供上下文。
|
|
773
|
+
- 其他短文本:`RecursiveChunker`。
|
|
774
|
+
|
|
775
|
+
检索器:
|
|
776
|
+
|
|
777
|
+
- `DenseRetriever`: embedding + vector store。
|
|
778
|
+
- `SparseRetriever`: keyword store。
|
|
779
|
+
- `HybridRetriever`: dense/sparse 并行检索,使用 RRF 融合,可接 reranker。
|
|
780
|
+
- `ParentChildExpandingRetriever`: 把 child hit 扩展为 parent 或邻近 chunk。
|
|
781
|
+
- `DefaultReranker`: 未配置模型时 no-op;配置 `RerankModelClient` 后按 rerank 分数重排。
|
|
782
|
+
- `HttpRerankModelClient`: 适配 Jina/Cohere/常见网关 `/rerank` 端点。
|
|
783
|
+
|
|
784
|
+
Rerank 示例:
|
|
785
|
+
|
|
786
|
+
```ts
|
|
787
|
+
import {
|
|
788
|
+
DefaultReranker,
|
|
789
|
+
HttpRerankModelClient,
|
|
790
|
+
HybridRetriever,
|
|
791
|
+
} from '@fllf/agent-sdk';
|
|
792
|
+
|
|
793
|
+
const reranker = new DefaultReranker({
|
|
794
|
+
model: new HttpRerankModelClient({
|
|
795
|
+
baseURL: process.env.RERANK_BASE_URL,
|
|
796
|
+
apiKey: process.env.RERANK_API_KEY,
|
|
797
|
+
model: process.env.RERANK_MODEL ?? 'jina-reranker-v2-base-multilingual',
|
|
798
|
+
}),
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
const retriever = new HybridRetriever({
|
|
802
|
+
denseRetriever,
|
|
803
|
+
sparseRetriever,
|
|
804
|
+
reranker,
|
|
805
|
+
});
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### Postgres/pgvector RAG 存储
|
|
809
|
+
|
|
810
|
+
```ts
|
|
811
|
+
import {
|
|
812
|
+
createPostgresRagSchemaSql,
|
|
813
|
+
PgVectorStore,
|
|
814
|
+
PostgresDocumentStore,
|
|
815
|
+
PostgresKeywordStore,
|
|
816
|
+
} from '@fllf/agent-sdk';
|
|
817
|
+
|
|
818
|
+
await db.query(createPostgresRagSchemaSql({
|
|
819
|
+
tablePrefix: 'rag',
|
|
820
|
+
dimensions: embedder.dimensions,
|
|
821
|
+
}));
|
|
822
|
+
|
|
823
|
+
const documentStore = new PostgresDocumentStore({ db, tablePrefix: 'rag' });
|
|
824
|
+
const vectorStore = new PgVectorStore({
|
|
825
|
+
db,
|
|
826
|
+
tablePrefix: 'rag',
|
|
827
|
+
dimensions: embedder.dimensions,
|
|
828
|
+
});
|
|
829
|
+
const keywordStore = new PostgresKeywordStore({ db, tablePrefix: 'rag' });
|
|
830
|
+
```
|
|
298
831
|
|
|
299
|
-
|
|
300
|
-
- `tenantId`: tenant boundary.
|
|
301
|
-
- `knowledgeBaseId`: knowledge-base boundary.
|
|
302
|
-
- `acl`: access-control labels used at retrieval time.
|
|
303
|
-
- `replaceExisting`: delete previous chunks and indexes for the same document id
|
|
304
|
-
before writing new ones.
|
|
832
|
+
建表 SQL 默认:
|
|
305
833
|
|
|
306
|
-
|
|
834
|
+
- 创建 `vector` 扩展。
|
|
835
|
+
- 创建 documents、chunks、embeddings、keyword_entries 表。
|
|
836
|
+
- 创建 HNSW 向量索引、GIN 关键词/ACL/extra 索引等。
|
|
837
|
+
|
|
838
|
+
所有 Postgres RAG store 只依赖注入的 `query(sql, params)`,可直接使用 `pg.Pool` 或兼容封装。
|
|
839
|
+
|
|
840
|
+
### Knowledge-base QA Agent
|
|
307
841
|
|
|
308
842
|
```ts
|
|
309
843
|
import { Agent, RagExecutor } from '@fllf/agent-sdk';
|
|
@@ -329,11 +863,17 @@ const result = await knowledgeAgent.run('What is the refund window?', {
|
|
|
329
863
|
},
|
|
330
864
|
});
|
|
331
865
|
|
|
332
|
-
|
|
333
|
-
|
|
866
|
+
return {
|
|
867
|
+
message: result.output,
|
|
868
|
+
citations: (result.raw as any).citations,
|
|
869
|
+
confidence: (result.raw as any).confidence,
|
|
870
|
+
usage: result.usage,
|
|
871
|
+
};
|
|
334
872
|
```
|
|
335
873
|
|
|
336
|
-
`
|
|
874
|
+
`RagExecutor` 会从 `metadata` 读取 `tenantId`、`knowledgeBaseId`、`acl`、`documentIds`、`sources`、`metadataFilter`、`topK`、`minScore`、`maxContextTokens`。
|
|
875
|
+
|
|
876
|
+
`RagAnswer` 形状:
|
|
337
877
|
|
|
338
878
|
```ts
|
|
339
879
|
type RagAnswer = {
|
|
@@ -349,27 +889,15 @@ type RagAnswer = {
|
|
|
349
889
|
quote?: string;
|
|
350
890
|
}>;
|
|
351
891
|
confidence: 'high' | 'medium' | 'low';
|
|
352
|
-
retrieved:
|
|
353
|
-
usage?:
|
|
892
|
+
retrieved: RetrievedChunk[];
|
|
893
|
+
usage?: TokenUsage;
|
|
354
894
|
metadata?: Record<string, unknown>;
|
|
355
895
|
};
|
|
356
896
|
```
|
|
357
897
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
```ts
|
|
361
|
-
return {
|
|
362
|
-
message: result.output,
|
|
363
|
-
citations: (result.raw as any).citations,
|
|
364
|
-
confidence: (result.raw as any).confidence,
|
|
365
|
-
usage: result.usage,
|
|
366
|
-
};
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### RAG as an optional search tool
|
|
898
|
+
### RAG 作为可选工具
|
|
370
899
|
|
|
371
|
-
|
|
372
|
-
base.
|
|
900
|
+
模型需要时才检索知识库:
|
|
373
901
|
|
|
374
902
|
```ts
|
|
375
903
|
import {
|
|
@@ -386,6 +914,9 @@ const agent = new Agent({
|
|
|
386
914
|
new RagSearchTool({
|
|
387
915
|
pipeline: ragPipeline,
|
|
388
916
|
defaultRetrieveOptions: {
|
|
917
|
+
tenantId: 'tenant_1',
|
|
918
|
+
knowledgeBaseId: 'kb_support',
|
|
919
|
+
acl: ['user:user_42', 'role:support'],
|
|
389
920
|
topK: 5,
|
|
390
921
|
},
|
|
391
922
|
}),
|
|
@@ -394,30 +925,53 @@ const agent = new Agent({
|
|
|
394
925
|
});
|
|
395
926
|
```
|
|
396
927
|
|
|
397
|
-
|
|
928
|
+
安全边界:`RagSearchTool` 不把 `tenantId`、`knowledgeBaseId`、`acl` 暴露给模型参数,这些权限字段只能由服务端通过 `defaultRetrieveOptions` 设置。模型可传的仅是 `query`、`topK`、`minScore`、`documentIds`、`sources`、`metadataFilter` 等检索调优字段。
|
|
929
|
+
|
|
930
|
+
## 内置搜索工具
|
|
931
|
+
|
|
932
|
+
`AdvancedSearchTool` 可接入 Tavily 和 SerpAPI:
|
|
398
933
|
|
|
399
934
|
```ts
|
|
400
|
-
import {
|
|
401
|
-
import { LLM } from '@fllf/agent-sdk';
|
|
935
|
+
import { AdvancedSearchTool } from '@fllf/agent-sdk';
|
|
402
936
|
|
|
403
|
-
const
|
|
937
|
+
const tools = [new AdvancedSearchTool({ logger })];
|
|
938
|
+
```
|
|
404
939
|
|
|
405
|
-
|
|
406
|
-
messages: [
|
|
407
|
-
{ role: 'user', content: 'Return a short project summary.' },
|
|
408
|
-
],
|
|
409
|
-
schema: z.object({
|
|
410
|
-
summary: z.string(),
|
|
411
|
-
confidence: z.number(),
|
|
412
|
-
}),
|
|
413
|
-
});
|
|
940
|
+
环境变量:
|
|
414
941
|
|
|
415
|
-
|
|
942
|
+
```bash
|
|
943
|
+
TAVILY_API_KEY=...
|
|
944
|
+
SERPAPI_API_KEY=...
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
若两个 key 都未配置,工具会返回提示文本,不会抛出配置错误。该工具会真实调用外部搜索 API,第三方系统应自行处理网络权限、配额、审计和结果可信度。
|
|
948
|
+
|
|
949
|
+
## 生命周期钩子
|
|
950
|
+
|
|
951
|
+
```ts
|
|
952
|
+
import { Agent } from '@fllf/agent-sdk';
|
|
953
|
+
|
|
954
|
+
const agent = new Agent({
|
|
955
|
+
name: 'audited-chat',
|
|
956
|
+
hooks: {
|
|
957
|
+
beforeRun(context) {
|
|
958
|
+
authorize(context.metadata);
|
|
959
|
+
},
|
|
960
|
+
afterRun(context, result) {
|
|
961
|
+
audit.info({ runId: context.runId, outputLength: result.output.length });
|
|
962
|
+
},
|
|
963
|
+
onRunError(context, error) {
|
|
964
|
+
audit.error({ runId: context.runId, error });
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
});
|
|
416
968
|
```
|
|
417
969
|
|
|
418
|
-
|
|
970
|
+
`beforeRun` 失败会中止 run。`afterRun` 和 `onRunError` 失败不会替换主结果/主错误,会作为 warn 级 `agent:error` observer event 发出。
|
|
971
|
+
|
|
972
|
+
## 可观测性
|
|
419
973
|
|
|
420
|
-
|
|
974
|
+
SDK 默认不打印日志。接入方可通过 observer 收集事件。
|
|
421
975
|
|
|
422
976
|
```ts
|
|
423
977
|
import { Agent, ConsoleObserver } from '@fllf/agent-sdk';
|
|
@@ -430,7 +984,7 @@ const agent = new Agent({
|
|
|
430
984
|
await agent.run('Hello');
|
|
431
985
|
```
|
|
432
986
|
|
|
433
|
-
|
|
987
|
+
事件类型:
|
|
434
988
|
|
|
435
989
|
- `agent:start`
|
|
436
990
|
- `agent:end`
|
|
@@ -438,37 +992,101 @@ Event types include:
|
|
|
438
992
|
- `llm:start`
|
|
439
993
|
- `llm:end`
|
|
440
994
|
- `llm:error`
|
|
995
|
+
- `llm:retry`
|
|
441
996
|
- `tool:start`
|
|
442
997
|
- `tool:end`
|
|
443
998
|
- `tool:error`
|
|
444
999
|
|
|
445
|
-
|
|
1000
|
+
自定义 observer:
|
|
446
1001
|
|
|
447
1002
|
```ts
|
|
448
1003
|
const observer = {
|
|
1004
|
+
redactEvent(event) {
|
|
1005
|
+
return {
|
|
1006
|
+
...event,
|
|
1007
|
+
metadata: undefined,
|
|
1008
|
+
};
|
|
1009
|
+
},
|
|
449
1010
|
onEvent(event) {
|
|
450
1011
|
logger.info({ event }, 'agent runtime event');
|
|
451
1012
|
},
|
|
452
1013
|
};
|
|
453
1014
|
```
|
|
454
1015
|
|
|
455
|
-
|
|
1016
|
+
事件包含 `runId`、`timestamp`、可选 `spanId` / `parentSpanId`、provider/model、toolName/toolCallId、duration、usage、error 等字段。不要把敏感信息放入 `metadata`,因为 metadata 可能出现在消息、工具上下文和 observer 事件中。
|
|
1017
|
+
|
|
1018
|
+
测试中可使用:
|
|
1019
|
+
|
|
1020
|
+
```ts
|
|
1021
|
+
import { CollectingObserver } from '@fllf/agent-sdk';
|
|
1022
|
+
|
|
1023
|
+
const observer = new CollectingObserver();
|
|
1024
|
+
await agent.run('Hello', { observer });
|
|
1025
|
+
|
|
1026
|
+
console.log(observer.events);
|
|
1027
|
+
observer.clear();
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
## 错误处理
|
|
1031
|
+
|
|
1032
|
+
核心错误类型从根包和 `@fllf/agent-sdk/errors` 导出:
|
|
1033
|
+
|
|
1034
|
+
- `AgentSDKError`: SDK 统一错误基类,含 `code`、`retryable`、`metadata`。
|
|
1035
|
+
- `AgentError`、`AgentSessionError`、`AgentExecutionError`。
|
|
1036
|
+
- `ToolError`、`ToolConfigurationError`、`ToolPermissionError`、`ToolTimeoutError`、`ToolAbortError`、`ToolExecutionError`、`ToolNotFoundError`。
|
|
1037
|
+
- `HistoryError`、`HistoryVersionConflictError`。
|
|
1038
|
+
- `ConfigError`。
|
|
1039
|
+
- LLM 层错误:`LLMAuthenticationError`、`LLMRateLimitError`、`LLMTimeoutError`、`LLMAbortError`、`LLMUnsupportedCapabilityError`、`LLMProviderError`、`LLMInvalidResponseError`、`LLMStructuredOutputError`。
|
|
1040
|
+
|
|
1041
|
+
示例:
|
|
1042
|
+
|
|
1043
|
+
```ts
|
|
1044
|
+
import { AgentSDKError } from '@fllf/agent-sdk';
|
|
1045
|
+
|
|
1046
|
+
try {
|
|
1047
|
+
await agent.run(input, options);
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
if (error instanceof AgentSDKError) {
|
|
1050
|
+
logger.warn({
|
|
1051
|
+
code: error.code,
|
|
1052
|
+
retryable: error.retryable,
|
|
1053
|
+
metadata: error.metadata,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
throw error;
|
|
1057
|
+
}
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
LLM 会把 401/403、429、超时、调用方 abort、常见瞬态网络错误归一化为稳定错误类型。可重试错误会触发内置重试,并产生 `llm:retry` 事件。
|
|
456
1061
|
|
|
457
|
-
|
|
1062
|
+
## 测试辅助工具
|
|
458
1063
|
|
|
459
|
-
|
|
460
|
-
- A persistent `MessageHistoryStore`.
|
|
461
|
-
- Persistent RAG stores and a vector/search backend.
|
|
462
|
-
- AuthN/AuthZ before `Agent.run()` or `RagPipeline.retrieve()`.
|
|
463
|
-
- Tenant, knowledge-base, and ACL filters in every RAG request.
|
|
464
|
-
- Background document ingestion and index rebuild jobs.
|
|
465
|
-
- Rate limits, model cost controls, and request timeouts.
|
|
466
|
-
- Redaction or filtering for logs that may contain user messages or document
|
|
467
|
-
snippets.
|
|
1064
|
+
下游系统可使用 SDK 提供的测试工具编写稳定集成测试:
|
|
468
1065
|
|
|
469
|
-
|
|
1066
|
+
```ts
|
|
1067
|
+
import {
|
|
1068
|
+
assertMessageHistoryStoreContract,
|
|
1069
|
+
assertToolSchemaSnapshot,
|
|
1070
|
+
createTestAgent,
|
|
1071
|
+
createToolSchemaSnapshot,
|
|
1072
|
+
FakeChatModel,
|
|
1073
|
+
FakeTool,
|
|
1074
|
+
ScriptedChatModel,
|
|
1075
|
+
} from '@fllf/agent-sdk';
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
用途:
|
|
470
1079
|
|
|
471
|
-
|
|
1080
|
+
- `ScriptedChatModel`: 队列化返回固定模型响应,并记录请求。
|
|
1081
|
+
- `FakeChatModel`: 回显最后一条 user 消息。
|
|
1082
|
+
- `FakeTool`: 校验输入并记录调用。
|
|
1083
|
+
- `createTestAgent()`: 快速创建测试 Agent。
|
|
1084
|
+
- `createToolSchemaSnapshot()` / `assertToolSchemaSnapshot()`: 对工具暴露 schema 做快照测试。
|
|
1085
|
+
- `assertMessageHistoryStoreContract()`: 校验自定义历史存储满足 session 隔离、窗口读取、防御性拷贝、可选 CAS 语义等契约。
|
|
1086
|
+
|
|
1087
|
+
## 包导出
|
|
1088
|
+
|
|
1089
|
+
推荐从根包导入稳定 API:
|
|
472
1090
|
|
|
473
1091
|
```ts
|
|
474
1092
|
import {
|
|
@@ -482,33 +1100,75 @@ import {
|
|
|
482
1100
|
} from '@fllf/agent-sdk';
|
|
483
1101
|
```
|
|
484
1102
|
|
|
485
|
-
|
|
1103
|
+
也支持子路径导入:
|
|
486
1104
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
1105
|
+
```ts
|
|
1106
|
+
import { Agent } from '@fllf/agent-sdk/agent';
|
|
1107
|
+
import { Config } from '@fllf/agent-sdk/config';
|
|
1108
|
+
import { AgentSDKError } from '@fllf/agent-sdk/errors';
|
|
1109
|
+
import { LLM } from '@fllf/agent-sdk/llm';
|
|
1110
|
+
import { Message } from '@fllf/agent-sdk/messages';
|
|
1111
|
+
import { ConsoleObserver } from '@fllf/agent-sdk/observability';
|
|
1112
|
+
import { DefaultRagPipeline } from '@fllf/agent-sdk/rag';
|
|
1113
|
+
import { Tool } from '@fllf/agent-sdk/tools';
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
稳定接入面:
|
|
1117
|
+
|
|
1118
|
+
- Agent: `Agent`、`createAgent`、`AgentRunOptions`、`AgentResult`、`AgentLifecycleHooks`。
|
|
1119
|
+
- Executors: `SimpleChatExecutor`、`ToolCallingExecutor`、`RagExecutor`、`BaseExecutor`、`buildChatMessages`、`buildChatRequest`。
|
|
1120
|
+
- LLM: `LLM`、`ChatModel`、`ChatRequest`、`ChatResponse`、`ModelProviderFactory`、`ModelProviderRegistry`。
|
|
1121
|
+
- Messages: `Message`、`MessageContent`、`ContentBlock`、`textBlock()`、`imageUrlBlock()`、`imageBase64Block()`、`contentToText()`。
|
|
1122
|
+
- Tools: `Tool`、`ToolRegistry`、`ToolExecutor`、`RagSearchTool`、`AdvancedSearchTool`。
|
|
1123
|
+
- History: `MessageHistory`、`MessageHistoryStore`、`InMemoryMessageHistoryStore`、`PostgresMessageHistoryStore`、`MessageJSON`、`reviveMessage()`。
|
|
1124
|
+
- RAG: pipeline、loader、normalizer、chunker、embedder、retriever、reranker、store、generator、verifier 相关接口与默认实现。
|
|
1125
|
+
- Observability: `Observer`、`RuntimeEvent`、`ConsoleObserver`、`CollectingObserver`。
|
|
1126
|
+
- Testing: `ScriptedChatModel`、`FakeChatModel`、`FakeTool`、`createTestAgent()`、契约/快照 helper。
|
|
1127
|
+
|
|
1128
|
+
包仍处于 `0.x` 阶段。应用代码应优先依赖上述稳定集成面;低层 provider 映射细节、SQL helper、实验性 chunker 等可能在小版本中调整。
|
|
1129
|
+
|
|
1130
|
+
## 生产接入清单
|
|
1131
|
+
|
|
1132
|
+
- 为每个外部请求建立明确的 tenant/user/conversation/session 边界。
|
|
1133
|
+
- 在调用 `Agent.run()`、`Agent.stream()`、`RagPipeline.retrieve()` 前完成认证与授权。
|
|
1134
|
+
- 使用持久化 `MessageHistoryStore`,多实例部署时处理 `HistoryVersionConflictError` 重试。
|
|
1135
|
+
- RAG 检索必须带 `tenantId`、`knowledgeBaseId` 和 ACL/metadata filter。
|
|
1136
|
+
- 文档 ingestion 放入后台任务或管理端流程,支持重建索引。
|
|
1137
|
+
- 为模型请求、工具、外部搜索和 rerank 设置超时与配额。
|
|
1138
|
+
- 对日志、observer event、metadata、工具结果和 RAG 片段做脱敏。
|
|
1139
|
+
- 不把用户可控字段作为 RAG 权限字段;`RagSearchTool` 权限字段只由服务端设置。
|
|
1140
|
+
- 更换 embedding 模型或维度后重建向量索引。
|
|
495
1141
|
|
|
496
|
-
##
|
|
1142
|
+
## 本仓库开发命令
|
|
497
1143
|
|
|
498
1144
|
```bash
|
|
499
1145
|
npm install
|
|
500
1146
|
npm run typecheck
|
|
501
1147
|
npm test
|
|
502
1148
|
npm run build
|
|
1149
|
+
npm run test:exports
|
|
503
1150
|
```
|
|
504
1151
|
|
|
505
|
-
|
|
1152
|
+
示例:
|
|
506
1153
|
|
|
507
1154
|
```bash
|
|
508
1155
|
npm run example:simple-chat
|
|
1156
|
+
npm run example:streaming-chat
|
|
509
1157
|
npm run example:tool-calling
|
|
1158
|
+
npm run example:tool-error-policy
|
|
510
1159
|
npm run example:local-ollama
|
|
1160
|
+
npm run example:custom-provider
|
|
1161
|
+
npm run example:custom-history-store
|
|
1162
|
+
npm run example:custom-executor
|
|
1163
|
+
npm run example:custom-observer
|
|
511
1164
|
```
|
|
512
1165
|
|
|
513
|
-
|
|
514
|
-
|
|
1166
|
+
发布前建议:
|
|
1167
|
+
|
|
1168
|
+
```bash
|
|
1169
|
+
npm run typecheck
|
|
1170
|
+
npm test
|
|
1171
|
+
npm run build
|
|
1172
|
+
npm run test:exports
|
|
1173
|
+
npm run pack:dry-run
|
|
1174
|
+
```
|