AI Agent学习日记 Day4

张开发
2026/4/9 17:00:10 15 分钟阅读

分享文章

AI Agent学习日记 Day4
上周Claude code的源码泄露了大家都在学习和分析我也打算借鉴其中的一些实现方法和优秀思想。首先我把Claude code的双重熔断机制加进了我的Agent。Claude code整体流程图抄的双重熔断机制实现分析Claude Code 的实时 Token 计数机制Claude code的最大token数是Token 实时累计它不是等一次完整对话结束才算用了多少 token而是每收到一段流式响应就立刻把这段的 token 用量累加进去。Claude API 的流式响应中token 使用量并不在 content_block 或 text 事件中而是在单独的 message_delta 事件中发送。Claude Code 在收到 message_delta 时立即累加 usage.output_tokens同时 message_start 中也有 usage.input_tokens。QueryEngine.ts 第 789 行if (message.event.type message_start) { // 新消息开始重置本条消息的计数 currentMessageUsage EMPTY_USAGE currentMessageUsage updateUsage( currentMessageUsage, message.event.message.usage, ) } if (message.event.type message_delta) { // 流式增量立刻累加 currentMessageUsage updateUsage( currentMessageUsage, message.event.usage, ) } if (message.event.type message_stop) { // 一条消息结束追加到全局总量 this.totalUsage accumulateUsage( this.totalUsage, currentMessageUsage, ) }LangChain 的 Token 计数限制Langchain的on_chat_model_stream 事件中的 chunk 通常是一个 AIMessageChunk它的 usage_metadata 字段在流式过程中为 None只有最后一块或者调用结束时才会有值。正包含完整 usage_metadata 的是 on_chat_model_end 事件中的 output一个完整的 AIMessage。所以我的实现是在每次on_chat_model_end的时候统计消耗的token数。代码实现elif kind on_chat_model_end: # 获取完整的 AIMessage不是 Chunk output_msg event[data][output] # 提取 token 用量 input_toks 0 output_toks 0 try: if hasattr(output_msg, usage_metadata) and output_msg.usage_metadata: input_toks output_msg.usage_metadata.get(input_tokens, 0) output_toks output_msg.usage_metadata.get(output_tokens, 0) except Exception as e: print(f提取 token 失败: {e}) total_input_tokens input_toks total_output_tokens output_toks total_all_tokens total_input_tokens total_output_tokens循环控制机制实现在 LangGraph包括create_agent底层使用的图中recursion_limit限制的是图中节点被执行的次数而不是“LLM 调用次数”或“用户感知的回合数”。一个典型的 ReAct Agent 图结构大致如下agent_node (调用 LLM) - should_continue (条件边) - tools_node (执行工具) - 回到 agent_node每执行一次agent_node或tools_node都算一个“step”。这虽然和Claude code的Agent循环次数不太一样但是换汤不换药都可以用来表示Agent执行的次数。实现代码在config里写上recursion_limit: MAX_TURNS然后再在调用astream_events的时候加上config参数就行了config { configurable: {thread_id: 1}, recursion_limit: MAX_TURNS } event_stream st.session_state.agent.astream_events( {messages: [HumanMessage(contentuser_input)]}, configconfig, versionv2 )遇到的问题需要在创建大模型的时候加上stream_options{include_usage: True}否则在返回的AIMessage里不会包含usage_metadata也就无法获取到token信息。LLM ChatOpenAI( modelqwen3.5-plus, temperature0.1, api_keyDASHSCOPE_API_KEY, base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, timeout300, extra_body{enable_search: True}, stream_options{include_usage: True}, )astream_events是异步的当触发熔断的时候需要显示关掉异步生成器。否则它仍在后台运行会持续向 LangSmith 发送追踪事件LangSmith上面Agent执行就会显示pending。if total_all_tokens MAX_BUDGET_TOKENS: should_stop True token_placeholder.markdown(f **预算超限累计 {total_all_tokens} token 已达到上限 {MAX_BUDGET_TOKENS}任务终止。**) await event_stream.aclose() break

更多文章