ADK.js LlmAgent深度定制指南:从请求拦截到业务落地
2026-03-07 05:55:58作者:凤尚柏Louis
核心概念篇:揭开LlmAgent的黑箱
要点概览
- LlmAgent是ADK.js的核心交互组件,负责管理LLM通信全生命周期
- 处理器(Processors)用于系统性修改请求/响应流,钩子(Callbacks)用于插入离散逻辑
- 自定义扩展需遵循"最小侵入"原则,优先复用内置组件
如何理解LlmAgent的工作原理?
LlmAgent作为AI代理的"大脑",协调着与大型语言模型(LLM)的所有交互。其核心工作流包含四个阶段:
- 请求预处理:将用户输入转换为LLM可理解的格式
- 模型调用:处理与LLM的网络通信和错误恢复
- 响应后处理:解析LLM返回结果并提取结构化信息
- 工具协调:根据LLM决策调用外部工具并处理结果
这种模块化设计允许开发者在不修改核心代码的情况下,通过处理器和钩子实现深度定制。
处理器与钩子:两种扩展方式的对比
| 特性 | 处理器(Processors) | 钩子(Callbacks) |
|---|---|---|
| 用途 | 系统性修改数据流 | 离散事件响应 |
| 实现复杂度 | 中高 | 低 |
| 执行时机 | 固定流程节点 | 特定事件触发 |
| 典型应用 | 请求格式化、内容转换 | 日志记录、权限检查 |
| 返回值 | 生成器(可产生事件流) | 可修改上下文数据 |
场景化实践篇:从零开始定制代理行为
要点概览
- 请求处理器可实现从简单修改到复杂逻辑的全范围定制
- 钩子系统适合实现横切关注点如日志、监控和安全检查
- 所有自定义组件需通过ADK.js提供的接口进行类型约束
如何拦截并修改LLM请求?
当需要系统性修改发送给LLM的请求时,自定义请求处理器是最佳选择。以下是一个实现多语言支持的请求处理器:
import { BaseLlmRequestProcessor, InvocationContext, LlmRequest } from '../core/src/agents/base_llm_processor.ts';
// 多语言支持请求处理器
class LocalizationRequestProcessor extends BaseLlmRequestProcessor {
// 构造函数接收支持的语言列表
constructor(private supportedLanguages: string[] = ['en', 'zh', 'es']) {
super();
}
async *runAsync(
invocationContext: InvocationContext,
llmRequest: LlmRequest
): AsyncGenerator<Event, void, void> {
// 1. 从上下文中获取用户语言偏好
const userLang = invocationContext.session.get('preferredLanguage') || 'en';
// 2. 验证语言是否支持
const targetLang = this.supportedLanguages.includes(userLang) ? userLang : 'en';
// 3. 添加语言指令到系统消息
llmRequest.contents.unshift({
role: 'system',
parts: [{
text: `请用${this.getLanguageName(targetLang)}回答问题,保持专业术语准确但表达通俗。`
}]
});
// 4. 生成处理事件
yield createEvent({
invocationId: invocationContext.invocationId,
type: 'localization_applied',
content: { targetLanguage: targetLang }
});
// 5. 将控制权传递给下一个处理器
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
// 辅助方法:获取语言名称
private getLanguageName(code: string): string {
const names = { 'en': '英语', 'zh': '中文', 'es': '西班牙语' };
return names[code] || code;
}
}
注册处理器时需注意顺序,基础处理器应先于自定义处理器执行:
const agent = new LlmAgent({
model: 'gemini-pro',
requestProcessors: [
BASIC_LLM_REQUEST_PROCESSOR, // 基础配置处理器
IDENTITY_LLM_REQUEST_PROCESSOR, // 身份信息处理器
new LocalizationRequestProcessor(['en', 'zh', 'ja']), // 自定义多语言处理器
CONTENT_REQUEST_PROCESSOR // 内容格式化处理器
]
});
如何在关键节点插入自定义逻辑?
钩子机制允许在不创建完整处理器的情况下,在特定事件发生时执行自定义逻辑。以下是一个实现请求限流的beforeModel钩子:
// 请求频率限制钩子
const rateLimitHook = async ({ context, request }: BeforeModelCallbackArgs) => {
// 1. 获取当前用户ID
const userId = context.session.get('userId');
// 2. 检查请求频率(伪代码)
const requestCount = await rateLimitService.getCount(userId, 'hour');
// 3. 如果超过限制,返回错误响应
if (requestCount > 100) {
return {
content: {
parts: [{
text: '抱歉,您的请求频率已超出限制,请1小时后再试。'
}]
},
isFinal: true // 标记为最终响应,不再调用LLM
};
}
// 4. 记录请求(伪代码)
await rateLimitService.incrementCount(userId);
// 5. 返回undefined继续正常流程
return undefined;
};
// 注册钩子
const agent = new LlmAgent({
// 其他配置...
beforeModelCallback: rateLimitHook
});
钩子可以是单个函数或函数数组,当使用数组时会按顺序执行:
// 组合多个钩子
agent.configure({
afterToolCallback: [
logToolUsage, // 记录工具使用
validateToolOutput, // 验证输出格式
enhanceToolResult // 增强结果数据
]
});
真实场景应用篇:行业落地案例解析
要点概览
- 金融领域可利用定制代理实现智能风控和合规检查
- 医疗场景中通过响应处理确保信息准确性和隐私保护
- 教育行业可构建个性化学习助手,动态调整教学策略
金融风控代理:实时交易监控系统
某大型银行使用ADK.js构建了实时交易监控代理,通过自定义处理器实现风险控制:
// 交易风险评估处理器
class TransactionRiskProcessor extends BaseLlmRequestProcessor {
async *runAsync(invocationContext: InvocationContext, llmRequest: LlmRequest) {
// 1. 从上下文中提取交易数据
const transaction = invocationContext.get('transactionData');
// 2. 添加风险评估指令
llmRequest.contents.push({
role: 'system',
parts: [{
text: `分析以下交易是否存在欺诈风险,重点关注:
1. 金额是否超出用户历史交易范围
2. 交易地点是否与常用地区不符
3. 交易时间是否在非活跃时段
请返回风险评分(0-100)和关键风险点。`
}]
});
// 3. 添加交易数据作为用户消息
llmRequest.contents.push({
role: 'user',
parts: [{ text: JSON.stringify(transaction, null, 2) }]
});
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
}
// 风险决策钩子
const riskDecisionHook = async ({ context, response }: AfterModelCallbackArgs) => {
// 解析LLM返回的风险评分
const riskScore = extractRiskScore(response.content.parts[0].text);
// 根据评分执行不同操作
if (riskScore > 70) {
// 高风险:触发人工审核
context.session.set('status', 'pending_review');
return {
...response,
content: { parts: [{ text: '交易已标记为高风险,正在等待人工审核。' }] }
};
} else if (riskScore > 40) {
// 中风险:要求额外验证
context.session.set('status', 'requires_verification');
return {
...response,
content: { parts: [{ text: '请完成手机验证码验证以继续交易。' }] }
};
}
// 低风险:正常处理
context.session.set('status', 'approved');
return response;
};
医疗信息处理:患者数据保护代理
某医疗机构实现了保护患者隐私的医疗信息处理代理:
// 患者数据脱敏处理器
class MedicalDataAnonymizer extends BaseLlmRequestProcessor {
async *runAsync(invocationContext: InvocationContext, llmRequest: LlmRequest) {
// 1. 识别并替换患者标识符
for (const content of llmRequest.contents) {
if (content.role === 'user' && content.parts) {
for (const part of content.parts) {
if (typeof part.text === 'string') {
// 脱敏处理:替换姓名、身份证等敏感信息
part.text = part.text
.replace(/患者姓名:\w+/g, '患者姓名:[已脱敏]')
.replace(/身份证号:\d+/g, '身份证号:[已脱敏]')
.replace(/病历号:\w+/g, '病历号:[已脱敏]');
}
}
}
}
// 2. 添加隐私保护指令
llmRequest.contents.unshift({
role: 'system',
parts: [{
text: '作为医疗信息处理助手,你必须遵守HIPAA隐私法规,不得在响应中包含任何患者身份信息。'
}]
});
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
}
教育个性化学习:自适应教学代理
某在线教育平台实现了根据学生水平动态调整内容的学习代理:
// 学习内容适配处理器
class AdaptiveLearningProcessor extends BaseLlmRequestProcessor {
async *runAsync(invocationContext: InvocationContext, llmRequest: LlmRequest) {
// 1. 获取学生学习数据
const studentData = invocationContext.session.get('studentProfile');
// 2. 确定难度级别
const difficulty = this.determineDifficulty(studentData);
// 3. 调整教学内容难度
llmRequest.contents.push({
role: 'system',
parts: [{
text: `根据学生水平调整教学内容:
- 当前水平: ${difficulty}
- 解释复杂度: ${this.getExplanationComplexity(difficulty)}
- 示例数量: ${this.getExampleCount(difficulty)}
- 问题难度: ${this.getQuestionDifficulty(difficulty)}`
}]
});
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
// 根据学生数据确定难度级别
private determineDifficulty(studentData: StudentProfile): 'beginner' | 'intermediate' | 'advanced' {
// 基于历史表现的逻辑实现...
return studentData.averageScore > 85 ? 'advanced' :
studentData.averageScore > 65 ? 'intermediate' : 'beginner';
}
// 根据难度调整解释复杂度
private getExplanationComplexity(difficulty: string): string {
const levels = {
beginner: '使用简单术语和类比,避免专业词汇',
intermediate: '使用适当专业术语并提供解释',
advanced: '使用专业术语,提供深入解释'
};
return levels[difficulty];
}
// 其他辅助方法...
}
进阶技巧篇:打造高性能定制代理
要点概览
- 处理器组合可实现复杂业务逻辑,同时保持代码模块化
- 钩子优先级机制可精确控制执行顺序
- 缓存策略能显著提升代理响应速度和降低API成本
如何组合多个处理器实现复杂逻辑?
通过处理器链可以组合多个独立功能,实现复杂业务需求:
// 处理器1: 内容过滤
class ContentFilterProcessor extends BaseLlmRequestProcessor {
async *runAsync(invocationContext: InvocationContext, llmRequest: LlmRequest) {
// 过滤不当内容...
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
}
// 处理器2: 请求优化
class RequestOptimizerProcessor extends BaseLlmRequestProcessor {
async *runAsync(invocationContext: InvocationContext, llmRequest: LlmRequest) {
// 优化请求大小和格式...
return yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
}
}
// 处理器3: 响应格式化
class ResponseFormatterProcessor extends BaseLlmResponseProcessor {
async *runAsync(invocationContext: InvocationContext, llmResponse: LlmResponse) {
// 格式化响应为统一格式...
return yield* this.nextProcessor.runAsync(invocationContext, llmResponse);
}
}
// 组合使用处理器
const agent = new LlmAgent({
requestProcessors: [
BASIC_LLM_REQUEST_PROCESSOR,
new ContentFilterProcessor(),
new RequestOptimizerProcessor(),
CONTENT_REQUEST_PROCESSOR
],
responseProcessors: [
new ResponseFormatterProcessor(),
BASIC_LLM_RESPONSE_PROCESSOR
]
});
如何实现钩子优先级控制?
当多个钩子需要按特定顺序执行时,可以使用优先级机制:
// 带优先级的钩子实现
interface PrioritizedHook {
priority: number; // 数值越大,执行优先级越高
hook: Function;
}
// 钩子管理器
class HookManager {
private hooks: Map<string, PrioritizedHook[]> = new Map();
// 注册带优先级的钩子
registerHook(type: string, hook: Function, priority: number = 0) {
if (!this.hooks.has(type)) {
this.hooks.set(type, []);
}
this.hooks.get(type)!.push({ priority, hook });
// 按优先级排序
this.hooks.get(type)!.sort((a, b) => b.priority - a.priority);
}
// 执行钩子
async executeHooks(type: string, args: any): Promise<any> {
const hooks = this.hooks.get(type) || [];
let result;
for (const { hook } of hooks) {
result = await hook(args);
// 如果钩子返回结果,短路执行
if (result !== undefined) break;
}
return result;
}
}
// 使用示例
const hookManager = new HookManager();
// 高优先级:安全检查
hookManager.registerHook('beforeModel', securityCheckHook, 100);
// 中优先级:日志记录
hookManager.registerHook('beforeModel', loggingHook, 50);
// 低优先级:性能监控
hookManager.registerHook('beforeModel', performanceHook, 10);
性能优化:请求缓存策略实现
实现请求缓存可以显著减少重复LLM调用,降低延迟和成本:
// 请求缓存处理器
class CachingRequestProcessor extends BaseLlmRequestProcessor {
private cache: Map<string, LlmResponse> = new Map();
private ttl: number = 3600000; // 缓存有效期1小时
constructor(private cacheService: CacheService) {
super();
}
async *runAsync(
invocationContext: InvocationContext,
llmRequest: LlmRequest
): AsyncGenerator<Event, void, LlmResponse> {
// 1. 生成请求唯一标识
const cacheKey = this.generateCacheKey(llmRequest);
// 2. 尝试从缓存获取
const cachedResponse = await this.cacheService.get(cacheKey);
if (cachedResponse) {
// 3. 返回缓存结果
yield createEvent({
type: 'cache_hit',
content: { cacheKey, ttl: this.ttl }
});
return cachedResponse;
}
// 4. 缓存未命中,继续处理流程
yield createEvent({ type: 'cache_miss', content: { cacheKey } });
const response = yield* this.nextProcessor.runAsync(invocationContext, llmRequest);
// 5. 缓存新结果
await this.cacheService.set(cacheKey, response, this.ttl);
return response;
}
// 生成缓存键
private generateCacheKey(request: LlmRequest): string {
// 基于请求内容生成唯一哈希
const requestString = JSON.stringify(request);
return createHash('md5').update(requestString).digest('hex');
}
}
避坑指南篇:常见问题与解决方案
要点概览
- 处理器顺序错误会导致意外行为,需遵循特定注册顺序
- 钩子返回值处理不当可能导致流程异常终止
- 内存泄漏常源于未清理的事件监听器和上下文引用
问题1:处理器执行顺序导致的功能异常
症状:自定义处理器未按预期修改请求内容 排查流程:
- 检查处理器注册顺序,确保基础处理器先于自定义处理器
- 使用事件日志验证处理器执行顺序
- 确认处理器是否正确调用
nextProcessor传递控制权
解决方案:
// 正确的处理器顺序
const agent = new LlmAgent({
requestProcessors: [
// 1. 基础配置处理器(必须首先执行)
BASIC_LLM_REQUEST_PROCESSOR,
// 2. 身份和指令处理器
IDENTITY_LLM_REQUEST_PROCESSOR,
INSTRUCTIONS_LLM_REQUEST_PROCESSOR,
// 3. 自定义处理器
new CustomRequestProcessor(),
// 4. 内容格式化处理器(最后执行)
CONTENT_REQUEST_PROCESSOR
]
});
问题2:钩子短路执行导致意外结果
症状:代理在钩子执行后提前终止,未调用LLM 排查流程:
- 检查钩子是否返回了非undefined值
- 验证
isFinal标志是否被错误设置为true - 检查钩子异常处理是否正确
解决方案:
// 错误示例:意外短路
const badHook = async ({ request }) => {
console.log('处理请求');
// 错误:无意图地返回了对象,导致LLM调用被跳过
return { debug: '处理完成' };
};
// 正确示例:不短路执行
const goodHook = async ({ request }) => {
console.log('处理请求');
// 正确:不返回值或返回undefined
return undefined;
};
问题3:内存泄漏导致代理性能下降
症状:长时间运行后代理内存占用持续增长 排查流程:
- 使用内存分析工具检测泄漏源
- 检查处理器/钩子中是否有全局缓存未设置过期策略
- 验证事件监听器是否正确移除
解决方案:
// 错误示例:无限制缓存
class LeakyProcessor extends BaseLlmRequestProcessor {
private cache = new Map(); // 永远不会清理
async *runAsync(context, request) {
const key = generateKey(request);
if (!this.cache.has(key)) {
this.cache.set(key, await computeValue());
}
// ...
}
}
// 正确示例:带过期策略的缓存
class SafeProcessor extends BaseLlmRequestProcessor {
private cache = new Map();
private cacheExpiry = new Map();
private ttl = 3600000; // 1小时过期
async *runAsync(context, request) {
const key = generateKey(request);
const now = Date.now();
// 清理过期缓存
if (this.cacheExpiry.has(key) && now > this.cacheExpiry.get(key)) {
this.cache.delete(key);
this.cacheExpiry.delete(key);
}
if (!this.cache.has(key)) {
this.cache.set(key, await computeValue());
this.cacheExpiry.set(key, now + this.ttl);
}
// ...
}
}
问题4:并发请求导致的上下文污染
症状:多用户同时使用时出现数据交叉污染 排查流程:
- 检查是否在处理器/钩子中使用了共享状态
- 验证上下文对象是否正确隔离
- 检查会话管理逻辑是否存在漏洞
解决方案:
// 错误示例:使用共享状态
class UnsafeProcessor extends BaseLlmRequestProcessor {
private userData; // 所有请求共享的状态
async *runAsync(context, request) {
this.userData = context.session.get('userData'); // 会被后续请求覆盖
// ...
}
}
// 正确示例:使用局部状态
class SafeProcessor extends BaseLlmRequestProcessor {
async *runAsync(context, request) {
const userData = context.session.get('userData'); // 每个请求的局部变量
// ...
}
}
问题5:错误处理不当导致代理崩溃
症状:遇到异常时代理完全停止工作 排查流程:
- 检查处理器/钩子中是否有未捕获的异常
- 验证错误处理逻辑是否覆盖所有可能失败点
- 检查异步操作是否正确处理拒绝状态
解决方案:
// 错误示例:无错误处理
class RiskyProcessor extends BaseLlmRequestProcessor {
async *runAsync(context, request) {
// 没有try/catch,异常会传播并导致代理崩溃
const data = await riskyOperation();
// ...
}
}
// 正确示例:完善的错误处理
class SafeProcessor extends BaseLlmRequestProcessor {
async *runAsync(context, request) {
try {
const data = await riskyOperation();
// ...
} catch (error) {
// 1. 记录错误
yield createEvent({
type: 'processor_error',
level: 'error',
content: { message: error.message, stack: error.stack }
});
// 2. 提供回退方案
const fallbackData = getFallbackData();
// 3. 继续处理流程或返回错误响应
if (fallbackData) {
// 使用回退数据继续
request.contents.push({ role: 'system', parts: [{ text: '使用回退数据处理' }] });
return yield* this.nextProcessor.runAsync(context, request);
} else {
// 返回错误响应
return createErrorResponse('处理失败,请稍后重试');
}
}
}
}
附录:项目结构与扩展资源
核心代码结构
ADK.js的LlmAgent相关核心代码位于以下目录:
- 核心处理器定义:core/src/agents/base_llm_processor.ts
- LlmAgent实现:core/src/agents/llm_agent.ts
- 内置处理器:core/src/agents/instructions.ts
- 钩子类型定义:core/src/agents/invocation_context.ts
扩展资源
- 官方示例:dev/samples/目录包含多种代理实现示例
- 测试用例:core/test/agents/提供处理器和钩子的测试示例
- API文档:项目根目录下的docs/文件夹包含完整API文档
项目贡献
要为ADK.js贡献自定义处理器或钩子,请遵循以下步骤:
- 克隆项目仓库:
git clone https://gitcode.com/GitHub_Trending/ad/adk-js - 在
core/src/agents/processors/目录下创建新处理器 - 添加相应的测试用例到
core/test/agents/目录 - 提交PR并描述你的自定义组件功能和使用场景
登录后查看全文
热门项目推荐
相关项目推荐
GLM-5智谱 AI 正式发布 GLM-5,旨在应对复杂系统工程和长时域智能体任务。Jinja00
GLM-5-w4a8GLM-5-w4a8基于混合专家架构,专为复杂系统工程与长周期智能体任务设计。支持单/多节点部署,适配Atlas 800T A3,采用w4a8量化技术,结合vLLM推理优化,高效平衡性能与精度,助力智能应用开发Jinja00
jiuwenclawJiuwenClaw 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。Python0238- QQwen3.5-397B-A17BQwen3.5 实现了重大飞跃,整合了多模态学习、架构效率、强化学习规模以及全球可访问性等方面的突破性进展,旨在为开发者和企业赋予前所未有的能力与效率。Jinja00
AtomGit城市坐标计划AtomGit 城市坐标计划开启!让开源有坐标,让城市有星火。致力于与城市合伙人共同构建并长期运营一个健康、活跃的本地开发者生态。01
electerm开源终端/ssh/telnet/serialport/RDP/VNC/Spice/sftp/ftp客户端(linux, mac, win)JavaScript00
热门内容推荐
最新内容推荐
4个步骤掌握DeepEval:从入门到实践3大场景解锁pyLDAvis:从学术研究到商业决策的主题模型可视化实战指南BiliTools全场景解析指南:高效管理B站资源的跨平台解决方案5个core83核心能力:提升Node.js开发效率的全方位解决方案AI模型云端部署无代码实践:从本地训练到生产服务的完整指南macOS平台Windows启动盘制作工具:WindiskWriter全面指南Vue3短视频架构实战:从交互到部署的全链路指南开源CRM解决方案:企业级客户关系管理系统全栈实践指南轻量高效的macOS录屏新选择:QuickRecorder全面评测与使用指南3种PDF拆分模式,让文档管理效率提升80%
项目优选
收起
deepin linux kernel
C
27
13
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
632
4.16 K
Ascend Extension for PyTorch
Python
471
567
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
932
835
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
1.51 K
861
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
383
266
暂无简介
Dart
880
210
昇腾LLM分布式训练框架
Python
138
162
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
123
188
Oohos_react_native
React Native鸿蒙化仓库
JavaScript
327
382