- Add checkpoint guard in all 4 tool-executors ensuring every tool_call_id gets a ToolMessage response - Add cleanPollutedToolCalls utility to heal corrupted message history - Add error banner with "重置此对话" button in frontend Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
106 lines
4.1 KiB
TypeScript
106 lines
4.1 KiB
TypeScript
/**
|
||
* Coder agent node: LLM decides whether to write/execute code.
|
||
*/
|
||
import { createLlm, type ModelMode } from "@/agent/utils/create-llm";
|
||
import { truncateMessages } from "@/agent/utils/truncate-messages";
|
||
import { cleanPollutedToolCalls } from "@/agent/utils/clean-polluted-tool-calls";
|
||
import { LangGraphRunnableConfig } from "@langchain/langgraph";
|
||
import { CoderState, CoderUpdate } from "../types.js";
|
||
import { z } from "zod";
|
||
|
||
const SYSTEM_PROMPT = `你是代码执行助手。通过编写和运行代码来解决用户问题。
|
||
|
||
## 能力
|
||
- Python / JavaScript / Bash 代码编写与执行
|
||
- 数据分析(pandas, numpy)、可视化(matplotlib)
|
||
- 文件处理(CSV, JSON, Excel)
|
||
- 数学计算
|
||
|
||
## 工作流程
|
||
1. 理解用户需求,确定用什么语言和库
|
||
2. 如果需要额外依赖,先用 code_install 安装
|
||
3. 用 code_execute 执行代码
|
||
4. 根据执行结果给出分析和回答
|
||
|
||
## 错误恢复
|
||
- 执行报错时分析原因(语法 / 依赖 / 逻辑),修正后重试
|
||
- 缺少依赖 → code_install 安装后重新执行
|
||
- 最多重试 3 次,仍失败则说明原因并给替代方案
|
||
|
||
## 回答结构
|
||
1. **结论先行**:1-2 句话概括执行结果
|
||
2. **关键数据**:表格或列表呈现结果
|
||
3. **分析洞察**:趋势、异常、对比(如适用)
|
||
|
||
## 回答风格
|
||
- 用中文回复
|
||
- 代码执行成功后,2-4 句话给出结果分析,不要再多余地调用工具
|
||
- 执行失败时简要说明原因和下一步
|
||
- 数据结果用表格展示,图表输出 JSON 数据供前端渲染
|
||
|
||
## 工具失败处理规范
|
||
当工具调用失败或返回错误时:
|
||
1. 不要在回答中暴露技术报错、HTTP 状态码、堆栈信息
|
||
2. 用中文友好地说明:发生了什么、为什么(用户能理解的语言)
|
||
3. 给出至少一条可操作的替代建议,例如:
|
||
- 知识库无结果 → 建议换个关键词,或说明知识库可能暂未收录该内容
|
||
- 工单查询失败 → 建议直接联系工单管理员,或稍后重试
|
||
- 代码执行失败 → 直接分析代码逻辑给出结果,说明沙盒暂时不可用
|
||
- 搜索失败 → 基于已有知识给出答案,标注[模型推断]
|
||
4. 语气要稳定专业,不要说"抱歉"超过一次,不要表现出慌乱
|
||
|
||
## 思考过程输出规范
|
||
在调用任何工具之前,先在 content 中用一句简洁的中文说明你的执行计划,不超过 30 字,例如:
|
||
- "用 Python 计算统计指标..."
|
||
- "先安装 pandas 依赖..."
|
||
- "执行代码分析数据..."
|
||
这句话必须出现在 tool_calls 之前的 content 字段中。`;
|
||
|
||
export const codeExecuteSchema = z.object({
|
||
code: z.string().describe("The code to execute"),
|
||
language: z
|
||
.enum(["python", "javascript", "bash"])
|
||
.optional()
|
||
.describe("Programming language, defaults to python"),
|
||
});
|
||
|
||
export const codeInstallSchema = z.object({
|
||
packages: z
|
||
.array(z.string())
|
||
.describe("Package names to install (pip for python, npm for javascript)"),
|
||
});
|
||
|
||
export const CODER_TOOLS = [
|
||
{
|
||
name: "code_execute",
|
||
description:
|
||
"在安全沙盒中执行代码,支持 Python、JavaScript、Bash。如果执行失败,请分析错误后修正代码重新执行。",
|
||
schema: codeExecuteSchema,
|
||
},
|
||
{
|
||
name: "code_install",
|
||
description:
|
||
"安装依赖包(Python 用 pip,JavaScript 用 npm),在执行代码前如果需要额外的库请先安装",
|
||
schema: codeInstallSchema,
|
||
},
|
||
] as const;
|
||
|
||
export async function agentNode(
|
||
state: CoderState,
|
||
config: LangGraphRunnableConfig,
|
||
): Promise<CoderUpdate> {
|
||
const modelMode = ((config.configurable?.modelMode as string) ?? "auto") as ModelMode;
|
||
const llm = createLlm({ modelMode });
|
||
|
||
const truncated = truncateMessages(state.messages);
|
||
// Clean up polluted messages before sending to LLM
|
||
const cleanedMessages = cleanPollutedToolCalls(truncated);
|
||
const messagesWithSystem = [
|
||
{ role: "system" as const, content: SYSTEM_PROMPT },
|
||
...cleanedMessages,
|
||
];
|
||
|
||
const message = await llm.bindTools([...CODER_TOOLS], { parallel_tool_calls: false }).invoke(messagesWithSystem);
|
||
return { messages: [message], timestamp: Date.now() };
|
||
}
|