| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- 'use strict';
- const AiDify = require('../lib/ai_dify');
- /**
- * 全局业务查询助手
- *
- * @param {Object} app Egg应用
- * @return {Object} 助手Controller
- * @author Codex
- */
- module.exports = app => {
- class AssistantController extends app.BaseController {
- /**
- * 查询业务数据
- *
- * @param {Object} ctx - egg全局页面
- * @return {void}
- */
- async query(ctx) {
- try {
- const data = JSON.parse(ctx.request.body.data || '{}');
- const question = data.question ? data.question.trim() : '';
- if (!question) throw '请输入要查询的内容';
- if (question.length > 200) throw '问题不能超过200个字符';
- ctx.body = {
- err: 0,
- msg: '',
- data: await ctx.service.assistant.query(question, data.context || {}),
- };
- } catch (err) {
- console.log(err);
- this.log(err);
- ctx.body = this.ajaxErrorBody(err, '助手查询失败');
- }
- }
- /** v2:Chatflow 分类和解析,共用 assistant Service 的业务查询。 */
- async queryV2(ctx) {
- try {
- const data = JSON.parse(ctx.request.body.data || '{}');
- const question = typeof data.question === 'string' ? data.question.trim() : '';
- if (!question) throw '请输入要查询的内容';
- if (question.length > 200) throw '问题不能超过200个字符';
- const sessionUser = ctx.session.sessionUser;
- if (sessionUser.loginType === 4) throw '游客账号暂不支持使用查询助手';
- const context = data.context && typeof data.context === 'object' ? data.context : {};
- const path = typeof context.path === 'string' ? context.path.split(/[?#]/)[0] : '';
- if (path.length > 1024 || (path && !/^\/(?!\/)/.test(path))) throw '页面路径不正确';
- const conversationId = data.conversationId || '';
- if (typeof conversationId !== 'string' || (conversationId && !/^[a-f0-9-]{36}$/i.test(conversationId))) {
- throw '助手会话参数不正确,请清空对话后重试';
- }
- const config = app.config.difyInfo;
- if (!config || !config.apiKey || !config.apiKey.assistant || !config.baseUrl) throw '尚未配置查询助手 Chatflow';
- const dify = new AiDify({ baseUrl: config.baseUrl, apiKey: config.apiKey.assistant });
- // 会话归属由 Dify 按服务端生成的 user 校验,不能使用客户端传入的用户 ID。
- const user = `assistant-${app.config.env}-${ctx.session.sessionProject.id}-${sessionUser.accountId}`;
- let response;
- try {
- response = await dify.askAssistant({ question, path, user, conversationId });
- } catch (err) {
- // ai_dify 已将网络错误转换为不含密钥、请求正文的安全提示。
- throw err.message;
- }
- const parsed = response.result;
- let result = {
- answer: parsed.answer,
- summary: [],
- items: parsed.sources.filter(source => source && typeof source.title === 'string' &&
- typeof source.url === 'string' && /^https?:\/\//i.test(source.url))
- .slice(0, 20).map(source => ({ title: source.title, url: source.url })),
- suggestions: [],
- };
- if (parsed.type === 'business' && parsed.status === 'query') {
- try {
- result = await ctx.service.assistant.queryParameters(parsed.query, Object.assign({}, context, { path }));
- } catch (err) {
- this.log(err);
- // 保留已完成解析的会话,让用户能补充标段、期号等条件。
- result = { answer: typeof err === 'string' ? err : '业务查询失败,请稍后重试。', summary: [], items: [], suggestions: [] };
- }
- }
- result.conversationId = response.conversationId;
- ctx.body = { err: 0, msg: '', data: result };
- } catch (err) {
- console.log(err);
- this.log(err);
- ctx.body = this.ajaxErrorBody(err, '助手查询失败');
- }
- }
- /** 下载查询助手返回的统一附件。 */
- async attachment(ctx) {
- try {
- const file = await ctx.service.assistant.getAttachmentFile(ctx.params.source, ctx.params.id);
- const disposition = `attachment; filename*=UTF-8''${encodeURIComponent(file.filename)}`;
- const headers = {
- 'Content-Type': 'application/octet-stream',
- 'Content-Disposition': disposition,
- };
- if (/^[1-9]\d*$/.test(String(file.filesize))) headers['Content-Length'] = file.filesize;
- ctx.response.set(headers);
- ctx.body = await ctx.helper.ossFileGet(file.filepath);
- } catch (err) {
- this.log(err);
- ctx.status = 404;
- ctx.body = this.ajaxErrorBody(err, '附件下载失败');
- }
- }
- }
- return AssistantController;
- };
|