第四章 06:4.6 统一入口 ------ analyze() 能力收束
课程专题 :《AI Agents 开发实践》
原文链接 :
https://s.juejin.cn/ds/TAqu_hbSmd8/章节 :第四章 4.6 统一入口
作者:Java 开发者转型 AI 全栈
4.6.1 问题:能力都在,但散着
到 4.5 为止,我们有了四个能力服务:Memory(记忆)、Filesystem(文件工具)、VectorStore(语义检索)、Orchestrator(多 Agent 编排)。但它们各自为战:
- 调
orchestrate拿到报告,不会落盘 - 报告不写回记忆,下次对话模型不知道上次分析过什么
- 前端要调 3 个接口才能拼出完整体验
需要一个应用层门面,把一次「需求分析」的完整闭环收进一个方法。
4.6.2 analyze() 的四步职责
typescript
import { Injectable } from '@nestjs/common';
import { OrchestratorService } from './agents/orchestrator.service';
import { FilesystemService } from './filesystem/filesystem.service';
import { RunnableMemoryService } from './memory/runnable-memory.service';
export type AnalyzeResult =
| { needsClarification: true; questions: string[] }
| { needsClarification: false; report: string; reportPath: string; usedAgents: string[] };
@Injectable()
export class AdvancedAnalysisService {
constructor(
private readonly orchestrator: OrchestratorService,
private readonly filesystem: FilesystemService,
private readonly memory: RunnableMemoryService
) {}
async analyze(sessionId: string, input: string): Promise<AnalyzeResult> {
// ① 编排:跑多 Agent 流水线(内含澄清短路)
const result = await this.orchestrator.orchestrate(input);
// ② 澄清返回:不落盘、不写记忆,把问题抛回给用户
if (result.status === 'need_clarification') {
return { needsClarification: true, questions: result.clarificationQuestions };
}
// ③ 报告落盘:reports/<sessionId>-<时间戳>.md
const report = result.report ?? '分析未生成报告';
const reportPath = this.filesystem.writeReport(
`reports/${sessionId}-${Date.now()}.md`,
report
);
// ④ 写回记忆:appendMessage 直接写历史,不重新调用模型
await this.memory.appendMessage(sessionId, input, report);
return { needsClarification: false, report, reportPath, usedAgents: result.usedAgents };
}
}
最值得品味的是第 ④ 步 :报告已经生成,写回记忆时用 appendMessage 直接写历史,而不是再调一次模型复述。省一次调用、省一份 token,且保证「记忆里的 AI 消息」和「API 返回的报告」一字不差。
闭环意义:下一次用户在这个 sessionId 上继续对话时,模型通过 4.2 的记忆回填,天然「知道」上次分析过什么------第四章开头的问题(失真)在这里被正式闭环。
Controller 与 Module 注册
typescript
// advanced.controller.ts 末尾
@Controller('advanced')
export class AdvancedController {
constructor(private readonly advanced: AdvancedAnalysisService) {}
@Post('analyze')
analyze(@Body() body: { sessionId: string; input: string }) {
return this.advanced.analyze(body.sessionId, body.input);
}
}
// advanced.module.ts:注册服务与控制器
@Module({
controllers: [MemoryController, FilesystemController, EmbeddingController, AgentsController, AdvancedController],
providers: [RunnableMemoryService, FilesystemService, EmbeddingService, VectorStoreService, OrchestratorService, AdvancedAnalysisService],
})
export class AdvancedModule {}
4.6.3 验收(三条命令看闭环)
cmd
:: a. 完整链路 ------ 编排 + 落盘 + 写回记忆
curl.exe -s -X POST http://localhost:4001/api/advanced/analyze -H "Content-Type: application/json" -d "{\"sessionId\":\"demo\",\"input\":\"帮我判断这个需求是否完整,并产出一份需求分析报告:开发一个面向需求分析师的会话记忆系统,支持多轮澄清并自动裁剪长对话上下文\"}"
:: b. 报告真的落盘了
dir e:\code\gitee\cookieboty\autix-learning\ch04\services\chat\workspace\reports
:: c. 报告真的写回记忆了
curl.exe -s "http://localhost:4001/api/memory/history/demo"
实际结果:
- a:返回完整
report+reportPath: "reports/demo-1792313491229.md"+usedAgents五个全齐; - b:目录下有 4.3 的
test.md(53 字节)和新的demo-1792313491229.md(3,713 字节); - c:恰好 2 条消息(Human + AI),且 AI 消息内容 = a 返回的 report,一字不差 ------证明是
appendMessage直写,没有重新调模型。
一个已知现象:report 结尾停在半截(finish_reason: length 截断,见 4.5 坑二),不影响闭环验证。
Java 对照
analyze() 就是教科书级的 Application Service / Facade:
java
@Service
public class RequirementAnalysisFacade {
public AnalyzeResult analyze(String sessionId, String input) {
OrchestratorResult result = orchestrator.orchestrate(input); // ① 编排
if (result.isNeedClarification()) {
return AnalyzeResult.clarify(result.getQuestions()); // ② 短路
}
String path = filesystem.writeReport(reportName(sessionId), result.getReport()); // ③ 落盘
memory.append(sessionId, input, result.getReport()); // ④ 写回
return AnalyzeResult.done(result.getReport(), path);
}
}
// 编排、存储、记忆各层只被它调用,前端只认这一个入口 ------ 典型的门面模式收口
DDD 的说法:领域服务(各 Agent/能力)保持纯粹,应用服务负责事务脚本式的流程编排 。四层各司其职,analyze() 一百行都不到。
判断标准
什么时候建统一入口? 当多个能力服务开始被「同一个业务场景」以固定顺序组合消费时。
收口的好处立刻可见 :换编排策略、加权限、加审计,只改一个地方;前端永远只认
POST /api/advanced/analyze。