支持DeepSeek

This commit is contained in:
2025-12-10 16:24:06 +08:00
parent ff3ec65139
commit a53bf1d6e4
5 changed files with 363 additions and 560 deletions
+91 -123
View File
@@ -1,73 +1,66 @@
import Anthropic from '@anthropic-ai/sdk';
import type { Tool, ToolResult, Message, AgentConfig } from '../types/index.js';
import { createAnthropic } from '@ai-sdk/anthropic';
import { createDeepSeek } from '@ai-sdk/deepseek';
import { generateText, streamText, type ModelMessage, type Tool as AITool, type LanguageModel } from 'ai';
import type { Tool, ToolResult, Message, AgentConfig, ProviderType } from '../types/index.js';
import { buildZodSchema } from '../types/index.js';
// Provider 工厂函数类型
type ProviderFactory = (apiKey: string) => (model: string) => LanguageModel;
// Provider 注册表
const providers: Record<ProviderType, ProviderFactory> = {
anthropic: (apiKey) => {
const client = createAnthropic({ apiKey });
return (model) => client(model);
},
deepseek: (apiKey) => {
const client = createDeepSeek({ apiKey });
return (model) => client(model);
},
};
export class Agent {
private client: Anthropic;
private getModel: (model: string) => LanguageModel;
private config: AgentConfig;
private tools: Map<string, Tool> = new Map();
private conversationHistory: Message[] = [];
private conversationHistory: ModelMessage[] = [];
constructor(config: AgentConfig) {
this.config = config;
this.client = new Anthropic({
apiKey: config.apiKey,
});
const providerFactory = providers[config.provider];
if (!providerFactory) {
throw new Error(`不支持的 provider: ${config.provider}`);
}
this.getModel = providerFactory(config.apiKey);
}
// 注册工具
registerTool(tool: Tool): void {
this.tools.set(tool.name, tool);
registerTool(customTool: Tool): void {
this.tools.set(customTool.name, customTool);
}
// 获取所有工具定义(用于 Claude API)
private getToolDefinitions(): Anthropic.Tool[] {
return Array.from(this.tools.values()).map((tool) => ({
name: tool.name,
description: tool.description,
input_schema: {
type: 'object' as const,
properties: Object.fromEntries(
Object.entries(tool.parameters).map(([key, param]) => [
key,
{
type: param.type,
description: param.description,
},
])
),
required: Object.entries(tool.parameters)
.filter(([, param]) => param.required)
.map(([key]) => key),
},
}));
}
// 将自定义工具转换为 Vercel AI SDK 的工具格式
private getVercelTools(): Record<string, AITool> {
const vercelTools: Record<string, AITool> = {};
// 执行工具
private async executeTool(
toolName: string,
input: Record<string, unknown>
): Promise<ToolResult> {
const tool = this.tools.get(toolName);
if (!tool) {
return {
success: false,
output: '',
error: `Tool "${toolName}" not found`,
};
for (const [name, customTool] of this.tools) {
const schema = buildZodSchema(customTool.parameters);
vercelTools[name] = {
description: customTool.description,
inputSchema: schema,
execute: async (params) => {
const result = await customTool.execute(params as Record<string, unknown>);
return result;
},
} as AITool;
}
try {
return await tool.execute(input);
} catch (error) {
return {
success: false,
output: '',
error: error instanceof Error ? error.message : String(error),
};
}
return vercelTools;
}
// 发送消息并处理响应
// 发送消息并处理响应(流式)
async chat(
userMessage: string,
onStream?: (text: string) => void
@@ -78,84 +71,52 @@ export class Agent {
content: userMessage,
});
const messages: Anthropic.MessageParam[] = this.conversationHistory.map(
(msg) => ({
role: msg.role,
content: msg.content,
})
);
const vercelTools = this.getVercelTools();
let fullResponse = '';
// 循环处理,直到没有工具调用
while (true) {
const response = await this.client.messages.create({
model: this.config.model,
max_tokens: this.config.maxTokens,
if (onStream) {
// 流式模式
const result = streamText({
model: this.getModel(this.config.model),
system: this.config.systemPrompt,
tools: this.getToolDefinitions(),
messages,
});
// 处理响应内容
const textBlocks: string[] = [];
const toolUseBlocks: Anthropic.ToolUseBlock[] = [];
for (const block of response.content) {
if (block.type === 'text') {
textBlocks.push(block.text);
if (onStream) {
onStream(block.text);
messages: this.conversationHistory,
tools: vercelTools,
maxOutputTokens: this.config.maxTokens,
onChunk: ({ chunk }) => {
if (chunk.type === 'tool-call') {
onStream(`\n[调用工具: ${chunk.toolName}]\n`);
} else if (chunk.type === 'tool-result') {
const output = (chunk as { output?: ToolResult }).output;
if (output && typeof output === 'object') {
if (output.success) {
onStream(`[结果: ${output.output}]\n`);
} else {
onStream(`[错误: ${output.error}]\n`);
}
}
}
} else if (block.type === 'tool_use') {
toolUseBlocks.push(block);
}
}
fullResponse += textBlocks.join('');
// 如果没有工具调用,结束循环
if (toolUseBlocks.length === 0) {
break;
}
// 添加 assistant 消息
messages.push({
role: 'assistant',
content: response.content,
},
});
// 处理工具调用
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const toolUse of toolUseBlocks) {
if (onStream) {
onStream(`\n[调用工具: ${toolUse.name}]\n`);
}
const result = await this.executeTool(
toolUse.name,
toolUse.input as Record<string, unknown>
);
if (onStream) {
onStream(
result.success ? `[结果: ${result.output}]\n` : `[错误: ${result.error}]\n`
);
}
toolResults.push({
type: 'tool_result',
tool_use_id: toolUse.id,
content: result.success ? result.output : `Error: ${result.error}`,
});
// 流式输出文本
for await (const chunk of result.textStream) {
fullResponse += chunk;
onStream(chunk);
}
// 添加工具结果
messages.push({
role: 'user',
content: toolResults,
// 等待完成
await result.response;
} else {
// 非流式模式
const result = await generateText({
model: this.getModel(this.config.model),
system: this.config.systemPrompt,
messages: this.conversationHistory,
tools: vercelTools,
maxOutputTokens: this.config.maxTokens,
});
fullResponse = result.text;
}
// 保存助手响应到历史
@@ -174,6 +135,13 @@ export class Agent {
// 获取对话历史
getHistory(): Message[] {
return [...this.conversationHistory];
return this.conversationHistory
.filter((msg): msg is ModelMessage & { role: 'user' | 'assistant' } =>
msg.role === 'user' || msg.role === 'assistant'
)
.map((msg) => ({
role: msg.role,
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
}));
}
}