agent.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. 'use strict';
  2. // 智能问数智能体路由:运行生命周期(异步执行 + 轮询)与会话索引
  3. // POST /api/agent/runs 发起运行(agent/quick 双模式),立即返回 run 记录
  4. // GET /api/agent/runs 会话内运行摘要列表(?entity&sessionId)
  5. // GET /api/agent/runs/:id 运行详情(含步骤/产物;运行中为实时进度)
  6. // POST /api/agent/runs/:id/cancel 请求取消(回合间隙生效)
  7. // GET /api/agent/sessions 会话列表(?entity)
  8. const { sendJson, readBody } = require('../util.js');
  9. const { ASK_CLIENT_CODES } = require('../middleware.js');
  10. const { makeChatCfg } = require('../context.js');
  11. const naposService = require('../../napos-service.js');
  12. const aitableFactory = require('../services/aitable.js');
  13. const agent = require('../agent/index.js');
  14. const agentSkills = require('../agent/skills.js');
  15. const digest = require('../agent/digest.js');
  16. const deepReport = require('../agent/deep-report.js');
  17. function requireEntity(query, body) {
  18. const entity = (body && body.entity) || query.entity
  19. || (naposService.store.listEntities()[0] || {}).key;
  20. if (!entity) {
  21. const e = new Error('暂无数据主体,请先完成一次同步');
  22. e.code = 'ASK_NO_ENTITY';
  23. throw e;
  24. }
  25. return entity;
  26. }
  27. function register(app, ctx) {
  28. // 云端协同(湖↔云比对工具用):复用 server.js 装配的服务实例(无则兜底自建)
  29. const aitableSvc = ctx.aitable || aitableFactory.createAitableService(ctx);
  30. app.post('/api/agent/runs', async ({ req, res }) => {
  31. const body = await readBody(req);
  32. const entity = requireEntity({}, body);
  33. // 参数类错误(空问题/未配模型/无主体)→ 400;其余交给错误边界
  34. try {
  35. const rec = agent.startRun({
  36. entity,
  37. question: body.question,
  38. mode: ['quick', 'agent', 'auto'].includes(body.mode) ? body.mode : 'auto',
  39. sessionId: body.sessionId,
  40. skill: body.skill,
  41. cloud: aitableSvc,
  42. cfg: makeChatCfg(ctx),
  43. svc: naposService,
  44. });
  45. sendJson(res, 200, { run: rec });
  46. } catch (e) {
  47. if (!ASK_CLIENT_CODES.has(e.code)) e.status = 502;
  48. throw e;
  49. }
  50. });
  51. app.get('/api/agent/runs', async ({ res, query }) => {
  52. const entity = requireEntity(query);
  53. if (!query.sessionId) return sendJson(res, 400, { error: '缺少 sessionId 参数' });
  54. sendJson(res, 200, { entity, runs: agent.listRuns(entity, query.sessionId) });
  55. });
  56. app.get('/api/agent/runs/:id', async ({ res, query, params }) => {
  57. const entity = requireEntity(query);
  58. const rec = agent.getRun(entity, params.id);
  59. if (!rec) return sendJson(res, 404, { error: '运行记录不存在' });
  60. sendJson(res, 200, { run: rec });
  61. });
  62. app.post('/api/agent/runs/:id/cancel', async ({ req, res, params }) => {
  63. await readBody(req).catch(() => ({}));
  64. const rec = agent.cancelRun(params.id);
  65. if (!rec) return sendJson(res, 404, { error: '运行记录不存在' });
  66. sendJson(res, 200, { run: rec });
  67. });
  68. app.get('/api/agent/sessions', async ({ res, query }) => {
  69. const entity = requireEntity(query);
  70. sendJson(res, 200, { entity, sessions: agent.listSessions(entity) });
  71. });
  72. app.del('/api/agent/sessions/:id', async ({ res, query, params }) => {
  73. const entity = requireEntity(query);
  74. const ok = agent.deleteSession(entity, params.id);
  75. if (!ok) return sendJson(res, 404, { error: '会话不存在' });
  76. sendJson(res, 200, { ok: true });
  77. });
  78. app.get('/api/agent/skills', async ({ res }) => {
  79. sendJson(res, 200, { skills: agentSkills.SKILLS.map(({ key, name, desc }) => ({ key, name, desc })) });
  80. });
  81. app.get('/api/agent/digests', async ({ res, query }) => {
  82. const entity = requireEntity(query);
  83. sendJson(res, 200, { entity, digests: digest.loadDigests(entity) });
  84. });
  85. // 手动重建晨报(每日自动同步后也会自动生成)
  86. app.post('/api/agent/digests/build', async ({ req, res }) => {
  87. const body = await readBody(req);
  88. const entity = requireEntity({}, body);
  89. const d = digest.buildAndSaveDigest(entity, naposService);
  90. sendJson(res, 200, { digest: d });
  91. });
  92. // 答案反馈:采纳(打法入语料)/ 纠错(进反馈日志供评测提炼)
  93. app.post('/api/agent/feedback', async ({ req, res }) => {
  94. const body = await readBody(req);
  95. const entity = requireEntity({}, body);
  96. const rec = agent.feedback({
  97. entity, runId: body.runId,
  98. kind: body.kind === 'up' ? 'up' : 'down',
  99. note: body.note,
  100. });
  101. if (!rec) return sendJson(res, 404, { error: '运行记录不存在' });
  102. sendJson(res, 200, { run: rec });
  103. });
  104. // 深度分析报告(确定性生成:体检/环比/归因/异常/对标/明细,自包含 HTML)
  105. app.get('/api/agent/report-deep', async ({ res, query }) => {
  106. const entity = requireEntity(query);
  107. const html = deepReport.buildDeepReportHtml({ entity, from: query.from, to: query.to, svc: naposService });
  108. res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  109. return res.end(html);
  110. });
  111. }
  112. module.exports = { register };