@renxqoo/renx-code 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/renx.cjs +16 -0
- package/package.json +2 -45
- package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
- package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
- package/src/agent/runtime/runtime.test.ts +7 -4
- package/src/agent/runtime/runtime.ts +3 -9
- package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
- package/src/agent/runtime/source-modules.test.ts +16 -35
- package/src/agent/runtime/source-modules.ts +17 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
- package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
- package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
- package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
- package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
- package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
- package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
- package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
- package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
- package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
- package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
- package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
- package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
- package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
- package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
- package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
- package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
- package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
- package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
- package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
- package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
- package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
- package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
- package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
- package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
- package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
- package/vendor/agent-root/src/agent/agent/error.ts +198 -0
- package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
- package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
- package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
- package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
- package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
- package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
- package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
- package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
- package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
- package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
- package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
- package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
- package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
- package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
- package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
- package/vendor/agent-root/src/agent/app/index.ts +5 -0
- package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
- package/vendor/agent-root/src/agent/app/ports.ts +72 -0
- package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
- package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
- package/vendor/agent-root/src/agent/error-contract.ts +154 -0
- package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
- package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
- package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
- package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
- package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
- package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
- package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
- package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
- package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
- package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
- package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
- package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
- package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
- package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
- package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
- package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
- package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
- package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
- package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
- package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
- package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
- package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
- package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
- package/vendor/agent-root/src/agent/tool/error.ts +131 -0
- package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
- package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
- package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
- package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
- package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
- package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
- package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
- package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
- package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
- package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
- package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
- package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
- package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
- package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
- package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
- package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
- package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
- package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
- package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
- package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
- package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
- package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
- package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
- package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
- package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
- package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
- package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
- package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
- package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
- package/vendor/agent-root/src/agent/tool/task.ts +209 -0
- package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
- package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
- package/vendor/agent-root/src/agent/tool/types.ts +116 -0
- package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
- package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
- package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
- package/vendor/agent-root/src/agent/types.ts +232 -0
- package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
- package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
- package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
- package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
- package/vendor/agent-root/src/agent/utils/index.ts +16 -0
- package/vendor/agent-root/src/agent/utils/message.ts +171 -0
- package/vendor/agent-root/src/agent/utils/token.ts +28 -0
- package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
- package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
- package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
- package/vendor/agent-root/src/config/index.ts +54 -0
- package/vendor/agent-root/src/config/loader.ts +431 -0
- package/vendor/agent-root/src/config/paths.ts +30 -0
- package/vendor/agent-root/src/config/runtime.ts +163 -0
- package/vendor/agent-root/src/config/types.ts +70 -0
- package/vendor/agent-root/src/logger/index.ts +57 -0
- package/vendor/agent-root/src/logger/logger.ts +819 -0
- package/vendor/agent-root/src/logger/types.ts +150 -0
- package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
- package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
- package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
- package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
- package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
- package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
- package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
- package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
- package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
- package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
- package/vendor/agent-root/src/providers/http/client.ts +289 -0
- package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
- package/vendor/agent-root/src/providers/index.ts +76 -0
- package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
- package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
- package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
- package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
- package/vendor/agent-root/src/providers/registry.ts +135 -0
- package/vendor/agent-root/src/providers/types/api.ts +284 -0
- package/vendor/agent-root/src/providers/types/config.ts +58 -0
- package/vendor/agent-root/src/providers/types/errors.ts +323 -0
- package/vendor/agent-root/src/providers/types/index.ts +72 -0
- package/vendor/agent-root/src/providers/types/provider.ts +45 -0
- package/vendor/agent-root/src/providers/types/registry.ts +88 -0
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
<!DOCTYPE html><html><head>
|
|
2
|
+
<title>ENTERPRISE_REALTIME</title>
|
|
3
|
+
<meta charset="utf-8">
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
|
+
|
|
6
|
+
<link rel="stylesheet" href="file:///c:\Users\Administrator\.trae-cn\extensions\shd101wyy.markdown-preview-enhanced-0.8.20-universal\crossnote\dependencies\katex\katex.min.css">
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
<style>
|
|
13
|
+
code[class*=language-],pre[class*=language-]{color:#333;background:0 0;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.4;-moz-tab-size:8;-o-tab-size:8;tab-size:8;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:.8em;overflow:auto;border-radius:3px;background:#f5f5f5}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal;background:#f5f5f5}.token.blockquote,.token.comment{color:#969896}.token.cdata{color:#183691}.token.doctype,.token.macro.property,.token.punctuation,.token.variable{color:#333}.token.builtin,.token.important,.token.keyword,.token.operator,.token.rule{color:#a71d5d}.token.attr-value,.token.regex,.token.string,.token.url{color:#183691}.token.atrule,.token.boolean,.token.code,.token.command,.token.constant,.token.entity,.token.number,.token.property,.token.symbol{color:#0086b3}.token.prolog,.token.selector,.token.tag{color:#63a35c}.token.attr-name,.token.class,.token.class-name,.token.function,.token.id,.token.namespace,.token.pseudo-class,.token.pseudo-element,.token.url-reference .token.variable{color:#795da3}.token.entity{cursor:help}.token.title,.token.title .token.punctuation{font-weight:700;color:#1d3e81}.token.list{color:#ed6a43}.token.inserted{background-color:#eaffea;color:#55a532}.token.deleted{background-color:#ffecec;color:#bd2c00}.token.bold{font-weight:700}.token.italic{font-style:italic}.language-json .token.property{color:#183691}.language-markup .token.tag .token.punctuation{color:#333}.language-css .token.function,code.language-css{color:#0086b3}.language-yaml .token.atrule{color:#63a35c}code.language-yaml{color:#183691}.language-ruby .token.function{color:#333}.language-markdown .token.url{color:#795da3}.language-makefile .token.symbol{color:#795da3}.language-makefile .token.variable{color:#183691}.language-makefile .token.builtin{color:#0086b3}.language-bash .token.keyword{color:#0086b3}pre[data-line]{position:relative;padding:1em 0 1em 3em}pre[data-line] .line-highlight-wrapper{position:absolute;top:0;left:0;background-color:transparent;display:block;width:100%}pre[data-line] .line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}pre[data-line] .line-highlight:before,pre[data-line] .line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}pre[data-line] .line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}html body{font-family:'Helvetica Neue',Helvetica,'Segoe UI',Arial,freesans,sans-serif;font-size:16px;line-height:1.6;color:#333;background-color:#fff;overflow:initial;box-sizing:border-box;word-wrap:break-word}html body>:first-child{margin-top:0}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{line-height:1.2;margin-top:1em;margin-bottom:16px;color:#000}html body h1{font-size:2.25em;font-weight:300;padding-bottom:.3em}html body h2{font-size:1.75em;font-weight:400;padding-bottom:.3em}html body h3{font-size:1.5em;font-weight:500}html body h4{font-size:1.25em;font-weight:600}html body h5{font-size:1.1em;font-weight:600}html body h6{font-size:1em;font-weight:600}html body h1,html body h2,html body h3,html body h4,html body h5{font-weight:600}html body h5{font-size:1em}html body h6{color:#5c5c5c}html body strong{color:#000}html body del{color:#5c5c5c}html body a:not([href]){color:inherit;text-decoration:none}html body a{color:#08c;text-decoration:none}html body a:hover{color:#00a3f5;text-decoration:none}html body img{max-width:100%}html body>p{margin-top:0;margin-bottom:16px;word-wrap:break-word}html body>ol,html body>ul{margin-bottom:16px}html body ol,html body ul{padding-left:2em}html body ol.no-list,html body ul.no-list{padding:0;list-style-type:none}html body ol ol,html body ol ul,html body ul ol,html body ul ul{margin-top:0;margin-bottom:0}html body li{margin-bottom:0}html body li.task-list-item{list-style:none}html body li>p{margin-top:0;margin-bottom:0}html body .task-list-item-checkbox{margin:0 .2em .25em -1.8em;vertical-align:middle}html body .task-list-item-checkbox:hover{cursor:pointer}html body blockquote{margin:16px 0;font-size:inherit;padding:0 15px;color:#5c5c5c;background-color:#f0f0f0;border-left:4px solid #d6d6d6}html body blockquote>:first-child{margin-top:0}html body blockquote>:last-child{margin-bottom:0}html body hr{height:4px;margin:32px 0;background-color:#d6d6d6;border:0 none}html body table{margin:10px 0 15px 0;border-collapse:collapse;border-spacing:0;display:block;width:100%;overflow:auto;word-break:normal;word-break:keep-all}html body table th{font-weight:700;color:#000}html body table td,html body table th{border:1px solid #d6d6d6;padding:6px 13px}html body dl{padding:0}html body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}html body dl dd{padding:0 16px;margin-bottom:16px}html body code{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:.85em;color:#000;background-color:#f0f0f0;border-radius:3px;padding:.2em 0}html body code::after,html body code::before{letter-spacing:-.2em;content:'\00a0'}html body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:0 0;border:0}html body .highlight{margin-bottom:16px}html body .highlight pre,html body pre{padding:1em;overflow:auto;line-height:1.45;border:#d6d6d6;border-radius:3px}html body .highlight pre{margin-bottom:0;word-break:normal}html body pre code,html body pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}html body pre code:after,html body pre code:before,html body pre tt:after,html body pre tt:before{content:normal}html body blockquote,html body dl,html body ol,html body p,html body pre,html body ul{margin-top:0;margin-bottom:16px}html body kbd{color:#000;border:1px solid #d6d6d6;border-bottom:2px solid #c7c7c7;padding:2px 4px;background-color:#f0f0f0;border-radius:3px}@media print{html body{background-color:#fff}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{color:#000;page-break-after:avoid}html body blockquote{color:#5c5c5c}html body pre{page-break-inside:avoid}html body table{display:table}html body img{display:block;max-width:100%;max-height:100%}html body code,html body pre{word-wrap:break-word;white-space:pre}}.markdown-preview{width:100%;height:100%;box-sizing:border-box}.markdown-preview ul{list-style:disc}.markdown-preview ul ul{list-style:circle}.markdown-preview ul ul ul{list-style:square}.markdown-preview ol{list-style:decimal}.markdown-preview ol ol,.markdown-preview ul ol{list-style-type:lower-roman}.markdown-preview ol ol ol,.markdown-preview ol ul ol,.markdown-preview ul ol ol,.markdown-preview ul ul ol{list-style-type:lower-alpha}.markdown-preview .newpage,.markdown-preview .pagebreak{page-break-before:always}.markdown-preview pre.line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}.markdown-preview pre.line-numbers>code{position:relative}.markdown-preview pre.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:1em;font-size:100%;left:0;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.markdown-preview pre.line-numbers .line-numbers-rows>span{pointer-events:none;display:block;counter-increment:linenumber}.markdown-preview pre.line-numbers .line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}.markdown-preview .mathjax-exps .MathJax_Display{text-align:center!important}.markdown-preview:not([data-for=preview]) .code-chunk .code-chunk-btn-group{display:none}.markdown-preview:not([data-for=preview]) .code-chunk .status{display:none}.markdown-preview:not([data-for=preview]) .code-chunk .output-div{margin-bottom:16px}.markdown-preview .md-toc{padding:0}.markdown-preview .md-toc .md-toc-link-wrapper .md-toc-link{display:inline;padding:.25rem 0}.markdown-preview .md-toc .md-toc-link-wrapper .md-toc-link div,.markdown-preview .md-toc .md-toc-link-wrapper .md-toc-link p{display:inline}.markdown-preview .md-toc .md-toc-link-wrapper.highlighted .md-toc-link{font-weight:800}.scrollbar-style::-webkit-scrollbar{width:8px}.scrollbar-style::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}.scrollbar-style::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,.66);border:4px solid rgba(150,150,150,.66);background-clip:content-box}html body[for=html-export]:not([data-presentation-mode]){position:relative;width:100%;height:100%;top:0;left:0;margin:0;padding:0;overflow:auto}html body[for=html-export]:not([data-presentation-mode]) .markdown-preview{position:relative;top:0;min-height:100vh}@media screen and (min-width:914px){html body[for=html-export]:not([data-presentation-mode]) .markdown-preview{padding:2em calc(50% - 457px + 2em)}}@media screen and (max-width:914px){html body[for=html-export]:not([data-presentation-mode]) .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for=html-export]:not([data-presentation-mode]) .markdown-preview{font-size:14px!important;padding:1em}}@media print{html body[for=html-export]:not([data-presentation-mode]) #sidebar-toc-btn{display:none}}html body[for=html-export]:not([data-presentation-mode]) #sidebar-toc-btn{position:fixed;bottom:8px;left:8px;font-size:28px;cursor:pointer;color:inherit;z-index:99;width:32px;text-align:center;opacity:.4}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] #sidebar-toc-btn{opacity:1}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc{position:fixed;top:0;left:0;width:300px;height:100%;padding:32px 0 48px 0;font-size:14px;box-shadow:0 0 4px rgba(150,150,150,.33);box-sizing:border-box;overflow:auto;background-color:inherit}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar{width:8px}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,.66);border:4px solid rgba(150,150,150,.66);background-clip:content-box}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc a{text-decoration:none}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc .md-toc{padding:0 16px}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc .md-toc .md-toc-link-wrapper .md-toc-link{display:inline;padding:.25rem 0}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc .md-toc .md-toc-link-wrapper .md-toc-link div,html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc .md-toc .md-toc-link-wrapper .md-toc-link p{display:inline}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc .md-toc .md-toc-link-wrapper.highlighted .md-toc-link{font-weight:800}html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{left:300px;width:calc(100% - 300px);padding:2em calc(50% - 457px - 300px / 2);margin:0;box-sizing:border-box}@media screen and (max-width:1274px){html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for=html-export]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{width:100%}}html body[for=html-export]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .markdown-preview{left:50%;transform:translateX(-50%)}html body[for=html-export]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .md-sidebar-toc{display:none}
|
|
14
|
+
/* Please visit the URL below for more information: */
|
|
15
|
+
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
|
|
16
|
+
|
|
17
|
+
</style>
|
|
18
|
+
<!-- The content below will be included at the end of the <head> element. --><script type="text/javascript">
|
|
19
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
20
|
+
// your code here
|
|
21
|
+
});
|
|
22
|
+
</script></head><body for="html-export">
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
<div class="crossnote markdown-preview ">
|
|
26
|
+
|
|
27
|
+
<h1 id="企业级无状态-agent-实时存储方案">企业级无状态 Agent 实时存储方案 </h1>
|
|
28
|
+
<h2 id="一-问题分析">一、问题分析 </h2>
|
|
29
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>当前设计的问题:
|
|
30
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
31
|
+
│ │
|
|
32
|
+
│ Agent 执行 (可能 10 分钟+) │
|
|
33
|
+
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
34
|
+
│ │ Step 1 │→ │ Step 2 │→ │ Step 3 │→ │ Step N │ │
|
|
35
|
+
│ │ │ │ │ │ │ │ │ │
|
|
36
|
+
│ │ 消息在 │ │ 消息在 │ │ 消息在 │ │ 消息在 │ │
|
|
37
|
+
│ │ 内存 │ │ 内存 │ │ 内存 │ │ 内存 │ │
|
|
38
|
+
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
|
39
|
+
│ │ │
|
|
40
|
+
│ ▼ │
|
|
41
|
+
│ ❌ 只有完成时才存储 │
|
|
42
|
+
│ │
|
|
43
|
+
│ 问题: │
|
|
44
|
+
│ - Agent 崩溃 = 所有消息丢失 │
|
|
45
|
+
│ - 页面关闭 = 请求中断 │
|
|
46
|
+
│ - 无法实时查看执行进度 │
|
|
47
|
+
│ - 无法从中间步骤恢复 │
|
|
48
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
49
|
+
</code></pre><hr>
|
|
50
|
+
<h2 id="二-解决方案概述">二、解决方案概述 </h2>
|
|
51
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>核心思路: 实时存储 + 后台执行 + 检查点恢复
|
|
52
|
+
|
|
53
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
54
|
+
│ │
|
|
55
|
+
│ 1. 实时存储 │
|
|
56
|
+
│ - 每条消息产生时立即存储 │
|
|
57
|
+
│ - 每个 Step 完成时保存检查点 │
|
|
58
|
+
│ │
|
|
59
|
+
│ 2. 后台执行 (Fire and Forget) │
|
|
60
|
+
│ - API 立即返回 executionId │
|
|
61
|
+
│ - Worker 后台执行 │
|
|
62
|
+
│ - 页面关闭不受影响 │
|
|
63
|
+
│ │
|
|
64
|
+
│ 3. 故障恢复 │
|
|
65
|
+
│ - 检查点记录执行位置 │
|
|
66
|
+
│ - 从 lastMessageId 恢复 │
|
|
67
|
+
│ │
|
|
68
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
69
|
+
</code></pre><hr>
|
|
70
|
+
<h2 id="三-核心概念">三、核心概念 </h2>
|
|
71
|
+
<table>
|
|
72
|
+
<thead>
|
|
73
|
+
<tr>
|
|
74
|
+
<th>概念</th>
|
|
75
|
+
<th>说明</th>
|
|
76
|
+
</tr>
|
|
77
|
+
</thead>
|
|
78
|
+
<tbody>
|
|
79
|
+
<tr>
|
|
80
|
+
<td><strong>sessionId</strong></td>
|
|
81
|
+
<td>整个对话(用户和 AI 之间的会话)</td>
|
|
82
|
+
</tr>
|
|
83
|
+
<tr>
|
|
84
|
+
<td><strong>executionId</strong></td>
|
|
85
|
+
<td>一次 agent.run()(用户的一次请求)</td>
|
|
86
|
+
</tr>
|
|
87
|
+
<tr>
|
|
88
|
+
<td><strong>stepIndex</strong></td>
|
|
89
|
+
<td>每次循环(内部步骤)</td>
|
|
90
|
+
</tr>
|
|
91
|
+
</tbody>
|
|
92
|
+
</table>
|
|
93
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>Session: sess_001 (用户整个对话)
|
|
94
|
+
│
|
|
95
|
+
└── Execution: exec_001 (用户第一次请求)
|
|
96
|
+
│
|
|
97
|
+
├── Step 1 (stepIndex=1)
|
|
98
|
+
├── Step 2 (stepIndex=2)
|
|
99
|
+
└── Step 3 (stepIndex=3)
|
|
100
|
+
│
|
|
101
|
+
└── 返回结果
|
|
102
|
+
</code></pre><hr>
|
|
103
|
+
<h2 id="四-实时存储架构">四、实时存储架构 </h2>
|
|
104
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>┌─────────────────────────────────────────────────────────────────┐
|
|
105
|
+
│ 实时存储架构 │
|
|
106
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
107
|
+
│ │
|
|
108
|
+
│ ┌───────────────────────────────────────────────────────────┐ │
|
|
109
|
+
│ │ 回调驱动 │ │
|
|
110
|
+
│ │ │ │
|
|
111
|
+
│ │ onMessage: 每条消息产生时触发 │ │
|
|
112
|
+
│ │ - Redis 实时写入 (< 1ms) │ │
|
|
113
|
+
│ │ - Kafka 异步持久化 │ │
|
|
114
|
+
│ │ - SSE 推送客户端 │ │
|
|
115
|
+
│ │ │ │
|
|
116
|
+
│ │ onCheckpoint: 每个 Step 完成时触发 │ │
|
|
117
|
+
│ │ - 保存执行位置 (stepIndex + lastMessageId) │ │
|
|
118
|
+
│ │ - 用于故障恢复 │ │
|
|
119
|
+
│ │ │ │
|
|
120
|
+
│ └───────────────────────────────────────────────────────────┘ │
|
|
121
|
+
│ │ │
|
|
122
|
+
│ ▼ │
|
|
123
|
+
│ ┌───────────────────────────────────────────────────────────┐ │
|
|
124
|
+
│ │ 存储层 │ │
|
|
125
|
+
│ │ │ │
|
|
126
|
+
│ │ ┌─────────────┐ ┌─────────────┐ │ │
|
|
127
|
+
│ │ │ Redis │ │ Kafka │ │ │
|
|
128
|
+
│ │ │ (缓存/检查点)│ │ (持久化) │ │ │
|
|
129
|
+
│ │ └─────────────┘ └─────────────┘ │ │
|
|
130
|
+
│ │ │ │
|
|
131
|
+
│ └───────────────────────────────────────────────────────────┘ │
|
|
132
|
+
│ │
|
|
133
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
134
|
+
</code></pre><hr>
|
|
135
|
+
<h2 id="五-agent-回调机制设计">五、Agent 回调机制设计 </h2>
|
|
136
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token comment">// ============================================================</span>
|
|
137
|
+
<span class="token comment">// 消息类型定义</span>
|
|
138
|
+
<span class="token comment">// ============================================================</span>
|
|
139
|
+
|
|
140
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">Message</span> <span class="token punctuation">{</span>
|
|
141
|
+
messageId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
142
|
+
role<span class="token operator">:</span> <span class="token string">'system'</span> <span class="token operator">|</span> <span class="token string">'user'</span> <span class="token operator">|</span> <span class="token string">'assistant'</span> <span class="token operator">|</span> <span class="token string">'tool'</span><span class="token punctuation">;</span>
|
|
143
|
+
content<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
144
|
+
tool_call_id<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
145
|
+
tool_calls<span class="token operator">?</span><span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
146
|
+
timestamp<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
147
|
+
metadata<span class="token operator">?</span><span class="token operator">:</span> Record<span class="token operator"><</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">any</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
148
|
+
<span class="token punctuation">}</span>
|
|
149
|
+
|
|
150
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">ToolCall</span> <span class="token punctuation">{</span>
|
|
151
|
+
id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
152
|
+
type<span class="token operator">:</span> <span class="token string">'function'</span><span class="token punctuation">;</span>
|
|
153
|
+
name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
154
|
+
arguments<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
155
|
+
<span class="token punctuation">}</span>
|
|
156
|
+
|
|
157
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">Tool</span> <span class="token punctuation">{</span>
|
|
158
|
+
name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
159
|
+
description<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
160
|
+
parameters<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
|
|
161
|
+
<span class="token punctuation">}</span>
|
|
162
|
+
|
|
163
|
+
<span class="token comment">// ============================================================</span>
|
|
164
|
+
<span class="token comment">// Agent 输入输出接口</span>
|
|
165
|
+
<span class="token comment">// ============================================================</span>
|
|
166
|
+
|
|
167
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">AgentInput</span> <span class="token punctuation">{</span>
|
|
168
|
+
executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// 任务 ID</span>
|
|
169
|
+
conversationId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// 会话 ID</span>
|
|
170
|
+
messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Context 消息列表</span>
|
|
171
|
+
systemPrompt<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// 系统提示</span>
|
|
172
|
+
tools<span class="token operator">?</span><span class="token operator">:</span> Tool<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 可用工具</span>
|
|
173
|
+
config<span class="token operator">?</span><span class="token operator">:</span> LLMConfig<span class="token punctuation">;</span> <span class="token comment">// LLM 配置</span>
|
|
174
|
+
maxSteps<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 最大步数</span>
|
|
175
|
+
startStep<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 起始步骤 (用于恢复)</span>
|
|
176
|
+
<span class="token punctuation">}</span>
|
|
177
|
+
|
|
178
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">AgentOutput</span> <span class="token punctuation">{</span>
|
|
179
|
+
messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 包含新增消息</span>
|
|
180
|
+
finishReason<span class="token operator">:</span> <span class="token string">'stop'</span> <span class="token operator">|</span> <span class="token string">'max_steps'</span> <span class="token operator">|</span> <span class="token string">'error'</span><span class="token punctuation">;</span>
|
|
181
|
+
steps<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 执行的步数</span>
|
|
182
|
+
<span class="token punctuation">}</span>
|
|
183
|
+
|
|
184
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">LLMConfig</span> <span class="token punctuation">{</span>
|
|
185
|
+
model<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
186
|
+
temperature<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
187
|
+
maxTokens<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
188
|
+
topP<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
189
|
+
<span class="token punctuation">}</span>
|
|
190
|
+
|
|
191
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">LLMResponse</span> <span class="token punctuation">{</span>
|
|
192
|
+
message<span class="token operator">:</span> Message<span class="token punctuation">;</span>
|
|
193
|
+
toolCalls<span class="token operator">?</span><span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
194
|
+
usage<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
195
|
+
prompt_tokens<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
196
|
+
completion_tokens<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
197
|
+
total_tokens<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
198
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
199
|
+
finishReason<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
200
|
+
<span class="token punctuation">}</span>
|
|
201
|
+
|
|
202
|
+
<span class="token comment">// ============================================================</span>
|
|
203
|
+
<span class="token comment">// 回调接口</span>
|
|
204
|
+
<span class="token comment">// ============================================================</span>
|
|
205
|
+
|
|
206
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">AgentCallbacks</span> <span class="token punctuation">{</span>
|
|
207
|
+
<span class="token comment">// 每产生一条新消息时回调 (实时存储)</span>
|
|
208
|
+
<span class="token function-variable function">onMessage</span><span class="token operator">:</span> <span class="token punctuation">(</span>message<span class="token operator">:</span> Message<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword keyword-void">void</span> <span class="token operator">|</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
209
|
+
|
|
210
|
+
<span class="token comment">// 每个 Step 完成时回调 (检查点)</span>
|
|
211
|
+
<span class="token function-variable function">onCheckpoint</span><span class="token operator">:</span> <span class="token punctuation">(</span>checkpoint<span class="token operator">:</span> ExecutionCheckpoint<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword keyword-void">void</span> <span class="token operator">|</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
212
|
+
|
|
213
|
+
<span class="token comment">// 进度更新回调</span>
|
|
214
|
+
onProgress<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>progress<span class="token operator">:</span> ExecutionProgress<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword keyword-void">void</span> <span class="token operator">|</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
215
|
+
|
|
216
|
+
<span class="token comment">// 错误回调</span>
|
|
217
|
+
onError<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>error<span class="token operator">:</span> Error<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword keyword-void">void</span> <span class="token operator">|</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
218
|
+
<span class="token punctuation">}</span>
|
|
219
|
+
|
|
220
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">ExecutionCheckpoint</span> <span class="token punctuation">{</span>
|
|
221
|
+
executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
222
|
+
stepIndex<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 当前步骤</span>
|
|
223
|
+
lastMessageId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// 最后一条消息 ID (用于恢复)</span>
|
|
224
|
+
lastMessageTime<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 时间戳</span>
|
|
225
|
+
canResume<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span> <span class="token comment">// 是否可恢复</span>
|
|
226
|
+
<span class="token punctuation">}</span>
|
|
227
|
+
|
|
228
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">ExecutionProgress</span> <span class="token punctuation">{</span>
|
|
229
|
+
executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
230
|
+
stepIndex<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
231
|
+
currentAction<span class="token operator">:</span> <span class="token string">'llm'</span> <span class="token operator">|</span> <span class="token string">'tool'</span> <span class="token operator">|</span> <span class="token string">'waiting'</span><span class="token punctuation">;</span>
|
|
232
|
+
messageCount<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
233
|
+
<span class="token punctuation">}</span>
|
|
234
|
+
|
|
235
|
+
<span class="token comment">// ============================================================</span>
|
|
236
|
+
<span class="token comment">// LLM Provider 接口</span>
|
|
237
|
+
<span class="token comment">// ============================================================</span>
|
|
238
|
+
|
|
239
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">LLMProvider</span> <span class="token punctuation">{</span>
|
|
240
|
+
<span class="token comment">// 生成响应 (非流式)</span>
|
|
241
|
+
<span class="token function">generate</span><span class="token punctuation">(</span>messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> config<span class="token operator">?</span><span class="token operator">:</span> LLMConfig<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>LLMResponse<span class="token operator">></span><span class="token punctuation">;</span>
|
|
242
|
+
|
|
243
|
+
<span class="token comment">// 生成响应 (流式)</span>
|
|
244
|
+
<span class="token function">generateStream</span><span class="token punctuation">(</span>
|
|
245
|
+
messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
246
|
+
config<span class="token operator">?</span><span class="token operator">:</span> LLMConfig
|
|
247
|
+
<span class="token punctuation">)</span><span class="token operator">:</span> AsyncGenerator<span class="token operator"><</span>Chunk<span class="token operator">></span><span class="token punctuation">;</span>
|
|
248
|
+
<span class="token punctuation">}</span>
|
|
249
|
+
|
|
250
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">Chunk</span> <span class="token punctuation">{</span>
|
|
251
|
+
id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
252
|
+
choices<span class="token operator">:</span> <span class="token builtin">Array</span><span class="token operator"><</span><span class="token punctuation">{</span>
|
|
253
|
+
index<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
|
|
254
|
+
delta<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
255
|
+
content<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
256
|
+
tool_calls<span class="token operator">?</span><span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
257
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
258
|
+
finish_reason<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
|
|
259
|
+
<span class="token punctuation">}</span><span class="token operator">></span><span class="token punctuation">;</span>
|
|
260
|
+
<span class="token punctuation">}</span>
|
|
261
|
+
|
|
262
|
+
<span class="token comment">// ============================================================</span>
|
|
263
|
+
<span class="token comment">// Tool Executor 接口</span>
|
|
264
|
+
<span class="token comment">// ============================================================</span>
|
|
265
|
+
|
|
266
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">ToolExecutor</span> <span class="token punctuation">{</span>
|
|
267
|
+
<span class="token function">execute</span><span class="token punctuation">(</span>toolCall<span class="token operator">:</span> ToolCall<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>Message<span class="token operator">></span><span class="token punctuation">;</span>
|
|
268
|
+
<span class="token function">registerTool</span><span class="token punctuation">(</span>tool<span class="token operator">:</span> Tool<span class="token punctuation">,</span> handler<span class="token operator">:</span> <span class="token builtin">Function</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword keyword-void">void</span><span class="token punctuation">;</span>
|
|
269
|
+
<span class="token punctuation">}</span>
|
|
270
|
+
|
|
271
|
+
<span class="token comment">// ============================================================</span>
|
|
272
|
+
<span class="token comment">// 无状态 Agent 完整实现</span>
|
|
273
|
+
<span class="token comment">// ============================================================</span>
|
|
274
|
+
|
|
275
|
+
<span class="token keyword keyword-class">class</span> <span class="token class-name">StatelessAgent</span> <span class="token punctuation">{</span>
|
|
276
|
+
<span class="token keyword keyword-private">private</span> llmProvider<span class="token operator">:</span> LLMProvider<span class="token punctuation">;</span>
|
|
277
|
+
<span class="token keyword keyword-private">private</span> toolExecutor<span class="token operator">:</span> ToolExecutor<span class="token punctuation">;</span>
|
|
278
|
+
|
|
279
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span>llmProvider<span class="token operator">:</span> LLMProvider<span class="token punctuation">,</span> toolExecutor<span class="token operator">:</span> ToolExecutor<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
280
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>llmProvider <span class="token operator">=</span> llmProvider<span class="token punctuation">;</span>
|
|
281
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>toolExecutor <span class="token operator">=</span> toolExecutor<span class="token punctuation">;</span>
|
|
282
|
+
<span class="token punctuation">}</span>
|
|
283
|
+
|
|
284
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">run</span><span class="token punctuation">(</span>input<span class="token operator">:</span> AgentInput<span class="token punctuation">,</span> callbacks<span class="token operator">?</span><span class="token operator">:</span> AgentCallbacks<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>AgentOutput<span class="token operator">></span> <span class="token punctuation">{</span>
|
|
285
|
+
<span class="token keyword keyword-let">let</span> <span class="token punctuation">{</span>
|
|
286
|
+
messages<span class="token punctuation">,</span>
|
|
287
|
+
maxSteps <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
|
|
288
|
+
startStep <span class="token operator">=</span> <span class="token number">1</span>
|
|
289
|
+
<span class="token punctuation">}</span> <span class="token operator">=</span> input<span class="token punctuation">;</span>
|
|
290
|
+
|
|
291
|
+
<span class="token keyword keyword-let">let</span> stepIndex <span class="token operator">=</span> startStep <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
|
|
292
|
+
<span class="token keyword keyword-let">let</span> finishReason<span class="token operator">:</span> <span class="token string">'stop'</span> <span class="token operator">|</span> <span class="token string">'max_steps'</span> <span class="token operator">|</span> <span class="token string">'error'</span> <span class="token operator">=</span> <span class="token string">'stop'</span><span class="token punctuation">;</span>
|
|
293
|
+
|
|
294
|
+
<span class="token comment">// 主循环</span>
|
|
295
|
+
<span class="token keyword keyword-while">while</span> <span class="token punctuation">(</span>stepIndex <span class="token operator"><</span> maxSteps<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
296
|
+
stepIndex<span class="token operator">++</span><span class="token punctuation">;</span>
|
|
297
|
+
|
|
298
|
+
<span class="token keyword keyword-try">try</span> <span class="token punctuation">{</span>
|
|
299
|
+
<span class="token comment">// 回调: 进度</span>
|
|
300
|
+
callbacks<span class="token operator">?.</span>onProgress<span class="token operator">?.</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
|
|
301
|
+
executionId<span class="token operator">:</span> input<span class="token punctuation">.</span>executionId<span class="token punctuation">,</span>
|
|
302
|
+
stepIndex<span class="token punctuation">,</span>
|
|
303
|
+
currentAction<span class="token operator">:</span> <span class="token string">'llm'</span><span class="token punctuation">,</span>
|
|
304
|
+
messageCount<span class="token operator">:</span> messages<span class="token punctuation">.</span>length
|
|
305
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
306
|
+
|
|
307
|
+
<span class="token comment">// 1. 调用 LLM</span>
|
|
308
|
+
<span class="token keyword keyword-const">const</span> response <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>llmProvider<span class="token punctuation">.</span><span class="token function">generate</span><span class="token punctuation">(</span>messages<span class="token punctuation">,</span> input<span class="token punctuation">.</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
309
|
+
|
|
310
|
+
<span class="token comment">// 2. 添加助手消息</span>
|
|
311
|
+
<span class="token keyword keyword-const">const</span> assistantMessage <span class="token operator">=</span> response<span class="token punctuation">.</span>message<span class="token punctuation">;</span>
|
|
312
|
+
messages<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>assistantMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
313
|
+
|
|
314
|
+
<span class="token comment">// 回调: 新消息 (实时存储)</span>
|
|
315
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onMessage<span class="token punctuation">,</span> assistantMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
316
|
+
|
|
317
|
+
<span class="token comment">// 3. 检查工具调用</span>
|
|
318
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>toolCalls <span class="token operator">&&</span> response<span class="token punctuation">.</span>toolCalls<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
319
|
+
<span class="token comment">// 回调: 进度</span>
|
|
320
|
+
callbacks<span class="token operator">?.</span>onProgress<span class="token operator">?.</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
|
|
321
|
+
executionId<span class="token operator">:</span> input<span class="token punctuation">.</span>executionId<span class="token punctuation">,</span>
|
|
322
|
+
stepIndex<span class="token punctuation">,</span>
|
|
323
|
+
currentAction<span class="token operator">:</span> <span class="token string">'tool'</span><span class="token punctuation">,</span>
|
|
324
|
+
messageCount<span class="token operator">:</span> messages<span class="token punctuation">.</span>length
|
|
325
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
326
|
+
|
|
327
|
+
<span class="token comment">// 4. 执行工具调用</span>
|
|
328
|
+
<span class="token keyword keyword-for">for</span> <span class="token punctuation">(</span><span class="token keyword keyword-const">const</span> toolCall <span class="token keyword keyword-of">of</span> response<span class="token punctuation">.</span>toolCalls<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
329
|
+
<span class="token keyword keyword-const">const</span> toolResult <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>toolExecutor<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>toolCall<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
330
|
+
messages<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>toolResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
331
|
+
|
|
332
|
+
<span class="token comment">// 回调: 工具结果 (实时存储)</span>
|
|
333
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onMessage<span class="token punctuation">,</span> toolResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
334
|
+
<span class="token punctuation">}</span>
|
|
335
|
+
|
|
336
|
+
<span class="token comment">// 回调: 检查点 (每个 Step 完成)</span>
|
|
337
|
+
<span class="token keyword keyword-const">const</span> lastMessage <span class="token operator">=</span> messages<span class="token punctuation">[</span>messages<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
338
|
+
<span class="token keyword keyword-const">const</span> checkpoint<span class="token operator">:</span> ExecutionCheckpoint <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
339
|
+
executionId<span class="token operator">:</span> input<span class="token punctuation">.</span>executionId<span class="token punctuation">,</span>
|
|
340
|
+
stepIndex<span class="token punctuation">,</span>
|
|
341
|
+
lastMessageId<span class="token operator">:</span> lastMessage<span class="token operator">?.</span>messageId <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">,</span>
|
|
342
|
+
lastMessageTime<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
343
|
+
canResume<span class="token operator">:</span> <span class="token boolean">true</span>
|
|
344
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
345
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onCheckpoint<span class="token punctuation">,</span> checkpoint<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
346
|
+
|
|
347
|
+
<span class="token comment">// 继续下一轮</span>
|
|
348
|
+
<span class="token keyword keyword-continue">continue</span><span class="token punctuation">;</span>
|
|
349
|
+
<span class="token punctuation">}</span>
|
|
350
|
+
|
|
351
|
+
<span class="token comment">// 5. 无工具调用,完成</span>
|
|
352
|
+
finishReason <span class="token operator">=</span> <span class="token string">'stop'</span><span class="token punctuation">;</span>
|
|
353
|
+
<span class="token keyword keyword-break">break</span><span class="token punctuation">;</span>
|
|
354
|
+
|
|
355
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-catch">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
356
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Agent] Step </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>stepIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> error:</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
357
|
+
|
|
358
|
+
<span class="token comment">// 回调: 错误</span>
|
|
359
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onError<span class="token punctuation">,</span> error <span class="token keyword keyword-as">as</span> Error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
360
|
+
|
|
361
|
+
<span class="token comment">// 检查是否是可恢复错误</span>
|
|
362
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span><span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">isRetryableError</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
363
|
+
<span class="token comment">// 可以重试,继续循环</span>
|
|
364
|
+
<span class="token keyword keyword-continue">continue</span><span class="token punctuation">;</span>
|
|
365
|
+
<span class="token punctuation">}</span>
|
|
366
|
+
|
|
367
|
+
finishReason <span class="token operator">=</span> <span class="token string">'error'</span><span class="token punctuation">;</span>
|
|
368
|
+
<span class="token keyword keyword-break">break</span><span class="token punctuation">;</span>
|
|
369
|
+
<span class="token punctuation">}</span>
|
|
370
|
+
<span class="token punctuation">}</span>
|
|
371
|
+
|
|
372
|
+
<span class="token comment">// 检查是否是达到最大步数</span>
|
|
373
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>stepIndex <span class="token operator">>=</span> maxSteps<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
374
|
+
finishReason <span class="token operator">=</span> <span class="token string">'max_steps'</span><span class="token punctuation">;</span>
|
|
375
|
+
<span class="token punctuation">}</span>
|
|
376
|
+
|
|
377
|
+
<span class="token keyword keyword-return">return</span> <span class="token punctuation">{</span>
|
|
378
|
+
messages<span class="token punctuation">,</span>
|
|
379
|
+
finishReason<span class="token punctuation">,</span>
|
|
380
|
+
steps<span class="token operator">:</span> stepIndex <span class="token operator">-</span> startStep <span class="token operator">+</span> <span class="token number">1</span>
|
|
381
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
382
|
+
<span class="token punctuation">}</span>
|
|
383
|
+
|
|
384
|
+
<span class="token comment">// 安全执行回调 (防止回调抛出异常)</span>
|
|
385
|
+
<span class="token keyword keyword-private">private</span> <span class="token keyword keyword-async">async</span> <span class="token generic-function"><span class="token function">safeCallback</span><span class="token generic class-name"><span class="token operator"><</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>
|
|
386
|
+
callback<span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>arg<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword keyword-void">void</span> <span class="token operator">|</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token keyword keyword-undefined">undefined</span><span class="token punctuation">,</span>
|
|
387
|
+
arg<span class="token operator">:</span> <span class="token constant">T</span>
|
|
388
|
+
<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
389
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>callback<span class="token punctuation">)</span> <span class="token keyword keyword-return">return</span><span class="token punctuation">;</span>
|
|
390
|
+
|
|
391
|
+
<span class="token keyword keyword-try">try</span> <span class="token punctuation">{</span>
|
|
392
|
+
<span class="token keyword keyword-await">await</span> <span class="token function">callback</span><span class="token punctuation">(</span>arg<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
393
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-catch">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
394
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'[Agent] Callback error:'</span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
395
|
+
<span class="token punctuation">}</span>
|
|
396
|
+
<span class="token punctuation">}</span>
|
|
397
|
+
|
|
398
|
+
<span class="token comment">// 判断是否可重试</span>
|
|
399
|
+
<span class="token keyword keyword-private">private</span> <span class="token function">isRetryableError</span><span class="token punctuation">(</span>error<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
|
|
400
|
+
<span class="token keyword keyword-const">const</span> retryableCodes <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'RATE_LIMIT'</span><span class="token punctuation">,</span> <span class="token string">'TIMEOUT'</span><span class="token punctuation">,</span> <span class="token string">'NETWORK_ERROR'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
401
|
+
<span class="token keyword keyword-return">return</span> error<span class="token operator">?.</span>code <span class="token operator">&&</span> retryableCodes<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>error<span class="token punctuation">.</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
402
|
+
<span class="token punctuation">}</span>
|
|
403
|
+
|
|
404
|
+
<span class="token comment">// ============================================================</span>
|
|
405
|
+
<span class="token comment">// 流式版本: runStream</span>
|
|
406
|
+
<span class="token comment">// 适用于需要实时显示每个 chunk 的场景</span>
|
|
407
|
+
<span class="token comment">// ============================================================</span>
|
|
408
|
+
|
|
409
|
+
async <span class="token operator">*</span><span class="token function">runStream</span><span class="token punctuation">(</span>
|
|
410
|
+
input<span class="token operator">:</span> AgentInput<span class="token punctuation">,</span>
|
|
411
|
+
callbacks<span class="token operator">?</span><span class="token operator">:</span> AgentCallbacks
|
|
412
|
+
<span class="token punctuation">)</span><span class="token operator">:</span> AsyncGenerator<span class="token operator"><</span>StreamEvent<span class="token punctuation">,</span> <span class="token builtin">any</span><span class="token punctuation">,</span> <span class="token builtin">unknown</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
413
|
+
<span class="token keyword keyword-let">let</span> <span class="token punctuation">{</span>
|
|
414
|
+
messages<span class="token punctuation">,</span>
|
|
415
|
+
maxSteps <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
|
|
416
|
+
startStep <span class="token operator">=</span> <span class="token number">1</span>
|
|
417
|
+
<span class="token punctuation">}</span> <span class="token operator">=</span> input<span class="token punctuation">;</span>
|
|
418
|
+
|
|
419
|
+
<span class="token keyword keyword-let">let</span> stepIndex <span class="token operator">=</span> startStep <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
|
|
420
|
+
|
|
421
|
+
<span class="token keyword keyword-while">while</span> <span class="token punctuation">(</span>stepIndex <span class="token operator"><</span> maxSteps<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
422
|
+
stepIndex<span class="token operator">++</span><span class="token punctuation">;</span>
|
|
423
|
+
|
|
424
|
+
<span class="token keyword keyword-try">try</span> <span class="token punctuation">{</span>
|
|
425
|
+
<span class="token comment">// 回调: 进度</span>
|
|
426
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
427
|
+
type<span class="token operator">:</span> <span class="token string">'progress'</span><span class="token punctuation">,</span>
|
|
428
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
429
|
+
executionId<span class="token operator">:</span> input<span class="token punctuation">.</span>executionId<span class="token punctuation">,</span>
|
|
430
|
+
stepIndex<span class="token punctuation">,</span>
|
|
431
|
+
currentAction<span class="token operator">:</span> <span class="token string">'llm'</span><span class="token punctuation">,</span>
|
|
432
|
+
messageCount<span class="token operator">:</span> messages<span class="token punctuation">.</span>length
|
|
433
|
+
<span class="token punctuation">}</span>
|
|
434
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
435
|
+
|
|
436
|
+
<span class="token comment">// 1. 流式调用 LLM</span>
|
|
437
|
+
<span class="token keyword keyword-const">const</span> stream <span class="token operator">=</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>llmProvider<span class="token punctuation">.</span><span class="token function">generateStream</span><span class="token punctuation">(</span>messages<span class="token punctuation">,</span> input<span class="token punctuation">.</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
438
|
+
|
|
439
|
+
<span class="token comment">// 构建助手消息</span>
|
|
440
|
+
<span class="token keyword keyword-const">const</span> assistantMessage<span class="token operator">:</span> Message <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
441
|
+
messageId<span class="token operator">:</span> <span class="token function">generateId</span><span class="token punctuation">(</span><span class="token string">'msg_'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
442
|
+
role<span class="token operator">:</span> <span class="token string">'assistant'</span><span class="token punctuation">,</span>
|
|
443
|
+
content<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
|
|
444
|
+
timestamp<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
|
|
445
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
446
|
+
|
|
447
|
+
<span class="token keyword keyword-let">let</span> toolCalls<span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
448
|
+
|
|
449
|
+
<span class="token comment">// 2. 处理流式响应</span>
|
|
450
|
+
<span class="token keyword keyword-for">for</span> <span class="token keyword keyword-await">await</span> <span class="token punctuation">(</span><span class="token keyword keyword-const">const</span> chunk <span class="token keyword keyword-of">of</span> stream<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
451
|
+
<span class="token keyword keyword-const">const</span> delta <span class="token operator">=</span> chunk<span class="token punctuation">.</span>choices<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">?.</span>delta<span class="token punctuation">;</span>
|
|
452
|
+
|
|
453
|
+
<span class="token comment">// 更新 content</span>
|
|
454
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>delta<span class="token operator">?.</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
455
|
+
assistantMessage<span class="token punctuation">.</span>content <span class="token operator">+=</span> delta<span class="token punctuation">.</span>content<span class="token punctuation">;</span>
|
|
456
|
+
|
|
457
|
+
<span class="token comment">// 回调: 内容更新</span>
|
|
458
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
459
|
+
type<span class="token operator">:</span> <span class="token string">'chunk'</span><span class="token punctuation">,</span>
|
|
460
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
461
|
+
messageId<span class="token operator">:</span> assistantMessage<span class="token punctuation">.</span>messageId<span class="token punctuation">,</span>
|
|
462
|
+
content<span class="token operator">:</span> delta<span class="token punctuation">.</span>content<span class="token punctuation">,</span>
|
|
463
|
+
delta<span class="token operator">:</span> <span class="token boolean">true</span>
|
|
464
|
+
<span class="token punctuation">}</span>
|
|
465
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
466
|
+
<span class="token punctuation">}</span>
|
|
467
|
+
|
|
468
|
+
<span class="token comment">// 更新 tool_calls</span>
|
|
469
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>delta<span class="token operator">?.</span>tool_calls<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
470
|
+
toolCalls <span class="token operator">=</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">mergeToolCalls</span><span class="token punctuation">(</span>toolCalls<span class="token punctuation">,</span> delta<span class="token punctuation">.</span>tool_calls<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
471
|
+
|
|
472
|
+
<span class="token comment">// 回调: 工具调用</span>
|
|
473
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
474
|
+
type<span class="token operator">:</span> <span class="token string">'tool_call'</span><span class="token punctuation">,</span>
|
|
475
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
476
|
+
messageId<span class="token operator">:</span> assistantMessage<span class="token punctuation">.</span>messageId<span class="token punctuation">,</span>
|
|
477
|
+
toolCalls
|
|
478
|
+
<span class="token punctuation">}</span>
|
|
479
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
480
|
+
<span class="token punctuation">}</span>
|
|
481
|
+
|
|
482
|
+
<span class="token comment">// 检查完成</span>
|
|
483
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>chunk<span class="token punctuation">.</span>choices<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">?.</span>finish_reason<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
484
|
+
<span class="token keyword keyword-break">break</span><span class="token punctuation">;</span>
|
|
485
|
+
<span class="token punctuation">}</span>
|
|
486
|
+
<span class="token punctuation">}</span>
|
|
487
|
+
|
|
488
|
+
<span class="token comment">// 3. 完成消息构建</span>
|
|
489
|
+
assistantMessage<span class="token punctuation">.</span>tool_calls <span class="token operator">=</span> toolCalls<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> toolCalls <span class="token operator">:</span> <span class="token keyword keyword-undefined">undefined</span><span class="token punctuation">;</span>
|
|
490
|
+
messages<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>assistantMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
491
|
+
|
|
492
|
+
<span class="token comment">// 回调: 新消息</span>
|
|
493
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onMessage<span class="token punctuation">,</span> assistantMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
494
|
+
|
|
495
|
+
<span class="token comment">// 4. 处理工具调用</span>
|
|
496
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>toolCalls<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
497
|
+
<span class="token keyword keyword-for">for</span> <span class="token punctuation">(</span><span class="token keyword keyword-const">const</span> toolCall <span class="token keyword keyword-of">of</span> toolCalls<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
498
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
499
|
+
type<span class="token operator">:</span> <span class="token string">'progress'</span><span class="token punctuation">,</span>
|
|
500
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
501
|
+
stepIndex<span class="token punctuation">,</span>
|
|
502
|
+
currentAction<span class="token operator">:</span> <span class="token string">'tool'</span><span class="token punctuation">,</span>
|
|
503
|
+
messageCount<span class="token operator">:</span> messages<span class="token punctuation">.</span>length
|
|
504
|
+
<span class="token punctuation">}</span>
|
|
505
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
506
|
+
|
|
507
|
+
<span class="token comment">// 执行工具</span>
|
|
508
|
+
<span class="token keyword keyword-const">const</span> toolResult <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>toolExecutor<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>toolCall<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
509
|
+
messages<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>toolResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
510
|
+
|
|
511
|
+
<span class="token comment">// 回调: 工具结果</span>
|
|
512
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onMessage<span class="token punctuation">,</span> toolResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
513
|
+
|
|
514
|
+
<span class="token comment">// 推送工具结果</span>
|
|
515
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
516
|
+
type<span class="token operator">:</span> <span class="token string">'tool_result'</span><span class="token punctuation">,</span>
|
|
517
|
+
data<span class="token operator">:</span> toolResult
|
|
518
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
519
|
+
<span class="token punctuation">}</span>
|
|
520
|
+
|
|
521
|
+
<span class="token comment">// 回调: 检查点</span>
|
|
522
|
+
<span class="token keyword keyword-const">const</span> lastMessage <span class="token operator">=</span> messages<span class="token punctuation">[</span>messages<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
523
|
+
<span class="token keyword keyword-const">const</span> checkpoint<span class="token operator">:</span> ExecutionCheckpoint <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
524
|
+
executionId<span class="token operator">:</span> input<span class="token punctuation">.</span>executionId<span class="token punctuation">,</span>
|
|
525
|
+
stepIndex<span class="token punctuation">,</span>
|
|
526
|
+
lastMessageId<span class="token operator">:</span> lastMessage<span class="token operator">?.</span>messageId <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">,</span>
|
|
527
|
+
lastMessageTime<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
528
|
+
canResume<span class="token operator">:</span> <span class="token boolean">true</span>
|
|
529
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
530
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onCheckpoint<span class="token punctuation">,</span> checkpoint<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
531
|
+
|
|
532
|
+
<span class="token comment">// 推送检查点</span>
|
|
533
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
534
|
+
type<span class="token operator">:</span> <span class="token string">'checkpoint'</span><span class="token punctuation">,</span>
|
|
535
|
+
data<span class="token operator">:</span> checkpoint
|
|
536
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
537
|
+
|
|
538
|
+
<span class="token keyword keyword-continue">continue</span><span class="token punctuation">;</span>
|
|
539
|
+
<span class="token punctuation">}</span>
|
|
540
|
+
|
|
541
|
+
<span class="token comment">// 5. 无工具调用,完成</span>
|
|
542
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
543
|
+
type<span class="token operator">:</span> <span class="token string">'done'</span><span class="token punctuation">,</span>
|
|
544
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
545
|
+
finishReason<span class="token operator">:</span> <span class="token string">'stop'</span><span class="token punctuation">,</span>
|
|
546
|
+
steps<span class="token operator">:</span> stepIndex <span class="token operator">-</span> startStep <span class="token operator">+</span> <span class="token number">1</span>
|
|
547
|
+
<span class="token punctuation">}</span>
|
|
548
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
549
|
+
|
|
550
|
+
<span class="token keyword keyword-break">break</span><span class="token punctuation">;</span>
|
|
551
|
+
|
|
552
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-catch">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
553
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Agent] Step </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>stepIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> error:</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
554
|
+
|
|
555
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">safeCallback</span><span class="token punctuation">(</span>callbacks<span class="token operator">?.</span>onError<span class="token punctuation">,</span> error <span class="token keyword keyword-as">as</span> Error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
556
|
+
|
|
557
|
+
<span class="token keyword keyword-yield">yield</span> <span class="token punctuation">{</span>
|
|
558
|
+
type<span class="token operator">:</span> <span class="token string">'error'</span><span class="token punctuation">,</span>
|
|
559
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span> message<span class="token operator">:</span> <span class="token punctuation">(</span>error <span class="token keyword keyword-as">as</span> Error<span class="token punctuation">)</span><span class="token punctuation">.</span>message <span class="token punctuation">}</span>
|
|
560
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
561
|
+
|
|
562
|
+
<span class="token keyword keyword-break">break</span><span class="token punctuation">;</span>
|
|
563
|
+
<span class="token punctuation">}</span>
|
|
564
|
+
<span class="token punctuation">}</span>
|
|
565
|
+
<span class="token punctuation">}</span>
|
|
566
|
+
|
|
567
|
+
<span class="token comment">// 合并工具调用</span>
|
|
568
|
+
<span class="token keyword keyword-private">private</span> <span class="token function">mergeToolCalls</span><span class="token punctuation">(</span>
|
|
569
|
+
existing<span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
570
|
+
newCalls<span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span>
|
|
571
|
+
<span class="token punctuation">)</span><span class="token operator">:</span> ToolCall<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>
|
|
572
|
+
<span class="token keyword keyword-for">for</span> <span class="token punctuation">(</span><span class="token keyword keyword-const">const</span> newCall <span class="token keyword keyword-of">of</span> newCalls<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
573
|
+
<span class="token keyword keyword-const">const</span> existingCall <span class="token operator">=</span> existing<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>c <span class="token operator">=></span> c<span class="token punctuation">.</span>id <span class="token operator">===</span> newCall<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
574
|
+
|
|
575
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>existingCall<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
576
|
+
existingCall<span class="token punctuation">.</span>arguments <span class="token operator">+=</span> newCall<span class="token punctuation">.</span>arguments<span class="token punctuation">;</span>
|
|
577
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-else">else</span> <span class="token punctuation">{</span>
|
|
578
|
+
existing<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>newCall <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
579
|
+
<span class="token punctuation">}</span>
|
|
580
|
+
<span class="token punctuation">}</span>
|
|
581
|
+
<span class="token keyword keyword-return">return</span> existing<span class="token punctuation">;</span>
|
|
582
|
+
<span class="token punctuation">}</span>
|
|
583
|
+
<span class="token punctuation">}</span>
|
|
584
|
+
|
|
585
|
+
<span class="token keyword keyword-interface">interface</span> <span class="token class-name">StreamEvent</span> <span class="token punctuation">{</span>
|
|
586
|
+
type<span class="token operator">:</span> <span class="token string">'chunk'</span> <span class="token operator">|</span> <span class="token string">'tool_call'</span> <span class="token operator">|</span> <span class="token string">'tool_result'</span> <span class="token operator">|</span> <span class="token string">'progress'</span> <span class="token operator">|</span> <span class="token string">'checkpoint'</span> <span class="token operator">|</span> <span class="token string">'done'</span> <span class="token operator">|</span> <span class="token string">'error'</span><span class="token punctuation">;</span>
|
|
587
|
+
data<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
|
|
588
|
+
<span class="token punctuation">}</span>
|
|
589
|
+
</code></pre><h2 id="六-实时存储实现">六、实时存储实现 </h2>
|
|
590
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">RealTimeStorage</span> <span class="token punctuation">{</span>
|
|
591
|
+
<span class="token comment">// 保存消息到 Redis (毫秒级延迟)</span>
|
|
592
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">saveMessage</span><span class="token punctuation">(</span>conversationId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> message<span class="token operator">:</span> Message<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
593
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">conversation:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:messages</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
594
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">rpush</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
595
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">expire</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token number">1800</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 30 分钟过期</span>
|
|
596
|
+
<span class="token punctuation">}</span>
|
|
597
|
+
|
|
598
|
+
<span class="token comment">// 保存检查点 (只存位置,不重复存储消息)</span>
|
|
599
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">saveCheckpoint</span><span class="token punctuation">(</span>checkpoint<span class="token operator">:</span> ExecutionCheckpoint<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
600
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>checkpoint<span class="token punctuation">.</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:checkpoint</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
601
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">hset</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
602
|
+
stepIndex<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>stepIndex<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
603
|
+
lastMessageId<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>lastMessageId<span class="token punctuation">,</span>
|
|
604
|
+
lastMessageTime<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>lastMessageTime<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
605
|
+
canResume<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>canResume <span class="token operator">?</span> <span class="token string">'1'</span> <span class="token operator">:</span> <span class="token string">'0'</span>
|
|
606
|
+
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token constant">EX</span><span class="token operator">:</span> <span class="token number">86400</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
607
|
+
<span class="token punctuation">}</span>
|
|
608
|
+
|
|
609
|
+
<span class="token comment">// 获取检查点</span>
|
|
610
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">getLatestCheckpoint</span><span class="token punctuation">(</span>executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>ExecutionCheckpoint <span class="token operator">|</span> <span class="token keyword keyword-null">null</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
611
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:checkpoint</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
612
|
+
<span class="token keyword keyword-const">const</span> data <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">hgetall</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
613
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>data<span class="token punctuation">)</span> <span class="token keyword keyword-return">return</span> <span class="token keyword keyword-null">null</span><span class="token punctuation">;</span>
|
|
614
|
+
|
|
615
|
+
<span class="token keyword keyword-return">return</span> <span class="token punctuation">{</span>
|
|
616
|
+
executionId<span class="token punctuation">,</span>
|
|
617
|
+
stepIndex<span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>stepIndex<span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
618
|
+
lastMessageId<span class="token operator">:</span> data<span class="token punctuation">.</span>lastMessageId<span class="token punctuation">,</span>
|
|
619
|
+
lastMessageTime<span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>lastMessageTime<span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
620
|
+
canResume<span class="token operator">:</span> data<span class="token punctuation">.</span>canResume <span class="token operator">===</span> <span class="token string">'1'</span>
|
|
621
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
622
|
+
<span class="token punctuation">}</span>
|
|
623
|
+
|
|
624
|
+
<span class="token comment">// 获取检查点后的消息 (用于恢复)</span>
|
|
625
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">getMessagesAfterCheckpoint</span><span class="token punctuation">(</span>
|
|
626
|
+
conversationId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span>
|
|
627
|
+
lastMessageId<span class="token operator">:</span> <span class="token builtin">string</span>
|
|
628
|
+
<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>Message<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
629
|
+
<span class="token keyword keyword-const">const</span> allMessages <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">lrange</span><span class="token punctuation">(</span>
|
|
630
|
+
<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">conversation:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:messages</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span>
|
|
631
|
+
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
632
|
+
|
|
633
|
+
<span class="token keyword keyword-const">const</span> messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
634
|
+
<span class="token keyword keyword-let">let</span> found <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
|
|
635
|
+
|
|
636
|
+
<span class="token keyword keyword-for">for</span> <span class="token punctuation">(</span><span class="token keyword keyword-const">const</span> msgStr <span class="token keyword keyword-of">of</span> allMessages<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
637
|
+
<span class="token keyword keyword-const">const</span> msg <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>msgStr<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
638
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>messageId <span class="token operator">===</span> lastMessageId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
639
|
+
found <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
|
|
640
|
+
<span class="token keyword keyword-continue">continue</span><span class="token punctuation">;</span>
|
|
641
|
+
<span class="token punctuation">}</span>
|
|
642
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>found<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
643
|
+
messages<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
644
|
+
<span class="token punctuation">}</span>
|
|
645
|
+
<span class="token punctuation">}</span>
|
|
646
|
+
|
|
647
|
+
<span class="token keyword keyword-return">return</span> messages<span class="token punctuation">;</span>
|
|
648
|
+
<span class="token punctuation">}</span>
|
|
649
|
+
<span class="token punctuation">}</span>
|
|
650
|
+
</code></pre><hr>
|
|
651
|
+
<h2 id="七-后台执行机制">七、后台执行机制 </h2>
|
|
652
|
+
<h3 id="问题">问题 </h3>
|
|
653
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>传统模式 (阻塞):
|
|
654
|
+
用户 ──▶ API ──▶ Agent 执行 ──▶ 等待完成 ──▶ 返回结果
|
|
655
|
+
│
|
|
656
|
+
└─ 页面关闭 = 请求中断 ❌
|
|
657
|
+
|
|
658
|
+
后台模式 (非阻塞):
|
|
659
|
+
用户 ──▶ API ──▶ 创建 Task ──▶ 返回 Task ID ──▶ 立即返回 ✓
|
|
660
|
+
│
|
|
661
|
+
▼
|
|
662
|
+
┌─────────┐
|
|
663
|
+
│ Task │ 后台执行
|
|
664
|
+
│ Queue │◀── 页面关闭不受影响
|
|
665
|
+
└─────────┘
|
|
666
|
+
│
|
|
667
|
+
▼
|
|
668
|
+
存储结果 ── 用户可查询
|
|
669
|
+
</code></pre><h3 id="执行流程">执行流程 </h3>
|
|
670
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>1. 用户发起请求
|
|
671
|
+
POST /api/v1/executions
|
|
672
|
+
{ conversationId, message: "帮我写排序算法" }
|
|
673
|
+
|
|
674
|
+
Response: { executionId: "exec_001", status: "CREATED" } ← 立即返回!
|
|
675
|
+
|
|
676
|
+
2. 创建任务并放入队列
|
|
677
|
+
TaskQueue.push({ executionId, message, createdAt })
|
|
678
|
+
更新状态: CREATED → QUEUED
|
|
679
|
+
|
|
680
|
+
3. Worker 消费任务
|
|
681
|
+
- 更新状态: QUEUED → RUNNING
|
|
682
|
+
- 执行 Agent
|
|
683
|
+
- onMessage: 实时存储 + SSE 推送
|
|
684
|
+
- onCheckpoint: 保存检查点
|
|
685
|
+
|
|
686
|
+
4. 执行完成
|
|
687
|
+
更新状态: RUNNING → COMPLETED
|
|
688
|
+
保存最终结果
|
|
689
|
+
|
|
690
|
+
5. 用户查询结果
|
|
691
|
+
GET /api/v1/executions/exec_001
|
|
692
|
+
{ status: "COMPLETED", messages: [...] }
|
|
693
|
+
</code></pre><h3 id="api-设计">API 设计 </h3>
|
|
694
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token comment">// 创建执行 (立即返回)</span>
|
|
695
|
+
<span class="token constant">POST</span> <span class="token operator">/</span>api<span class="token operator">/</span>v1<span class="token operator">/</span>executions
|
|
696
|
+
Body<span class="token operator">:</span> <span class="token punctuation">{</span> conversationId<span class="token punctuation">,</span> message <span class="token punctuation">}</span>
|
|
697
|
+
Response<span class="token operator">:</span> <span class="token punctuation">{</span> executionId<span class="token punctuation">,</span> status<span class="token operator">:</span> <span class="token string">'CREATED'</span> <span class="token punctuation">}</span>
|
|
698
|
+
|
|
699
|
+
<span class="token comment">// 查询执行状态和结果</span>
|
|
700
|
+
<span class="token constant">GET</span> <span class="token operator">/</span>api<span class="token operator">/</span>v1<span class="token operator">/</span>executions<span class="token operator">/</span><span class="token punctuation">{</span>executionId<span class="token punctuation">}</span>
|
|
701
|
+
Response<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
702
|
+
executionId<span class="token punctuation">,</span> conversationId<span class="token punctuation">,</span>
|
|
703
|
+
status<span class="token operator">:</span> <span class="token string">'CREATED'</span> <span class="token operator">|</span> <span class="token string">'QUEUED'</span> <span class="token operator">|</span> <span class="token string">'RUNNING'</span> <span class="token operator">|</span> <span class="token string">'COMPLETED'</span> <span class="token operator">|</span> <span class="token string">'FAILED'</span><span class="token punctuation">,</span>
|
|
704
|
+
stepIndex<span class="token operator">?</span><span class="token punctuation">,</span> result<span class="token operator">?</span><span class="token punctuation">,</span> messages<span class="token operator">?</span><span class="token punctuation">,</span> error<span class="token operator">?</span>
|
|
705
|
+
<span class="token punctuation">}</span>
|
|
706
|
+
|
|
707
|
+
<span class="token comment">// 实时消息流 (SSE)</span>
|
|
708
|
+
<span class="token constant">GET</span> <span class="token operator">/</span>api<span class="token operator">/</span>v1<span class="token operator">/</span>executions<span class="token operator">/</span><span class="token punctuation">{</span>executionId<span class="token punctuation">}</span><span class="token operator">/</span>stream
|
|
709
|
+
|
|
710
|
+
<span class="token comment">// 恢复执行 (崩溃后)</span>
|
|
711
|
+
<span class="token constant">POST</span> <span class="token operator">/</span>api<span class="token operator">/</span>v1<span class="token operator">/</span>executions<span class="token operator">/</span><span class="token punctuation">{</span>executionId<span class="token punctuation">}</span><span class="token operator">/</span>resume
|
|
712
|
+
</code></pre><h3 id="worker-实现">Worker 实现 </h3>
|
|
713
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token comment">// ============================================================</span>
|
|
714
|
+
<span class="token comment">// 1. 依赖注入配置</span>
|
|
715
|
+
<span class="token comment">// ============================================================</span>
|
|
716
|
+
|
|
717
|
+
<span class="token comment">// 创建依赖容器</span>
|
|
718
|
+
<span class="token keyword keyword-class">class</span> <span class="token class-name">DIContainer</span> <span class="token punctuation">{</span>
|
|
719
|
+
<span class="token operator">:</span> <span class="token keyword keyword-private">private</span> services Map<span class="token operator"><</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">any</span><span class="token operator">></span> <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">Map</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
720
|
+
|
|
721
|
+
<span class="token comment">// 注册服务</span>
|
|
722
|
+
<span class="token generic-function"><span class="token function">register</span><span class="token generic class-name"><span class="token operator"><</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> instance<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword keyword-void">void</span> <span class="token punctuation">{</span>
|
|
723
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>services<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> instance<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
724
|
+
<span class="token punctuation">}</span>
|
|
725
|
+
|
|
726
|
+
<span class="token comment">// 获取服务</span>
|
|
727
|
+
<span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span>
|
|
728
|
+
<span class="token keyword keyword-return">return</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>services<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
729
|
+
<span class="token punctuation">}</span>
|
|
730
|
+
<span class="token punctuation">}</span>
|
|
731
|
+
|
|
732
|
+
<span class="token comment">// 初始化容器</span>
|
|
733
|
+
<span class="token keyword keyword-const">const</span> container <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">DIContainer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
734
|
+
|
|
735
|
+
<span class="token comment">// 注册 Redis 客户端</span>
|
|
736
|
+
<span class="token keyword keyword-const">const</span> redis <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">Redis</span><span class="token punctuation">(</span><span class="token punctuation">{</span> host<span class="token operator">:</span> <span class="token string">'localhost'</span><span class="token punctuation">,</span> port<span class="token operator">:</span> <span class="token number">6379</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
737
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'redis'</span><span class="token punctuation">,</span> redis<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
738
|
+
|
|
739
|
+
<span class="token comment">// 注册消息队列</span>
|
|
740
|
+
<span class="token keyword keyword-const">const</span> taskQueue <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">TaskQueue</span><span class="token punctuation">(</span>redis<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
741
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'taskQueue'</span><span class="token punctuation">,</span> taskQueue<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
742
|
+
|
|
743
|
+
<span class="token comment">// 注册存储服务</span>
|
|
744
|
+
<span class="token keyword keyword-const">const</span> messageStorage <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">MessageStorage</span><span class="token punctuation">(</span>redis<span class="token punctuation">,</span> kafka<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
745
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'messageStorage'</span><span class="token punctuation">,</span> messageStorage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
746
|
+
|
|
747
|
+
<span class="token keyword keyword-const">const</span> checkpointService <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">CheckpointService</span><span class="token punctuation">(</span>redis<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
748
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'checkpointService'</span><span class="token punctuation">,</span> checkpointService<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
749
|
+
|
|
750
|
+
<span class="token keyword keyword-const">const</span> executionService <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">ExecutionService</span><span class="token punctuation">(</span>redis<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
751
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'executionService'</span><span class="token punctuation">,</span> executionService<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
752
|
+
|
|
753
|
+
<span class="token comment">// 注册 Context 服务</span>
|
|
754
|
+
<span class="token keyword keyword-const">const</span> contextService <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">ContextService</span><span class="token punctuation">(</span>redis<span class="token punctuation">,</span> clickhouse<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
755
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'contextService'</span><span class="token punctuation">,</span> contextService<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
756
|
+
|
|
757
|
+
<span class="token comment">// 注册 SSE 发布器</span>
|
|
758
|
+
<span class="token keyword keyword-const">const</span> ssePublisher <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">SSEPublisher</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
759
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'ssePublisher'</span><span class="token punctuation">,</span> ssePublisher<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
760
|
+
|
|
761
|
+
<span class="token comment">// 注册 LLM 提供商</span>
|
|
762
|
+
<span class="token keyword keyword-const">const</span> llmProvider <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">OpenAIProvider</span><span class="token punctuation">(</span><span class="token punctuation">{</span> apiKey<span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">OPENAI_KEY</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
763
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'llmProvider'</span><span class="token punctuation">,</span> llmProvider<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
764
|
+
|
|
765
|
+
<span class="token comment">// 注册工具执行器</span>
|
|
766
|
+
<span class="token keyword keyword-const">const</span> toolExecutor <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">ToolExecutor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
767
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'toolExecutor'</span><span class="token punctuation">,</span> toolExecutor<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
768
|
+
|
|
769
|
+
<span class="token comment">// 创建无状态 Agent</span>
|
|
770
|
+
<span class="token keyword keyword-const">const</span> agent <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">StatelessAgent</span><span class="token punctuation">(</span>llmProvider<span class="token punctuation">,</span> toolExecutor<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
771
|
+
container<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'agent'</span><span class="token punctuation">,</span> agent<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
772
|
+
|
|
773
|
+
<span class="token comment">// ============================================================</span>
|
|
774
|
+
<span class="token comment">// 2. ExecutionWorker 完整实现</span>
|
|
775
|
+
<span class="token comment">// ============================================================</span>
|
|
776
|
+
|
|
777
|
+
<span class="token keyword keyword-class">class</span> <span class="token class-name">ExecutionWorker</span> <span class="token punctuation">{</span>
|
|
778
|
+
<span class="token keyword keyword-private">private</span> queue<span class="token operator">:</span> TaskQueue<span class="token punctuation">;</span>
|
|
779
|
+
<span class="token keyword keyword-private">private</span> agent<span class="token operator">:</span> StatelessAgent<span class="token punctuation">;</span>
|
|
780
|
+
<span class="token keyword keyword-private">private</span> executionService<span class="token operator">:</span> ExecutionService<span class="token punctuation">;</span>
|
|
781
|
+
<span class="token keyword keyword-private">private</span> messageStorage<span class="token operator">:</span> MessageStorage<span class="token punctuation">;</span>
|
|
782
|
+
<span class="token keyword keyword-private">private</span> checkpointService<span class="token operator">:</span> CheckpointService<span class="token punctuation">;</span>
|
|
783
|
+
<span class="token keyword keyword-private">private</span> contextService<span class="token operator">:</span> ContextService<span class="token punctuation">;</span>
|
|
784
|
+
<span class="token keyword keyword-private">private</span> ssePublisher<span class="token operator">:</span> SSEPublisher<span class="token punctuation">;</span>
|
|
785
|
+
|
|
786
|
+
<span class="token comment">// 构造函数注入依赖</span>
|
|
787
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span>
|
|
788
|
+
queue<span class="token operator">:</span> TaskQueue<span class="token punctuation">,</span>
|
|
789
|
+
agent<span class="token operator">:</span> StatelessAgent<span class="token punctuation">,</span>
|
|
790
|
+
executionService<span class="token operator">:</span> ExecutionService<span class="token punctuation">,</span>
|
|
791
|
+
messageStorage<span class="token operator">:</span> MessageStorage<span class="token punctuation">,</span>
|
|
792
|
+
checkpointService<span class="token operator">:</span> CheckpointService<span class="token punctuation">,</span>
|
|
793
|
+
contextService<span class="token operator">:</span> ContextService<span class="token punctuation">,</span>
|
|
794
|
+
ssePublisher<span class="token operator">:</span> SSEPublisher
|
|
795
|
+
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
796
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>queue <span class="token operator">=</span> queue<span class="token punctuation">;</span>
|
|
797
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>agent <span class="token operator">=</span> agent<span class="token punctuation">;</span>
|
|
798
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService <span class="token operator">=</span> executionService<span class="token punctuation">;</span>
|
|
799
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>messageStorage <span class="token operator">=</span> messageStorage<span class="token punctuation">;</span>
|
|
800
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>checkpointService <span class="token operator">=</span> checkpointService<span class="token punctuation">;</span>
|
|
801
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>contextService <span class="token operator">=</span> contextService<span class="token punctuation">;</span>
|
|
802
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>ssePublisher <span class="token operator">=</span> ssePublisher<span class="token punctuation">;</span>
|
|
803
|
+
<span class="token punctuation">}</span>
|
|
804
|
+
|
|
805
|
+
<span class="token comment">// 启动 Worker</span>
|
|
806
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
807
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[Worker] Starting...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
808
|
+
|
|
809
|
+
<span class="token keyword keyword-while">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
810
|
+
<span class="token keyword keyword-try">try</span> <span class="token punctuation">{</span>
|
|
811
|
+
<span class="token comment">// 从队列获取任务 (阻塞等待)</span>
|
|
812
|
+
<span class="token comment">// key: 队列名称, timeout: 超时时间(0=无限等待)</span>
|
|
813
|
+
<span class="token keyword keyword-const">const</span> task <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>queue<span class="token punctuation">.</span><span class="token function">brpop</span><span class="token punctuation">(</span><span class="token string">'task_queue'</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
814
|
+
|
|
815
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>task<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
816
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Received task: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>task<span class="token punctuation">.</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
817
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">processTask</span><span class="token punctuation">(</span>task<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
818
|
+
<span class="token punctuation">}</span>
|
|
819
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-catch">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
820
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'[Worker] Error:'</span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
821
|
+
<span class="token comment">// 短暂等待后继续</span>
|
|
822
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
823
|
+
<span class="token punctuation">}</span>
|
|
824
|
+
<span class="token punctuation">}</span>
|
|
825
|
+
<span class="token punctuation">}</span>
|
|
826
|
+
|
|
827
|
+
<span class="token comment">// 处理单个任务</span>
|
|
828
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">processTask</span><span class="token punctuation">(</span>task<span class="token operator">:</span> Task<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
829
|
+
<span class="token keyword keyword-const">const</span> <span class="token punctuation">{</span> executionId<span class="token punctuation">,</span> conversationId<span class="token punctuation">,</span> message <span class="token punctuation">}</span> <span class="token operator">=</span> task<span class="token punctuation">;</span>
|
|
830
|
+
|
|
831
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Processing task: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
832
|
+
|
|
833
|
+
<span class="token comment">// 1. 尝试获取锁 (防止重复执行)</span>
|
|
834
|
+
<span class="token keyword keyword-const">const</span> lockAcquired <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">acquireLock</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token string">'worker-1'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
835
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>lockAcquired<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
836
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Task </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is being processed by another worker</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
837
|
+
<span class="token keyword keyword-return">return</span><span class="token punctuation">;</span>
|
|
838
|
+
<span class="token punctuation">}</span>
|
|
839
|
+
|
|
840
|
+
<span class="token keyword keyword-try">try</span> <span class="token punctuation">{</span>
|
|
841
|
+
<span class="token comment">// 2. 更新状态为 RUNNING</span>
|
|
842
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token string">'RUNNING'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
843
|
+
|
|
844
|
+
<span class="token comment">// 3. 检查是否有未完成的检查点</span>
|
|
845
|
+
<span class="token keyword keyword-const">const</span> checkpoint <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>checkpointService<span class="token punctuation">.</span><span class="token function">getLatestCheckpoint</span><span class="token punctuation">(</span>executionId<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
846
|
+
|
|
847
|
+
<span class="token keyword keyword-let">let</span> messages<span class="token operator">:</span> Message<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
848
|
+
<span class="token keyword keyword-let">let</span> startStep <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
|
|
849
|
+
|
|
850
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>checkpoint <span class="token operator">&&</span> checkpoint<span class="token punctuation">.</span>canResume<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
851
|
+
<span class="token comment">// 4a. 恢复执行</span>
|
|
852
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Resuming from checkpoint: step </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>checkpoint<span class="token punctuation">.</span>stepIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
853
|
+
|
|
854
|
+
<span class="token comment">// 加载上下文</span>
|
|
855
|
+
<span class="token keyword keyword-const">const</span> context <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>contextService<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>conversationId<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
856
|
+
|
|
857
|
+
<span class="token comment">// 获取检查点后的消息</span>
|
|
858
|
+
<span class="token keyword keyword-const">const</span> newMessages <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>messageStorage<span class="token punctuation">.</span><span class="token function">getMessagesAfterCheckpoint</span><span class="token punctuation">(</span>
|
|
859
|
+
conversationId<span class="token punctuation">,</span>
|
|
860
|
+
checkpoint<span class="token punctuation">.</span>lastMessageId
|
|
861
|
+
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
862
|
+
|
|
863
|
+
messages <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>context<span class="token punctuation">.</span>messages<span class="token punctuation">,</span> <span class="token operator">...</span>newMessages<span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
864
|
+
startStep <span class="token operator">=</span> checkpoint<span class="token punctuation">.</span>stepIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
|
|
865
|
+
|
|
866
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-else">else</span> <span class="token punctuation">{</span>
|
|
867
|
+
<span class="token comment">// 4b. 全新执行</span>
|
|
868
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Starting new execution</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
869
|
+
|
|
870
|
+
<span class="token comment">// 加载上下文</span>
|
|
871
|
+
<span class="token keyword keyword-const">const</span> context <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>contextService<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>conversationId<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
872
|
+
|
|
873
|
+
<span class="token comment">// 添加用户消息</span>
|
|
874
|
+
<span class="token keyword keyword-const">const</span> userMessage<span class="token operator">:</span> Message <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
875
|
+
messageId<span class="token operator">:</span> <span class="token function">generateId</span><span class="token punctuation">(</span><span class="token string">'msg_'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
876
|
+
role<span class="token operator">:</span> <span class="token string">'user'</span><span class="token punctuation">,</span>
|
|
877
|
+
content<span class="token operator">:</span> message<span class="token punctuation">.</span>content<span class="token punctuation">,</span>
|
|
878
|
+
timestamp<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
|
|
879
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
880
|
+
|
|
881
|
+
messages <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>context<span class="token punctuation">.</span>messages<span class="token punctuation">,</span> userMessage<span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
882
|
+
|
|
883
|
+
<span class="token comment">// 实时存储用户消息</span>
|
|
884
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>messageStorage<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>conversationId<span class="token punctuation">,</span> userMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
885
|
+
<span class="token punctuation">}</span>
|
|
886
|
+
|
|
887
|
+
<span class="token comment">// 5. 获取系统提示和工具配置</span>
|
|
888
|
+
<span class="token keyword keyword-const">const</span> context <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>contextService<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>conversationId<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
889
|
+
|
|
890
|
+
<span class="token comment">// 6. 构造 Agent 输入</span>
|
|
891
|
+
<span class="token keyword keyword-const">const</span> input<span class="token operator">:</span> AgentInput <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
892
|
+
executionId<span class="token punctuation">,</span>
|
|
893
|
+
conversationId<span class="token punctuation">,</span>
|
|
894
|
+
messages<span class="token punctuation">,</span>
|
|
895
|
+
systemPrompt<span class="token operator">:</span> context<span class="token punctuation">.</span>systemPrompt<span class="token punctuation">,</span>
|
|
896
|
+
tools<span class="token operator">:</span> context<span class="token punctuation">.</span>tools<span class="token punctuation">,</span>
|
|
897
|
+
startStep<span class="token punctuation">,</span> <span class="token comment">// 从指定步骤开始</span>
|
|
898
|
+
callbacks<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
899
|
+
<span class="token comment">// 实时消息回调</span>
|
|
900
|
+
<span class="token function-variable function">onMessage</span><span class="token operator">:</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>msg<span class="token operator">:</span> Message<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
901
|
+
<span class="token comment">// 存储消息到 Redis + Kafka</span>
|
|
902
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>messageStorage<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>conversationId<span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
903
|
+
|
|
904
|
+
<span class="token comment">// SSE 推送给在线用户</span>
|
|
905
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>ssePublisher<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
906
|
+
type<span class="token operator">:</span> <span class="token string">'message'</span><span class="token punctuation">,</span>
|
|
907
|
+
data<span class="token operator">:</span> msg
|
|
908
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
909
|
+
|
|
910
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Message saved: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>msg<span class="token punctuation">.</span>messageId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
911
|
+
<span class="token punctuation">}</span><span class="token punctuation">,</span>
|
|
912
|
+
|
|
913
|
+
<span class="token comment">// 检查点回调</span>
|
|
914
|
+
<span class="token function-variable function">onCheckpoint</span><span class="token operator">:</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>checkpoint<span class="token operator">:</span> ExecutionCheckpoint<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
915
|
+
<span class="token comment">// 保存检查点</span>
|
|
916
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>checkpointService<span class="token punctuation">.</span><span class="token function">saveCheckpoint</span><span class="token punctuation">(</span>checkpoint<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
917
|
+
|
|
918
|
+
<span class="token comment">// 更新执行进度</span>
|
|
919
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateProgress</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
920
|
+
stepIndex<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>stepIndex<span class="token punctuation">,</span>
|
|
921
|
+
lastMessageId<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>lastMessageId
|
|
922
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
923
|
+
|
|
924
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Checkpoint saved: step </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>checkpoint<span class="token punctuation">.</span>stepIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
925
|
+
<span class="token punctuation">}</span><span class="token punctuation">,</span>
|
|
926
|
+
|
|
927
|
+
<span class="token comment">// 进度回调</span>
|
|
928
|
+
<span class="token function-variable function">onProgress</span><span class="token operator">:</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>progress<span class="token operator">:</span> ExecutionProgress<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
929
|
+
<span class="token comment">// 更新进度</span>
|
|
930
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateProgress</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
931
|
+
currentAction<span class="token operator">:</span> progress<span class="token punctuation">.</span>currentAction<span class="token punctuation">,</span>
|
|
932
|
+
stepIndex<span class="token operator">:</span> progress<span class="token punctuation">.</span>stepIndex<span class="token punctuation">,</span>
|
|
933
|
+
messageCount<span class="token operator">:</span> progress<span class="token punctuation">.</span>messageCount
|
|
934
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
935
|
+
|
|
936
|
+
<span class="token comment">// SSE 推送进度</span>
|
|
937
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>ssePublisher<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
938
|
+
type<span class="token operator">:</span> <span class="token string">'progress'</span><span class="token punctuation">,</span>
|
|
939
|
+
data<span class="token operator">:</span> progress
|
|
940
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
941
|
+
<span class="token punctuation">}</span><span class="token punctuation">,</span>
|
|
942
|
+
|
|
943
|
+
<span class="token comment">// 错误回调</span>
|
|
944
|
+
<span class="token function-variable function">onError</span><span class="token operator">:</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>error<span class="token operator">:</span> Error<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
945
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Error:</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
946
|
+
|
|
947
|
+
<span class="token comment">// 保存错误检查点</span>
|
|
948
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>checkpointService<span class="token punctuation">.</span><span class="token function">saveCheckpoint</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
|
|
949
|
+
executionId<span class="token punctuation">,</span>
|
|
950
|
+
stepIndex<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
|
|
951
|
+
lastMessageId<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
|
|
952
|
+
lastMessageTime<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
953
|
+
canResume<span class="token operator">:</span> <span class="token boolean">false</span>
|
|
954
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
955
|
+
|
|
956
|
+
<span class="token comment">// 更新状态为失败</span>
|
|
957
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token string">'FAILED'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
958
|
+
error<span class="token operator">:</span> error<span class="token punctuation">.</span>message
|
|
959
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
960
|
+
|
|
961
|
+
<span class="token comment">// SSE 推送错误</span>
|
|
962
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>ssePublisher<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
963
|
+
type<span class="token operator">:</span> <span class="token string">'error'</span><span class="token punctuation">,</span>
|
|
964
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span> message<span class="token operator">:</span> error<span class="token punctuation">.</span>message <span class="token punctuation">}</span>
|
|
965
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
966
|
+
<span class="token punctuation">}</span>
|
|
967
|
+
<span class="token punctuation">}</span>
|
|
968
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
969
|
+
|
|
970
|
+
<span class="token comment">// 7. 执行 Agent</span>
|
|
971
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Starting agent execution from step </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>startStep<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
972
|
+
<span class="token keyword keyword-const">const</span> result <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>agent<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
973
|
+
|
|
974
|
+
<span class="token comment">// 8. 执行完成</span>
|
|
975
|
+
<span class="token keyword keyword-const">const</span> finalMessage <span class="token operator">=</span> result<span class="token punctuation">.</span>messages<span class="token punctuation">[</span>result<span class="token punctuation">.</span>messages<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
976
|
+
|
|
977
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token string">'COMPLETED'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
978
|
+
result<span class="token operator">:</span> finalMessage<span class="token operator">?.</span>content<span class="token punctuation">,</span>
|
|
979
|
+
steps<span class="token operator">:</span> result<span class="token punctuation">.</span>steps<span class="token punctuation">,</span>
|
|
980
|
+
finishReason<span class="token operator">:</span> result<span class="token punctuation">.</span>finishReason
|
|
981
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
982
|
+
|
|
983
|
+
<span class="token comment">// SSE 推送完成</span>
|
|
984
|
+
<span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>ssePublisher<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
985
|
+
type<span class="token operator">:</span> <span class="token string">'done'</span><span class="token punctuation">,</span>
|
|
986
|
+
data<span class="token operator">:</span> <span class="token punctuation">{</span>
|
|
987
|
+
result<span class="token operator">:</span> finalMessage<span class="token operator">?.</span>content<span class="token punctuation">,</span>
|
|
988
|
+
steps<span class="token operator">:</span> result<span class="token punctuation">.</span>steps
|
|
989
|
+
<span class="token punctuation">}</span>
|
|
990
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
991
|
+
|
|
992
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Task completed: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
993
|
+
|
|
994
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-catch">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
995
|
+
<span class="token comment">// 执行失败</span>
|
|
996
|
+
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[Worker] Task failed: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
997
|
+
|
|
998
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span>executionId<span class="token punctuation">,</span> <span class="token string">'FAILED'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
999
|
+
error<span class="token operator">:</span> <span class="token punctuation">(</span>error <span class="token keyword keyword-as">as</span> Error<span class="token punctuation">)</span><span class="token punctuation">.</span>message
|
|
1000
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1001
|
+
|
|
1002
|
+
<span class="token punctuation">}</span> <span class="token keyword keyword-finally">finally</span> <span class="token punctuation">{</span>
|
|
1003
|
+
<span class="token comment">// 释放锁</span>
|
|
1004
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>executionService<span class="token punctuation">.</span><span class="token function">releaseLock</span><span class="token punctuation">(</span>executionId<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1005
|
+
<span class="token punctuation">}</span>
|
|
1006
|
+
<span class="token punctuation">}</span>
|
|
1007
|
+
|
|
1008
|
+
<span class="token comment">// 辅助方法: 睡眠</span>
|
|
1009
|
+
<span class="token keyword keyword-private">private</span> <span class="token function">sleep</span><span class="token punctuation">(</span>ms<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1010
|
+
<span class="token keyword keyword-return">return</span> <span class="token keyword keyword-new">new</span> <span class="token class-name"><span class="token builtin">Promise</span></span><span class="token punctuation">(</span>resolve <span class="token operator">=></span> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> ms<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1011
|
+
<span class="token punctuation">}</span>
|
|
1012
|
+
<span class="token punctuation">}</span>
|
|
1013
|
+
|
|
1014
|
+
<span class="token comment">// ============================================================</span>
|
|
1015
|
+
<span class="token comment">// 3. 启动 Worker</span>
|
|
1016
|
+
<span class="token comment">// ============================================================</span>
|
|
1017
|
+
|
|
1018
|
+
<span class="token keyword keyword-async">async</span> <span class="token keyword keyword-function">function</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
1019
|
+
<span class="token comment">// 从容器获取依赖</span>
|
|
1020
|
+
<span class="token keyword keyword-const">const</span> queue <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>TaskQueue<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'taskQueue'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1021
|
+
<span class="token keyword keyword-const">const</span> agent <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>StatelessAgent<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'agent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1022
|
+
<span class="token keyword keyword-const">const</span> executionService <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>ExecutionService<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'executionService'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1023
|
+
<span class="token keyword keyword-const">const</span> messageStorage <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>MessageStorage<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'messageStorage'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1024
|
+
<span class="token keyword keyword-const">const</span> checkpointService <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>CheckpointService<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'checkpointService'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1025
|
+
<span class="token keyword keyword-const">const</span> contextService <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>ContextService<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'contextService'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1026
|
+
<span class="token keyword keyword-const">const</span> ssePublisher <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token generic-function"><span class="token function">get</span><span class="token generic class-name"><span class="token operator"><</span>SSEPublisher<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'ssePublisher'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1027
|
+
|
|
1028
|
+
<span class="token comment">// 创建 Worker 实例</span>
|
|
1029
|
+
<span class="token keyword keyword-const">const</span> worker <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token class-name">ExecutionWorker</span><span class="token punctuation">(</span>
|
|
1030
|
+
queue<span class="token punctuation">,</span>
|
|
1031
|
+
agent<span class="token punctuation">,</span>
|
|
1032
|
+
executionService<span class="token punctuation">,</span>
|
|
1033
|
+
messageStorage<span class="token punctuation">,</span>
|
|
1034
|
+
checkpointService<span class="token punctuation">,</span>
|
|
1035
|
+
contextService<span class="token punctuation">,</span>
|
|
1036
|
+
ssePublisher
|
|
1037
|
+
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1038
|
+
|
|
1039
|
+
<span class="token comment">// 启动</span>
|
|
1040
|
+
<span class="token keyword keyword-await">await</span> worker<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1041
|
+
<span class="token punctuation">}</span>
|
|
1042
|
+
|
|
1043
|
+
<span class="token comment">// 运行</span>
|
|
1044
|
+
<span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token builtin">console</span><span class="token punctuation">.</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1045
|
+
</code></pre><hr>
|
|
1046
|
+
<h3 id="依赖服务详细说明">依赖服务详细说明 </h3>
|
|
1047
|
+
<h4 id="1-taskqueue-任务队列">1. TaskQueue (任务队列) </h4>
|
|
1048
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">TaskQueue</span> <span class="token punctuation">{</span>
|
|
1049
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword keyword-private">private</span> redis<span class="token operator">:</span> Redis<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
|
|
1050
|
+
|
|
1051
|
+
<span class="token comment">// 放入任务 (LPUSH)</span>
|
|
1052
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">push</span><span class="token punctuation">(</span>task<span class="token operator">:</span> Task<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1053
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">lpush</span><span class="token punctuation">(</span><span class="token string">'task_queue'</span><span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>task<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1054
|
+
<span class="token punctuation">}</span>
|
|
1055
|
+
|
|
1056
|
+
<span class="token comment">// 阻塞取出任务 (BRPOP)</span>
|
|
1057
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">brpop</span><span class="token punctuation">(</span>timeout<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>Task <span class="token operator">|</span> <span class="token keyword keyword-null">null</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1058
|
+
<span class="token keyword keyword-const">const</span> result <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">brpop</span><span class="token punctuation">(</span><span class="token string">'task_queue'</span><span class="token punctuation">,</span> timeout<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1059
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>result<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
1060
|
+
<span class="token keyword keyword-return">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>result<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1061
|
+
<span class="token punctuation">}</span>
|
|
1062
|
+
<span class="token keyword keyword-return">return</span> <span class="token keyword keyword-null">null</span><span class="token punctuation">;</span>
|
|
1063
|
+
<span class="token punctuation">}</span>
|
|
1064
|
+
<span class="token punctuation">}</span>
|
|
1065
|
+
</code></pre><h4 id="2-executionservice-执行状态管理">2. ExecutionService (执行状态管理) </h4>
|
|
1066
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">ExecutionService</span> <span class="token punctuation">{</span>
|
|
1067
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword keyword-private">private</span> redis<span class="token operator">:</span> Redis<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
|
|
1068
|
+
|
|
1069
|
+
<span class="token comment">// 更新状态</span>
|
|
1070
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span>executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> status<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> extra<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1071
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1072
|
+
<span class="token keyword keyword-const">const</span> update<span class="token operator">:</span> <span class="token builtin">any</span> <span class="token operator">=</span> <span class="token punctuation">{</span> status<span class="token punctuation">,</span> updatedAt<span class="token operator">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">...</span>extra <span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
1073
|
+
|
|
1074
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>status <span class="token operator">===</span> <span class="token string">'COMPLETED'</span> <span class="token operator">||</span> status <span class="token operator">===</span> <span class="token string">'FAILED'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
1075
|
+
update<span class="token punctuation">.</span>completedAt <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1076
|
+
<span class="token punctuation">}</span>
|
|
1077
|
+
|
|
1078
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">hset</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> update<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1079
|
+
<span class="token punctuation">}</span>
|
|
1080
|
+
|
|
1081
|
+
<span class="token comment">// 获取锁 (防止重复执行)</span>
|
|
1082
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">acquireLock</span><span class="token punctuation">(</span>executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> workerId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token builtin">boolean</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1083
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">lock:execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1084
|
+
<span class="token keyword keyword-const">const</span> result <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> workerId<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
1085
|
+
<span class="token constant">NX</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 只有不存在时设置</span>
|
|
1086
|
+
<span class="token constant">EX</span><span class="token operator">:</span> <span class="token number">300</span> <span class="token comment">// 5分钟超时</span>
|
|
1087
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1088
|
+
<span class="token keyword keyword-return">return</span> result <span class="token operator">===</span> <span class="token string">'OK'</span><span class="token punctuation">;</span>
|
|
1089
|
+
<span class="token punctuation">}</span>
|
|
1090
|
+
|
|
1091
|
+
<span class="token comment">// 释放锁</span>
|
|
1092
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">releaseLock</span><span class="token punctuation">(</span>executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1093
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">del</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">lock:execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1094
|
+
<span class="token punctuation">}</span>
|
|
1095
|
+
<span class="token punctuation">}</span>
|
|
1096
|
+
</code></pre><h4 id="3-messagestorage-消息存储">3. MessageStorage (消息存储) </h4>
|
|
1097
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">MessageStorage</span> <span class="token punctuation">{</span>
|
|
1098
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span>
|
|
1099
|
+
<span class="token keyword keyword-private">private</span> redis<span class="token operator">:</span> Redis<span class="token punctuation">,</span>
|
|
1100
|
+
<span class="token keyword keyword-private">private</span> kafka<span class="token operator">:</span> Kafka
|
|
1101
|
+
<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
|
|
1102
|
+
|
|
1103
|
+
<span class="token comment">// 保存消息</span>
|
|
1104
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">save</span><span class="token punctuation">(</span>conversationId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> message<span class="token operator">:</span> Message<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1105
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">conversation:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:messages</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1106
|
+
|
|
1107
|
+
<span class="token comment">// 1. 实时写入 Redis</span>
|
|
1108
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">rpush</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1109
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">expire</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token number">1800</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 30分钟过期</span>
|
|
1110
|
+
|
|
1111
|
+
<span class="token comment">// 2. 发送到 Kafka (异步)</span>
|
|
1112
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>kafka<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
|
|
1113
|
+
topic<span class="token operator">:</span> <span class="token string">'messages'</span><span class="token punctuation">,</span>
|
|
1114
|
+
messages<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
|
|
1115
|
+
key<span class="token operator">:</span> conversationId<span class="token punctuation">,</span>
|
|
1116
|
+
value<span class="token operator">:</span> <span class="token punctuation">{</span> event<span class="token operator">:</span> <span class="token string">'message_created'</span><span class="token punctuation">,</span> conversationId<span class="token punctuation">,</span> message <span class="token punctuation">}</span>
|
|
1117
|
+
<span class="token punctuation">}</span><span class="token punctuation">]</span>
|
|
1118
|
+
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1119
|
+
<span class="token punctuation">}</span>
|
|
1120
|
+
<span class="token punctuation">}</span>
|
|
1121
|
+
</code></pre><h4 id="4-checkpointservice-检查点服务">4. CheckpointService (检查点服务) </h4>
|
|
1122
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">CheckpointService</span> <span class="token punctuation">{</span>
|
|
1123
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword keyword-private">private</span> redis<span class="token operator">:</span> Redis<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
|
|
1124
|
+
|
|
1125
|
+
<span class="token comment">// 保存检查点</span>
|
|
1126
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">saveCheckpoint</span><span class="token punctuation">(</span>checkpoint<span class="token operator">:</span> ExecutionCheckpoint<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span><span class="token keyword keyword-void">void</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1127
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>checkpoint<span class="token punctuation">.</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:checkpoint</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1128
|
+
|
|
1129
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">hset</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token punctuation">{</span>
|
|
1130
|
+
stepIndex<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>stepIndex<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
1131
|
+
lastMessageId<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>lastMessageId<span class="token punctuation">,</span>
|
|
1132
|
+
lastMessageTime<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>lastMessageTime<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
1133
|
+
canResume<span class="token operator">:</span> checkpoint<span class="token punctuation">.</span>canResume <span class="token operator">?</span> <span class="token string">'1'</span> <span class="token operator">:</span> <span class="token string">'0'</span>
|
|
1134
|
+
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token constant">EX</span><span class="token operator">:</span> <span class="token number">86400</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1135
|
+
<span class="token punctuation">}</span>
|
|
1136
|
+
|
|
1137
|
+
<span class="token comment">// 获取检查点</span>
|
|
1138
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">getLatestCheckpoint</span><span class="token punctuation">(</span>executionId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>ExecutionCheckpoint <span class="token operator">|</span> <span class="token keyword keyword-null">null</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1139
|
+
<span class="token keyword keyword-const">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">execution:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>executionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:checkpoint</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1140
|
+
<span class="token keyword keyword-const">const</span> data <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">hgetall</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1141
|
+
|
|
1142
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>data <span class="token operator">||</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
1143
|
+
<span class="token keyword keyword-return">return</span> <span class="token keyword keyword-null">null</span><span class="token punctuation">;</span>
|
|
1144
|
+
<span class="token punctuation">}</span>
|
|
1145
|
+
|
|
1146
|
+
<span class="token keyword keyword-return">return</span> <span class="token punctuation">{</span>
|
|
1147
|
+
executionId<span class="token punctuation">,</span>
|
|
1148
|
+
stepIndex<span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>stepIndex<span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
1149
|
+
lastMessageId<span class="token operator">:</span> data<span class="token punctuation">.</span>lastMessageId<span class="token punctuation">,</span>
|
|
1150
|
+
lastMessageTime<span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>lastMessageTime<span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
1151
|
+
canResume<span class="token operator">:</span> data<span class="token punctuation">.</span>canResume <span class="token operator">===</span> <span class="token string">'1'</span>
|
|
1152
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
1153
|
+
<span class="token punctuation">}</span>
|
|
1154
|
+
<span class="token punctuation">}</span>
|
|
1155
|
+
</code></pre><h4 id="5-contextservice-上下文服务">5. ContextService (上下文服务) </h4>
|
|
1156
|
+
<pre data-role="codeBlock" data-info="typescript" class="language-typescript typescript"><code><span class="token keyword keyword-class">class</span> <span class="token class-name">ContextService</span> <span class="token punctuation">{</span>
|
|
1157
|
+
<span class="token function">constructor</span><span class="token punctuation">(</span>
|
|
1158
|
+
<span class="token keyword keyword-private">private</span> redis<span class="token operator">:</span> Redis<span class="token punctuation">,</span>
|
|
1159
|
+
<span class="token keyword keyword-private">private</span> clickhouse<span class="token operator">:</span> ClickHouse
|
|
1160
|
+
<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
|
|
1161
|
+
|
|
1162
|
+
<span class="token comment">// 加载上下文</span>
|
|
1163
|
+
<span class="token keyword keyword-async">async</span> <span class="token function">load</span><span class="token punctuation">(</span>conversationId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>ConversationContext<span class="token operator">></span> <span class="token punctuation">{</span>
|
|
1164
|
+
<span class="token keyword keyword-const">const</span> cacheKey <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">conversation:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:context</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
|
|
1165
|
+
|
|
1166
|
+
<span class="token comment">// 1. 先从 Redis 缓存获取</span>
|
|
1167
|
+
<span class="token keyword keyword-const">const</span> cached <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1168
|
+
<span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>cached<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
1169
|
+
<span class="token keyword keyword-return">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>cached<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1170
|
+
<span class="token punctuation">}</span>
|
|
1171
|
+
|
|
1172
|
+
<span class="token comment">// 2. 缓存不存在,从 ClickHouse 加载</span>
|
|
1173
|
+
<span class="token keyword keyword-const">const</span> messages <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>clickhouse<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
|
|
1174
|
+
SELECT * FROM messages
|
|
1175
|
+
WHERE conversation_id = '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">'
|
|
1176
|
+
ORDER BY timestamp ASC
|
|
1177
|
+
LIMIT 1000
|
|
1178
|
+
</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1179
|
+
|
|
1180
|
+
<span class="token comment">// 3. 加载系统提示和工具配置</span>
|
|
1181
|
+
<span class="token keyword keyword-const">const</span> conversation <span class="token operator">=</span> <span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>clickhouse<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
|
|
1182
|
+
SELECT * FROM conversations
|
|
1183
|
+
WHERE id = '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>conversationId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">'
|
|
1184
|
+
</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1185
|
+
|
|
1186
|
+
<span class="token keyword keyword-const">const</span> context<span class="token operator">:</span> ConversationContext <span class="token operator">=</span> <span class="token punctuation">{</span>
|
|
1187
|
+
messages<span class="token punctuation">,</span>
|
|
1188
|
+
systemPrompt<span class="token operator">:</span> conversation<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">?.</span>system_prompt<span class="token punctuation">,</span>
|
|
1189
|
+
tools<span class="token operator">:</span> conversation<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">?.</span>tools
|
|
1190
|
+
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
1191
|
+
|
|
1192
|
+
<span class="token comment">// 4. 写入缓存</span>
|
|
1193
|
+
<span class="token keyword keyword-await">await</span> <span class="token keyword keyword-this">this</span><span class="token punctuation">.</span>redis<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token constant">EX</span><span class="token operator">:</span> <span class="token number">300</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
1194
|
+
|
|
1195
|
+
<span class="token keyword keyword-return">return</span> context<span class="token punctuation">;</span>
|
|
1196
|
+
<span class="token punctuation">}</span>
|
|
1197
|
+
<span class="token punctuation">}</span>
|
|
1198
|
+
</code></pre><hr>
|
|
1199
|
+
<h2 id="八-故障恢复机制">八、故障恢复机制 </h2>
|
|
1200
|
+
<h3 id="场景-1-agent-进程崩溃">场景 1: Agent 进程崩溃 </h3>
|
|
1201
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>发生时间: Step 3 ~ Step 4 之间
|
|
1202
|
+
|
|
1203
|
+
已存储:
|
|
1204
|
+
✅ Step 1 消息 (Redis + Kafka)
|
|
1205
|
+
✅ Step 2 消息 (Redis + Kafka)
|
|
1206
|
+
✅ Step 3 消息 (Redis + Kafka)
|
|
1207
|
+
✅ Step 4 消息 (部分)
|
|
1208
|
+
✅ 检查点: stepIndex=3, lastMessageId=msg_3
|
|
1209
|
+
|
|
1210
|
+
丢失: Step 4 未完成的工具调用结果
|
|
1211
|
+
|
|
1212
|
+
恢复:
|
|
1213
|
+
1. 从检查点获取 lastMessageId=msg_3
|
|
1214
|
+
2. 查询 msg_3 之后的消息
|
|
1215
|
+
3. 从 Step 4 继续执行
|
|
1216
|
+
</code></pre><h3 id="场景-2-redis-崩溃">场景 2: Redis 崩溃 </h3>
|
|
1217
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>已存储:
|
|
1218
|
+
✅ Kafka 保留所有消息 (7 天)
|
|
1219
|
+
|
|
1220
|
+
恢复:
|
|
1221
|
+
1. 从 Kafka 重放消息
|
|
1222
|
+
2. 重建 Redis 缓存
|
|
1223
|
+
</code></pre><h3 id="场景-3-整个节点崩溃">场景 3: 整个节点崩溃 </h3>
|
|
1224
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>恢复:
|
|
1225
|
+
1. Task Manager 检测到超时
|
|
1226
|
+
2. 从检查点恢复
|
|
1227
|
+
3. 调度到新节点继续执行
|
|
1228
|
+
</code></pre><hr>
|
|
1229
|
+
<h2 id="九-完整数据流">九、完整数据流 </h2>
|
|
1230
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>┌─────────────────────────────────────────────────────────────────┐
|
|
1231
|
+
│ 完整数据流 │
|
|
1232
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
1233
|
+
│ │
|
|
1234
|
+
│ 用户请求 │
|
|
1235
|
+
│ │ │
|
|
1236
|
+
│ ▼ │
|
|
1237
|
+
│ API Gateway (鉴权/限流) │
|
|
1238
|
+
│ │ │
|
|
1239
|
+
│ ▼ │
|
|
1240
|
+
│ Controller (创建 Task) │
|
|
1241
|
+
│ │ │
|
|
1242
|
+
│ │ 立即返回 executionId │
|
|
1243
|
+
│ ▼ │
|
|
1244
|
+
│ Task Queue (Redis) │
|
|
1245
|
+
│ │ │
|
|
1246
|
+
│ ▼ │
|
|
1247
|
+
│ Worker (执行 Agent) ────────────────────────────────────── │
|
|
1248
|
+
│ │ │
|
|
1249
|
+
│ ├─ onMessage ──▶ Redis + Kafka + SSE │
|
|
1250
|
+
│ │ │
|
|
1251
|
+
│ ├─ onCheckpoint ──▶ Redis │
|
|
1252
|
+
│ │ │
|
|
1253
|
+
│ ▼ │
|
|
1254
|
+
│ 执行完成 │
|
|
1255
|
+
│ │ │
|
|
1256
|
+
│ ▼ │
|
|
1257
|
+
│ 用户查询结果 (REST API / SSE) │
|
|
1258
|
+
│ │
|
|
1259
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
1260
|
+
</code></pre><hr>
|
|
1261
|
+
<h2 id="十-技术选型">十、技术选型 </h2>
|
|
1262
|
+
<table>
|
|
1263
|
+
<thead>
|
|
1264
|
+
<tr>
|
|
1265
|
+
<th>层级</th>
|
|
1266
|
+
<th>组件</th>
|
|
1267
|
+
<th>用途</th>
|
|
1268
|
+
</tr>
|
|
1269
|
+
</thead>
|
|
1270
|
+
<tbody>
|
|
1271
|
+
<tr>
|
|
1272
|
+
<td>API</td>
|
|
1273
|
+
<td>Express/Spring Boot</td>
|
|
1274
|
+
<td>REST API + SSE</td>
|
|
1275
|
+
</tr>
|
|
1276
|
+
<tr>
|
|
1277
|
+
<td>任务队列</td>
|
|
1278
|
+
<td>Redis List</td>
|
|
1279
|
+
<td>任务缓冲</td>
|
|
1280
|
+
</tr>
|
|
1281
|
+
<tr>
|
|
1282
|
+
<td>缓存</td>
|
|
1283
|
+
<td>Redis</td>
|
|
1284
|
+
<td>消息 + 检查点</td>
|
|
1285
|
+
</tr>
|
|
1286
|
+
<tr>
|
|
1287
|
+
<td>消息队列</td>
|
|
1288
|
+
<td>Kafka</td>
|
|
1289
|
+
<td>异步持久化</td>
|
|
1290
|
+
</tr>
|
|
1291
|
+
<tr>
|
|
1292
|
+
<td>主存储</td>
|
|
1293
|
+
<td>ClickHouse</td>
|
|
1294
|
+
<td>消息历史</td>
|
|
1295
|
+
</tr>
|
|
1296
|
+
<tr>
|
|
1297
|
+
<td>搜索</td>
|
|
1298
|
+
<td>Elasticsearch</td>
|
|
1299
|
+
<td>全文搜索</td>
|
|
1300
|
+
</tr>
|
|
1301
|
+
</tbody>
|
|
1302
|
+
</table>
|
|
1303
|
+
<hr>
|
|
1304
|
+
<h2 id="十一-总结">十一、总结 </h2>
|
|
1305
|
+
<pre data-role="codeBlock" data-info="" class="language-text"><code>┌─────────────────────────────────────────────────────────────────┐
|
|
1306
|
+
│ 方案总结 │
|
|
1307
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
1308
|
+
│ │
|
|
1309
|
+
│ 1. 无状态 Agent │
|
|
1310
|
+
│ - 输入: sessionId + messages + callbacks │
|
|
1311
|
+
│ - 输出: new_messages + finishReason │
|
|
1312
|
+
│ - 内部通过回调实时存储 │
|
|
1313
|
+
│ │
|
|
1314
|
+
│ 2. 实时存储 │
|
|
1315
|
+
│ - onMessage: 每条消息实时存储 (Redis + Kafka) │
|
|
1316
|
+
│ - onCheckpoint: 记录位置 (stepIndex + lastMessageId) │
|
|
1317
|
+
│ │
|
|
1318
|
+
│ 3. 后台执行 │
|
|
1319
|
+
│ - API 立即返回 executionId │
|
|
1320
|
+
│ - Task Queue 缓冲任务 │
|
|
1321
|
+
│ - Worker 后台消费 │
|
|
1322
|
+
│ │
|
|
1323
|
+
│ 4. 用户交互 │
|
|
1324
|
+
│ - SSE 实时推送 (在线用户) │
|
|
1325
|
+
│ - REST API 查询 (离线用户) │
|
|
1326
|
+
│ - 支持页面关闭后继续执行 │
|
|
1327
|
+
│ │
|
|
1328
|
+
│ 5. 故障恢复 │
|
|
1329
|
+
│ - 检查点恢复: 从 lastMessageId 继续 │
|
|
1330
|
+
│ - Worker 挂了: Task Queue 重试 │
|
|
1331
|
+
│ - Redis 挂了: Kafka 重放 │
|
|
1332
|
+
│ │
|
|
1333
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
1334
|
+
</code></pre>
|
|
1335
|
+
</div>
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
</body></html>
|