TerminalOJ Dify Agent 工作流编排方案
最后更新:2026-05-31
目标:在不改变前端/api/ai/*调用契约的前提下,把 TerminalOJ 的 AI pipeline 对接到 Dify Workflow/Agent 编排。
1. 需求分析
worker.skill对 AI 侧的要求是“完成调用工作和 pipeline 工作”,核心能力包括:
| 能力 | 当前入口 | Dify 工作流任务 |
|---|---|---|
| AI 聊天 / 问题 | POST /api/ai/chat | task=chat |
| AI 辅助代码鉴别错误 | POST /api/ai/code-diagnosis | task=code_diagnosis |
| AI 整理做题信息 / 知识图谱 | POST /api/ai/knowledge-graph | task=knowledge_graph |
| AI 解题辅助 | POST /api/ai/solve | task=solve |
项目已有前端组件、Pinia store、后端 handler 和会话落库能力,因此 Dify 不直接暴露给浏览器。边界如下:
Frontend AI panel -> TerminalOJ backend /api/ai/* -> backend/internal/ai.Client -> Dify Workflow App /v1/workflows/run -> normalized response back to existing frontend contract这样可以保留 JWT 鉴权、用户隔离、题目上下文装配、最近提交摘要查询和 Mock 降级。
2. Dify 文档对齐
本设计对齐 Dify 官方文档中的这些约束:
- Workflow/Chatflow 通过节点画布组织 Start、LLM、Agent、Code、条件分支、End 等节点。
- Workflow API 使用
POST /v1/workflows/run,请求体包含inputs、response_mode、user。 - 当前后端同步 JSON 接口使用
response_mode=blocking;Dify streaming 可以后续通过 SSE 另开路由。 - Agent 节点适合承载“解题教练”“代码诊断员”等带工具/推理策略的分支。
参考:
- Dify Workflow API:
https://docs.dify.ai/api-reference/workflows/run-workflow - Dify Workflow by ID API:
https://docs.dify.ai/api-reference/workflows/run-workflow-by-id - Dify Agent 节点:
https://docs.dify.ai/en/guides/workflow/node/agent - Dify Workflow 节点文档:
https://docs.dify.ai/en/guides/workflow/node
3. 后端接入配置
backend/config.yaml新增 Dify provider 兼容项:
ai:enabled:trueprovider:"dify"endpoint:"https://api.dify.ai/v1"api_key:"app-xxxxxxxx"model:"gpt-4o-mini"workflow_id:""response_mode:"blocking"timeout_seconds:30说明:
enabled=false:仍走本地 Mock。provider=external:沿用原先{task, model, payload}envelope,调用{endpoint}/chat等端点。provider=dify:调用 Dify Workflow API。workflow_id留空时调用/workflows/run;填写后调用/workflows/{workflow_id}/run。api_key使用 Dify App API Key,不放到前端。
后端传给 Dify 的inputs固定为:
{"task":"chat | code_diagnosis | knowledge_graph | solve","model":"gpt-4o-mini","payload":"{...JSON string...}"}其中payload是现有后端请求对象序列化后的 JSON 字符串,包含userId、problem、history、code、recentSubmissions等上下文。
4. 工作流模型
在 Dify 中建立一个 Workflow App,Start 节点定义 3 个输入变量:
| 变量 | 类型 | 说明 |
|---|---|---|
task | Short Text | 路由任务名 |
model | Short Text | 后端配置的模型标识,可作为提示词变量 |
payload | Paragraph | JSON 字符串,上游已装配业务上下文 |
节点编排:
Start -> Code: Normalize Payload -> IF/ELSE: Route by task chat -> Agent: OJ Tutor code_diagnosis -> Agent: Code Doctor knowledge_graph -> LLM: Learning Graph Builder -> Code: Graph JSON Normalizer solve -> Agent: Problem Solver -> Code: Pack Result -> End各分支职责:
| 分支 | 节点建议 | 目标输出 |
|---|---|---|
chat | Agent | { "reply": "...", "metadata": {} } |
code_diagnosis | Agent + structured JSON | { "summary": "...", "issues": [], "suggestions": [], "rawMarkdown": "..." } |
knowledge_graph | LLM + Code | { "summary": "...", "nodes": [], "edges": [], "rawMarkdown": "..." } |
solve | Agent | { "answer": "...", "hints": [], "complexity": "..." } |
End 节点只暴露一个变量:
{"result":"{...task response JSON...}"}后端适配器也兼容这些输出形式:
outputs.result为 JSON 字符串或 JSON 对象。outputs.response/outputs.output/outputs.data为 JSON 字符串或 JSON 对象。- 直接把
reply、rawMarkdown、answer、summary等字段作为 outputs 返回。 - 仅返回纯文本时,后端会按任务包装成
reply、rawMarkdown或answer。
5. Prompt 约束
各 Agent/LLM 节点统一遵守:
- 不直接给出完整 AC 代码,除非
task=solve且level=full。 - 题目页上下文优先使用
payload.problem,不要猜测不存在的题目条件。 - 代码诊断必须区分语法错误、边界条件、复杂度风险和评测信息。
- 知识图谱节点输出稳定 JSON,
nodes[].id要可复用,如tag:dynamic_programming、problem:1001。 - 所有 Markdown 都允许包含 LaTeX 和代码块,前端已有渲染能力。
6. 联调流程
- 在 Dify 新建 Workflow App,按
dify/terminaloj-agent-workflow.model.yaml搭建节点。 - 发布 App,复制 API Key。
- 修改
backend/config.yaml:
ai:enabled:trueprovider:"dify"endpoint:"https://api.dify.ai/v1"api_key:"app-xxxxxxxx"response_mode:"blocking"- 启动后端并调用现有前端 AI 功能。
- 若返回
dify workflow response missing outputs,检查 End 节点是否暴露了result。
7. 后续扩展
- 新增
/api/ai/chat/stream,将 Difyresponse_mode=streaming转发为 SSE。 - 将 Dify Workflow 拆成多个 App:对话、诊断、图谱、解题分别用不同 API Key。
- 在 Dify 中配置 HTTP 工具访问只读题库 API,但仍建议通过后端代理,避免泄露内部服务地址和用户权限。