start
This commit is contained in:
116
agent/agent.js
Normal file
116
agent/agent.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'dotenv/config';
|
||||
import { createDeepAgent } from "deepagents";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import { AIMessageChunk, ToolMessage } from "langchain";
|
||||
import { SystemMessage, HumanMessage, AIMessage } from "@langchain/core/messages";
|
||||
import { ChatDeepSeek } from "@langchain/deepseek";
|
||||
import Prompts from "./prompts.js";
|
||||
|
||||
export default class EscortAgent {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
clearMessages() {
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
// msg: { ts: "2023-08-01 10:00:00", content: "你好" }
|
||||
async streamChat(userInfo, msgs, callback) {
|
||||
if (!msgs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const agent = this._genAgent(userInfo);
|
||||
msgs.forEach(msg => {
|
||||
if (msg.type === "clear") {
|
||||
this.messages = [];
|
||||
} else {
|
||||
this.messages.push(new HumanMessage(msg.content));
|
||||
}
|
||||
});
|
||||
|
||||
if (this.messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const INTERESTING_NODES = new Set(["model_request", "tools"]);
|
||||
for await (const [namespace, mode, data] of await agent.stream(
|
||||
{ messages: this.messages },
|
||||
{
|
||||
recursion_limit: 50,
|
||||
streamMode: ["updates", "messages", "custom"], subgraphs: true,
|
||||
configurable: {
|
||||
thread_id: 'default-session'
|
||||
}
|
||||
})) {
|
||||
const isSubagent = namespace.some(s => s.startsWith("tools:"));
|
||||
const source = isSubagent ? "subagent" : "main";
|
||||
if (mode === "updates") {
|
||||
for (const nodeName of Object.keys(data)) {
|
||||
if (!INTERESTING_NODES.has(nodeName)) continue;
|
||||
// Main agent updates (empty namespace)
|
||||
if (namespace.length === 0) {
|
||||
for (const [nodeName, data_] of Object.entries(data)) {
|
||||
if (nodeName === "tools") {
|
||||
// Subagent results returned to main agent
|
||||
for (const msg of data_.messages ?? []) {
|
||||
if (msg.type === "tool") {
|
||||
console.log(`\nSubagent complete: ${msg.name}`);
|
||||
console.log(` Result: ${String(msg.content).slice(0, 200)}...`);
|
||||
}
|
||||
}
|
||||
} else if (nodeName === "model_request") {
|
||||
this.messages.push(...data_.messages);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Subagent updates (non-empty namespace)
|
||||
for (const [nodeName, data_] of Object.entries(data)) {
|
||||
console.log(` [${namespace[0]}] step: ${nodeName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode === "messages") {
|
||||
const [message] = data;
|
||||
if (message.tool_call_chunks?.length) {
|
||||
continue;
|
||||
}
|
||||
if (AIMessageChunk.isInstance(message)) {
|
||||
if (message.text && !message.tool_call_chunks?.length) {
|
||||
callback(source, "ai", message.text, message.id);
|
||||
}
|
||||
if (message.additional_kwargs.reasoning_content && !message.tool_call_chunks?.length) {
|
||||
callback(source, "reasoning", message.additional_kwargs.reasoning_content, message.id);
|
||||
}
|
||||
}
|
||||
if (ToolMessage.isInstance(message) && message.text) {
|
||||
callback(source, "tool", message.text, message.id);
|
||||
}
|
||||
} else if (mode === "custom") {
|
||||
this.logger.info("custom: ", data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_genAgent(userInfo) {
|
||||
if (this.agent) {
|
||||
return this.agent;
|
||||
}
|
||||
|
||||
this.messages = [];
|
||||
|
||||
this.model = new ChatDeepSeek({
|
||||
model: 'deepseek-v4-pro',
|
||||
apiKey: 'sk-a58ccd82b7ba4ce3ac176a88c9381095',
|
||||
temperature: 0.3
|
||||
});
|
||||
|
||||
this.agent = createDeepAgent({
|
||||
name: "deep-agent",
|
||||
model: this.model,
|
||||
systemPrompt: Prompts.buildSystemPrompt(userInfo)
|
||||
});
|
||||
|
||||
return this.agent;
|
||||
}
|
||||
}
|
||||
56
agent/prompts.js
Normal file
56
agent/prompts.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import moment from "moment";
|
||||
import services from "../resource/services.js";
|
||||
|
||||
class Prompts {
|
||||
static buildSystemPrompt(userInfo) {
|
||||
let userInfo_str = "用户未登录,提示用户先登录,并在'我的'中完善个人信息";
|
||||
if (userInfo) {
|
||||
userInfo_str = JSON.stringify({
|
||||
name: userInfo.profile.name,
|
||||
mobile: userInfo.profile.mobile,
|
||||
sex: userInfo.profile.sex,
|
||||
birth: userInfo.profile.birth,
|
||||
province: userInfo.location.province,
|
||||
city: userInfo.location.city,
|
||||
address: userInfo.addresses || [],
|
||||
});
|
||||
}
|
||||
|
||||
return `
|
||||
# 角色定义
|
||||
你是小橙,一名陪诊服务顾问。温暖、直接、高效。
|
||||
|
||||
# 核心能力
|
||||
- 查询医院、科室、医生信息
|
||||
- 创建、查询陪诊订单
|
||||
- 解答服务流程、价格、注意事项
|
||||
- 提供就诊准备建议
|
||||
|
||||
# 铁律(必须遵守)
|
||||
1. 极度简洁:能一句话说完绝不两句。禁止长篇大论,直接给答案。
|
||||
2. 聊专业问题时,要严肃专注,不偏离主题;要客观公正,绝不主观臆造;同时给用户专业真诚的反馈。
|
||||
3. 聚焦用户最新问题,理解意图,高情商个性化的跟用户沟通,不要一开口就问“需要陪诊服务吗”。
|
||||
4. 主动追问:回答后,一句追问收尾,引导用户给出下一步关键信息。
|
||||
5. 输出要排版层次清晰,格式统一整洁,不要使用markdown格式。
|
||||
6. 医疗问题时,末尾加一句"最终以医生诊断为准"。
|
||||
7. 保护用户隐私,不泄露个人信息。
|
||||
8. 用户询问怎么加入团队或怎么合作时,首先欢迎用户加入团队,然后让用户电话或微信联系。
|
||||
9. 你无法回答的业务问题,要提示用户联系客服。
|
||||
|
||||
# 工作流程
|
||||
1. 问清城市、医院、科室
|
||||
2. 推荐匹配选项
|
||||
3. 确认就诊时间
|
||||
4. 创建订单、确认细节
|
||||
5. 就诊前提醒
|
||||
|
||||
## 参考信息
|
||||
当前日期:${moment().format("YYYY-MM-DD")};
|
||||
用户信息:${userInfo_str};
|
||||
服务项目:${JSON.stringify(services)};
|
||||
服务电话: 18618162956 (微信同号)
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default Prompts;
|
||||
29
agent/task.js
Normal file
29
agent/task.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import EscortAgent from "./agent.js";
|
||||
|
||||
class ChatTask {
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
modelProvider: options.modelProvider || "deepseek",
|
||||
apiKey: options.apiKey,
|
||||
baseURL: options.baseURL,
|
||||
modelName: options.modelName,
|
||||
temperature: options.temperature ?? 0.7,
|
||||
maxIterations: options.maxIterations || 10,
|
||||
};
|
||||
|
||||
this.agents = {};
|
||||
}
|
||||
|
||||
async streamChat(userInfo, message, callback) {
|
||||
const userId = userInfo ? userInfo._id : message.appId;
|
||||
if (!this.agents[userId]) {
|
||||
this.agents[userId] = new EscortAgent();
|
||||
}
|
||||
return this.agents[userId].streamChat(userInfo, [message], callback);
|
||||
}
|
||||
}
|
||||
|
||||
const chatTask = new ChatTask();
|
||||
|
||||
export { ChatTask, chatTask };
|
||||
export default chatTask;
|
||||
Reference in New Issue
Block a user