@limo-labs/deity 0.1.3-alpha.2 → 0.2.0-alpha.0
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/README.md +455 -414
- package/dist/index.cjs +3980 -3710
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1755 -2772
- package/dist/index.d.ts +1755 -2772
- package/dist/index.js +3920 -3533
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime-CKrGWO-8.d.cts +1520 -0
- package/dist/jsx-dev-runtime-CKrGWO-8.d.ts +1520 -0
- package/dist/jsx-dev-runtime.cjs +63 -0
- package/dist/jsx-dev-runtime.cjs.map +1 -0
- package/dist/jsx-dev-runtime.d.cts +2 -0
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +58 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.cjs +63 -0
- package/dist/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime.d.cts +2 -0
- package/dist/jsx-runtime.d.ts +2 -0
- package/dist/jsx-runtime.js +58 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/package.json +22 -2
package/README.md
CHANGED
|
@@ -1,533 +1,574 @@
|
|
|
1
|
-
# @limo-labs/deity
|
|
1
|
+
# @limo-labs/deity
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Declarative AI Agent Framework** — Build type-safe AI agents with JSX/TSX syntax
|
|
4
4
|
|
|
5
|
-
Deity
|
|
5
|
+
Deity is a declarative framework for building AI agents using familiar JSX/TSX syntax. Write agents like React components, with full TypeScript support and zero runtime overhead.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@limo-labs/deity)
|
|
8
8
|
[](https://github.com/limo-labs/deity/blob/main/LICENSE)
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
10
|
|
|
11
|
-
## ✨
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- **
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- ✅ **对话管理** - 自动修剪、角色索引、token 预算
|
|
25
|
-
- ✅ **内存管理** - 分层内存(核心/详细)、自动优先级
|
|
26
|
-
- ✅ **会话持久化** - 自动保存/恢复、灾难恢复
|
|
27
|
-
- ✅ **UI 集成** - 实时进度更新、事件驱动
|
|
28
|
-
- ✅ **工具调用** - 自动工具执行循环
|
|
29
|
-
- ✅ **统计跟踪** - 执行时间、token 使用、重试次数
|
|
30
|
-
|
|
31
|
-
### 🔧 开发体验
|
|
32
|
-
- 🎨 **直观的 API** - 简洁的函数式接口
|
|
33
|
-
- 📝 **完整的文档** - API 文档 + 使用示例
|
|
34
|
-
- 🧪 **305 个测试** - 100% 通过率
|
|
35
|
-
- 📊 **性能基准** - 27 个基准测试验证
|
|
36
|
-
- 🔍 **详细的跟踪** - 完整的执行日志
|
|
37
|
-
|
|
38
|
-
## 📦 安装
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
- 🎨 **JSX/TSX Syntax** - Write agents with familiar React-like syntax
|
|
14
|
+
- 🔒 **Type Safe** - Full TypeScript support with Zod schema validation
|
|
15
|
+
- 🔧 **Declarative** - Compose agents from reusable components
|
|
16
|
+
- 🚀 **Zero Runtime Overhead** - Compiles to lightweight AST nodes
|
|
17
|
+
- 🎯 **LLM Agnostic** - Works with any LLM adapter
|
|
18
|
+
- 🔁 **Auto Retry** - Built-in retry logic with LLM feedback
|
|
19
|
+
- ✅ **Output Validation** - Declarative validation rules
|
|
20
|
+
- 🛠️ **Tool Support** - Native tool calling integration
|
|
21
|
+
- 📊 **Observability** - Built-in execution tracing
|
|
22
|
+
|
|
23
|
+
## 📦 Installation
|
|
39
24
|
|
|
40
25
|
```bash
|
|
41
|
-
npm install @limo-labs/deity
|
|
26
|
+
npm install @limo-labs/deity zod
|
|
42
27
|
```
|
|
43
28
|
|
|
44
29
|
```bash
|
|
45
|
-
yarn add @limo-labs/deity
|
|
30
|
+
yarn add @limo-labs/deity zod
|
|
46
31
|
```
|
|
47
32
|
|
|
48
33
|
```bash
|
|
49
|
-
pnpm add @limo-labs/deity
|
|
34
|
+
pnpm add @limo-labs/deity zod
|
|
50
35
|
```
|
|
51
36
|
|
|
52
|
-
## 🚀
|
|
37
|
+
## 🚀 Quick Start
|
|
53
38
|
|
|
54
|
-
###
|
|
39
|
+
### 1. Configure TypeScript
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
import { z } from 'zod';
|
|
58
|
-
import { executeComponent, createEnhancedContext, InMemoryStore, InMemoryTrace } from '@limo-labs/deity';
|
|
59
|
-
import type { AgentComponent, LLMAdapter } from '@limo-labs/deity';
|
|
60
|
-
|
|
61
|
-
// 定义组件
|
|
62
|
-
const summarizer: AgentComponent<{ text: string }, { summary: string }> = {
|
|
63
|
-
id: 'summarizer',
|
|
64
|
-
inputSchema: z.object({ text: z.string() }),
|
|
65
|
-
outputSchema: z.object({ summary: z.string() }),
|
|
66
|
-
|
|
67
|
-
buildPrompt(ctx) {
|
|
68
|
-
return [
|
|
69
|
-
{ role: 'system', content: 'You are a summarization expert.' },
|
|
70
|
-
{ role: 'user', content: `Summarize: ${ctx.inputs.text}` }
|
|
71
|
-
];
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
validate(output) {
|
|
75
|
-
if (output.summary.length < 10) {
|
|
76
|
-
return { valid: false, errors: ['Summary too short'] };
|
|
77
|
-
}
|
|
78
|
-
return { valid: true };
|
|
79
|
-
},
|
|
41
|
+
Add JSX support to your `tsconfig.json`:
|
|
80
42
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"compilerOptions": {
|
|
46
|
+
"jsx": "react-jsx",
|
|
47
|
+
"jsxImportSource": "@limo-labs/deity"
|
|
84
48
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 创建上下文
|
|
88
|
-
const ctx = await createEnhancedContext({
|
|
89
|
-
inputs: { text: 'Long article text...' },
|
|
90
|
-
store: new InMemoryStore(),
|
|
91
|
-
trace: new InMemoryTrace()
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// 执行组件
|
|
95
|
-
const result = await executeComponent(summarizer, ctx, llmAdapter);
|
|
96
|
-
console.log(result.output.summary);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 多步工作流
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { runWorkflow, createStepNode, createSequenceNode } from '@limo-labs/deity';
|
|
103
|
-
import type { WorkflowConfig } from '@limo-labs/deity';
|
|
104
|
-
|
|
105
|
-
// 定义步骤
|
|
106
|
-
const step1: AgentComponent = {
|
|
107
|
-
id: 'analyze',
|
|
108
|
-
inputSchema: z.object({ code: z.string() }),
|
|
109
|
-
outputSchema: z.object({ issues: z.array(z.string()) }),
|
|
110
|
-
buildPrompt: (ctx) => [
|
|
111
|
-
{ role: 'user', content: `Analyze this code: ${ctx.inputs.code}` }
|
|
112
|
-
],
|
|
113
|
-
validate: () => ({ valid: true })
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const step2: AgentComponent = {
|
|
117
|
-
id: 'fix',
|
|
118
|
-
inputSchema: z.object({}),
|
|
119
|
-
outputSchema: z.object({ fixes: z.array(z.string()) }),
|
|
120
|
-
buildPrompt: (ctx) => {
|
|
121
|
-
const analysis = ctx.getOutput<{ issues: string[] }>('analyze');
|
|
122
|
-
return [
|
|
123
|
-
{ role: 'user', content: `Fix these issues: ${analysis.issues.join(', ')}` }
|
|
124
|
-
];
|
|
125
|
-
},
|
|
126
|
-
validate: () => ({ valid: true })
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// 配置工作流
|
|
130
|
-
const config: WorkflowConfig = {
|
|
131
|
-
name: 'code-fix-workflow',
|
|
132
|
-
graph: createSequenceNode([
|
|
133
|
-
createStepNode(step1),
|
|
134
|
-
createStepNode(step2)
|
|
135
|
-
]),
|
|
136
|
-
defaultModel: { adapter: llmAdapter }
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// 运行工作流
|
|
140
|
-
const result = await runWorkflow(config, { code: 'function test() { ... }' });
|
|
141
|
-
console.log(result.fixes);
|
|
49
|
+
}
|
|
142
50
|
```
|
|
143
51
|
|
|
144
|
-
###
|
|
52
|
+
### 2. Create Your First Agent
|
|
145
53
|
|
|
146
|
-
```
|
|
147
|
-
import {
|
|
54
|
+
```tsx
|
|
55
|
+
import { Agent, Prompt, System, User, Result } from '@limo-labs/deity';
|
|
56
|
+
import { z } from 'zod';
|
|
148
57
|
|
|
149
|
-
//
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
store: new InMemoryStore(),
|
|
153
|
-
trace: new InMemoryTrace(),
|
|
154
|
-
enableConversation: {
|
|
155
|
-
maxTokens: 4000,
|
|
156
|
-
pruneWhenFull: true
|
|
157
|
-
}
|
|
58
|
+
// Define schemas
|
|
59
|
+
const InputSchema = z.object({
|
|
60
|
+
topic: z.string()
|
|
158
61
|
});
|
|
159
62
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
conv.addMessage({ role: 'assistant', content: 'Hi there!' });
|
|
165
|
-
|
|
166
|
-
// 获取历史
|
|
167
|
-
const history = conv.getHistory();
|
|
168
|
-
|
|
169
|
-
// 按角色过滤
|
|
170
|
-
const userMessages = conv.getMessages('user');
|
|
63
|
+
const OutputSchema = z.object({
|
|
64
|
+
summary: z.string(),
|
|
65
|
+
keyPoints: z.array(z.string())
|
|
66
|
+
});
|
|
171
67
|
|
|
172
|
-
//
|
|
173
|
-
|
|
68
|
+
// Create agent using JSX
|
|
69
|
+
const SummarizerAgent = (
|
|
70
|
+
<Agent id="summarizer" input={InputSchema} output={OutputSchema}>
|
|
71
|
+
<Prompt>
|
|
72
|
+
<System>You are a summarization expert.</System>
|
|
73
|
+
<User>{(ctx: any): string => `Summarize this topic: ${ctx.inputs.topic}`}</User>
|
|
74
|
+
</Prompt>
|
|
75
|
+
<Result>
|
|
76
|
+
{(ctx: any, llmResult: any) => {
|
|
77
|
+
// Extract output from LLM response
|
|
78
|
+
const content = llmResult.response.content;
|
|
79
|
+
return JSON.parse(content);
|
|
80
|
+
}}
|
|
81
|
+
</Result>
|
|
82
|
+
</Agent>
|
|
83
|
+
);
|
|
174
84
|
```
|
|
175
85
|
|
|
176
|
-
###
|
|
86
|
+
### 3. Execute the Agent
|
|
177
87
|
|
|
178
88
|
```typescript
|
|
179
|
-
import { createEnhancedContext } from '@limo-labs/deity';
|
|
180
|
-
|
|
181
|
-
// 启用内存
|
|
182
|
-
const ctx = await createEnhancedContext({
|
|
183
|
-
inputs: {},
|
|
184
|
-
store: new InMemoryStore(),
|
|
185
|
-
trace: new InMemoryTrace(),
|
|
186
|
-
enableMemory: {
|
|
187
|
-
coreBudget: {
|
|
188
|
-
maxItems: 10,
|
|
189
|
-
maxTotalSize: 8192
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
});
|
|
89
|
+
import { compileAgent, executeComponent, createEnhancedContext } from '@limo-labs/deity';
|
|
193
90
|
|
|
194
|
-
//
|
|
195
|
-
const
|
|
91
|
+
// Compile JSX to executable component
|
|
92
|
+
const component = compileAgent(SummarizerAgent);
|
|
196
93
|
|
|
197
|
-
//
|
|
198
|
-
await
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// 添加核心记忆
|
|
202
|
-
await memory.addCoreMemory({
|
|
203
|
-
id: 'core1',
|
|
204
|
-
content: 'User prefers concise responses',
|
|
205
|
-
importance: 9,
|
|
206
|
-
tags: ['preference']
|
|
94
|
+
// Create execution context
|
|
95
|
+
const ctx = await createEnhancedContext({
|
|
96
|
+
inputs: { topic: 'Climate change' }
|
|
207
97
|
});
|
|
208
98
|
|
|
209
|
-
//
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
keywords: ['preference'],
|
|
213
|
-
maxItems: 5
|
|
214
|
-
});
|
|
99
|
+
// Execute
|
|
100
|
+
const result = await executeComponent(component, ctx, llmAdapter);
|
|
101
|
+
console.log(result.output.summary);
|
|
215
102
|
```
|
|
216
103
|
|
|
217
|
-
|
|
104
|
+
## 📖 Core Concepts
|
|
218
105
|
|
|
219
|
-
|
|
220
|
-
import { createUIBridge, runWorkflow } from '@limo-labs/deity';
|
|
106
|
+
### Components
|
|
221
107
|
|
|
222
|
-
|
|
223
|
-
const ui = createUIBridge();
|
|
108
|
+
Deity provides declarative components for building agents:
|
|
224
109
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
110
|
+
- **`<Agent>`** - Root component defining the agent
|
|
111
|
+
- **`<Prompt>`** - Prompt construction
|
|
112
|
+
- **`<System>`** - System message
|
|
113
|
+
- **`<User>`** - User message
|
|
114
|
+
- **`<Result>`** - Output extraction
|
|
115
|
+
- **`<Validate>`** - Output validation
|
|
116
|
+
- **`<Retry>`** - Retry configuration
|
|
117
|
+
- **`<Observe>`** - LLM loop observation
|
|
229
118
|
|
|
230
|
-
|
|
231
|
-
console.log(` ${update.progress}% - ${update.message}`);
|
|
232
|
-
});
|
|
119
|
+
### Agent Component
|
|
233
120
|
|
|
234
|
-
|
|
235
|
-
console.log(`✅ ${update.name} completed`);
|
|
236
|
-
});
|
|
121
|
+
The `<Agent>` component is the root of every agent:
|
|
237
122
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
123
|
+
```tsx
|
|
124
|
+
<Agent
|
|
125
|
+
id="unique-id"
|
|
126
|
+
input={InputSchema}
|
|
127
|
+
output={OutputSchema}
|
|
128
|
+
loopConfig={{ maxToolRounds: 10, timeout: 30000 }}
|
|
129
|
+
tools={[...]}
|
|
130
|
+
>
|
|
131
|
+
{/* Children components */}
|
|
132
|
+
</Agent>
|
|
133
|
+
```
|
|
245
134
|
|
|
246
|
-
|
|
135
|
+
**Props:**
|
|
136
|
+
- `id` (required) - Unique agent identifier
|
|
137
|
+
- `input` (required) - Zod schema for input validation
|
|
138
|
+
- `output` (required) - Zod schema for output validation
|
|
139
|
+
- `loopConfig` (optional) - LLM loop configuration
|
|
140
|
+
- `loopValidator` (optional) - Custom loop validator
|
|
141
|
+
- `tools` (optional) - Tool definitions
|
|
142
|
+
|
|
143
|
+
### Prompt Component
|
|
144
|
+
|
|
145
|
+
The `<Prompt>` component defines how to build the LLM prompt:
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<Prompt>
|
|
149
|
+
<System>
|
|
150
|
+
{async (): Promise<string> => {
|
|
151
|
+
// Load prompt from file or construct dynamically
|
|
152
|
+
return await loadPromptTemplate();
|
|
153
|
+
}}
|
|
154
|
+
</System>
|
|
155
|
+
<User>
|
|
156
|
+
{(ctx: any): string => `Process: ${ctx.inputs.task}`}
|
|
157
|
+
</User>
|
|
158
|
+
</Prompt>
|
|
247
159
|
```
|
|
248
160
|
|
|
249
|
-
|
|
161
|
+
### Result Component
|
|
250
162
|
|
|
251
|
-
|
|
163
|
+
The `<Result>` component extracts structured output:
|
|
252
164
|
|
|
253
|
-
|
|
165
|
+
```tsx
|
|
166
|
+
<Result>
|
|
167
|
+
{(ctx: any, llmResult: any): OutputType => {
|
|
168
|
+
// Extract from tool calls
|
|
169
|
+
const toolCall = llmResult.response.toolCalls?.find(
|
|
170
|
+
tc => tc.name === 'submit_result'
|
|
171
|
+
);
|
|
254
172
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
inputSchema: ZodSchema<I>; // 输入验证
|
|
259
|
-
outputSchema: ZodSchema<O>; // 输出验证
|
|
260
|
-
buildPrompt(ctx: ExecutionContext<I>): Message[]; // 构建提示
|
|
261
|
-
validate(output: O, ctx: ExecutionContext<I>): ValidationResult; // 语义验证
|
|
262
|
-
tools?: Tool[]; // 可选工具
|
|
263
|
-
retry?: RetryConfig; // 重试配置
|
|
264
|
-
model?: ModelConfig; // 模型覆盖
|
|
265
|
-
}
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### ExecutionContext(执行上下文)
|
|
173
|
+
if (toolCall) {
|
|
174
|
+
return JSON.parse(toolCall.arguments);
|
|
175
|
+
}
|
|
269
176
|
|
|
270
|
-
|
|
177
|
+
// Or extract from text content
|
|
178
|
+
const match = llmResult.response.content.match(/```json\n(.*?)\n```/s);
|
|
179
|
+
return JSON.parse(match[1]);
|
|
180
|
+
}}
|
|
181
|
+
</Result>
|
|
182
|
+
```
|
|
271
183
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
184
|
+
### Validate Component
|
|
185
|
+
|
|
186
|
+
The `<Validate>` component defines validation rules:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<Validate>
|
|
190
|
+
{(output: any, ctx: any) => ({
|
|
191
|
+
rules: [
|
|
192
|
+
{
|
|
193
|
+
check: output.summary.length >= 100,
|
|
194
|
+
error: 'Summary must be at least 100 characters'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
check: output.keyPoints.length >= 3,
|
|
198
|
+
error: 'Must have at least 3 key points'
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
})}
|
|
202
|
+
</Validate>
|
|
289
203
|
```
|
|
290
204
|
|
|
291
|
-
###
|
|
205
|
+
### Retry Component
|
|
292
206
|
|
|
293
|
-
|
|
207
|
+
The `<Retry>` component configures retry behavior:
|
|
294
208
|
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
|
|
209
|
+
```tsx
|
|
210
|
+
<Retry
|
|
211
|
+
maxAttempts={3}
|
|
212
|
+
feedbackOnError={true}
|
|
213
|
+
retryOnToolError={true}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
298
216
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
217
|
+
## 🎯 Examples
|
|
218
|
+
|
|
219
|
+
### Simple Summarizer
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
const Summarizer = (
|
|
223
|
+
<Agent id="summarizer" input={InputSchema} output={OutputSchema}>
|
|
224
|
+
<Prompt>
|
|
225
|
+
<System>You are a summarization expert.</System>
|
|
226
|
+
<User>{(ctx: any): string => `Summarize: ${ctx.inputs.text}`}</User>
|
|
227
|
+
</Prompt>
|
|
228
|
+
<Result>
|
|
229
|
+
{(ctx: any, llmResult: any) => ({
|
|
230
|
+
summary: llmResult.response.content
|
|
231
|
+
})}
|
|
232
|
+
</Result>
|
|
233
|
+
<Validate>
|
|
234
|
+
{(output: any) => ({
|
|
235
|
+
rules: [
|
|
236
|
+
{
|
|
237
|
+
check: output.summary.length >= 50,
|
|
238
|
+
error: 'Summary too short'
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
})}
|
|
242
|
+
</Validate>
|
|
243
|
+
<Retry maxAttempts={2} feedbackOnError={true} />
|
|
244
|
+
</Agent>
|
|
245
|
+
);
|
|
246
|
+
```
|
|
305
247
|
|
|
306
|
-
|
|
307
|
-
|
|
248
|
+
### Code Analyzer with Tools
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { createMemoryTools } from '@limo-labs/deity';
|
|
252
|
+
|
|
253
|
+
const CodeAnalyzer = (
|
|
254
|
+
<Agent
|
|
255
|
+
id="analyzer"
|
|
256
|
+
input={z.object({ repoPath: z.string() })}
|
|
257
|
+
output={z.object({ findings: z.array(z.any()) })}
|
|
258
|
+
tools={[
|
|
259
|
+
...createMemoryTools(),
|
|
260
|
+
{
|
|
261
|
+
name: 'read_file',
|
|
262
|
+
description: 'Read file contents',
|
|
263
|
+
inputSchema: z.object({ path: z.string() }),
|
|
264
|
+
async execute(input) {
|
|
265
|
+
return { content: await fs.readFile(input.path, 'utf-8') };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
]}
|
|
269
|
+
>
|
|
270
|
+
<Prompt>
|
|
271
|
+
<System>
|
|
272
|
+
{async (): Promise<string> => {
|
|
273
|
+
return await fs.readFile('./prompts/analyzer.md', 'utf-8');
|
|
274
|
+
}}
|
|
275
|
+
</System>
|
|
276
|
+
<User>
|
|
277
|
+
{(ctx: any): string => `Analyze repository at: ${ctx.inputs.repoPath}`}
|
|
278
|
+
</User>
|
|
279
|
+
</Prompt>
|
|
280
|
+
<Result>
|
|
281
|
+
{(ctx: any, llmResult: any) => {
|
|
282
|
+
// Count memory_store tool calls to verify analysis
|
|
283
|
+
const findings = llmResult.response.toolCalls
|
|
284
|
+
?.filter((tc: any) => tc.name === 'memory_store')
|
|
285
|
+
.map((tc: any) => JSON.parse(tc.arguments));
|
|
286
|
+
|
|
287
|
+
return { findings };
|
|
288
|
+
}}
|
|
289
|
+
</Result>
|
|
290
|
+
</Agent>
|
|
291
|
+
);
|
|
292
|
+
```
|
|
308
293
|
|
|
309
|
-
|
|
310
|
-
|
|
294
|
+
### Multi-Step Workflow
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
const MultiStepAgent = (
|
|
298
|
+
<Agent id="multi-step" input={InputSchema} output={OutputSchema}>
|
|
299
|
+
<Prompt>
|
|
300
|
+
<System>
|
|
301
|
+
{async (): Promise<string> => {
|
|
302
|
+
const step = ctx.inputs.currentStep;
|
|
303
|
+
return await loadStepPrompt(step);
|
|
304
|
+
}}
|
|
305
|
+
</System>
|
|
306
|
+
<User>{(ctx: any): string => buildStepMessage(ctx)}</User>
|
|
307
|
+
</Prompt>
|
|
308
|
+
<Observe>
|
|
309
|
+
{(llmResult: any) => ({
|
|
310
|
+
toolCallCount: llmResult.response.toolCalls?.length || 0,
|
|
311
|
+
roundsExecuted: llmResult.rounds
|
|
312
|
+
})}
|
|
313
|
+
</Observe>
|
|
314
|
+
<Result>
|
|
315
|
+
{(ctx: any, llmResult: any, observed: any) => {
|
|
316
|
+
console.log(`Executed ${observed.roundsExecuted} rounds`);
|
|
317
|
+
return extractStepOutput(llmResult);
|
|
318
|
+
}}
|
|
319
|
+
</Result>
|
|
320
|
+
</Agent>
|
|
321
|
+
);
|
|
311
322
|
```
|
|
312
323
|
|
|
313
|
-
##
|
|
324
|
+
## 🔧 Advanced Features
|
|
314
325
|
|
|
315
|
-
|
|
316
|
-
- [使用示例](./docs/EXAMPLES.md) - 实际使用案例
|
|
317
|
-
- [迁移指南](./docs/MIGRATION.md) - 从 3.x 迁移
|
|
318
|
-
- [最佳实践](./docs/BEST_PRACTICES.md) - 设计模式和建议
|
|
319
|
-
- [性能报告](./PHASE_9_PERFORMANCE_REPORT.md) - 基准测试结果
|
|
326
|
+
### Loop Validation
|
|
320
327
|
|
|
321
|
-
|
|
328
|
+
Use `loopValidator` to validate during LLM execution loops:
|
|
322
329
|
|
|
323
|
-
|
|
330
|
+
```typescript
|
|
331
|
+
import type { Validator, LLMLoopState, ExecutionContext } from '@limo-labs/deity';
|
|
332
|
+
|
|
333
|
+
class CustomValidator implements Validator {
|
|
334
|
+
async validate(
|
|
335
|
+
ctx: ExecutionContext,
|
|
336
|
+
loopState: LLMLoopState
|
|
337
|
+
): Promise<ValidationResult> {
|
|
338
|
+
// Check if required tool was called
|
|
339
|
+
const requiredTool = loopState.toolCallsThisRound.find(
|
|
340
|
+
tc => tc.name === 'submit_plan'
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
if (!requiredTool) {
|
|
344
|
+
return {
|
|
345
|
+
valid: false,
|
|
346
|
+
feedback: 'You must call submit_plan tool to complete this task'
|
|
347
|
+
};
|
|
348
|
+
}
|
|
324
349
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
│ (executeComponent, runWorkflow) │
|
|
329
|
-
└─────────────────────────────────────────┘
|
|
330
|
-
↓
|
|
331
|
-
┌─────────────────────────────────────────┐
|
|
332
|
-
│ Execution Context │
|
|
333
|
-
│ ┌──────────┬──────────┬──────────┐ │
|
|
334
|
-
│ │Conversation│Memory │ Session │ │
|
|
335
|
-
│ │ Manager │Manager │ Store │ │
|
|
336
|
-
│ └──────────┴──────────┴──────────┘ │
|
|
337
|
-
│ ┌──────────┬──────────┬──────────┐ │
|
|
338
|
-
│ │ UI │ Stats │ Trace │ │
|
|
339
|
-
│ │ Bridge │ Tracker │ Logger │ │
|
|
340
|
-
│ └──────────┴──────────┴──────────┘ │
|
|
341
|
-
└─────────────────────────────────────────┘
|
|
342
|
-
↓
|
|
343
|
-
┌─────────────────────────────────────────┐
|
|
344
|
-
│ LLM Adapter │
|
|
345
|
-
│ (OpenAI, Anthropic, etc.) │
|
|
346
|
-
└─────────────────────────────────────────┘
|
|
347
|
-
```
|
|
350
|
+
return { valid: true };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
348
353
|
|
|
349
|
-
|
|
354
|
+
const agent = (
|
|
355
|
+
<Agent
|
|
356
|
+
id="planner"
|
|
357
|
+
input={InputSchema}
|
|
358
|
+
output={OutputSchema}
|
|
359
|
+
loopValidator={new CustomValidator()}
|
|
360
|
+
>
|
|
361
|
+
{/* ... */}
|
|
362
|
+
</Agent>
|
|
363
|
+
);
|
|
364
|
+
```
|
|
350
365
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
366
|
+
### Loop Configuration
|
|
367
|
+
|
|
368
|
+
Configure LLM execution loop behavior:
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
<Agent
|
|
372
|
+
id="agent"
|
|
373
|
+
input={InputSchema}
|
|
374
|
+
output={OutputSchema}
|
|
375
|
+
loopConfig={{
|
|
376
|
+
maxToolRounds: 15, // Max tool execution rounds
|
|
377
|
+
timeout: 180000 // 3 minute timeout
|
|
378
|
+
}}
|
|
379
|
+
>
|
|
380
|
+
{/* ... */}
|
|
381
|
+
</Agent>
|
|
382
|
+
```
|
|
358
383
|
|
|
359
|
-
|
|
384
|
+
### Dynamic Prompts
|
|
360
385
|
|
|
361
|
-
|
|
386
|
+
Load prompts from files or construct dynamically:
|
|
362
387
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
| **扩展性** | O(n) 线性 |
|
|
369
|
-
| **内存泄漏** | 无 (-5.3% growth) |
|
|
388
|
+
```tsx
|
|
389
|
+
<System>
|
|
390
|
+
{async (ctx: any): Promise<string> => {
|
|
391
|
+
// Load from file
|
|
392
|
+
const template = await fs.readFile('./prompts/system.md', 'utf-8');
|
|
370
393
|
|
|
371
|
-
|
|
394
|
+
// Inject dynamic content
|
|
395
|
+
return template.replace('{{language}}', ctx.inputs.language);
|
|
396
|
+
}}
|
|
397
|
+
</System>
|
|
398
|
+
```
|
|
372
399
|
|
|
373
|
-
##
|
|
400
|
+
## 🎨 Component Composition
|
|
374
401
|
|
|
375
|
-
|
|
402
|
+
Agents can be composed from reusable functions:
|
|
376
403
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
404
|
+
```typescript
|
|
405
|
+
// Reusable prompt builders
|
|
406
|
+
function buildSystemPrompt(role: string) {
|
|
407
|
+
return async (): Promise<string> => {
|
|
408
|
+
return `You are a ${role}.`;
|
|
409
|
+
};
|
|
410
|
+
}
|
|
380
411
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
412
|
+
function buildUserPrompt(template: string) {
|
|
413
|
+
return (ctx: any): string => {
|
|
414
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => ctx.inputs[key]);
|
|
415
|
+
};
|
|
416
|
+
}
|
|
384
417
|
|
|
385
|
-
|
|
386
|
-
|
|
418
|
+
// Reusable result extractors
|
|
419
|
+
function extractJSONFromToolCall(toolName: string) {
|
|
420
|
+
return (ctx: any, llmResult: any) => {
|
|
421
|
+
const toolCall = llmResult.response.toolCalls?.find(
|
|
422
|
+
(tc: any) => tc.name === toolName
|
|
423
|
+
);
|
|
424
|
+
return JSON.parse(toolCall.arguments);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
387
427
|
|
|
388
|
-
|
|
389
|
-
|
|
428
|
+
// Compose agent
|
|
429
|
+
const agent = (
|
|
430
|
+
<Agent id="composer" input={InputSchema} output={OutputSchema}>
|
|
431
|
+
<Prompt>
|
|
432
|
+
<System>{buildSystemPrompt('expert')}</System>
|
|
433
|
+
<User>{buildUserPrompt('Process {{task}}')}</User>
|
|
434
|
+
</Prompt>
|
|
435
|
+
<Result>{extractJSONFromToolCall('submit')}</Result>
|
|
436
|
+
</Agent>
|
|
437
|
+
);
|
|
390
438
|
```
|
|
391
439
|
|
|
392
|
-
##
|
|
440
|
+
## 📚 Utilities
|
|
393
441
|
|
|
394
|
-
###
|
|
442
|
+
### Tool Result Extraction
|
|
395
443
|
|
|
396
444
|
```typescript
|
|
397
|
-
|
|
398
|
-
const workflow = createSequenceNode([
|
|
399
|
-
createStepNode(indexRepo),
|
|
400
|
-
createStepNode(analyzeCode),
|
|
401
|
-
createStepNode(generateReport)
|
|
402
|
-
]);
|
|
403
|
-
```
|
|
445
|
+
import { extractLastToolResult, extractAllToolResults, countToolCalls } from '@limo-labs/deity';
|
|
404
446
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const ctx = await createEnhancedContext({
|
|
410
|
-
inputs: {},
|
|
411
|
-
enableConversation: true,
|
|
412
|
-
enableMemory: true
|
|
447
|
+
// Extract single tool result
|
|
448
|
+
const result = extractLastToolResult(llmResult, 'tool_name', {
|
|
449
|
+
unwrap: true,
|
|
450
|
+
required: true
|
|
413
451
|
});
|
|
414
452
|
|
|
415
|
-
//
|
|
416
|
-
|
|
453
|
+
// Extract all results from a tool
|
|
454
|
+
const allResults = extractAllToolResults(llmResult, 'memory_store');
|
|
417
455
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
// 启用会话持久化
|
|
422
|
-
const ctx = await createEnhancedContext({
|
|
423
|
-
inputs: {},
|
|
424
|
-
enableSession: { directory: './.deity-sessions' }
|
|
456
|
+
// Count tool calls
|
|
457
|
+
const count = countToolCalls(llmResult, 'memory_store', (result) => {
|
|
458
|
+
return result?.success === true;
|
|
425
459
|
});
|
|
426
|
-
|
|
427
|
-
// 自动保存进度,支持崩溃恢复
|
|
428
460
|
```
|
|
429
461
|
|
|
430
|
-
###
|
|
462
|
+
### Memory Tools
|
|
431
463
|
|
|
432
464
|
```typescript
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
465
|
+
import { createMemoryTools } from '@limo-labs/deity';
|
|
466
|
+
|
|
467
|
+
const agent = (
|
|
468
|
+
<Agent
|
|
469
|
+
id="agent"
|
|
470
|
+
input={InputSchema}
|
|
471
|
+
output={OutputSchema}
|
|
472
|
+
tools={createMemoryTools()}
|
|
473
|
+
>
|
|
474
|
+
{/* Agent can now use memory_store, memory_recall, etc. */}
|
|
475
|
+
</Agent>
|
|
476
|
+
);
|
|
437
477
|
```
|
|
438
478
|
|
|
439
|
-
##
|
|
440
|
-
|
|
441
|
-
### 工具调用
|
|
479
|
+
## 🏗️ Architecture
|
|
442
480
|
|
|
443
|
-
```typescript
|
|
444
|
-
const component: AgentComponent = {
|
|
445
|
-
id: 'search',
|
|
446
|
-
tools: [{
|
|
447
|
-
name: 'search_code',
|
|
448
|
-
description: 'Search codebase',
|
|
449
|
-
inputSchema: z.object({ query: z.string() }),
|
|
450
|
-
async execute(input) {
|
|
451
|
-
return { results: await searchCode(input.query) };
|
|
452
|
-
}
|
|
453
|
-
}],
|
|
454
|
-
// ...
|
|
455
|
-
};
|
|
456
481
|
```
|
|
482
|
+
┌─────────────────────────────────────────┐
|
|
483
|
+
│ JSX/TSX Components │
|
|
484
|
+
│ <Agent>, <Prompt>, <Result>, etc. │
|
|
485
|
+
└─────────────────────────────────────────┘
|
|
486
|
+
↓ compileAgent()
|
|
487
|
+
┌─────────────────────────────────────────┐
|
|
488
|
+
│ AST Nodes │
|
|
489
|
+
│ AgentNode, PromptNode, ResultNode │
|
|
490
|
+
└─────────────────────────────────────────┘
|
|
491
|
+
↓ executeComponent()
|
|
492
|
+
┌─────────────────────────────────────────┐
|
|
493
|
+
│ Execution Engine │
|
|
494
|
+
│ - Build Prompt │
|
|
495
|
+
│ - Call LLM Loop │
|
|
496
|
+
│ - Extract Output │
|
|
497
|
+
│ - Validate │
|
|
498
|
+
│ - Retry if needed │
|
|
499
|
+
└─────────────────────────────────────────┘
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## 🧪 Testing
|
|
457
503
|
|
|
458
|
-
|
|
504
|
+
Deity includes utilities for testing agents:
|
|
459
505
|
|
|
460
506
|
```typescript
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
validate(output, ctx) {
|
|
469
|
-
// 自定义验证逻辑
|
|
470
|
-
if (output.quality < 0.8) {
|
|
471
|
-
return {
|
|
472
|
-
valid: false,
|
|
473
|
-
errors: ['Quality too low'],
|
|
474
|
-
feedback: 'Please improve quality by focusing on...'
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
return { valid: true };
|
|
507
|
+
import { testFullAgent } from '@limo-labs/deity';
|
|
508
|
+
|
|
509
|
+
const result = await testFullAgent(agent, {
|
|
510
|
+
inputs: { task: 'test' },
|
|
511
|
+
mockLLMResponse: {
|
|
512
|
+
content: '{"result": "success"}',
|
|
513
|
+
toolCalls: []
|
|
478
514
|
}
|
|
479
|
-
};
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
expect(result.output).toEqual({ result: 'success' });
|
|
480
518
|
```
|
|
481
519
|
|
|
482
|
-
|
|
520
|
+
## 📖 Documentation
|
|
483
521
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
]);
|
|
493
|
-
```
|
|
522
|
+
- [API Reference](./docs/API.md) - Full API documentation
|
|
523
|
+
- [Migration Guide](./docs/MIGRATION.md) - Migrating from imperative API
|
|
524
|
+
- [Best Practices](./docs/BEST_PRACTICES.md) - Design patterns and tips
|
|
525
|
+
- [Examples](./docs/EXAMPLES.md) - Real-world examples
|
|
526
|
+
|
|
527
|
+
## 🔗 Integration
|
|
528
|
+
|
|
529
|
+
### LLM Adapters
|
|
494
530
|
|
|
495
|
-
|
|
531
|
+
Deity works with any LLM adapter that implements the `LLMAdapter` interface:
|
|
496
532
|
|
|
497
533
|
```typescript
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
534
|
+
interface LLMAdapter {
|
|
535
|
+
generate(
|
|
536
|
+
messages: Message[],
|
|
537
|
+
tools?: Tool[],
|
|
538
|
+
config?: GenerationConfig,
|
|
539
|
+
ctx?: ExecutionContext
|
|
540
|
+
): Promise<LLMResponse>;
|
|
541
|
+
}
|
|
502
542
|
```
|
|
503
543
|
|
|
504
|
-
|
|
544
|
+
Example adapters:
|
|
545
|
+
- `@limo-labs/deity-adapter-openai` - OpenAI GPT models
|
|
546
|
+
- `@limo-labs/deity-adapter-anthropic` - Claude models
|
|
547
|
+
- `@limo-labs/deity-adapter-copilot` - GitHub Copilot SDK
|
|
548
|
+
|
|
549
|
+
### Framework Integration
|
|
505
550
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
| **会话持久化** | ❌ | ✅ 自动保存/恢复 |
|
|
511
|
-
| **UI 集成** | ❌ | ✅ 事件驱动更新 |
|
|
512
|
-
| **性能** | ~100 ops/sec | 678,532 ops/sec |
|
|
513
|
-
| **类型安全** | ✅ | ✅ 增强 |
|
|
514
|
-
| **工作流节点** | 基础 | ✅ 条件/循环/并行 |
|
|
551
|
+
- **Express/Fastify** - Use as API handlers
|
|
552
|
+
- **Next.js** - Server actions and API routes
|
|
553
|
+
- **Electron** - Desktop app agents
|
|
554
|
+
- **CLI Tools** - Command-line AI agents
|
|
515
555
|
|
|
516
|
-
## 🤝
|
|
556
|
+
## 🤝 Contributing
|
|
517
557
|
|
|
518
|
-
|
|
558
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
519
559
|
|
|
520
|
-
## 📄
|
|
560
|
+
## 📄 License
|
|
521
561
|
|
|
522
562
|
MIT © [Limo Labs](https://github.com/limo-labs)
|
|
523
563
|
|
|
524
|
-
## 🔗
|
|
564
|
+
## 🔗 Links
|
|
525
565
|
|
|
526
566
|
- [GitHub](https://github.com/limo-labs/deity)
|
|
527
|
-
- [
|
|
528
|
-
- [
|
|
529
|
-
- [
|
|
567
|
+
- [npm](https://www.npmjs.com/package/@limo-labs/deity)
|
|
568
|
+
- [Documentation](https://deity.limo.run)
|
|
569
|
+
- [Issues](https://github.com/limo-labs/deity/issues)
|
|
570
|
+
- [Changelog](./CHANGELOG.md)
|
|
530
571
|
|
|
531
572
|
---
|
|
532
573
|
|
|
533
|
-
|
|
574
|
+
**Built with ❤️ by Limo Labs**
|