Skip to content

Commit

Permalink
Implement agent
Browse files Browse the repository at this point in the history
  • Loading branch information
JacksonTian committed Jun 7, 2024
1 parent 00ab3a9 commit 6ec43f0
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
71 changes: 71 additions & 0 deletions bin/kimi-agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Agent from '../lib/agent.js';
import Kimi from '../lib/kimi.js';
import { getAPIKey } from '../lib/apikey.js';

const apiKey = await getAPIKey();

const kimi = new Kimi({
apiKey
});

const agent = new Agent(kimi, 'moonshot-v1-8k');

agent.registerTool('call', {
description: '根据人名判断性别,如果是男孩子,会返回男,如果是女孩子,会返回女',
parameters: {
name: {
type: 'string',
description: '姓名,人的名字'
}
},
method: async function (args) {
return Math.random() > 0.5 ? '男' : '女';
}
});
agent.registerTool('searchAPI', {
description: '搜索 API,根据需求、功能描述,找到可以实现该功能的接口',
parameters: {
query: {
type: 'string',
description: '问题描述,比如发送短信该用哪个 API'
}
},
method: async function (args) {
return 'CreateInstance';
}
});

agent.registerTool('searchProduct', {
description: '根据意图确定产品是什么',
parameters: {
query: {
type: 'string',
description: '用户的意图,可能是在找一个功能,也可能是在找一个接口'
}
},
method: async function (args) {
return `ecs`;
}
});

agent.registerTool('clicommand', {
description: '参数,生成 aliyun 命令行语句,产品名依赖其它工具来进行判断',
parameters: {
product: {
type: 'string',
description: '产品名,产品名需要通过其它工具来进行确认'
},
api: {
type: 'string',
description: 'API 名字'
}
},
method: async function (args) {
return `aliyun ${args.product} ${args.api}`;
}
});

const [prompt] = process.argv.slice(2);
await agent.chat(prompt);

process.exit(0);
127 changes: 127 additions & 0 deletions lib/agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { readAsSSE } from 'httpx';

function getTools(toolMap) {
const tools = [];
for (const [key, tool] of toolMap) {
const properties = {};
for (const [paramName, parameter] of Object.entries(tool.parameters)) {
properties[paramName] = parameter;
}

tools.push({
'type': 'function',
function: {
name: key,
description: tool.description,
parameters: {
'properties': properties,
'type': 'object'
}
}
});
}

return tools;
}

export default class Agent {
constructor(kimi, model) {
this.kimi = kimi;
this.model = model;
this.tools = new Map();
}

registerTool(define, method) {
this.tools.set(define, method);
}

async chatWithTool(message) {
const response = await this.kimi.chat([
{
role: 'user',
content: message
}
], {
model: this.model,
tools: getTools(this.tools)
});

let toolCalls = [];
let content = '';
for await (const event of readAsSSE(response)) {
if (event.data !== '[DONE]') {
const data = JSON.parse(event.data);

const choice = data.choices[0];
if (choice.finish_reason !== 'tool_calls') {
if (choice.delta.content) {
content += choice.delta.content;
}
if (choice.delta.tool_calls) {
const toolCall = choice.delta.tool_calls[0];
const index = toolCall.index;

if (!toolCalls[index]) {
toolCalls[index] = toolCall;
} else {
toolCalls[index].function.arguments += toolCall.function.arguments;
}
}
}
}
}

return {content, toolCalls};
}

async chat(promt) {
const {content, toolCalls} = await this.chatWithTool(promt);
const toolResults = [];
for (const toolCall of toolCalls) {
const result = await this.runTool(toolCall);
toolResults.push({
role: 'tool',
tool_call_id: toolCall.id,
content: result
});
}

const messages = [
{role: 'user', content: promt},
{role: 'assistant', content, tool_calls: toolCalls},
...toolResults
];
console.log(messages);
const response = await this.kimi.chat(messages, {
model: this.model,
});
let lastEvent;
let message = '';
for await (const event of readAsSSE(response)) {
if (event.data !== '[DONE]') {
const data = JSON.parse(event.data);
const choice = data.choices[0];
if (choice.finish_reason === 'content_filter') {
console.log(event);
} else if (choice.finish_reason === 'stop') {
lastEvent = event;

Check failure on line 107 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (14.x)

'lastEvent' is assigned a value but never used

Check failure on line 107 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (16.x)

'lastEvent' is assigned a value but never used

Check failure on line 107 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'lastEvent' is assigned a value but never used

Check failure on line 107 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'lastEvent' is assigned a value but never used
} else if (!choice.finish_reason) {
const content = choice.delta.content;
if (content) {
process.stdout.write(content);
message += content;

Check failure on line 112 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (14.x)

'message' is assigned a value but never used

Check failure on line 112 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (16.x)

'message' is assigned a value but never used

Check failure on line 112 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'message' is assigned a value but never used

Check failure on line 112 in lib/agent.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'message' is assigned a value but never used
}
}
} else {
console.log();
}
}
}

async runTool(call) {
const tool = this.tools.get(call.function.name);
const args = JSON.parse(call.function.arguments);
const result = await tool.method(args);
return result;
}
}

0 comments on commit 6ec43f0

Please sign in to comment.