[{"title":"AgentSession 源码分析 — Pi Coding Agent 核心生命周期管理","permalink":"https://yukar.icu/posts/pi-architecture/pi-coding-agent-agentsession/","summary":"概述 agent-session.ts（3159 行）是 Pi Coding Agent 的核心枢纽，位于 F:\\Pi\\packages\\coding-agent\\src\\core\\。它是所有运行模式（interactive、print、rpc）共享的 AgentSession 类，封装了 agent 生命周期的完整管理逻辑。\n一句话定位：AgentSession 是所有模式在 pi-agent-core 之上叠加的会话管理层 — 连接了 agent 循环、扩展系统、会话持久化、模型管理、compaction、自动重试等所有主要子系统。\n核心职责 职责领域 说明 Agent 生命周期 管理 prompt 发送、流式响应、消息持久化 扩展系统桥接 事件转发到 ExtensionRunner，处理扩展注册的工具/命令 会话管理 消息持久化、会话切换、分支导航、压缩 模型管理 模型选择、切换、thinking level 控制 Compaction 自动/手动上下文压缩，overflow 恢复 自动重试 可配置指数退避重试（429/500/网络错误） Bash 执行 命令执行、结果记录、流式输出 消息队列 steering/followUp 消息的排队与消费 架构位置与依赖关系 数据流层次（按代码执行顺序） user message: /skill:name args 扩展 ↓ (text expansion) AgentSession.prompt() ↓ (input event → skill → template → streaming? → auth → message build) _runAgentPrompt() ↓ agent.prompt() / agent.continue() ↓ (agent-core 内部循环：LLM → 工具 → LLM ...) _handleAgentEvent() ←── agent event 回调 ↓ _emitExtensionEvent() → ExtensionRunner ↓ _emit() → AgentSessionEventListener ↓ sessionManager.appendMessage() → 会话持久化 外部依赖全景 agent-session.ts ├── @earendil-works/pi-agent-core (Agent, AgentEvent, AgentMessage, AgentState, AgentTool, ThinkingLevel) ├── @earendil-works/pi-ai/compat (AssistantMessage, ImageContent, Model, TextContent, streamSimple, isContextOverflow, clampThinkingLevel 等) ├── node:fs / node:path (文件读写) │ ├── ./extensions/index.ts (ExtensionRunner, 扩展事件类型, wrapRegisteredTools) ├── ./extensions/runner.ts (emitSessionShutdownEvent) ├── ./session-manager.ts (SessionManager, BranchSummaryEntry, CompactionEntry, SessionHeader) ├── ./model-registry.ts (ModelRegistry) ├── ./settings-manager.ts (SettingsManager) ├── ./resource-loader.ts (ResourceLoader, ResourceExtensionPaths) │ ├── ./compaction/index.ts (compact, prepareCompaction, shouldCompact, estimateContextTokens 等) ├── ./bash-executor.ts (executeBashWithOperations, BashResult) ├── ./messages.ts (CustomMessage, BashExecutionMessage) ├── ./system-prompt.ts (buildSystemPrompt, BuildSystemPromptOptions) ├── ./prompt-templates.ts (expandPromptTemplate, PromptTemplate) ├── ./slash-commands.ts (SlashCommandInfo) ├── ./source-info.ts (SourceInfo, createSyntheticSourceInfo) ├── ./tools/index.ts (createAllToolDefinitions) ├── ./tools/bash.ts (BashOperations, createLocalBashOperations) ├── ./tools/tool-definition-wrapper.ts (createToolDefinitionFromAgentTool) │ ├── ./auth-guidance.ts (formatNoApiKeyFoundMessage, formatNoModelSelectedMessage) ├── ./defaults.ts (DEFAULT_THINKING_LEVEL) ├── ./export-html/index.ts (exportSessionToHtml, ToolHtmlRenderer) ├── ./export-html/tool-renderer.ts (createToolHtmlRenderer) ├── ./keybindings.ts (KeybindingsConfig) ├── ./diagnostics.ts (ResourceDiagnostic) │ ├── ../utils/frontmatter.ts (stripFrontmatter) ├── ../utils/paths.ts (resolvePath) ├── ../utils/sleep.ts (sleep) └── ../modes/interactive/theme/theme.ts (getThemeByName, theme) 类结构总览 AgentSession ├── 构造函数 ──── _buildRuntime() → 初始化工具注册表、扩展系统 ├── 公开方法 │ ├── prompt() → 主入口：处理用户输入 │ ├── steer() / followUp() → 流式状态下的消息排队 │ ├── sendCustomMessage() → 扩展发送自定义消息 │ ├── sendUserMessage() → 扩展发送用户消息 │ ├── setModel() / cycleModel() → 模型管理 │ ├── setThinkingLevel() / cycleThinkingLevel() → Thinking level │ ├── compact() → 手动压缩 │ ├── navigateTree() → 会话树导航 │ ├── exportToHtml/Jsonl() → 导出 │ ├── executeBash() → Bash 执行 │ ├── subscribe() → 事件订阅 │ ├── bindExtensions() → 扩展绑定 │ └── dispose() → 资源清理 ├── 内部方法 │ ├── _handleAgentEvent() → agent event 回调枢纽 │ ├── _emitExtensionEvent() → 转发到 ExtensionRunner │ ├── _runAgentPrompt() → agent.prompt() + post-run 处理循环 │ ├── _handlePostAgentRun() → agent 停止后的处理（重试/compaction/队列） │ ├── _checkCompaction() → 压缩检查（overflow + threshold） │ ├── _runAutoCompaction() → 自动压缩执行 │ ├── _prepareRetry() → 指数退避重试 │ └── _buildRuntime() → 初始化工具注册表和扩展运行器 ├── Getter 属性 │ ├── model / isStreaming / thinkingLevel / state │ ├── sessionFile / sessionId / sessionName │ ├── isCompacting / isRetrying / isBashRunning │ ├── pendingMessageCount / activeToolNames │ └── autoCompactionEnabled / autoRetryEnabled └── 内部状态 ├── _steeringMessages / _followUpMessages / _pendingNextTurnMessages ├── _compactionAbortController / _autoCompactionAbortController ├── _retryAbortController / _retryAttempt ├── _bashAbortController / _pendingBashMessages └── _toolRegistry / _toolDefinitions / _toolPromptSnippets 逐段解读 段 1：类型定义与常量（第 1-123 行） ParsedSkillBlock 与 parseSkillBlock() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export interface ParsedSkillBlock { name: string; location: string; content: string; userMessage: string | undefined; } export function parseSkillBlock(text: string): ParsedSkillBlock | null { const match = text.match( /^\u0026lt;skill name=\u0026#34;([^\u0026#34;]+)\u0026#34; location=\u0026#34;([^\u0026#34;]+)\u0026#34;\u0026gt;\\n([\\s\\S]*?)\\n\u0026lt;\\/skill\u0026gt;(?:\\n\\n([\\s\\S]+))?$/ ); if (!match) return null; return { name: match[1], location: match[2], content: match[3], userMessage: match[4]?.trim() || undefined }; } 功能：从用户消息中解析 \u0026lt;skill\u0026gt; XML 块。正则匹配四个捕获组：name、location、content 和可选的 userMessage。用于将技能文件的内容嵌入用户消息上下文。\n","content":"概述 agent-session.ts（3159 行）是 Pi Coding Agent 的核心枢纽，位于 F:\\Pi\\packages\\coding-agent\\src\\core\\。它是所有运行模式（interactive、print、rpc）共享的 AgentSession 类，封装了 agent 生命周期的完整管理逻辑。\n一句话定位：AgentSession 是所有模式在 pi-agent-core 之上叠加的会话管理层 — 连接了 agent 循环、扩展系统、会话持久化、模型管理、compaction、自动重试等所有主要子系统。\n核心职责 职责领域 说明 Agent 生命周期 管理 prompt 发送、流式响应、消息持久化 扩展系统桥接 事件转发到 ExtensionRunner，处理扩展注册的工具/命令 会话管理 消息持久化、会话切换、分支导航、压缩 模型管理 模型选择、切换、thinking level 控制 Compaction 自动/手动上下文压缩，overflow 恢复 自动重试 可配置指数退避重试（429/500/网络错误） Bash 执行 命令执行、结果记录、流式输出 消息队列 steering/followUp 消息的排队与消费 架构位置与依赖关系 数据流层次（按代码执行顺序） user message: /skill:name args 扩展 ↓ (text expansion) AgentSession.prompt() ↓ (input event → skill → template → streaming? → auth → message build) _runAgentPrompt() ↓ agent.prompt() / agent.continue() ↓ (agent-core 内部循环：LLM → 工具 → LLM ...) _handleAgentEvent() ←── agent event 回调 ↓ _emitExtensionEvent() → ExtensionRunner ↓ _emit() → AgentSessionEventListener ↓ sessionManager.appendMessage() → 会话持久化 外部依赖全景 agent-session.ts ├── @earendil-works/pi-agent-core (Agent, AgentEvent, AgentMessage, AgentState, AgentTool, ThinkingLevel) ├── @earendil-works/pi-ai/compat (AssistantMessage, ImageContent, Model, TextContent, streamSimple, isContextOverflow, clampThinkingLevel 等) ├── node:fs / node:path (文件读写) │ ├── ./extensions/index.ts (ExtensionRunner, 扩展事件类型, wrapRegisteredTools) ├── ./extensions/runner.ts (emitSessionShutdownEvent) ├── ./session-manager.ts (SessionManager, BranchSummaryEntry, CompactionEntry, SessionHeader) ├── ./model-registry.ts (ModelRegistry) ├── ./settings-manager.ts (SettingsManager) ├── ./resource-loader.ts (ResourceLoader, ResourceExtensionPaths) │ ├── ./compaction/index.ts (compact, prepareCompaction, shouldCompact, estimateContextTokens 等) ├── ./bash-executor.ts (executeBashWithOperations, BashResult) ├── ./messages.ts (CustomMessage, BashExecutionMessage) ├── ./system-prompt.ts (buildSystemPrompt, BuildSystemPromptOptions) ├── ./prompt-templates.ts (expandPromptTemplate, PromptTemplate) ├── ./slash-commands.ts (SlashCommandInfo) ├── ./source-info.ts (SourceInfo, createSyntheticSourceInfo) ├── ./tools/index.ts (createAllToolDefinitions) ├── ./tools/bash.ts (BashOperations, createLocalBashOperations) ├── ./tools/tool-definition-wrapper.ts (createToolDefinitionFromAgentTool) │ ├── ./auth-guidance.ts (formatNoApiKeyFoundMessage, formatNoModelSelectedMessage) ├── ./defaults.ts (DEFAULT_THINKING_LEVEL) ├── ./export-html/index.ts (exportSessionToHtml, ToolHtmlRenderer) ├── ./export-html/tool-renderer.ts (createToolHtmlRenderer) ├── ./keybindings.ts (KeybindingsConfig) ├── ./diagnostics.ts (ResourceDiagnostic) │ ├── ../utils/frontmatter.ts (stripFrontmatter) ├── ../utils/paths.ts (resolvePath) ├── ../utils/sleep.ts (sleep) └── ../modes/interactive/theme/theme.ts (getThemeByName, theme) 类结构总览 AgentSession ├── 构造函数 ──── _buildRuntime() → 初始化工具注册表、扩展系统 ├── 公开方法 │ ├── prompt() → 主入口：处理用户输入 │ ├── steer() / followUp() → 流式状态下的消息排队 │ ├── sendCustomMessage() → 扩展发送自定义消息 │ ├── sendUserMessage() → 扩展发送用户消息 │ ├── setModel() / cycleModel() → 模型管理 │ ├── setThinkingLevel() / cycleThinkingLevel() → Thinking level │ ├── compact() → 手动压缩 │ ├── navigateTree() → 会话树导航 │ ├── exportToHtml/Jsonl() → 导出 │ ├── executeBash() → Bash 执行 │ ├── subscribe() → 事件订阅 │ ├── bindExtensions() → 扩展绑定 │ └── dispose() → 资源清理 ├── 内部方法 │ ├── _handleAgentEvent() → agent event 回调枢纽 │ ├── _emitExtensionEvent() → 转发到 ExtensionRunner │ ├── _runAgentPrompt() → agent.prompt() + post-run 处理循环 │ ├── _handlePostAgentRun() → agent 停止后的处理（重试/compaction/队列） │ ├── _checkCompaction() → 压缩检查（overflow + threshold） │ ├── _runAutoCompaction() → 自动压缩执行 │ ├── _prepareRetry() → 指数退避重试 │ └── _buildRuntime() → 初始化工具注册表和扩展运行器 ├── Getter 属性 │ ├── model / isStreaming / thinkingLevel / state │ ├── sessionFile / sessionId / sessionName │ ├── isCompacting / isRetrying / isBashRunning │ ├── pendingMessageCount / activeToolNames │ └── autoCompactionEnabled / autoRetryEnabled └── 内部状态 ├── _steeringMessages / _followUpMessages / _pendingNextTurnMessages ├── _compactionAbortController / _autoCompactionAbortController ├── _retryAbortController / _retryAttempt ├── _bashAbortController / _pendingBashMessages └── _toolRegistry / _toolDefinitions / _toolPromptSnippets 逐段解读 段 1：类型定义与常量（第 1-123 行） ParsedSkillBlock 与 parseSkillBlock() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export interface ParsedSkillBlock { name: string; location: string; content: string; userMessage: string | undefined; } export function parseSkillBlock(text: string): ParsedSkillBlock | null { const match = text.match( /^\u0026lt;skill name=\u0026#34;([^\u0026#34;]+)\u0026#34; location=\u0026#34;([^\u0026#34;]+)\u0026#34;\u0026gt;\\n([\\s\\S]*?)\\n\u0026lt;\\/skill\u0026gt;(?:\\n\\n([\\s\\S]+))?$/ ); if (!match) return null; return { name: match[1], location: match[2], content: match[3], userMessage: match[4]?.trim() || undefined }; } 功能：从用户消息中解析 \u0026lt;skill\u0026gt; XML 块。正则匹配四个捕获组：name、location、content 和可选的 userMessage。用于将技能文件的内容嵌入用户消息上下文。\nAgentSessionEvent — 自定义事件类型 1 2 3 4 5 6 7 8 9 10 export type AgentSessionEvent = | Exclude\u0026lt;AgentEvent, { type: \u0026#34;agent_end\u0026#34; }\u0026gt; | { type: \u0026#34;agent_end\u0026#34;; messages: AgentMessage[]; willRetry: boolean } | { type: \u0026#34;queue_update\u0026#34;; steering: readonly string[]; followUp: readonly string[] } | { type: \u0026#34;compaction_start\u0026#34;; reason: \u0026#34;manual\u0026#34; | \u0026#34;threshold\u0026#34; | \u0026#34;overflow\u0026#34; } | { type: \u0026#34;compaction_end\u0026#34;; reason: ...; result: ...; aborted: boolean; willRetry: boolean; errorMessage?: string } | { type: \u0026#34;auto_retry_start\u0026#34;; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string } | { type: \u0026#34;auto_retry_end\u0026#34;; success: boolean; attempt: number; finalError?: string } | { type: \u0026#34;session_info_changed\u0026#34;; name: string | undefined } | { type: \u0026#34;thinking_level_changed\u0026#34;; level: ThinkingLevel }; 语法要点 — Exclude\u0026lt;T, U\u0026gt;：从联合类型 T 中排除匹配 U 的成员。这里排除 agent-core 的 agent_end 类型，用自定义的（带 willRetry 字段）替换。\n设计：新增的事件类型（compaction_start/end、auto_retry_start/end、queue_update 等）是 AgentSession 层独有的，agent-core 不知道它们的存在。\nAgentSessionConfig — 构造函数配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export interface AgentSessionConfig { agent: Agent; // agent-core 的 Agent 实例 sessionManager: SessionManager; // 会话持久化管理器 settingsManager: SettingsManager; // 设置管理器 cwd: string; scopedModels?: Array\u0026lt;{ model: Model\u0026lt;any\u0026gt;; thinkingLevel?: ThinkingLevel }\u0026gt;; // --models 标志 resourceLoader: ResourceLoader; customTools?: ToolDefinition[]; modelRegistry: ModelRegistry; initialActiveToolNames?: string[]; allowedToolNames?: string[]; // 工具 allowlist excludedToolNames?: string[]; // 工具 denylist baseToolsOverride?: Record\u0026lt;string, AgentTool\u0026gt;; extensionRunnerRef?: { current?: ExtensionRunner }; sessionStartEvent?: SessionStartEvent; } 依赖注入：AgentSession 通过构造函数接收所有依赖，不做服务定位。这让它在不同运行模式（interactive、print、rpc）下可独立实例化，每个模式提供不同的实现。\n段 2：类构造函数与初始化（第 259-340 行） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export class AgentSession { // ~30 个私有状态字段 private _steeringMessages: string[] = []; private _followUpMessages: string[] = []; private _pendingNextTurnMessages: CustomMessage[] = []; private _compactionAbortController: AbortController | undefined; private _retryAbortController: AbortController | undefined; private _bashAbortController: AbortController | undefined; // ... constructor(config: AgentSessionConfig) { // 1. 保存配置 this.agent = config.agent; this.sessionManager = config.sessionManager; // ... // 2. 订阅 agent 事件 this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent); // 3. 安装工具钩子（beforeToolCall / afterToolCall） this._installAgentToolHooks(); // 4. 构建运行时（工具注册表、ExtensionRunner、系统提示） this._buildRuntime({ activeToolNames: this._initialActiveToolNames, includeAllExtensionTools: true }); } _installAgentToolHooks() — 工具拦截钩子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private _installAgentToolHooks(): void { this.agent.beforeToolCall = async ({ toolCall, args }) =\u0026gt; { const runner = this._extensionRunner; if (!runner.hasHandlers(\u0026#34;tool_call\u0026#34;)) return undefined; return await runner.emitToolCall({ type: \u0026#34;tool_call\u0026#34;, toolName: toolCall.name, toolCallId: toolCall.id, input: args as Record\u0026lt;string, unknown\u0026gt; }); }; this.agent.afterToolCall = async ({ toolCall, args, result, isError }) =\u0026gt; { const runner = this._extensionRunner; if (!runner.hasHandlers(\u0026#34;tool_result\u0026#34;)) return undefined; const hookResult = await runner.emitToolResult({ type: \u0026#34;tool_result\u0026#34;, toolName: toolCall.name, ... }); if (!hookResult) return undefined; return { content: hookResult.content, details: hookResult.details, isError: hookResult.isError ?? isError }; }; } 设计要点：钩子只安装一次，但每次调用时读取 this._extensionRunner 的最新值。这意味着扩展重载后新 runner 自动生效，无需重新安装钩子。\n段 3：事件系统（第 340-580 行） 事件订阅 1 2 3 4 subscribe(listener: AgentSessionEventListener): () =\u0026gt; void { this._eventListeners.push(listener); return () =\u0026gt; { const i = this._eventListeners.indexOf(listener); if (i !== -1) this._eventListeners.splice(i, 1); }; } 模式：返回取消函数（类似 DOM addEventListener 返回的移除函数）。每个 listener 独立管理。\n_handleAgentEvent() — 事件处理枢纽 收到 AgentEvent │ ├── Phase 1: 队列管理 — 从 steer/followUp 队列移除已消费消息 │ ├── Phase 2: 扩展系统 — _emitExtensionEvent() 转发 │ ├── Phase 3: 自定义 listener — _emit() 含 willRetry 增强 │ └── Phase 4: 消息持久化 — sessionManager.appendMessage() └── 如果是 assistant → 重试计数器管理 _replaceMessageInPlace() — 原地替换消息 1 2 3 4 5 6 private _replaceMessageInPlace(target: AgentMessage, replacement: AgentMessage): void { if (target === replacement) return; const targetRecord = target as unknown as Record\u0026lt;string, unknown\u0026gt;; for (const key of Object.keys(targetRecord)) delete targetRecord[key]; Object.assign(targetRecord, replacement); } 语法要点 — 可变对象替换：当扩展的 message_end handler 修改消息时，使用此方法将新属性覆盖到原对象上，保持所有引用同步。删除现有属性后再 Object.assign，确保删除旧值中没有的属性。\n_emitExtensionEvent() — 事件映射桥 AgentEvent ExtensionEvent ────────── ────────────── agent_start ────────────────── agent_start agent_end ────────────────── agent_end turn_start ───→ TurnStartEvent turn_end ───→ TurnEndEvent message_start ───→ MessageStartEvent message_update ───→ MessageUpdateEvent message_end ───→ MessageEndEvent（+ 替换消息） tool_execution_start ──→ ToolExecutionStartEvent tool_execution_update ──→ ToolExecutionUpdateEvent tool_execution_end ──→ ToolExecutionEndEvent 段 4：Prompt 处理流程（第 580-800 行） prompt() — 7 步处理管道 用户输入 text │ Step 1: 扩展命令？(/command) → 直接执行，返回 │ Step 2: input 事件给扩展 → \u0026#34;handled\u0026#34; 返回 / \u0026#34;transform\u0026#34; 修改文本 │ Step 3: 展开 /skill:name 和提示模板 │ Step 4: 流式中？→ 排队（steer/followUp），返回 │ Step 5: 认证检查（模型 + API key）→ 无则抛错 │ Step 6: 预 prompt compaction → 有则 agent.continue() │ Step 7: before_agent_start 事件 + 构建消息 → _runAgentPrompt() _runAgentPrompt() / _handlePostAgentRun() — 后处理链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private async _runAgentPrompt(messages): Promise\u0026lt;void\u0026gt; { try { await this.agent.prompt(messages); while (await this._handlePostAgentRun()) { await this.agent.continue(); } } finally { this._flushPendingBashMessages(); } } private async _handlePostAgentRun(): Promise\u0026lt;boolean\u0026gt; { // 1. 重试检查 → 是则准备重试，返回 true if (this._isRetryableError(msg) \u0026amp;\u0026amp; await this._prepareRetry(msg)) return true; // 2. 压缩检查 → 是则执行压缩，返回 true（overflow 会继续重试） if (await this._checkCompaction(msg)) return true; // 3. 队列检查 → extension 在 agent_end 中排队了消息 return this.agent.hasQueuedMessages(); } 关键设计：每次 agent.continue() 后都重新跑整个后处理链。重试 → 压缩 → 队列消费，直到三者都不满足才结束循环。\n段 5：Steer / FollowUp 消息队列（第 800-960 行） prompt(\u0026#34;text\u0026#34;) isStreaming=true │ ┌─────┴──────┐ │ streaming │ │ Behavior? │ └─────┬──────┘ ┌─────┴───────┐ ▼ ▼ steer() followUp() │ │ ▼ ▼ agent.steer() agent.followUp() │ │ ▼ ▼ 中断当前 LLM 等待工具链完成 立即处理 LLM 空闲时消费 1 2 3 4 5 6 7 8 9 10 11 12 async steer(text: string, images?: ImageContent[]): Promise\u0026lt;void\u0026gt; { if (text.startsWith(\u0026#34;/\u0026#34;)) this._throwIfExtensionCommand(text); // 命令不能排队 let expandedText = this._expandSkillCommand(text); expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]); await this._queueSteer(expandedText, images); } private async _queueSteer(text: string, images?: ImageContent[]): Promise\u0026lt;void\u0026gt; { this._steeringMessages.push(text); this._emitQueueUpdate(); this.agent.steer({ role: \u0026#34;user\u0026#34;, content: [{ type: \u0026#34;text\u0026#34;, text }, ...(images ?? [])], timestamp: Date.now() }); } 两种策略：steer 中断当前 LLM 立即处理，followUp 等当前工具链完成。模式有 \u0026quot;all\u0026quot;（全排）和 \u0026quot;one-at-a-time\u0026quot;（一次一个），由 settings 控制。\n段 6：模型管理（第 960-1130 行） 模型切换 1 2 3 4 5 6 7 8 9 async setModel(model: Model\u0026lt;any\u0026gt;): Promise\u0026lt;void\u0026gt; { if (!this._modelRegistry.hasConfiguredAuth(model)) throw new Error(...); const previousModel = this.model; // 保存旧模型 this.agent.state.model = model; // 设置新模型 this.sessionManager.appendModelChange(model.provider, model.id); // 持久化 this.settingsManager.setDefaultModelAndProvider(model.provider, model.id); // 保存为默认 this.setThinkingLevel(thinkingLevel); // 重新钳位 thinking level await this._emitModelSelect(model, previousModel, \u0026#34;set\u0026#34;); // 通知扩展 } 模型循环 1 2 3 4 async cycleModel(direction: \u0026#34;forward\u0026#34; | \u0026#34;backward\u0026#34;): Promise\u0026lt;ModelCycleResult | undefined\u0026gt; { if (this._scopedModels.length \u0026gt; 0) return this._cycleScopedModel(direction); // --models 优先 return this._cycleAvailableModel(direction); // 回退到 ModelRegistry } 两阶段：scopedModels（CLI --models）→ ModelRegistry（所有已认证模型）。\nThinking Level 钳位 1 2 3 4 5 6 7 setThinkingLevel(level: ThinkingLevel): void { const effectiveLevel = this.getAvailableThinkingLevels().includes(level) ? level : this._clampThinkingLevel(level, this.getAvailableThinkingLevels()); // 只在实际改变时持久化 if (effectiveLevel !== this.agent.state.thinkingLevel) { ... } } clampThinkingLevel() 来自 @earendil-works/pi-ai/compat，将请求的 level 适配到模型能力范围内。\n段 7：Compaction 机制（第 1130-1600 行） 两种触发场景 场景 1: Context Overflow（溢出） LLM 返回 stopReason=error + isContextOverflow() → 移除错误消息（保留 session 历史） → 压缩 → 自动重试（一次，_overflowRecoveryAttempted 防循环） 场景 2: Context Threshold（超阈值） assistant message usage \u0026gt; settings.threshold → 压缩 → 不自动重试，用户手动继续 _checkCompaction() — 多层过滤 1 2 3 4 5 6 7 8 9 10 11 12 private async _checkCompaction(assistantMessage, skipAbortedCheck = true): Promise\u0026lt;boolean\u0026gt; { // 1. 跳过中止消息 if (skipAbortedCheck \u0026amp;\u0026amp; stopReason === \u0026#34;aborted\u0026#34;) return false; // 2. 跳过来自不同模型的消息（用户已切换模型） if (!sameModel) return false; // 3. 跳过最近一次压缩之前的消息（防重复触发） if (assistantIsFromBeforeCompaction) return false; // 4. Overflow → 压缩 + 重试 if (isContextOverflow(assistantMessage, contextWindow)) { ... } // 5. Threshold → 压缩 if (shouldCompact(contextTokens, contextWindow, settings)) { ... } } _runAutoCompaction() — 自动压缩流程 1. compaction_start 事件（reason: manual/threshold/overflow） 2. 获取 API key + auth 3. prepareCompaction() → 选择要压缩的条目 4. session_before_compact 事件 → 扩展可 cancel / 提供自己的摘要 5. compact() — LLM 生成摘要 或 扩展提供 6. sessionManager.appendCompaction() 7. sessionManager.buildSessionContext() → 更新 agent.state.messages 8. session_compact 事件 9. compaction_end 事件 10. willRetry? → true → agent.continue() 扩展的 3 种干涉方式：{ cancel: true } 阻止压缩；{ compaction: CompactionResult } 提供自己的摘要替代 LLM；{ customInstructions } 影响摘要方向。\n段 8：自动重试机制（第 1600-1750 点） _isRetryableError() — 错误模式匹配 1 2 3 4 5 6 7 8 private _isRetryableError(message: AssistantMessage): boolean { const err = message.errorMessage; if (this._isNonRetryableProviderLimitError(err)) return false; // 覆盖数百种错误： return /overloaded|provider.?returned.?error|rate.?limit|too many requests| 429|500|502|503|504|service.?unavailable|connection.?error|connection.?refused| websocket.?closed|fetch failed|socket hang up|timed? out|terminated|retry delay/i.test(err); } 正则覆盖：HTTP 429/5xx、网络错误、WebSocket 断开、超时、terminated。通过 _isNonRetryableProviderLimitError 排除\u0026quot;不可重试的 provider limit\u0026quot;（如配额用尽）。\n_prepareRetry() — 指数退避 1 2 3 4 5 6 7 8 9 private async _prepareRetry(message: AssistantMessage): Promise\u0026lt;boolean\u0026gt; { this._retryAttempt++; if (this._retryAttempt \u0026gt; settings.maxRetries) { this._retryAttempt--; return false; } const delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1); // auto_retry_start 事件 → 移除错误消息 → sleep(delayMs, abortable) // 若 sleep 被中止 → auto_retry_end(success: false) return true; } 公式：baseDelayMs * 2^(attempt-1)。_retryAbortController 使等待可中断。\n计数器重置：收到非错误的 assistant 响应时 _retryAttempt = 0，避免跨多轮 LLM 调用累积。\n段 9：Bash 执行（第 1750-1900 行） 1 2 3 4 5 6 7 8 9 10 11 async executeBash(command, onChunk?, options?): Promise\u0026lt;BashResult\u0026gt; { this._bashAbortController = new AbortController(); const result = await executeBashWithOperations( resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), { onChunk, signal: this._bashAbortController.signal }, ); this.recordBashResult(command, result, options); return result; } 流式兼容：如果 agent 正在流式处理，bash 结果不立即加入 agent state（避免破坏 tool_use/tool_result 顺序），而是排队到 _pendingBashMessages，在 _flushPendingBashMessages()（_runAgentPrompt 的 finally 块中）统一刷新。\n段 10：会话树导航（第 1900-2200 行） navigateTree() — 完整流程 用户请求跳转到 entry X │ Step 1: 检查是否已在目标 → 无操作 │ Step 2: collectEntriesForBranchSummary() （从当前叶节点回溯到共同祖先） │ Step 3: session_before_tree 事件 （扩展可 cancel / 提供摘要 / 覆盖指令/标签） │ Step 4: 需要摘要？→ generateBranchSummary()（LLM）或扩展提供 │ Step 5: 确定新叶节点位置 ├── user 消息 → 叶=parent（编辑器恢复文本） ├── custom_msg → 叶=parent（恢复内容到编辑器） └── 其他 → 叶=选中节点 │ Step 6: sessionManager.branchWithSummary() / branch() / resetLeaf() → 附加标签（label） │ Step 7: sessionManager.buildSessionContext() → agent.state.messages │ Step 8: session_tree 事件 fork / session 切换 1 2 3 4 // ExtensionCommandContextActions 中定义，由各运行模式实现 newSession(options?: { parentSession?, setup?, withSession? }) fork(entryId, options?: { position?, withSession? }) switchSession(sessionPath, options?: { withSession? }) 段 11：扩展系统集成（第 2200-2650 行） bindExtensions() → _bindExtensionCore() 这是 AgentSession 和 ExtensionRunner 之间的核心桥接代码，将所有 ExtensionAPI 方法连接到 AgentSession 的内部实现。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 runner.bindCore( { // ExtensionActions — 扩展的 pi.sendMessage / pi.sendUserMessage 等 sendMessage: (message, options) =\u0026gt; this.sendCustomMessage(message, options), sendUserMessage: (content, options) =\u0026gt; this.sendUserMessage(content, options), appendEntry: (customType, data) =\u0026gt; this.sessionManager.appendCustomEntry(customType, data), setSessionName: (name) =\u0026gt; this.setSessionName(name), getSessionName: () =\u0026gt; this.sessionManager.getSessionName(), getActiveTools: () =\u0026gt; this.getActiveToolNames(), getAllTools: () =\u0026gt; this.getAllTools(), setActiveTools: (names) =\u0026gt; this.setActiveToolsByName(names), refreshTools: () =\u0026gt; this._refreshToolRegistry(), getCommands: () =\u0026gt; [...extensionCommands, ...templates, ...skills], setModel: async (model) =\u0026gt; { ... await this.setModel(model); return true; }, getThinkingLevel: () =\u0026gt; this.thinkingLevel, setThinkingLevel: (level) =\u0026gt; this.setThinkingLevel(level), }, { /* ExtensionContextActions — ctx.* 方法 */ }, { /* Provider registration */ }, ); getCommands() 的三源合并：扩展命令（runner.getRegisteredCommands()）+ 提示模板（this.promptTemplates）+ 技能（this._resourceLoader.getSkills().skills），统一返回用户可调用的斜杠命令列表。\n段 12：工具注册与管理（第 2650-2900 行） _buildRuntime() — 初始化工具 内置工具定义 (createAllToolDefinitions) + 基础工具覆盖 (baseToolsOverride) + SDK 自定义工具 (customTools) + 内置工具 AgentTool → ToolDefinition 转换 + allowlist / denylist 过滤 + 扩展注册的工具 │ ▼ _toolDefinitions Map\u0026lt;string, ToolDefinitionEntry\u0026gt; _toolRegistry Map\u0026lt;string, AgentTool\u0026gt; (给 agent-core 使用) _toolPromptSnippets / _toolPromptGuidelines │ ▼ _rebuildSystemPrompt() → 构建系统提示 _bindExtensionCore() → 注册到 ExtensionRunner setActiveToolsByName() — 切换激活工具 1 2 3 4 5 6 7 8 9 10 setActiveToolsByName(toolNames: string[]): void { const tools: AgentTool[] = []; for (const name of toolNames) { const tool = this._toolRegistry.get(name); if (tool) { tools.push(tool); } } this.agent.state.tools = tools; // 更新 agent this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames); // 重建提示 this.agent.state.systemPrompt = this._baseSystemPrompt; } 段 13：上下文使用统计（第 2900-3050 行） getContextUsage() — 复杂计算 1 2 3 4 5 6 7 8 9 10 11 getContextUsage(): ContextUsage | undefined { // 压缩后：只信任压缩边界之后的 assistant usage // （压缩前的 usage 反映的是老的大上下文） const latestCompaction = getLatestCompactionEntry(branchEntries); if (latestCompaction \u0026amp;\u0026amp; !hasPostCompactionUsage) { return { tokens: null, contextWindow, percent: null }; // 未知 } // 正常情况：估算 token 数 const estimate = estimateContextTokens(this.messages); return { tokens: estimate.tokens, contextWindow, percent: (estimate.tokens / contextWindow) * 100 }; } 段 14：导出与其他工具（第 3050-3159 行） exportToHtml / exportToJsonl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async exportToHtml(outputPath?: string): Promise\u0026lt;string\u0026gt; { // 创建工具渲染器（支持自定义工具 HTML 渲染） const toolRenderer = createToolHtmlRenderer({ getToolDefinition: (name) =\u0026gt; this.getToolDefinition(name), ... }); return await exportSessionToHtml(this.sessionManager, this.state, { outputPath, themeName, toolRenderer }); } exportToJsonl(outputPath?: string): string { // 线性化分支：重新建立 parentId 链，写入 JSONL 文件 let prevId: string | null = null; for (const entry of branchEntries) { const linear = { ...entry, parentId: prevId }; writeFileSync(filePath, `${lines.join(\u0026#34;\\n\u0026#34;)}\\n`); prevId = entry.id; } } createReplacedSessionContext() 1 2 3 4 5 6 7 createReplacedSessionContext(): ReplacedSessionContext { // 使用 Object.defineProperties 复制惰性 getter const context = Object.defineProperties({}, Object.getOwnPropertyDescriptors(this._extensionRunner.createCommandContext())) as ReplacedSessionContext; context.sendMessage = (message, options) =\u0026gt; this.sendCustomMessage(message, options); context.sendUserMessage = (content, options) =\u0026gt; this.sendUserMessage(content, options); return context; } 语法要点 — Object.getOwnPropertyDescriptors()：正确复制惰性 getter 的唯一方式。普通展开 { ...obj } 会立即求值所有 getter，丢失惰性特性。\n关系总结 引用与被引用关系 agent-session.ts 被谁引用？ ├── modes/interactive/InteractiveMode.ts (构造 AgentSession) ├── modes/print/PrintMode.ts (构造 AgentSession) ├── modes/rpc/RPCMode.ts (构造 AgentSession) └── ... (其他使用 AgentSession 的模式文件) agent-session.ts 引用了什么？ ├── ./extensions/* → ExtensionRunner 和事件类型 ├── ./session-manager.ts → 会话持久化 ├── ./model-registry.ts → 模型注册中心 ├── ./settings-manager.ts → 设置管理 ├── ./resource-loader.ts → 技能/提示/主题加载 ├── ./compaction/index.ts → 压缩逻辑 ├── ./bash-executor.ts → Bash 执行 └── ... (共 ~25 个内部模块) 核心数据流 用户输入 │ ▼ AgentSession.prompt() │ ├── input event → ExtensionRunner (扩展可拦截/转换) ├── skill/template 展开 ├── auth 检查 → ModelRegistry │ ▼ agent.prompt() / agent.continue() │ ▼ (agent-core 内部循环) ├── LLM 调用 (Model + ApiKey) ├── 工具执行 → beforeToolCall/afterToolCall → ExtensionRunner │ ▼ _handleAgentEvent() │ ├── _emitExtensionEvent() → ExtensionRunner → 扩展 ├── _emit() → AgentSessionEventListener (UI/日志) └── sessionManager.appendMessage() → 文件持久化 │ ▼ _handlePostAgentRun() ├── 重试? → _prepareRetry() → 指数退避 ├── 压缩? → _runAutoCompaction() → LLM 摘要 └── 队列? → agent.continue() 关键设计模式总结 模式 位置 说明 桥接模式 _emitExtensionEvent() AgentEvent → ExtensionEvent 的类型转换与转发 责任链模式 _handlePostAgentRun() 重试 → 压缩 → 队列消费的链式检查 策略模式 prompt() 中 streaming 分支 steer/followUp 两种排队策略 观察者模式 subscribe() / _emit() 自定义 listener 订阅 AgentSession 事件 AbortController 多处 可中断的 sleep、compaction、bash、retry 两阶段模型解析 cycleModel() \u0026ndash;models 限定 → ModelRegistry 全局 惰性求值 createReplacedSessionContext() Object.getOwnPropertyDescriptors 复制 getter 两阶段初始化 _buildRuntime() → bindExtensions() ","tags":["Pi","AgentSession","Architecture","TypeScript","Session Management"],"categories":["Code Analysis"]},{"title":"Computer Networking — Chapter 1: Computer Networks and the Internet","permalink":"https://yukar.icu/posts/networking/computer-networking-ch1/","summary":"What Is the Internet? The Internet can be described in two ways:\nNuts-and-Bolts Description — The Internet is a computer network that interconnects billions of computing devices (called hosts or end systems) worldwide through a network of communication links and packet switches (routers and link-layer switches). End systems access the Internet through Internet Service Providers (ISPs).\nServices Description — The Internet is an infrastructure that provides services to distributed applications (e.g., Web, email, streaming, social networks). Applications run on end systems, not on packet switches. End systems provide a socket interface that specifies how a program asks the Internet to deliver data to a destination program.\n","content":"What Is the Internet? The Internet can be described in two ways:\nNuts-and-Bolts Description — The Internet is a computer network that interconnects billions of computing devices (called hosts or end systems) worldwide through a network of communication links and packet switches (routers and link-layer switches). End systems access the Internet through Internet Service Providers (ISPs).\nServices Description — The Internet is an infrastructure that provides services to distributed applications (e.g., Web, email, streaming, social networks). Applications run on end systems, not on packet switches. End systems provide a socket interface that specifies how a program asks the Internet to deliver data to a destination program.\nDefinition of a Protocol. A protocol defines the format and the order of messages exchanged between two or more communicating entities, as well as the actions taken on the transmission and/or receipt of a message or other event.\nInternet standards are developed by the Internet Engineering Task Force (IETF). Standards documents are called Requests for Comments (RFCs).\nThe Network Edge End systems are also referred to as hosts. Hosts are divided into clients and servers. Most servers reside in large data centers (e.g., Google has 50-100 data centers).\nAccess Networks — The network that physically connects an end system to the first router (edge router):\nType Technology Downstream Rate Key Feature DSL Telephone line (twisted-pair copper) up to 55 Mbps Asymmetric; uses frequency-division multiplexing Cable HFC (hybrid fiber coax) up to 42.8 Mbps (DOCSIS 2.0) Shared broadcast medium FTTH Optical fiber Gbps range PON or AON architecture Ethernet Twisted-pair copper 100 Mbps - 10 Gbps Dominant LAN technology WiFi (802.11) Radio spectrum up to 100+ Mbps Wireless LAN 3G/4G (LTE) Cellular radio 10+ Mbps Wide-area wireless Physical Media — Two categories:\nGuided media: twisted-pair copper wire, coaxial cable, fiber-optic cable Unguided media: terrestrial radio spectrum, satellite radio spectrum Optical Carrier (OC) standard link speeds: OC-n = n × 51.8 Mbps (OC-1, OC-3, \u0026hellip;, OC-768).\nThe Network Core Packet Switching — End systems break data into packets. Each packet travels through the network independently. Store-and-forward transmission: a packet switch must receive the entire packet before it can begin to transmit on the output link.\nStore-and-Forward Delay. If a packet of L bits is transmitted at rate R bps, the time to push the packet onto the link is L/R seconds. With N links of rate R between source and destination, the end-to-end delay for one packet is: d_end = N × (L/R)\nCircuit Switching — A dedicated end-to-end circuit is established before communication. Resources along the path are reserved for the duration. Example: traditional telephone network. Uses Frequency-Division Multiplexing (FDM) or Time-Division Multiplexing (TDM).\nKey Comparison:\nPacket switching: no reservation, statistical sharing, can be \u0026ldquo;best-effort\u0026rdquo; Circuit switching: guaranteed resources, but less efficient for bursty traffic A Network of Networks — ISPs are interconnected in a hierarchy:\nAccess ISPs (residential, corporate, university) Regional ISPs Tier-1 ISPs (e.g., Level 3, AT\u0026amp;T, Sprint, NTT) IXP (Internet Exchange Point) — a meeting point where multiple ISPs peer together PoP (Point of Presence) — a group of routers in an ISP\u0026rsquo;s network where routers from other ISPs can connect Delay, Loss, and Throughput Four Sources of Packet Delay:\nProcessing delay (d_proc) — time to examine packet header, check bit-level errors Queuing delay (d_queue) — time waiting in output buffer for transmission Transmission delay (d_trans = L/R) — time to push all bits of packet onto the link Propagation delay (d_prop = d/s) — time for signal to propagate across the medium Total Nodal Delay: d_nodal = d_proc + d_queue + d_trans + d_prop\nTraffic Intensity — Let L = packet length (bits), a = average packet arrival rate (packets/sec), R = transmission rate (bps). Traffic intensity = La/R.\nIf La/R \u0026gt; 1: queue grows without bound (infinite delay) If La/R \u0026lt;= 1: queuing delay depends on burstiness of arrivals Packet Loss — When a queue (output buffer) is full, arriving packets are dropped (lost).\nThroughput — The rate at which bits are transferred from sender to receiver. For a path with multiple links, the bottleneck link (the link with the smallest transmission rate) determines the end-to-end throughput.\nProtocol Layers and Service Models Five-Layer Internet Protocol Stack:\nLayer Description Protocols/Units Application Support network applications HTTP, SMTP, DNS, FTP — messages Transport Process-to-process data delivery TCP, UDP — segments Network Routing datagrams from source to destination IP, routing protocols — datagrams Link Data transfer between neighboring network elements Ethernet, WiFi, PPP — frames Physical Moving individual bits across the wire — bits OSI Model adds two layers: Presentation and Session.\nEncapsulation — Each layer takes data from the layer above, adds header information, and passes it to the layer below. At the receiving end, the process is reversed (de-encapsulation).\nNetworks Under Attack Malware: viruses (user interaction needed) and worms (self-propagating without user action) Botnet: network of compromised devices controlled by attackers DoS (Denial-of-Service) attacks: Vulnerability attack (crash the target with crafted messages) Bandwidth flooding (overwhelm the target\u0026rsquo;s access link) Connection flooding (bogus TCP connections) Packet sniffing: passive receiver captures packets (e.g., WiFi) IP spoofing: sending packets with a false source address Key Formulas Concept Formula Variable Meaning Transmission delay d_trans = L / R L = packet length (bits), R = link rate (bps) Propagation delay d_prop = d / s d = distance, s = propagation speed (~2.5×10⁸ m/s) Total nodal delay d_nodal = d_proc + d_queue + d_trans + d_prop Four delay components Traffic intensity I = La / R L = packet size, a = arrival rate, R = link rate End-to-end delay (N links, 1 packet) d_e2e = N × L / R Store-and-forward over N same-rate links References Computer Networking: A Top-Down Approach, 7th Edition — James F. Kurose and Keith W. Ross, Pearson, 2017 RFC 793 — Transmission Control Protocol RFC 768 — User Datagram Protocol ","tags":["Computer Networking","Internet","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 2: Application Layer","permalink":"https://yukar.icu/posts/networking/computer-networking-ch2/","summary":"Principles of Network Applications Network Application Architectures:\nClient-Server: a dedicated server (always-on, fixed IP) serves requests from clients. Clients do not communicate directly with each other. Peer-to-Peer (P2P): peers communicate directly, without always-on servers. Self-scalability. Processes Communicating:\nA process sends/receives messages via a socket (API between application and transport layer) The port number identifies the receiving process on a host The destination is uniquely identified by: (IP address, port number) Transport Services Available:\n","content":"Principles of Network Applications Network Application Architectures:\nClient-Server: a dedicated server (always-on, fixed IP) serves requests from clients. Clients do not communicate directly with each other. Peer-to-Peer (P2P): peers communicate directly, without always-on servers. Self-scalability. Processes Communicating:\nA process sends/receives messages via a socket (API between application and transport layer) The port number identifies the receiving process on a host The destination is uniquely identified by: (IP address, port number) Transport Services Available:\nService TCP UDP Reliable data transfer Yes No Throughput No guarantee No guarantee Timing No guarantee No guarantee Security (SSL) Yes (via TLS) No Connection-oriented Yes No Transport Services Provided by the Internet:\nTCP: connection-oriented, reliable, includes congestion control and flow control UDP: connectionless, unreliable, no frills SSL (Secure Sockets Layer): enhanced TCP with encryption, integrity, and authentication Application-Layer Protocols define:\nTypes of messages exchanged (request, response) Message syntax (format, fields) Message semantics (meaning of fields) Rules for when/how processes send/respond The Web and HTTP HTTP (HyperText Transfer Protocol):\nClient-server protocol (Web browser ↔ Web server) Uses TCP (port 80 by default) Stateless — server maintains no information about past client requests Non-Persistent vs. Persistent Connections:\nNon-persistent (HTTP/1.0): one TCP connection per request/response pair. RTT × 2 + file transmission time per object. Persistent (HTTP/1.1 default): multiple objects can be sent over a single TCP connection. Reduces overhead. HTTP with non-persistent connections: Time to receive one file = 2×RTT + (file size / transmission rate)\nHTTP Message Format:\nRequest message:\nRequest line (method, URL, version): e.g., GET /index.html HTTP/1.1 Header lines: Host:, Connection:, User-Agent:, Accept-Language: Blank line Entity body (for POST) Response message:\nStatus line (version, status code, phrase): e.g., HTTP/1.1 200 OK Header lines: Date:, Server:, Last-Modified:, Content-Length:, Content-Type: Blank line Entity body (requested data) Common status codes: 200 OK, 301 Moved Permanently, 400 Bad Request, 404 Not Found, 505 HTTP Version Not Supported.\nCookies — Allow websites to track users across sessions. Four components: cookie header in HTTP response, cookie header in HTTP request, cookie file on client, back-end database on server.\nWeb Caching (Proxy Server):\nA Web cache (proxy server) stores copies of recently requested objects Reduces response time for client requests Reduces traffic on an institution\u0026rsquo;s access link Conditional GET (If-Modified-Since header) ensures cache is fresh Electronic Mail Three Major Components:\nUser agents (Outlook, Thunderbird, mobile mail apps) Mail servers (mailbox + message queue) SMTP (Simple Mail Transfer Protocol) SMTP:\nUses TCP (port 25) Transfers messages from sender\u0026rsquo;s mail server to receiver\u0026rsquo;s mail server Direct transfer (no intermediate mail servers) Uses persistent connections Requires 7-bit ASCII encoding Mail Access Protocols:\nPOP3 (Post Office Protocol v3): download-and-delete or download-and-keep IMAP (Internet Mail Access Protocol): server stores messages in folders, allows remote folder management HTTP: Web-based email (Gmail, Outlook.com) Message Format:\nHeader: From:, To:, Subject: Body (ASCII text) MIME (Multimedia Internet Mail Extension) enables non-ASCII content DNS — The Internet\u0026rsquo;s Directory Service Services Provided by DNS:\nHostname to IP address translation Host aliasing (canonical vs. alias names) Mail server aliasing Load distribution (round-robin among replicated servers) DNS is a distributed, hierarchical database, implemented in a hierarchy of DNS servers. It uses UDP (port 53).\nDNS Hierarchy:\nRoot DNS servers (13 logical root servers, ~200 physical servers worldwide) Top-Level Domain (TLD) servers (.com, .org, .net, .edu, .gov, country TLDs) Authoritative DNS servers (provide authoritative hostname-to-IP mappings for organizations) Local DNS Server (LDNS) — acts as a proxy, forwarding queries into the hierarchy.\nQuery Types:\nIterative query: the contacted server replies with the next server to contact Recursive query: the contacted server takes responsibility for finding the answer DNS Caching — DNS servers cache mappings to improve performance. Entries are discarded after a time-to-live (TTL).\nDNS Records (Resource Records): stored in DNS servers with format: (Name, Value, Type, TTL)\nType Name Value A hostname IP address NS domain hostname of authoritative DNS server CNAME alias name canonical name MX name mail server canonical name DNS Messages: both query and reply use the same format with 12-byte header + question section + answer section + authority section + additional section.\nDNS Registration: an organization registers its domain name with a registrar, providing names/IPs of its primary and secondary authoritative DNS servers.\nPeer-to-Peer Applications P2P File Distribution (BitTorrent):\nTracker: tracks which peers are participating in the torrent Torrent: group of peers exchanging chunks of a file Chunks: a file is divided into 256KB chunks Tit-for-tat: a peer gives chunks to neighbors that are currently providing chunks at the highest rate P2P self-scalability: the total service capacity (upload bandwidth) grows with the number of peers, unlike the client-server model.\nVideo Streaming and Content Distribution Networks Internet Video: video is compressed; multiple versions at different quality/bitrates can be created.\nDASH (Dynamic Adaptive Streaming over HTTP):\nVideo is encoded into multiple versions at different rates Video is divided into chunks (a few seconds each) Client requests chunks dynamically, adapting to available bandwidth Manifest file provides URL and bitrate for each version Content Distribution Networks (CDN):\nPrivate CDN (e.g., Google\u0026rsquo;s CDN for YouTube) Third-party CDN (e.g., Akamai, Limelight, Level-3) CDN strategies:\nEnter deep: many server clusters close to users (Akamai) Bring home: fewer large clusters at key locations (Google, Netflix via Amazon Cloud) CDN selects the server cluster based on: DNS-based redirection, client LDNS IP address, real-time cluster load measurements.\nCase Studies:\nNetflix: uses Amazon Cloud + own Open Connect CDN appliances in ISPs YouTube: cache-friendly, Google\u0026rsquo;s private CDN in data centers Kankan: P2P-based streaming Socket Programming Socket — the API between an application process and the transport layer. Two types:\nUDP Socket:\nConnectionless: no handshake before sending Must attach destination address to each packet (datagram) UDPClient.sendto(message_bytes, (server_hostname, server_port)) 1 2 3 4 5 6 7 # UDP Client from socket import * serverName = \u0026#39;hostname\u0026#39; serverPort = 12000 clientSocket = socket(AF_INET, SOCK_DGRAM) clientSocket.sendto(message.encode(), (serverName, serverPort)) modifiedMessage, serverAddress = clientSocket.recvfrom(2048) TCP Socket:\nConnection-oriented: client establishes TCP connection with server via a three-way handshake Once connection established, sends bytes as a stream (no need to attach address) Server creates a welcome socket (port) and a connection socket for each client 1 2 3 4 5 6 7 8 9 10 11 # TCP Server from socket import * serverPort = 12000 serverSocket = socket(AF_INET, SOCK_STREAM) serverSocket.bind((\u0026#39;\u0026#39;, serverPort)) serverSocket.listen(1) # listen for up to 1 client while True: connectionSocket, addr = serverSocket.accept() sentence = connectionSocket.recv(1024).decode() connectionSocket.send(capitalizedSentence.encode()) connectionSocket.close() Key Formula Concept Formula Variable Meaning HTTP non-persistent (1 object) Time = 2×RTT + L/R RTT = round-trip time References Computer Networking: A Top-Down Approach, 7th Edition — James F. Kurose and Keith W. Ross, Pearson, 2017 RFC 2616 — HTTP/1.1 RFC 5321 — Simple Mail Transfer Protocol RFC 1034/1035 — Domain Names ","tags":["Computer Networking","Application Layer","HTTP","DNS","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 3: Transport Layer","permalink":"https://yukar.icu/posts/networking/computer-networking-ch3/","summary":"Introduction and Transport-Layer Services A transport-layer protocol provides for logical communication between application processes running on different hosts. The transport layer converts the network layer\u0026rsquo;s host-to-host delivery into process-to-process delivery.\nRelationship Between Transport and Network Layers:\nNetwork layer: logical communication between hosts (IP, best-effort) Transport layer: logical communication between processes (extending the network service) Transport protocols can be implemented only at the endpoints (end-to-end principle) Internet Transport Protocols:\nTCP — reliable, connection-oriented, congestion control, flow control UDP — unreliable, connectionless, no frills Multiplexing and Demultiplexing Demultiplexing — delivering the data in a transport-layer segment to the correct socket at the receiving host.\n","content":"Introduction and Transport-Layer Services A transport-layer protocol provides for logical communication between application processes running on different hosts. The transport layer converts the network layer\u0026rsquo;s host-to-host delivery into process-to-process delivery.\nRelationship Between Transport and Network Layers:\nNetwork layer: logical communication between hosts (IP, best-effort) Transport layer: logical communication between processes (extending the network service) Transport protocols can be implemented only at the endpoints (end-to-end principle) Internet Transport Protocols:\nTCP — reliable, connection-oriented, congestion control, flow control UDP — unreliable, connectionless, no frills Multiplexing and Demultiplexing Demultiplexing — delivering the data in a transport-layer segment to the correct socket at the receiving host.\nMultiplexing — gathering data from multiple sockets at the source host, encapsulating with header info (for demultiplexing), and passing to the network layer.\nHow demultiplexing works:\nUDP: uses destination IP + destination port (connectionless) TCP: uses source IP + source port + destination IP + destination port (connection-oriented) Multiplexing/demultiplexing is the mechanism by which transport layer extends host-to-host delivery to process-to-process delivery.\nConnectionless Transport: UDP UDP (User Datagram Protocol):\nNo handshake before sending (connectionless) Unreliable — segment may be lost or delivered out of order No congestion control — sender can pump data at any rate Lightweight — minimal header overhead (8 bytes) UDP Segment Structure:\nField Size Source port 16 bits Destination port 16 bits Length 16 bits Checksum 16 bits Application data (payload) variable UDP Checksum:\nDetects errors in the segment (header + data) One\u0026rsquo;s complement sum of: segment words + IP pseudo-header Receiver checksums the whole segment; if all bits are 1, no error detected Why use UDP? No connection establishment (no delay), no connection state (more clients), small header, no congestion control (can send at application-determined rate).\nPrinciples of Reliable Data Transfer rdt 1.0 — Reliable Transfer over a Perfectly Reliable Channel The underlying channel is perfectly reliable — no bit errors, no packet loss.\nrdt_send(data): pkt = make_pkt(data) udt_send(pkt) rdt_rcv(pkt): extract(pkt, data) deliver_data(data) rdt 2.0 — Reliable Transfer over a Channel with Bit Errors Use ACK (positive acknowledgment) and NAK (negative acknowledgment), plus checksum for error detection. This is an Automatic Repeat reQuest (ARQ) protocol.\nrdt_send(data): sndpkt = make_pkt(data, checksum) udt_send(sndpkt) rdt_rcv(rcvpkt) and isACK(rcvpkt): deliver_data(extract(rcvpkt)) rdt_rcv(rcvpkt) and isNAK(rcvpkt): retransmit(sndpkt) Problem: What if ACK/NAK itself is corrupted? Solution: Add sequence numbers.\nrdt 2.1 and rdt 2.2 — Handling Corrupted ACK/NAK Add a sequence number (0 or 1) to each packet Sender includes sequence number in packet Receiver includes ACK-ed sequence number in acknowledgment rdt 2.2: NAK-free protocol — receiver sends ACK with sequence number of last correctly received packet; sender retransmits if duplicate ACK received rdt 3.0 — Reliable Transfer over a Lossy Channel with Bit Errors Add a timer mechanism. Sender starts a timer after sending a packet. If timer expires before receiving ACK, retransmit the packet.\nrdt 3.0 (Alternating-Bit Protocol) handles both bit errors and packet loss. Performance problem: stop-and-wait limits throughput to 1 packet per RTT.\nPipelined Reliable Data Transfer Stop-and-wait utilization: U_sender = (L/R) / (RTT + L/R). With large RTT, this is very low.\nPipelining allows multiple packets to be in flight simultaneously, increasing throughput.\nGo-Back-N (GBN) Sender can have up to N unACK-ed packets Sender maintains a base (oldest unACK-ed) and nextseqnum (next to send) A single timer for the oldest unACK-ed packet If timeout occurs, retransmit all packets from base to nextseqnum-1 Receiver only accepts packets in order; discards out-of-order packets (no buffering) Cumulative ACK: ACK(n) acknowledges all packets up through n Selective Repeat (SR) Sender: timer for each in-flight packet Only retransmits the specific packet that timed out Receiver: acknowledges each correctly received packet, buffers out-of-order packets Sender window size must be \u0026lt;= half the sequence number space to avoid ambiguity Connection-Oriented Transport: TCP TCP Connection Full-duplex service Point-to-point (single sender, single receiver) Connection-oriented — handshake before data transfer MSS (Maximum Segment Size) — maximum application data in a segment (typically 1460 bytes) TCP Segment Structure Field Size Description Source port 16 bits Sending process port Dest port 16 bits Receiving process port Sequence number 32 bits Byte stream offset ACK number 32 bits Next expected byte Header length 4 bits In 32-bit words Reserved 6 bits Unused Flags (URG, ACK, PSH, RST, SYN, FIN) 6 bits Control flags Receive window 16 bits Flow control (bytes available) Internet checksum 16 bits Error detection Urgent pointer 16 bits Offset to urgent data Options variable e.g., MSS, timestamp Data variable Application payload Round-Trip Time Estimation and Timeout EstimatedRTT = (1 - alpha) * EstimatedRTT + alpha * SampleRTT\n(alpha is typically 1/8 = 0.125)\nDevRTT = (1 - beta) * DevRTT + beta * |SampleRTT - EstimatedRTT|\n(beta is typically 1/4 = 0.25)\nTimeoutInterval = EstimatedRTT + 4 * DevRTT\nTCP Reliable Data Transfer Retransmission on timeout Fast retransmit: if sender receives 3 duplicate ACKs for the same sequence number, retransmit the missing segment before the timer expires Flow Control Receiver advertises its available buffer space via the Receive Window (rwnd) field Sender must ensure: LastByteSent - LastByteAcked \u0026lt;= rwnd TCP Flow Control prevents the sender from overwhelming the receiver\u0026rsquo;s buffer. This is distinct from congestion control (which prevents overwhelming the network).\nTCP Connection Management Three-way handshake:\nClient sends SYN segment (SYN=1, seq=client_isn) Server sends SYNACK segment (SYN=1, ACK=client_isn+1, seq=server_isn) Client sends ACK (ACK=server_isn+1), may include data Closing connection:\nClient sends FIN (FIN=1) Server ACKs the FIN Server sends its own FIN Client ACKs the server\u0026rsquo;s FIN (enters TIME_WAIT) Principles of Congestion Control Causes and costs of congestion:\nWhen packet arrival rate exceeds link capacity, queues build up Cost 1: queuing delay increases Cost 2: retransmissions due to dropped packets waste bandwidth Cost 3: premature retransmissions (unnecessary duplicates) waste bandwidth Cost 4: when a packet is dropped, the work done to transport it is wasted Approaches to congestion control:\nEnd-to-end: no explicit network feedback; TCP deduces congestion from packet loss (implicit) Network-assisted: routers provide explicit feedback (e.g., ECN, ATM) TCP Congestion Control AIMD (Additive Increase Multiplicative Decrease):\nSlow start: cwnd starts at 1 MSS, doubles every RTT (exponential growth), until ssthresh is hit Congestion avoidance: cwnd increases by 1 MSS per RTT (linear growth) On triple duplicate ACK: cwnd = cwnd/2, ssthresh = cwnd/2 (multiplicative decrease) On timeout: cwnd = 1 MSS, ssthresh = half of cwnd before timeout, re-enter slow start TCP congestion control follows AIMD — sawtooth pattern: linear increase until loss, then halving the congestion window.\nTCP Fairness:\nAIMD converges to fair sharing of bottleneck bandwidth among competing TCP flows If K flows share a bottleneck of rate R, each gets roughly R/K Problem: UDP and other non-TCP flows can starve TCP flows ECN (Explicit Congestion Notification):\nNetwork-assisted congestion indication Router marks packets (CE = Congestion Experienced) when queue is near-full Receiver echoes the ECN-echo flag Sender reacts as if a packet was dropped (reduces window) Key Formulas Concept Formula Notes RTT estimation EstimatedRTT = (1-alpha)EstimatedRTT + alphaSampleRTT alpha = 1/8 RTT deviation DevRTT = (1-beta)DevRTT + beta SampleRTT - EstimatedRTT Timeout TimeoutInterval = EstimatedRTT + 4*DevRTT Starts at 1 second Stop-and-wait utilization U = (L/R) / (RTT + L/R) For link rate R, packet L TCP window at steady state Avg throughput = (3/4)W_maxMSS / RTT W_max = max cwnd References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 793 — Transmission Control Protocol RFC 768 — User Datagram Protocol RFC 5681 — TCP Congestion Control RFC 2581 — TCP Congestion Control (obsolete) ","tags":["Computer Networking","Transport Layer","TCP","UDP","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 4: The Network Layer: Data Plane","permalink":"https://yukar.icu/posts/networking/computer-networking-ch4/","summary":"Overview of Network Layer Forwarding — the router-local action of moving a packet from an input port to an output port (data plane, fast path, typically hardware).\nRouting — the network-wide process of determining the path from source to destination (control plane, slow path, typically software).\nData plane — per-packet processing (forwarding), implemented in hardware.\nControl plane — routing algorithms and protocols, implemented in software or a separate SDN controller.\n","content":"Overview of Network Layer Forwarding — the router-local action of moving a packet from an input port to an output port (data plane, fast path, typically hardware).\nRouting — the network-wide process of determining the path from source to destination (control plane, slow path, typically software).\nData plane — per-packet processing (forwarding), implemented in hardware.\nControl plane — routing algorithms and protocols, implemented in software or a separate SDN controller.\nNetwork Service Models:\nBest-effort (Internet) — no guarantees on delivery, timing, or bandwidth ATM CBR — constant bit rate, guaranteed ATM ABR — available bit rate, minimum rate guaranteed What\u0026rsquo;s Inside a Router? Router Architecture:\nInput ports — physical layer termination, link-layer processing, lookup and forwarding (longest prefix match using TCAM) Switching fabric — moves packets from input to output Output ports — buffering, link-layer processing, physical transmission Routing processor — control plane functions (routing protocols, routing table) Switching Fabrics:\nType Speed Description Switching via memory Slow Traditional computer bus; packet copied to processor memory, then to output Switching via bus Moderate Shared bus; input port puts packet on bus with tag, output port reads Switching via crossbar Fast NxN interconnect; multiple packets can be forwarded simultaneously (non-blocking) Queuing:\nInput queuing: HOL (Head-of-Line) blocking — a packet at the head of an input queue blocks all others behind it Output queuing: packets queue at output when switching fabric is faster than output link; requires packet scheduling Packet Scheduling:\nFIFO (First In First Out) — simplest, no differentiation Priority queuing — packets classified by priority; high-priority served first (starvation possible) Round Robin (RR) — cycles through classes, serving one packet from each WFQ (Weighted Fair Queuing) — weighted round robin; each class gets a minimum guaranteed share The Internet Protocol (IP) IPv4 Datagram Format Field Size Description Version 4 bits IP version (4) Header length 4 bits Length of header in 32-bit words (typically 5) Type of service 8 bits Differentiated services (Diffserv) Total length 16 bits Entire datagram length (header + data) in bytes Identification 16 bits For fragmentation/reassembly Flags 3 bits DF (Don\u0026rsquo;t Fragment), MF (More Fragments) Fragment offset 13 bits Position of fragment in original datagram Time-to-Live 8 bits Decremented at each router; dropped when 0 Protocol 8 bits Upper-layer protocol (6=TCP, 17=UDP) Header checksum 16 bits Error check on header only Source IP address 32 bits Sending host\u0026rsquo;s address Destination IP address 32 bits Receiving host\u0026rsquo;s address Options variable Rarely used Data variable Upper-layer segment IPv4 Datagram Fragmentation MTU (Maximum Transmission Unit) — maximum frame payload size for a link If IP datagram \u0026gt; MTU, it must be fragmented Each fragment is a separate IP datagram Reassembled at the destination host Fragmentation calculation:\nNumber of fragments = ceil((Original datagram length - 20) / (MTU - 20))\nEach fragment offset = (previous offset) + (fragment data length / 8)\nIPv4 Addressing CIDR (Classless InterDomain Routing):\nAddress format: a.b.c.d/x, where x is the number of bits in the network prefix Example: 200.23.16.0/23 has a 23-bit network prefix (512 addresses) Subnetting:\nA subnet is a network that connects hosts without an intervening router IP addresses within a subnet share the same prefix DHCP (Dynamic Host Configuration Protocol):\nHost gets IP address automatically on joining a network DORA: Discover, Offer, Request, Acknowledge Also provides: subnet mask, first-hop router, DNS server NAT (Network Address Translation) Private IP addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are used inside a private network NAT router maps (private IP, port) to (public IP, port) using a NAT translation table Controversial: violates end-to-end argument; workaround via NAT traversal IPv6 128-bit addresses (vs IPv4\u0026rsquo;s 32 bits) Fixed 40-byte header (no options, no fragmentation at routers) No checksum at network layer No NAT needed (sufficient addresses) Tunneling — carrying IPv6 inside IPv4 to support transition IPv6 Datagram Header:\nField Size Description Version 4 bits 6 Traffic class 8 bits Diffserv Flow label 20 bits Identify flows Payload length 16 bits Data after 40-byte header Next header 8 bits Upper-layer protocol Hop limit 8 bits TTL equivalent Source address 128 bits Destination address 128 bits Generalized Forwarding and SDN Match-plus-Action Forwarding:\nMatch: fields in packet header (IP src/dst, TCP port, etc.) Action: forward to port, drop, modify header, send to controller OpenFlow:\nOpen standard for SDN data plane Flow table entries: (match fields, priority, counters, instructions, timeouts, cookie) Examples: simple forwarding, load balancing, firewall, NAT SDN Data Plane separates forwarding hardware from routing logic. The flow table (controlled by a remote SDN controller) tells the switch what to do with each packet.\nKey Formulas Concept Formula Notes IP addressing Address = prefix + host a.b.c.d/x where x = prefix bits Fragmentation fragments = ceil((total - 20) / (MTU - 20)) Header is 20 bytes Fragment offset offset = data_in_bytes / 8 Offset in 8-byte units References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 791 — Internet Protocol RFC 8200 — Internet Protocol Version 6 RFC 2131 — Dynamic Host Configuration Protocol RFC 1631 — Network Address Translator (NAT) ","tags":["Computer Networking","Network Layer","IP","SDN","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 5: The Network Layer: Control Plane","permalink":"https://yukar.icu/posts/networking/computer-networking-ch5/","summary":"Introduction The control plane determines the paths taken by packets through the network. Two approaches:\nPer-router control — each router runs routing algorithms and builds its own forwarding table (traditional) Software-Defined Networking (SDN) — a remote SDN controller computes and distributes forwarding tables to routers Routing Algorithms Goal: find the least-cost path from source to destination.\nClassification:\nGlobal (Link-State): all routers have complete topology and link cost info Decentralized (Distance-Vector): each router knows neighbors, iterative computation Link-State (LS) Routing Algorithm — Dijkstra\u0026rsquo;s Algorithm All nodes know the complete network topology and link costs.\n","content":"Introduction The control plane determines the paths taken by packets through the network. Two approaches:\nPer-router control — each router runs routing algorithms and builds its own forwarding table (traditional) Software-Defined Networking (SDN) — a remote SDN controller computes and distributes forwarding tables to routers Routing Algorithms Goal: find the least-cost path from source to destination.\nClassification:\nGlobal (Link-State): all routers have complete topology and link cost info Decentralized (Distance-Vector): each router knows neighbors, iterative computation Link-State (LS) Routing Algorithm — Dijkstra\u0026rsquo;s Algorithm All nodes know the complete network topology and link costs.\nDijkstra\u0026rsquo;s Algorithm:\nN\u0026rsquo; = set of nodes whose least-cost path is known\nInitially N\u0026rsquo; = {u} (source)\nFor all nodes v: D(v) = c(u,v) (direct cost)\nLoop: Find w not in N\u0026rsquo; with minimum D(w), add w to N\u0026rsquo;, update D(v) = min(D(v), D(w) + c(w,v)) for all v not in N\u0026rsquo;\nStop when N\u0026rsquo; = all nodes\nComplexity: O(n^2) with simple implementation, O(n log n) with heap\nOscillations: Possible when link cost = traffic load, causing routing instability (solved by randomizing link update times)\nDistance-Vector (DV) Routing Algorithm — Bellman-Ford Distributed, asynchronous, based on the Bellman-Ford equation:\nBellman-Ford Equation:\nd_x(y) = min_v { c(x,v) + d_v(y) }\nWhere d_x(y) is the least cost from x to y, and v is x\u0026rsquo;s neighbors.\nPoison Reverse: If router Z routes to destination Y through X, Z tells X its cost to Y is infinity — preventing certain count-to-infinity scenarios.\nCount-to-Infinity Problem: Can still occur with loops involving 3+ nodes.\nLS vs DV Comparison:\nAspect LS DV Message complexity O(nE) for each node Only neighbor exchanges Convergence speed O(n^2) Can be slow (count-to-infinity) Robustness Each node computes independently A bad DV announcement can propagate Intra-AS Routing in the Internet: OSPF OSPF (Open Shortest Path First):\nLink-state routing protocol Uses Dijkstra\u0026rsquo;s algorithm Each router floods LSAs (Link-State Advertisements) to all other routers in the AS Hierarchical OSPF: two-level hierarchy Backbone area (Area 0) connects all other areas Area border routers summarize routing info between areas Supports authentication, multiple same-cost paths, TOS-aware routing Inter-AS Routing: BGP BGP (Border Gateway Protocol): the de facto inter-domain routing protocol\neBGP: between ASes (exterior) iBGP: within an AS (interior) BGP Route Advertisement:\nBGP routers exchange routes via UPDATE messages A route includes: NLRI (prefix) + AS-PATH + NEXT-HOP AS-PATH: list of ASes the route has traversed (loop detection) NEXT-HOP: IP address of the next-hop router BGP Route Selection (preference order):\nHighest local preference (local admin policy) Shortest AS-PATH Closest NEXT-HOP (hot potato routing / closest exit) Additional criteria (eBGP over iBGP, router ID, etc.) IP-Anycast: Multiple servers advertise the same IP prefix; BGP directs clients to the \u0026ldquo;closest\u0026rdquo; server.\nRouting Policy: ISPs use BGP to implement business relationships (customer-provider peering, settlement-free peering).\nObtaining Internet Presence:\nGet IP address block from ISP or registry Register domain name Run authoritative DNS server Advertise prefix via BGP The SDN Control Plane Four Key Features of SDN:\nFlow-based forwarding — forwarding decisions based on multiple header fields Separation of data and control planes — switches are simple, controller is smart Network control functions — external to forwarding devices Programmable network — control plane is software-based SDN Controller Architecture (e.g., OpenDaylight, ONOS):\nCommunication layer — communicates with switches (OpenFlow protocol) Network-wide state management layer — stores topology, state Interface to control apps — API for SDN applications OpenFlow Protocol:\nOperates between controller and switch over TCP (port 6653) Three message types: controller-to-switch, asynchronous (packet-in), symmetric (hello) Flow table entries: match fields, priority, counters, instructions, timeouts, cookie Data and Control Plane Interaction Example:\nPacket arrives at switch, no matching flow entry Switch sends PacketIn message to controller Controller determines path, installs flow entries on all switches along path First packet is forwarded; subsequent packets match flow entries (fast path) ICMP: The Internet Control Message Protocol Used by hosts and routers to communicate network-level information ICMP messages carried in IP datagrams Common message types: Type Code Description 0 0 Echo reply (ping) 3 0-3 Destination unreachable 8 0 Echo request (ping) 11 0 TTL expired (traceroute) Network Management and SNMP Network Management Framework:\nManaging server — runs network management application Managed devices — routers, switches, hosts MIB (Management Information Base) — collection of managed objects SMI (Structure of Management Information) — defines data types for MIB objects SNMP (Simple Network Management Protocol) — protocol for querying/updating MIB objects SNMP Protocol Operations:\nOperation Description GetRequest Query a MIB variable GetNextRequest Walk the MIB tree GetBulkRequest Query large blocks of data SetRequest Set a MIB variable Trap Unsolicited notification from agent to manager References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 2328 — OSPF Version 2 RFC 4271 — BGP-4 RFC 793 — ICMP RFC 3416 — SNMP Protocol Operations RFC 5340 — OSPF for IPv6 ","tags":["Computer Networking","Routing","BGP","OSPF","SDN","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 6: The Link Layer and LANs","permalink":"https://yukar.icu/posts/networking/computer-networking-ch6/","summary":"Introduction to the Link Layer The link layer is responsible for transferring a datagram from one node to an adjacent node over a single communication link.\nServices Provided:\nFraming — encapsulating datagram into a frame (header + trailer) Error detection — detecting bit errors (caused by signal attenuation, noise) Reliable delivery — retransmission of lost/corrupted frames (rarely used on low-BER links) Multiple access — coordinating access to a shared broadcast channel Where is the link layer implemented? On the network interface card (NIC) (e.g., Ethernet NIC, WiFi adapter). The NIC implements both link and physical layers.\n","content":"Introduction to the Link Layer The link layer is responsible for transferring a datagram from one node to an adjacent node over a single communication link.\nServices Provided:\nFraming — encapsulating datagram into a frame (header + trailer) Error detection — detecting bit errors (caused by signal attenuation, noise) Reliable delivery — retransmission of lost/corrupted frames (rarely used on low-BER links) Multiple access — coordinating access to a shared broadcast channel Where is the link layer implemented? On the network interface card (NIC) (e.g., Ethernet NIC, WiFi adapter). The NIC implements both link and physical layers.\nError-Detection and -Correction Techniques Parity Checks Single parity bit: even/odd parity — detects odd number of bit errors Two-dimensional parity: detects and corrects single bit errors Checksumming Methods Internet checksum (used in IP, TCP, UDP): one\u0026rsquo;s complement sum of 16-bit words Relatively weak error detection (can miss some multi-bit errors) Cyclic Redundancy Check (CRC) CRC: Given data D of d bits, generator G of r+1 bits, sender computes remainder R of r bits such that \u0026lt;D, R\u0026gt; is exactly divisible by G (mod 2 arithmetic). Receiver divides \u0026lt;D, R\u0026gt; by G; if remainder is non-zero, error detected.\nKey properties:\nDetects all single-bit errors Detects all odd-bit errors (if G is a factor of x+1) Detects all burst errors of length \u0026lt;= r Very strong detection for longer bursts (probability = 1 - (1/2)^r) Multiple Access Links and Protocols Two types of links:\nPoint-to-point: single sender, single receiver (e.g., PPP, DSL) Broadcast: shared medium (e.g., WiFi, Ethernet in legacy hub mode) Channel Partitioning Protocols Protocol Description Efficiency TDM Time slots assigned round-robin Fair, but wastes slots if no data FDM Frequency bands assigned per node Similar to TDM CDMA Each node gets a unique code; simultaneous transmission High, but complex Random Access Protocols Slotted ALOHA:\nNodes transmit only at slot boundaries If collision, retransmit with probability p Max efficiency: 1/e ≈ 0.37 Pure ALOHA:\nNodes transmit immediately Max efficiency: 1/(2e) ≈ 0.18 CSMA (Carrier Sense Multiple Access):\nListen before transmitting (carrier sensing) If channel busy, defer transmission Collisions still occur due to propagation delay CSMA/CD (CSMA with Collision Detection): Used in Ethernet. Algorithm:\nSense channel; if idle, transmit; if busy, wait If collision detected during transmission: Abort transmission immediately Send jam signal to ensure all nodes detect collision Wait random time (exponential backoff), then retry Ethernet exponential backoff: After m collisions, choose K randomly from {0, 1, \u0026hellip;, 2^m-1}. Wait K * 512 bit times. m = min(n, 10). After 16 attempts, give up.\nEfficiency of CSMA/CD: 1 / (1 + 5 * t_prop / t_trans)\nTaking-Turns Protocols Polling: master node polls each slave; higher overhead, single point of failure Token passing: token circulates; only token holder transmits; token recovery overhead DOCSIS (Data Over Cable Service Interface Specification):\nCable Internet access protocol CMTS (headend) allocates time slots; cable modems request and use slots Combination of FDM (channels) + TDM/TDMA (time slots) Switched Local Area Networks Link-Layer Addressing and ARP MAC Addresses:\n48-bit globally unique address (burned into NIC ROM) Flat structure (not hierarchical), used by ARP and switches Format: 1A-2B-3C-4D-5E-6F (hex) ARP (Address Resolution Protocol):\nMaps IP addresses to MAC addresses on a LAN ARP table: (IP, MAC, TTL) stored at each node ARP query: broadcast \u0026ldquo;who has IP X?\u0026rdquo;; ARP response: unicast \u0026ldquo;I am at MAC Y\u0026rdquo; Plug-and-play — table entries are created automatically ARP operation example:\nIP Address MAC Address TTL 192.168.1.1 00-11-22-33-44-55 120s 192.168.1.2 AA-BB-CC-DD-EE-FF 120s Ethernet Ethernet Frame Structure:\nField Size Description Preamble 8 bytes 7 bytes 10101010 + 1 byte 10101011 (SFD) Destination MAC 6 bytes Receiver\u0026rsquo;s MAC address Source MAC 6 bytes Sender\u0026rsquo;s MAC address Type/Length 2 bytes Upper-layer protocol (e.g., 0x0800 = IP) Data 46-1500 bytes IP datagram (min 46 bytes, pad if needed) CRC 4 bytes Cyclic redundancy check (trailer) Ethernet is connectionless and unreliable:\nNo handshake between sender and receiver No ACK/NAK — data delivery is not guaranteed Recovery via higher-layer protocols (TCP) Link-Layer Switches Switch Forwarding/Filtering:\nMaintains a switch table: (MAC address, interface, TTL) Self-learning: when frame arrives, record sender\u0026rsquo;s MAC + incoming port Filter: if dest MAC is on same segment as source, drop (no need to forward) Forward: if dest MAC is known, forward to that port only; otherwise flood Switches vs Routers:\nAspect Switch Router Layer Link layer (2) Network layer (3) Address type MAC addresses IP addresses Forwarding Self-learning Routing algorithms Plug-and-play Yes No Loop handling Spanning Tree Protocol TTL-based Virtual Local Area Networks (VLANs) A switch can be logically partitioned into multiple isolated LANs Port-based VLAN: each port assigned to a VLAN Trunk ports carry traffic for multiple VLANs (802.1Q tagging) VLAN tag (12-bit VLAN ID) inserted between source MAC and Type field Link Virtualization: MPLS MPLS (Multiprotocol Label Switching):\nAdds a label (20 bits) between link and network layer headers MPLS-capable routers forward based on label, not IP address Enables traffic engineering, VPNs, faster forwarding (fixed-length label lookup) Data Center Networking Top-of-Rack (ToR) switch: connects servers in a rack Spine-leaf topology: load balancing across multiple parallel paths Load balancing: ECMP (Equal-Cost Multi-Path) across multiple spine switches Full bisection bandwidth important for east-west traffic (server-to-server) Retrospective: A Day in the Life of a Web Page Request Sequence for requesting www.example.com:\nDHCP: Computer boots, broadcasts DHCP discover; DHCP server offers IP, DNS, gateway DNS: Computer sends DNS query to DNS server (via gateway router, intra-domain routing) to resolve www.example.com ARP: Computer needs gateway\u0026rsquo;s MAC address; broadcasts ARP query Intra-domain routing: DNS query forwarded through the AS to DNS server TCP: TCP three-way handshake to www.example.com server HTTP: HTTP GET request sent; server responds with the page References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 826 — Address Resolution Protocol IEEE 802.3 — Ethernet Standard IEEE 802.1Q — VLAN Tagging ","tags":["Computer Networking","Link Layer","Ethernet","LAN","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 7: Wireless and Mobile Networks","permalink":"https://yukar.icu/posts/networking/computer-networking-ch7/","summary":"Introduction Wireless networks are broadly classified by:\nSingle hop vs multi-hop: one wireless hop vs ad hoc routing Infrastructure-based vs infrastructure-less: base station present vs no base station Wireless Links and Network Characteristics Wireless link characteristics:\nSignal attenuation: signal strength decreases with distance (path loss) Multipath propagation: reflections cause multiple copies of signal to arrive Interference from other sources SNR (Signal-to-Noise Ratio) and BER (Bit Error Rate) tradeoff: higher SNR = lower BER; higher data rate = higher BER at same SNR Hidden terminal problem: Node A and C are both in range of B but out of range of each other; both transmit to B simultaneously, causing collision.\n","content":"Introduction Wireless networks are broadly classified by:\nSingle hop vs multi-hop: one wireless hop vs ad hoc routing Infrastructure-based vs infrastructure-less: base station present vs no base station Wireless Links and Network Characteristics Wireless link characteristics:\nSignal attenuation: signal strength decreases with distance (path loss) Multipath propagation: reflections cause multiple copies of signal to arrive Interference from other sources SNR (Signal-to-Noise Ratio) and BER (Bit Error Rate) tradeoff: higher SNR = lower BER; higher data rate = higher BER at same SNR Hidden terminal problem: Node A and C are both in range of B but out of range of each other; both transmit to B simultaneously, causing collision.\nCDMA (Code Division Multiple Access) Each sender gets a unique code Sender encodes data by multiplying with spreading code Receiver decodes by multiplying with the same code Many senders can transmit simultaneously (codes are orthogonal) WiFi: 802.11 Wireless LANs 802.11 Architecture BSS (Basic Service Set): group of stations communicating via an AP (Access Point) AP connects the BSS to the distribution system (wired Ethernet) Channels: 802.11 operates in ISM bands; 11 channels in 2.4 GHz (3 non-overlapping: 1, 6, 11) WiFi Standards Comparison:\nStandard Frequency Max Data Rate Notes 802.11b 2.4 GHz 11 Mbps First widely adopted 802.11a 5 GHz 54 Mbps Higher freq, shorter range 802.11g 2.4 GHz 54 Mbps Compatible with b 802.11n 2.4/5 GHz ~600 Mbps MIMO, channel bonding 802.11ac 5 GHz ~1+ Gbps MU-MIMO, wider channels 802.11 MAC Protocol: CSMA/CA CSMA/CA (Carrier Sense Multiple Access with Collision Avoidance):\nIf channel idle for DIFS time: transmit entire frame (no collision detection — can\u0026rsquo;t listen while transmitting) If channel busy: choose random backoff, decrement while idle, freeze when busy, transmit when counter hits 0 Receiver sends ACK after SIFS time If sender doesn\u0026rsquo;t receive ACK, retransmit (binary exponential backoff) Why collision avoidance instead of detection? Because in wireless, the transmitter can\u0026rsquo;t reliably detect collisions (signal strength is too weak compared to its own transmission).\nRTS/CTS (Optional):\nRTS (Request to Send) — small frame from sender CTS (Clear to Send) — small frame from AP/receiver Nearby stations hear CTS and defer transmission (solves hidden terminal problem) 802.11 Frame Structure Field Size Description Frame Control 2 bytes Type (data, control, mgmt), To/From DS Duration 2 bytes Duration for NAV (virtual carrier sensing) Address 1 6 bytes Receiver MAC Address 2 6 bytes Transmitter MAC Address 3 6 bytes Used for filtering by AP Sequence Control 2 bytes Fragment \u0026amp; sequence numbers Address 4 6 bytes Optional (ad hoc mode) Payload 0-2312 bytes IP datagram FCS (CRC) 4 bytes Error detection Four address fields are needed because frames can traverse APs on both sides.\nAdvanced Features in 802.11 MIMO (Multiple Input Multiple Output): multiple antennas for spatial multiplexing Channel bonding: combining multiple channels for higher throughput Beamforming: focusing signal toward specific receivers MU-MIMO: serving multiple clients simultaneously (802.11ac) Bluetooth and Zigbee Bluetooth (802.15.1): short-range, low-power, personal area network; master-slave piconets Zigbee (802.15.4): very low-power, low-rate; used in IoT sensor networks; mesh topology Cellular Internet Access 3G Cellular Architecture:\nBase station (NodeB) connected to RNC (Radio Network Controller) RNC connects to SGSN and GGSN for Internet access 4G LTE (Long-Term Evolution):\nAll-IP core network (EPC — Evolved Packet Core) eNodeB: evolved base station (combines NodeB and RNC roles) OFDMA (Orthogonal Frequency Division Multiple Access) on downlink SC-FDMA on uplink (better power efficiency) Data rates: 100+ Mbps downlink, 50+ Mbps uplink Mobility Management: Principles Terminology:\nHome network: permanent home of mobile node Foreign network: network the mobile node is visiting Home agent: entity in the home network that tracks the mobile node Foreign agent: entity in the foreign network that assists the mobile node Care-of address (COA): temporary address in the foreign network Addressing Each mobile node has a permanent home address (used by higher-layer applications). When visiting a foreign network, it obtains a care-of address.\nRouting to a Mobile Node Indirect Routing:\nSender sends to home address; home agent intercepts and forwards via tunnel to COA Foreign agent delivers to mobile node Transparent to sender but triangle routing problem (inefficient) Direct Routing:\nSender queries a database to get mobile\u0026rsquo;s COA Sender tunnels directly to COA More efficient but not transparent (sender needs support) Mobile IP Standard for supporting mobility in the Internet Agent discovery: home/foreign agents advertise their presence (ICMP router advertisement extensions) Registration: mobile node registers its COA with its home agent Tunneling: home agent encapsulates datagrams to COA (IP-in-IP tunneling) Managing Mobility in Cellular Networks GSM handoffs:\nMobile-assisted handoff: mobile measures signal strength of nearby base stations Reports to MSC MSC initiates handover to new base station Connection path is rerouted Anchor MSC: maintains the connection with the mobile as it moves across MSCs; prevents re-routing for every cell change.\nWireless and Mobility: Impact on Higher-Layer Protocols Wireless effects on TCP: high BER on wireless links causes TCP to incorrectly interpret losses as congestion; solutions: link-layer retransmission, split-connection, Snoop TCP Applications: streaming, VoIP affected by variable bandwidth and delay on wireless links References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 IEEE 802.11 — Wireless LAN Standard 3GPP — LTE Standard RFC 3344 — Mobile IPv4 RFC 6275 — Mobile IPv6 ","tags":["Computer Networking","Wireless","WiFi","Mobile Networks","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 8: Security in Computer Networks","permalink":"https://yukar.icu/posts/networking/computer-networking-ch8/","summary":"What Is Network Security? Security objectives:\nConfidentiality — only sender and receiver understand the message Authenticity — sender/receiver are who they claim to be Integrity — message was not modified in transit Availability — network services are accessible when needed Principles of Cryptography Symmetric Key Cryptography Both sender and receiver share the same key K.\nAlgorithm Type Key Size Notes Caesar cipher Monoalphabetic 26 Trivially breakable (frequency analysis) Polyalphabetic (Vigenere) Polyalphabetic Variable Stronger but breakable DES Block cipher 56 bits Broken by brute force (1998) 3DES Block cipher 168 bits Triple DES: K1 encrypt, K2 decrypt, K3 encrypt AES (Rijndael) Block cipher 128/192/256 bits Current standard Symmetric encryption: K_A-B encrypts the message; K_A-B decrypts the message. Security depends on keeping K_A-B secret.\n","content":"What Is Network Security? Security objectives:\nConfidentiality — only sender and receiver understand the message Authenticity — sender/receiver are who they claim to be Integrity — message was not modified in transit Availability — network services are accessible when needed Principles of Cryptography Symmetric Key Cryptography Both sender and receiver share the same key K.\nAlgorithm Type Key Size Notes Caesar cipher Monoalphabetic 26 Trivially breakable (frequency analysis) Polyalphabetic (Vigenere) Polyalphabetic Variable Stronger but breakable DES Block cipher 56 bits Broken by brute force (1998) 3DES Block cipher 168 bits Triple DES: K1 encrypt, K2 decrypt, K3 encrypt AES (Rijndael) Block cipher 128/192/256 bits Current standard Symmetric encryption: K_A-B encrypts the message; K_A-B decrypts the message. Security depends on keeping K_A-B secret.\nPublic Key Encryption Each party has a public key (known to all) and a private key (kept secret).\nRSA Algorithm:\nChoose two large primes p and q Compute n = pq, z = (p-1)(q-1) Choose e where 1 \u0026lt; e \u0026lt; z and gcd(e, z) = 1 Choose d such that e*d mod z = 1 Public key: (n, e) — Private key: (n, d) Encrypt: c = m^e mod n Decrypt: m = c^d mod n RSA security depends on the difficulty of factoring n = pq.\nMessage Integrity and Digital Signatures Cryptographic Hash Functions Takes an arbitrary-length input, produces a fixed-length output (message digest) Properties: one-way, collision-resistant MD5: 128-bit hash (broken, collision found) SHA-1: 160-bit hash (weak, collision found) SHA-2/SHA-3: current standards Message Authentication Code (MAC) Sender computes hash(message + s) where s is a shared secret Receiver recomputes and verifies Ensures both integrity and authenticity Digital Signatures Signing: sender encrypts hash of message with private key Verification: receiver decrypts signature with public key, compares hashes Provides non-repudiation (sender cannot deny signing) Sign then encrypt: Compute signature (sign hash with private key), then encrypt (message + signature) with receiver\u0026rsquo;s public key. Both confidentiality and authenticity.\nPublic Key Certification (CA):\nCertificate Authority (CA) binds a public key to an entity\u0026rsquo;s identity CA verifies identity, then creates a signed certificate (entity\u0026rsquo;s public key + identity, signed by CA\u0026rsquo;s private key) Certificate hierarchy: root CAs sign intermediate CAs, which sign server certificates End-Point Authentication Evolution of authentication protocols:\nProtocol Method Weakness ap1.0 \u0026ldquo;I am Alice\u0026rdquo; (IP address) IP spoofing ap2.0 Secret password Packet sniffing ap3.0 Encrypted password Playback attack (replay) ap3.1 Encrypted password + sequence number Sequence number prediction ap4.0 Nonce + encrypt(nonce, shared secret) Secure — protects against replay Securing E-Mail PGP (Pretty Good Privacy):\nCombines symmetric + public key cryptography Message encrypted with one-time session key (symmetric) Session key encrypted with receiver\u0026rsquo;s public key Signed with sender\u0026rsquo;s private key Provides confidentiality, integrity, authenticity Securing TCP Connections: SSL SSL (Secure Sockets Layer) / TLS (Transport Layer Security):\nSSL Handshake (4 phases):\nClient sends: list of supported crypto algorithms + client nonce Server responds: chosen algorithm + certificate (with server\u0026rsquo;s public key) + server nonce Client: verifies certificate, generates Pre-Master Secret (PMS), encrypts with server\u0026rsquo;s public key, sends encrypted PMS + MAC key Client and server: both derive Master Secret from PMS + nonces, then derive encryption keys and MAC keys Key derivation:\nMS = PRF(PMS, \u0026ldquo;master secret\u0026rdquo;, ClientNonce + ServerNonce) Encryption keys, MAC keys, IVs derived from MS SSL Data Transfer Record:\nFragment data into blocks Compute MAC (using MAC key) Pad if using CBC mode Encrypt (using encryption key) Add header (content type, version, length) Network-Layer Security: IPsec and VPNs IPsec services: confidentiality, integrity, authentication, replay protection.\nAH (Authentication Header): provides integrity and authentication (no encryption).\nESP (Encapsulating Security Payload): provides integrity and confidentiality (encryption).\nSecurity Association (SA): one-way logical connection between sender and receiver. Defines: key, algorithm, SPI, lifetime.\nIKE (Internet Key Exchange): protocol for establishing SAs (authenticated Diffie-Hellman).\nIPsec Transport Mode: SA between two hosts; only payload is encrypted. IPsec Tunnel Mode: SA between two gateways; entire original IP datagram is encrypted + new IP header added (used for VPNs).\nSecuring Wireless LANs Feature WEP 802.11i (WPA2) Encryption RC4 (broken) AES-CCMP Key management Static, shared key 4-way handshake, per-session keys Integrity CRC-32 (linear) MIC (Michael / CCMP) Authentication Open/Shared key 802.1X authentication Security Completely broken Strong (with proper auth) WPA2 4-way handshake:\nAP sends nonce to client Client sends nonce + MIC to AP AP sends GTK + MIC to client Client ACKs Operational Security: Firewalls and IDS Firewalls:\nIsolate organization\u0026rsquo;s internal network from the public Internet Stateless packet filter: examines each packet independently (IP, port, protocol) Stateful packet filter: tracks connection state (TCP handshake, sequence numbers) Application gateway: proxies application-level traffic (e.g., HTTP proxy) IDS (Intrusion Detection System):\nSignature-based: match patterns of known attacks Anomaly-based: detect deviations from normal traffic IPS (Intrusion Prevention System): inline IDS that drops suspicious packets.\nReferences Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 4251 — Secure Shell (SSH) RFC 5246 — TLS 1.2 RFC 4301 — IPsec Architecture IEEE 802.11i — Wireless Security Standard ","tags":["Computer Networking","Security","Cryptography","SSL","Firewall","Notes"],"categories":["Computer Science"]},{"title":"Computer Networking — Chapter 9: Multimedia Networking","permalink":"https://yukar.icu/posts/networking/computer-networking-ch9/","summary":"Multimedia Networking Applications Properties of Video:\nCompression: spatial (within frame) and temporal (between frames) Bit rate: CBR (Constant Bit Rate) vs VBR (Variable Bit Rate) Compression standards: MPEG-1/2/4, H.261/H.263/H.264, VP8/9 Properties of Audio:\nPCM: 64 kbps (8000 samples/s × 8 bits/sample) Compressed: GSM (13 kbps), G.729 (8 kbps), MP3, AAC Types of Multimedia Network Applications:\nApplication Type Tolerance Bandwidth Real-time Examples Streaming stored High delay, low jitter VBR No (startup delay OK) YouTube, Netflix Conversational voice Low delay (\u0026lt;150ms), low jitter CBR ~64kbps Yes VoIP (Skype) Conversational video Low delay, low jitter VBR Yes Zoom, FaceTime Streaming live Moderate delay VBR Yes (near real-time) Live streaming Streaming Stored Video UDP Streaming:\n","content":"Multimedia Networking Applications Properties of Video:\nCompression: spatial (within frame) and temporal (between frames) Bit rate: CBR (Constant Bit Rate) vs VBR (Variable Bit Rate) Compression standards: MPEG-1/2/4, H.261/H.263/H.264, VP8/9 Properties of Audio:\nPCM: 64 kbps (8000 samples/s × 8 bits/sample) Compressed: GSM (13 kbps), G.729 (8 kbps), MP3, AAC Types of Multimedia Network Applications:\nApplication Type Tolerance Bandwidth Real-time Examples Streaming stored High delay, low jitter VBR No (startup delay OK) YouTube, Netflix Conversational voice Low delay (\u0026lt;150ms), low jitter CBR ~64kbps Yes VoIP (Skype) Conversational video Low delay, low jitter VBR Yes Zoom, FaceTime Streaming live Moderate delay VBR Yes (near real-time) Live streaming Streaming Stored Video UDP Streaming:\nVideo sent over UDP (could adapt rate) Problem: many firewalls block UDP; no congestion control (unfair to TCP) HTTP Streaming:\nVideo stored as file on HTTP server Client requests via HTTP GET (byte ranges) Simple but non-adaptive — cannot adapt to changing available bandwidth DASH (Dynamic Adaptive Streaming over HTTP):\nVideo encoded at multiple bit rates (e.g., 300 kbps, 1 Mbps, 3 Mbps) Video divided into chunks (a few seconds each) Manifest file: provides URL for each chunk version + bit rate Client adaptively requests chunks — selects highest bit rate that can be downloaded without rebuffering Works over standard HTTP infrastructure (CDN-friendly) Voice-over-IP Limitations of Best-Effort IP Service:\nPacket loss: up to 1-5% tolerable (lost speech can be interpolated) Delay: one-way \u0026lt; 150ms good; 150-400ms acceptable; \u0026gt;400ms poor Jitter: variable delays cause uneven packet spacing Removing Jitter at the Receiver:\nPlayout buffering: receiver buffers incoming packets before playing Fixed playout delay: start playback after a fixed delay; simple but wasteful Adaptive playout delay: estimate network delay and adjust playout point dynamically Algorithm: estimate average delay, set playout time = estimated delay + k × delay variation Recovering from Packet Loss:\nMethod Description Overhead FEC (Forward Error Correction) Send redundant info (e.g., XOR of n packets) n-fold bandwidth increase Interleaving Reorder packets so loss affects isolated samples Adds delay Error concealment Repeat / interpolate lost speech Zero overhead Skype Case Study:\nProprietary codec (SILK — 40 kbps) Adaptive to network conditions P2P architecture for signaling (supernodes) Relay calls through intermediate nodes for NAT traversal Protocols for Real-Time Conversational Applications RTP (Real-Time Transport Protocol) Runs on top of UDP. Provides timestamp, sequence number, and payload type identification for real-time data.\nRTP Header:\nField Size Description Version 2 bits Currently 2 P (Padding) 1 bit Padding present X (Extension) 1 bit Header extension present CC (CSRC count) 4 bits Number of contributing sources M (Marker) 1 bit Frame boundary marker Payload type 7 bits Codec type (PCM, GSM, MPEG, H.264, etc.) Sequence number 16 bits Detection of lost packets Timestamp 32 bits Sampling instant of first byte SSRC 32 bits Synchronization source identifier CSRC 0-15 × 32 bits Contributing sources (for mixers) RTCP (RTP Control Protocol):\nSender/receiver reports: packet counts, loss rates, jitter, round-trip time Used for quality monitoring and flow adaptation SIP (Session Initiation Protocol) Application-layer protocol for setting up, managing, and tearing down multimedia sessions.\nSIP Architecture:\nUser agents: endpoints (client + server) SIP registrar: maps SIP addresses (sip:user@domain) to IP addresses SIP proxy: forwards SIP messages; assists with routing SIP Messages:\nINVITE: initiate a session (includes SDP — Session Description Protocol for codec negotiation) BYE: terminate a session ACK: confirm session establishment REGISTER: register current location Status codes similar to HTTP (200 OK, 404 Not Found, etc.) SIP Call Setup (simplified):\nAlice\u0026rsquo;s UA sends INVITE to Bob through SIP proxies Bob\u0026rsquo;s phone rings; Bob accepts Bob\u0026rsquo;s UA sends 200 OK with SDP (Bob\u0026rsquo;s codecs) Alice\u0026rsquo;s UA sends ACK Media flows directly (RTP between the two endpoints, not through SIP) SDP (Session Description Protocol): exchanged in SIP body; describes media type, codec, port, IP address for media.\nNetwork Support for Multimedia Dimensioning Best-Effort Networks Provision enough bandwidth so that congestion is rare Simple but costly; no QoS guarantees Providing Multiple Classes of Service Diffserv (Differentiated Services):\nPackets are marked at network edge with DS field (formerly ToS byte) Core routers use PHB (Per-Hop Behavior) to determine forwarding treatment Diffserv PHBs:\nPHB Codepoint Behavior Used For Default 000 000 Best-effort Regular traffic EF (Expedited Forwarding) 101 110 Low loss, low delay, low jitter (priority queuing) VoIP, premium traffic AF (Assured Forwarding) 001 xx0 - 100 xx0 4 classes × 3 drop precedences Assured bandwidth CS (Class Selector) xxx 000 Backward compatibility Legacy AF provides assured forwarding: within each of 4 classes, packets with lower drop precedence are less likely to be dropped during congestion.\nPer-Connection QoS Guarantees: Intserv and RSVP Intserv (Integrated Services): fine-grained, per-flow QoS guarantees.\nGuaranteed service: firm delay bounds Controlled load: approximates lightly loaded network RSVP (Resource Reservation Protocol): signaling protocol for Intserv.\nReceiver initiates reservation (multicast-friendly) Reserves resources along the path (routers maintain soft state) Scalability concern: per-flow state in every router References Computer Networking: A Top-Down Approach, 7th Edition — Kurose \u0026amp; Ross, Pearson, 2017 RFC 3550 — RTP/RTCP RFC 3261 — SIP RFC 2474 — Definition of DS Field RFC 2597 — Assured Forwarding PHB RFC 3246 — Expedited Forwarding PHB RFC 2205 — RSVP ","tags":["Computer Networking","Multimedia","VoIP","Streaming","QoS","Notes"],"categories":["Computer Science"]},{"title":"Pi Coding Agent 扩展系统源码分析","permalink":"https://yukar.icu/posts/pi-architecture/pi-coding-agent-extension-system/","summary":"概述 Pi Coding Agent 的扩展系统位于 F:\\Pi\\packages\\coding-agent\\src\\core\\extensions\\，包含 5 个 TypeScript 文件，构成了一个完整的 生命周期事件驱动 + 自定义工具注册 的扩展框架。\n扩展系统允许第三方模块：\n订阅 agent 生命周期事件（会话开始、agent 循环、消息流、工具执行等） 注册 LLM 可调用的自定义工具 注册命令、键盘快捷键、CLI 标志 通过 UI 基元与用户交互（选择、确认、输入、通知） 注册/覆盖模型 provider 提供自定义消息渲染器 架构总览 — 依赖与抽象关系 文件的职责定位 文件 行数 角色 核心职责 types.ts ~1615 行 类型定义层 (Foundation) 定义所有类型接口、事件结构、API 契约 loader.ts ~500 行 加载层 (Loader) 从文件系统发现并加载扩展模块，创建运行时骨架 runner.ts ~750 行 运行时层 (Runner) 管理扩展生命周期、事件分发、上下文创建 wrapper.ts ~30 行 适配层 (Adapter) 将扩展工具定义包装为 AgentTool index.ts ~80 行 入口层 (Barrel) 统一重导出公共 API，隐藏内部实现 依赖关系图 (引用链)： types.ts ←───────────┬─── loader.ts ├─── runner.ts ├─── wrapper.ts └─── index.ts runner.ts ←────────── wrapper.ts types.ts ←────────── index.ts loader.ts ←────────── index.ts runner.ts ←────────── index.ts wrapper.ts ←────────── index.ts 关键设计决策：\n","content":"概述 Pi Coding Agent 的扩展系统位于 F:\\Pi\\packages\\coding-agent\\src\\core\\extensions\\，包含 5 个 TypeScript 文件，构成了一个完整的 生命周期事件驱动 + 自定义工具注册 的扩展框架。\n扩展系统允许第三方模块：\n订阅 agent 生命周期事件（会话开始、agent 循环、消息流、工具执行等） 注册 LLM 可调用的自定义工具 注册命令、键盘快捷键、CLI 标志 通过 UI 基元与用户交互（选择、确认、输入、通知） 注册/覆盖模型 provider 提供自定义消息渲染器 架构总览 — 依赖与抽象关系 文件的职责定位 文件 行数 角色 核心职责 types.ts ~1615 行 类型定义层 (Foundation) 定义所有类型接口、事件结构、API 契约 loader.ts ~500 行 加载层 (Loader) 从文件系统发现并加载扩展模块，创建运行时骨架 runner.ts ~750 行 运行时层 (Runner) 管理扩展生命周期、事件分发、上下文创建 wrapper.ts ~30 行 适配层 (Adapter) 将扩展工具定义包装为 AgentTool index.ts ~80 行 入口层 (Barrel) 统一重导出公共 API，隐藏内部实现 依赖关系图 (引用链)： types.ts ←───────────┬─── loader.ts ├─── runner.ts ├─── wrapper.ts └─── index.ts runner.ts ←────────── wrapper.ts types.ts ←────────── index.ts loader.ts ←────────── index.ts runner.ts ←────────── index.ts wrapper.ts ←────────── index.ts 关键设计决策：\nloader.ts 和 runner.ts 互不依赖 — 加载器和运行时是解耦的，通过 ExtensionRuntime 接口桥接 wrapper.ts 依赖 runner.ts — 包装器需要 runner 的 createContext() 来传递一致的上下文 index.ts 不导出 loader.ts 的内部函数（如 loadExtensionModule） — 只导出公共 API 外部包类型详解 扩展系统引用了 4 个外部包和多个内部模块的类型。本节逐一说明每个外部包的核心类型及其在扩展系统中的用途。\n@earendil-works/pi-agent-core — Agent 核心类型 这是 agent 循环的底层核心包，定义了消息、工具执行、thinking 等基础类型。\n类型 定义 用途说明 AgentMessage LLM 与系统之间的消息单元（包含 role、content、tool_calls 等字段） 在 ContextEvent 中传递用户对话消息给扩展修改；在 AgentEndEvent 中暴露最终消息列表；作为 ExtensionAPI.sendUserMessage() 的参数基础 AgentToolResult\u0026lt;TDetails\u0026gt; 工具执行结果类型，泛型参数 TDetails 携带工具特定的详细信息 ToolDefinition.execute() 的返回值类型，扩展必须返回此类型的结果 AgentToolUpdateCallback\u0026lt;TDetails\u0026gt; 工具执行过程中的流式更新的回调函数类型 传递给 ToolDefinition.execute() 的 onUpdate 参数，扩展可调用它推送增量结果 ThinkingLevel 模型思考级别（通常为 none、low、medium、high 等枚举） 用于 ThinkingLevelSelectEvent 事件，以及 ExtensionAPI.getThinkingLevel() / setThinkingLevel() 的返回值/参数类型 ToolExecutionMode 工具执行模式：\u0026quot;sequential\u0026quot; 或 \u0026quot;parallel\u0026quot; 在 ToolDefinition.executionMode 字段中指定单工具的并行策略覆盖 AgentTool agent-core 可执行工具接口（包含 execute() 方法） wrapper.ts 的输出目标类型 — 扩展的 ToolDefinition 最终被包装为此类型供 agent-core 调用 在扩展系统中的流转路径：\nToolDefinition.execute() → AgentToolResult ↓ (wrapper.ts 包装) AgentTool ←── ToolDefinition 适配 ↓ (agent-core 调度) AgentMessage ←── LLM 调用结果 @earendil-works/pi-ai — AI Provider 类型 定义 AI 模型的接口、消息内容的格式、OAuth 认证等。\n类型 定义 用途说明 Api API 类型标识字符串（如 \u0026quot;anthropic-messages\u0026quot;、\u0026quot;openai-chat\u0026quot;、\u0026quot;openai-responses\u0026quot; 等） 在 ProviderConfig.api 和 ProviderModelConfig.api 中指定模型的 API 协议类型 Model\u0026lt;Api\u0026gt; 模型的完整配置（包括 id、name、baseUrl、contextWindow、maxTokens、cost、input capabilities 等），泛型 Api 标记协议类型 ExtensionContext.model 获取当前模型；ExtensionAPI.setModel() 的参数；ModelSelectEvent 的事件负载 AssistantMessageEvent 流式助手消息中的一个事件（token delta、content block start/end、thinking delta 等） 在 MessageUpdateEvent.assistantMessageEvent 中以流式增量形式传递，扩展可以逐帧观察 AI 输出 AssistantMessageEventStream AsyncIterable\u0026lt;AssistantMessageEvent\u0026gt; 异步可迭代流 ProviderConfig.streamSimple() 的返回值类型，用于自定义 API 的流处理器 Context AI provider 上下文（包含消息历史、系统提示、配置等） ProviderConfig.streamSimple() 的参数之一，提供执行上下文 TextContent 文本内容块（{ type: \u0026quot;text\u0026quot;, text: string }） 在 InputEvent.images 的替代内容中、ToolResultEvent.content 中、ExtensionAPI.sendUserMessage() 的参数中出现 ImageContent 图片内容块（{ type: \u0026quot;image\u0026quot;, source: { type, data } }） 用户输入中的图片附件；BeforeAgentStartEvent.images 中传递；InputEvent.images 中传递 ToolResultMessage LLM 使用的工具结果消息格式 TurnEndEvent.toolResults 的类型，包含该轮所有工具执行的结果数组 OAuthCredentials OAuth 凭据存储类型（包含 access token、refresh token、到期时间等） ProviderConfig.oauth.login() 的返回值类型，扩展的 OAuth 流程返回此类型 OAuthLoginCallbacks OAuth 登录流程中提供给扩展的回调集（如 openUrl(url)） ProviderConfig.oauth.login(callbacks) 的参数，扩展用它触发浏览器登录 SimpleStreamOptions 简单流处理器的配置选项（如 signal 中止信号、onEvent 等） ProviderConfig.streamSimple() 的可选参数 在扩展系统中的流转路径：\nProviderConfig.streamSimple → AssistantMessageEventStream ↓ (扩展注册 provider) ModelRegistry ──持有──→ Model\u0026lt;Api\u0026gt; 列表 ↓ (用户选择模型) ExtensionContext.model ──→ Model\u0026lt;Api\u0026gt; | undefined @earendil-works/pi-tui — 终端 UI 组件类型 定义了 TUI（终端用户界面）的全部组件系统，扩展用它构建自定义 UI。\n类型 定义 用途说明 Component 基础 UI 组件接口（包含 render()、onMount()、onUnmount() 等方法） ExtensionUIContext.setWidget()、setFooter()、setHeader()、custom() 的组件工厂返回值；ToolDefinition.renderCall() / renderResult() 的返回值 TUI TUI 核心接口（管理事件循环、屏幕渲染、图层等） 传递给组件工厂（如 setFooter((tui, theme, data) =\u0026gt; Component)），扩展可用它操作终端 KeyId 键盘按键标识符字符串类型（如 \u0026quot;ctrl-p\u0026quot;、\u0026quot;escape\u0026quot;、\u0026quot;enter\u0026quot; 等） ExtensionAPI.registerShortcut(shortcut, options) 的第一个参数；ExtensionShortcut.shortcut 字段的类型 AutocompleteItem 自动补全建议项（包含 label、description、insertText 等） RegisteredCommand.getArgumentCompletions() 的返回值元素类型 AutocompleteProvider 自动补全提供者接口（getSuggestions(prefix) 等） AutocompleteProviderFactory 的输入/输出类型 — 扩展可以包装当前 provider 叠加自定义补全 EditorComponent 编辑器组件接口（继承 Component，添加 getText()、setText()、handleInput() 等） EditorFactory 的工厂返回值类型 EditorTheme 编辑器主题配置（包含颜色、边框样式、选中高亮等） 传递给 EditorFactory(tui, theme, keybindings)，扩展用它自定义编辑器外观 OverlayHandle Overlay 控制器（包含 close()、update() 等方法） ExtensionUIContext.custom() 的 onHandle 回调参数，扩展可用它控制 overlay 的显示/关闭 OverlayOptions Overlay 配置选项（包含位置、尺寸、是否模态等） ExtensionUIContext.custom() 的 overlayOptions 字段类型 组件工厂的数据流：\n扩展调用 setWidget(key, factory) ↓ TUI 运行时调用 factory(tui, theme) ↓ Component 对象 (render, onMount, onUnmount) ↓ TUI 渲染到终端屏幕 typebox — 运行时类型校验 TypeBox 是一个 TypeScript 优先的运行时类型校验库，用于工具参数的 schema 声明与校验。\n类型 定义 用途说明 TSchema 所有 TypeBox schema 的基类/接口 ToolDefinition.parameters: TParams 的泛型约束 TParams extends TSchema，确保参数是合法的 TypeBox schema Static\u0026lt;T\u0026gt; 从 TypeBox schema T 中推断出其对应的静态 TypeScript 类型 ToolDefinition.prepareArguments() 的返回值类型 Static\u0026lt;TParams\u0026gt;，确保参数预处理函数返回符合 schema 的数据 使用示例（在扩展代码中）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { Type, type Static } from \u0026#34;typebox\u0026#34;; const MyParams = Type.Object({ name: Type.String(), count: Type.Optional(Type.Integer({ minimum: 1 })), }); pi.registerTool({ name: \u0026#34;my-tool\u0026#34;, parameters: MyParams, // TSchema 约束 execute: (id, params: Static\u0026lt;typeof MyParams\u0026gt;, signal, onUpdate, ctx) =\u0026gt; { // params.name 类型为 string // params.count 类型为 number | undefined }, }); 内部模块引用 除了外部包，扩展系统还引用了大量内部模块的类型。这些是 Pi Coding Agent 的内部子系统：\n源文件 导入类型 在扩展系统中的角色 ../../config.ts CONFIG_DIR_NAME, getAgentDir, isBunBinary 加载器获取配置目录名称（.pi）、agent 目录路径，判断是否为 Bun 二进制运行模式 ../event-bus.ts EventBus 扩展间通信的事件总线，ExtensionAPI.events 的类型 ../exec.ts ExecOptions, ExecResult, execCommand ExtensionAPI.exec() 的执行引擎 ../session-manager.ts SessionManager, ReadonlySessionManager, BranchSummaryEntry, CompactionEntry, SessionEntry 会话存储与导航，ExtensionContext.sessionManager 的类型 ../model-registry.ts ModelRegistry 模型注册中心，管理所有 provider 的模型列表 ../keybindings.ts KeybindingsManager, AppKeybinding 快捷键管理系统 ../system-prompt.ts BuildSystemPromptOptions 系统提示的构建选项 ../source-info.ts SourceInfo, createSyntheticSourceInfo 记录扩展/工具的来源元数据（文件路径、是否是内置等） ../messages.ts CustomMessage 自定义消息类型，用于 ExtensionAPI.sendMessage() ../tools/index.ts BashToolDetails, ReadToolDetails, EditToolDetails 等 内置工具的详情类型，用于事件中的类型细化 ../tools/bash.ts BashOperations UserBashEventResult.operations 的类型 ../compaction/index.ts CompactionPreparation, CompactionResult 会话压缩的事件参数类型 ../footer-data-provider.ts ReadonlyFooterDataProvider 底部栏数据提供者 ../slash-commands.ts SlashCommandInfo 斜杠命令信息 ../../modes/interactive/theme/theme.ts Theme 交互模式的主题类型 ../diagnostics.ts ResourceDiagnostic 资源诊断信息（快捷键冲突、命令问题等） 文件一：types.ts — 类型定义 (Foundation Layer) 角色与位置 这是整个扩展系统的类型基础。所有其他文件都从它导入类型。它定义了扩展系统的全部契约：事件结构、API 接口、工具定义、配置类型、上下文接口等。\n导入分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import type { AgentMessage, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel, ToolExecutionMode } from \u0026#34;@earendil-works/pi-agent-core\u0026#34;; import type { Api, AssistantMessageEvent, AssistantMessageEventStream, Context, ImageContent, Model, OAuthCredentials, OAuthLoginCallbacks, SimpleStreamOptions, TextContent, ToolResultMessage } from \u0026#34;@earendil-works/pi-ai\u0026#34;; import type { AutocompleteItem, AutocompleteProvider, Component, EditorComponent, EditorTheme, KeyId, OverlayHandle, OverlayOptions, TUI } from \u0026#34;@earendil-works/pi-tui\u0026#34;; import type { Static, TSchema } from \u0026#34;typebox\u0026#34;; import type { Theme } from \u0026#34;../../modes/interactive/theme/theme.ts\u0026#34;; import type { BashResult } from \u0026#34;../bash-executor.ts\u0026#34;; import type { CompactionPreparation, CompactionResult } from \u0026#34;../compaction/index.ts\u0026#34;; import type { EventBus } from \u0026#34;../event-bus.ts\u0026#34;; import type { ExecOptions, ExecResult } from \u0026#34;../exec.ts\u0026#34;; import type { ReadonlyFooterDataProvider } from \u0026#34;../footer-data-provider.ts\u0026#34;; import type { KeybindingsManager } from \u0026#34;../keybindings.ts\u0026#34;; import type { CustomMessage } from \u0026#34;../messages.ts\u0026#34;; import type { ModelRegistry } from \u0026#34;../model-registry.ts\u0026#34;; import type { SessionManager, ReadonlySessionManager, SessionEntry, BranchSummaryEntry, CompactionEntry } from \u0026#34;../session-manager.ts\u0026#34;; import type { SlashCommandInfo } from \u0026#34;../slash-commands.ts\u0026#34;; import type { SourceInfo } from \u0026#34;../source-info.ts\u0026#34;; import type { BuildSystemPromptOptions } from \u0026#34;../system-prompt.ts\u0026#34;; import type { BashOperations } from \u0026#34;../tools/bash.ts\u0026#34;; import type { EditToolDetails } from \u0026#34;../tools/edit.ts\u0026#34;; import type { BashToolDetails, BashToolInput, EditToolInput, FindToolDetails, FindToolInput, GrepToolDetails, GrepToolInput, LsToolDetails, LsToolInput, ReadToolDetails, ReadToolInput, WriteToolInput } from \u0026#34;../tools/index.ts\u0026#34;; 抽象依赖：types.ts 引用了 4 个外部包（pi-agent-core、pi-ai、pi-tui、typebox）和约 15 个内部模块。这体现了扩展系统位于应用架构的上层 — 它需要理解底层所有子系统的类型才能提供统一的扩展 API。\n外部包类型逐项详解 下面的表格展示了每个外部类型的具体使用位置和功能含义，以及它们在扩展生命周期中的流转路径。\n@earendil-works/pi-agent-core — Agent 核心包导入项：\n类型 在扩展系统中的具体使用位置 作用描述 AgentMessage ContextEvent.messages, AgentEndEvent.messages, MessageEndEvent.message, ExtensionAPI.sendUserMessage() Agent 循环中的核心消息单元。包含 role（user/assistant/tool）、content、tool_calls 等字段。扩展可以通过 context 事件修改消息列表 AgentToolResult\u0026lt;TDetails\u0026gt; ToolDefinition.execute() 返回值 工具执行的结果包装类型，包含 content（TextContent/ImageContent 数组）、isError（boolean）、details（TDetails 工具特定详情）三个字段 AgentToolUpdateCallback\u0026lt;TDetails\u0026gt; ToolDefinition.execute() 的 onUpdate 参数 工具执行过程中推送增量结果的回调。扩展在长时间运行的工具（如文件搜索、API 调用）中可以多次调用它 ThinkingLevel ThinkingLevelSelectEvent.level, ExtensionAPI.getThinkingLevel() / setThinkingLevel() 模型思考级别的枚举。控制模型在生成回答前进行内部推理的深度。典型值：none、low、medium、high ToolExecutionMode ToolDefinition.executionMode 字面量联合类型 `\u0026ldquo;sequential\u0026rdquo; AgentTool wrapper.ts 的输出目标 agent-core 的可执行工具接口。ToolDefinition 经过 wrapper.ts 包装后最终成为 AgentTool，被 agent-core 调度执行 在扩展系统中的流转路径：\nToolDefinition.execute() → AgentToolResult ↓ (wrapper.ts 包装) AgentTool ←── ToolDefinition 适配 ↓ (agent-core 调度执行) AgentMessage ←── LLM 调用结果中嵌入 @earendil-works/pi-ai — AI Provider 包导入项：\n类型 在扩展系统中的具体使用位置 作用描述 Api ProviderConfig.api, ProviderModelConfig.api 标识 API 协议类型的字符串。常见值：\u0026quot;anthropic-messages\u0026quot;、\u0026quot;openai-chat\u0026quot;、\u0026quot;openai-responses\u0026quot; 等 Model\u0026lt;Api\u0026gt; ExtensionContext.model, ModelSelectEvent.model/previousModel, ExtensionAPI.setModel() 模型的完整配置对象。包含 id、name、baseUrl、contextWindow、maxTokens、cost（input/output/cacheRead/cacheWrite）、input（支持的输入类型 text/image）等 AssistantMessageEvent MessageUpdateEvent.assistantMessageEvent AI 流式响应中的增量事件联合体。包含 content_block_delta（文本 delta）、thinking_delta（思维链 delta）、content_block_start/stop 等多种变体 AssistantMessageEventStream ProviderConfig.streamSimple() 返回值 AsyncIterable\u0026lt;AssistantMessageEvent\u0026gt; 异步可迭代流。扩展实现自定义 API 时将任意协议的流式响应包装为此类型 Context ProviderConfig.streamSimple() 的 context 参数 AI 会话上下文，包含消息历史、系统提示、模型配置、中止信号等。由系统传入，扩展不需要构造 ImageContent InputEvent.images, BeforeAgentStartEvent.images, ExtensionAPI.sendUserMessage() content 多模态消息中的图片内容块。结构为 `{ type: \u0026ldquo;image\u0026rdquo;, source: { type: \u0026ldquo;base64\u0026rdquo; TextContent ToolResultEvent.content, InputEvent 文本内容 消息中的纯文本内容块，结构为 { type: \u0026quot;text\u0026quot;, text: string } ToolResultMessage TurnEndEvent.toolResults LLM 接收的工具结果消息格式。包含 role: \u0026ldquo;tool\u0026rdquo;、tool_call_id、content（执行结果）等字段 OAuthCredentials ProviderConfig.oauth.login() 返回值 OAuth 认证凭据存储对象。包含 access（访问令牌）、refresh（刷新令牌）、expiresAt（过期时间）等，由系统加密持久化 OAuthLoginCallbacks ProviderConfig.oauth.login(callbacks) 参数 OAuth 登录流程回调集。openUrl(url) 打开浏览器；pollForCallback() 轮询等待回调 SimpleStreamOptions ProviderConfig.streamSimple() 可选参数 简单流处理器的配置选项，包含 signal（AbortSignal 中止信号）、onEvent 等 在扩展系统中的流转路径：\nProviderConfig.streamSimple → AssistantMessageEventStream ↓ (扩展注册 provider) ModelRegistry ──持有──→ Model\u0026lt;Api\u0026gt;[] 列表 ↓ (用户选择模型) ExtensionContext.model ──→ Model\u0026lt;Api\u0026gt; | undefined ↓ (LLM 调用) AssistantMessageEventStream ──stream──→ AssistantMessageEvent @earendil-works/pi-tui — 终端 UI 组件包导入项：\n类型 在扩展系统中的具体使用位置 作用描述 Component ToolDefinition.renderCall/renderResult 返回值；setWidget/setFooter/setHeader/custom 的工厂返回值 终端 UI 组件的基接口，包含 render() 渲染方法、onMount/onUnmount 生命周期钩子。所有 UI 构建的最终产物 TUI 组件工厂的参数，如 setFooter((tui, theme, data) =\u0026gt; Component) TUI 核心接口，扩展可通过它操作终端屏幕、注册图层、控制渲染循环、获取终端尺寸 KeyId ExtensionAPI.registerShortcut(shortcut)；ExtensionShortcut.shortcut 标准化键盘按键标识符。格式如 \u0026quot;ctrl-p\u0026quot;、\u0026quot;alt-enter\u0026quot;、\u0026quot;escape\u0026quot; 等，全部小写 AutocompleteItem RegisteredCommand.getArgumentCompletions() 返回值元素 自动补全建议项，包含 label（显示文本）、description（右侧描述）、insertText（选中后插入文本）等字段 AutocompleteProvider AutocompleteProviderFactory 输入/输出类型 自动补全提供者接口（getSuggestions(prefix) 方法）。扩展可通过 addAutocompleteProvider() 包装它叠加自定义补全 EditorComponent EditorFactory 返回值 编辑器组件接口，继承 Component。额外提供 getText()、setText()、handleInput(data) 等文本编辑方法 EditorTheme EditorFactory(tui, theme, keybindings) 参数 编辑器的主题配置，包含边框样式（border）、选中颜色（selectionColor）、自动补全颜色（completionColor）等 OverlayHandle ExtensionUIContext.custom() 的 onHandle 回调参数 浮层控制器，提供 close() 关闭方法、update(options) 更新方法，用于动态控制浮层 OverlayOptions ExtensionUIContext.custom() 的 overlayOptions 浮层的位置和大小配置。支持静态对象或动态函数 () =\u0026gt; OverlayOptions（用于跟随光标等动态场景） 组件工厂的数据流：\n扩展调用 setWidget(key, factory) ↓ TUI 系统在适当时机调用 factory(tui, theme) ↓ Component 对象 { render(), onMount(), onUnmount(), dispose()? } ↓ TUI 渲染引擎调用 render() 输出到终端 typebox — 运行时类型校验库导入项：\n类型 在扩展系统中的具体使用位置 作用描述 Static\u0026lt;T\u0026gt; ToolDefinition.prepareArguments() 的返回值类型标注 Static\u0026lt;TParams\u0026gt; 元编程类型工具：从 TypeBox schema T 推断对应的静态 TypeScript 类型。如 Static\u0026lt;typeof Type.Object({ name: Type.String() })\u0026gt; 结果是 { name: string } TSchema ToolDefinition 泛型约束 TParams extends TSchema 所有 TypeBox schema 的基接口，确保 ToolDefinition.parameters 字段是合法的 schema 使用示例（在扩展代码中）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { Type, type Static } from \u0026#34;typebox\u0026#34;; const MyParams = Type.Object({ name: Type.String(), count: Type.Optional(Type.Integer({ minimum: 1 })), }); pi.registerTool({ name: \u0026#34;my-tool\u0026#34;, label: \u0026#34;My Tool\u0026#34;, description: \u0026#34;A custom tool with validated parameters\u0026#34;, parameters: MyParams, // TSchema 约束确保合法性 execute: (id, params: Static\u0026lt;typeof MyParams\u0026gt;, signal, onUpdate, ctx) =\u0026gt; { // TypeScript 自动推断：params.name 是 string // TypeScript 自动推断：params.count 是 number | undefined // 运行时 TypeBox 校验确保传入参数符合 schema }, }); 内部模块引用分析 types.ts 中的内部模块导入提供了对其他 Pi Coding Agent 子系统的类型引用：\n源文件 导入类型 在扩展系统中的具体使用 该类型的含义 ../../modes/interactive/theme/theme.ts Theme ExtensionUIContext.theme；setTheme()/getTheme() 完整主题配置，包含颜色、边框、组件样式等 ../event-bus.ts EventBus ExtensionAPI.events 的类型 扩展间通信的事件总线，提供 on/emit/off 方法 ../exec.ts ExecOptions, ExecResult ExtensionAPI.exec() 的参数和返回值 系统命令执行的选项（cwd、timeout、env）和结果（exitCode、stdout、stderr） ../session-manager.ts SessionManager, ReadonlySessionManager, SessionEntry, BranchSummaryEntry, CompactionEntry ExtensionContext.sessionManager；SessionCompactEvent 事件负载 会话存储与导航系统，管理对话历史的读写、分支、压缩、持久化 ../model-registry.ts ModelRegistry ExtensionContext.modelRegistry 模型注册中心，管理所有 provider 注册的模型列表 ../keybindings.ts KeybindingsManager, AppKeybinding setEditorComponent 的 keybindings 参数；重导出类型 快捷键管理系统，管理绑定、冲突解决、事件分发 ../system-prompt.ts BuildSystemPromptOptions ExtensionCommandContext.getSystemPromptOptions()；BeforeAgentStartEvent 字段 系统提示构建选项，包含 cwd、active tools、session info 等 ../source-info.ts SourceInfo Extension.sourceInfo；RegisteredTool.sourceInfo 来源元数据：标记扩展/工具来自哪个文件，是内置还是用户安装 ../messages.ts CustomMessage ExtensionAPI.sendMessage() 的消息体 自定义消息结构：customType（类型标识）、content（内容）、display（显示选项）、details（附加数据） ../tools/index.ts 各内置工具详情和输入类型 ToolCallEvent/ToolResultEvent 的细化类型字段 内置工具的类型声明，用于事件处理器中对特定工具的类型细化 ../tools/bash.ts BashOperations UserBashEventResult.operations bash 操作抽象接口，提供 exec/execScript/spawn 等方法 ../compaction/index.ts CompactionPreparation, CompactionResult SessionBeforeCompactEvent 准备数据；SessionCompactEvent 结果 会话压缩的准备数据和结果，包含条目列表、摘要文本等 ../footer-data-provider.ts ReadonlyFooterDataProvider setFooter() 中传递给组件工厂的参数 底部栏数据提供者，提供 git 分支、扩展状态等只读数据 ../bash-executor.ts BashResult UserBashEventResult.result bash 命令执行的完整结果：exitCode、stdout、stderr、cwd 多层抽象：内部模块引用体现了扩展系统与内部实现解耦的设计。扩展系统不直接依赖这些模块的实现，只依赖它们的类型接口。这使得两者可以独立演进。\n逐段解读 段 1：UI 上下文 (ExtensionUIContext) 1 2 3 4 5 6 7 export interface ExtensionUIContext { select(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise\u0026lt;string | undefined\u0026gt;; confirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise\u0026lt;boolean\u0026gt;; input(title: string, placeholder?: string, opts?: ExtensionUIDialogOptions): Promise\u0026lt;string | undefined\u0026gt;; notify(message: string, type?: \u0026#34;info\u0026#34; | \u0026#34;warning\u0026#34; | \u0026#34;error\u0026#34;): void; // ... ~30 个方法 } 功能：定义了扩展与用户交互的全部 UI 基元。每个运行模式（TUI、RPC、print）提供自己的实现。\n关键方法解析：\n方法 签名 功能说明 select (title, options, opts) =\u0026gt; Promise\u0026lt;string | undefined\u0026gt; 显示选择器，返回用户选中的选项 confirm (title, message, opts) =\u0026gt; Promise\u0026lt;boolean\u0026gt; 确认对话框 input (title, placeholder, opts) =\u0026gt; Promise\u0026lt;string | undefined\u0026gt; 文本输入框 notify (message, type) =\u0026gt; void 推送通知（info/warning/error） onTerminalInput (handler) =\u0026gt; () =\u0026gt; void 监听原始终端输入，返回取消函数 setWidget 两个重载：字符串数组版 / 组件工厂版 在编辑器上方或下方显示组件 setFooter (factory | undefined) =\u0026gt; void 设置自定义底部组件 setHeader (factory | undefined) =\u0026gt; void 设置自定义顶部组件 custom (factory, options) =\u0026gt; Promise\u0026lt;T\u0026gt; 显示自定义组件，支持 overlay 模式 addAutocompleteProvider (factory) =\u0026gt; void 叠加自动补全行为 setEditorComponent (factory | undefined) =\u0026gt; void 替换编辑器组件（如 Vim 编辑器） 设计模式：ExtensionUIContext 使用了策略模式 — 统一接口，不同模式提供不同实现。noOpUIContext（在 runner.ts 中）是空操作的缺省实现。\n段 2：扩展上下文 (ExtensionContext) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 export interface ExtensionContext { ui: ExtensionUIContext; mode: ExtensionMode; // \u0026#34;tui\u0026#34; | \u0026#34;rpc\u0026#34; | \u0026#34;json\u0026#34; | \u0026#34;print\u0026#34; hasUI: boolean; cwd: string; sessionManager: ReadonlySessionManager; modelRegistry: ModelRegistry; model: Model\u0026lt;any\u0026gt; | undefined; isIdle(): boolean; isProjectTrusted(): boolean; signal: AbortSignal | undefined; abort(): void; hasPendingMessages(): boolean; shutdown(): void; getContextUsage(): ContextUsage | undefined; compact(options?: CompactOptions): void; getSystemPrompt(): string; } 抽象与实现：ExtensionContext 定义了扩展在事件处理中能访问的只读环境。它的所有 getter 和方法最终都委托到 ExtensionRunner 的内部函数（如 getModel、isIdleFn），这些函数通过 bindCore() 注入。\nExtensionCommandContext 继承了 ExtensionContext 并额外提供：\ngetSystemPromptOptions() — 系统提示构建选项 waitForIdle() — 等待 agent 空闲 newSession()、fork()、navigateTree()、switchSession()、reload() — 会话控制方法 安全设计：命令上下文只能在用户发起的命令中安全使用，因为它包含会话控制能力。\n段 3：工具定义 (ToolDefinition) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export interface ToolDefinition\u0026lt;TParams extends TSchema = TSchema, TDetails = unknown, TState = any\u0026gt; { name: string; label: string; description: string; promptSnippet?: string; // 可选：系统提示中的简短描述 promptGuidelines?: string[]; // 可选：系统提示指南中的要点 parameters: TParams; // TypeBox 模式（运行时校验） renderShell?: \u0026#34;default\u0026#34; | \u0026#34;self\u0026#34;; prepareArguments?: (args: unknown) =\u0026gt; Static\u0026lt;TParams\u0026gt;; executionMode?: ToolExecutionMode; // \u0026#34;sequential\u0026#34; | \u0026#34;parallel\u0026#34; execute(toolCallId, params, signal, onUpdate, ctx): Promise\u0026lt;AgentToolResult\u0026lt;TDetails\u0026gt;\u0026gt;; renderCall?: (args, theme, context) =\u0026gt; Component; renderResult?: (result, options, theme, context) =\u0026gt; Component; } 语法要点 — 泛型约束：\nTParams extends TSchema — 参数必须符合 TypeBox schema 类型 TDetails = unknown — 工具执行的详细结果类型（默认为 unknown） TState = any — 渲染状态类型 defineTool() 是一个类型辅助函数，用于在变量赋值时保留参数类型推断：\n1 2 3 export function defineTool\u0026lt;TParams extends TSchema, TDetails = unknown, TState = any\u0026gt;( tool: ToolDefinition\u0026lt;TParams, TDetails, TState\u0026gt;, ): ToolDefinition\u0026lt;TParams, TDetails, TState\u0026gt; \u0026amp; AnyToolDefinition { ... } 功能：不使用 defineTool() 时，TypeScript 会将放到数组中的 ToolDefinition 的 params 收窄为 unknown。defineTool() 返回交叉类型 \u0026amp; AnyToolDefinition，既保留具体类型信息，又兼容 any 参数位置。\n段 4：事件体系 事件系统定义了三层结构：\n4.1 事件类型 — 按领域分组：\n事件组 事件列表 触发时机 Startup/Resource project_trust, resources_discover 启动时、重载时 Session session_start, session_before_switch, session_before_fork, session_before_compact, session_compact, session_shutdown, session_before_tree, session_tree 会话生命周期 Agent context, before_provider_request, after_provider_response, before_agent_start, agent_start, agent_end Agent 循环 Turn/Message turn_start, turn_end, message_start, message_update, message_end 每次交互回合 Tool tool_execution_start, tool_execution_update, tool_execution_end, tool_call, tool_result 工具执行 Model model_select, thinking_level_select 模型切换 Input input 用户输入 User Bash user_bash 用户执行 bash 命令 4.2 事件结果类型 — 每个事件有对应的结果类型：\n1 2 3 4 5 6 export interface ToolCallEventResult { block?: boolean; reason?: string; } export interface ContextEventResult { messages?: AgentMessage[]; } export interface InputEventResult = | { action: \u0026#34;continue\u0026#34; } | { action: \u0026#34;transform\u0026#34;; text: string; images?: ImageContent[] } | { action: \u0026#34;handled\u0026#34; }; 设计模式：事件结果的 ? 可选字段允许扩展选择性地干涉，不影响正常流程。Result 类型体现的是观察者模式的增强变体 — 观察者不仅可以观察，还可以修改或阻止事件。\n4.3 类型守卫函数\n1 2 export function isBashToolResult(e: ToolResultEvent): e is BashToolResultEvent { ... } export function isToolCallEventType(toolName: string, event: ToolCallEvent): boolean { ... } 语法要点 — 函数重载 + 类型守卫：\n内置工具的 isToolCallEventType 不需要类型参数，自动窄化 自定义工具需要显式泛型参数：isToolCallEventType\u0026lt;\u0026quot;my_tool\u0026quot;, MyInput\u0026gt;(\u0026quot;my_tool\u0026quot;, event) 重载签名确保了类型安全；实现签名使用 boolean 返回 段 5：ExtensionAPI（扩展的入口对象） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export interface ExtensionAPI { on(event, handler): void; // 订阅事件 registerTool(tool): void; // 注册工具 registerCommand(name, options): void; // 注册命令 registerShortcut(shortcut, options): void; // 注册快捷键 registerFlag(name, options): void; // 注册 CLI 标志 registerMessageRenderer(customType, renderer): void; // 注册消息渲染器 sendMessage(message, options?): void; // 发送自定义消息 sendUserMessage(content, options?): void; // 发送用户消息 appendEntry(customType, data?): void; // 追加会话条目 registerProvider(name, config): void; // 注册/覆盖 Provider unregisterProvider(name): void; // 取消注册 Provider events: EventBus; // 扩展间事件总线 // ... 更多方法 } 抽象与实现：ExtensionAPI 是扩展作者看到的 pi 对象。它的 on()、registerTool() 等注册方法将数据写入 Extension 对象的 collections（handlers Map、tools Map 等）。动作方法（sendMessage()、setModel() 等）委托给共享的 ExtensionRuntime。\n段 6：Provider 注册类型 1 2 3 4 5 6 7 8 9 10 11 export interface ProviderConfig { name?: string; baseUrl?: string; apiKey?: string; // \u0026#34;$PROXY_API_KEY\u0026#34; — 环境变量插值 api?: Api; streamSimple?: (model, context, options?) =\u0026gt; AssistantMessageEventStream; // 自定义 API 流处理器 headers?: Record\u0026lt;string, string\u0026gt;; authHeader?: boolean; models?: ProviderModelConfig[]; oauth?: { login, refreshToken, getApiKey, modifyModels? }; } 功能：允许扩展注册自定义模型 provider，支持：\n纯 URL 覆盖（代理转发） 完整模型列表替换 OAuth 认证流程 streamSimple 自定义 API 兼容层 段 7：运行时和加载状态类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export interface ExtensionRuntime extends ExtensionRuntimeState, ExtensionActions {} // 继承关系：runtime = 状态 + 动作 export interface Extension { path: string; // 原始路径 resolvedPath: string; // 解析后路径 sourceInfo: SourceInfo; // 来源元数据 handlers: Map\u0026lt;string, HandlerFn[]\u0026gt;; // 事件 -\u0026gt; 处理器列表 tools: Map\u0026lt;string, RegisteredTool\u0026gt;; messageRenderers: Map\u0026lt;string, MessageRenderer\u0026gt;; commands: Map\u0026lt;string, RegisteredCommand\u0026gt;; flags: Map\u0026lt;string, ExtensionFlag\u0026gt;; shortcuts: Map\u0026lt;KeyId, ExtensionShortcut\u0026gt;; } 抽象关系：ExtensionRuntime 是共享单例（所有扩展共用），而 Extension 是按扩展实例化（每个加载的扩展有一个对象存储其注册内容）。这是关键的设计区分。\n文件二：loader.ts — 加载器 (Loader Layer) 角色与位置 将 TypeScript/JavaScript 文件加载为扩展模块，创建 Extension 对象和共享的 ExtensionRuntime。不涉及事件分发或生命周期管理。\n导入分析 1 2 3 4 5 6 import { createJiti } from \u0026#34;jiti/static\u0026#34;; import * as _bundledTypebox from \u0026#34;typebox\u0026#34;; import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from \u0026#34;../../config.ts\u0026#34;; import * as _bundledPiCodingAgent from \u0026#34;../../index.ts\u0026#34;; import { createEventBus, type EventBus } from \u0026#34;../event-bus.ts\u0026#34;; import { createSyntheticSourceInfo } from \u0026#34;../source-info.ts\u0026#34;; 语法要点 — Bundled Imports 与 virtualModules：\nimport * as _bundledXxx 使用 import * as 命名空间导入，将所有导出聚合为一个对象 这些前缀 _bundled 的导入必须是静态的，以便 Bun 打包时包含到二进制文件中 VIRTUAL_MODULES 对象映射包名到运行时对象，jiti 的 virtualModules 选项使它们在扩展代码中进行 import 时可用 逐段解读 段 1：VIRTUAL_MODULES 与 getAliases() 1 2 3 4 5 6 7 8 9 const VIRTUAL_MODULES: Record\u0026lt;string, unknown\u0026gt; = { typebox: _bundledTypebox, \u0026#34;typebox/compile\u0026#34;: _bundledTypeboxCompile, \u0026#34;@earendil-works/pi-agent-core\u0026#34;: _bundledPiAgentCore, \u0026#34;@earendil-works/pi-tui\u0026#34;: _bundledPiTui, \u0026#34;@earendil-works/pi-ai\u0026#34;: _bundledPiAiCompat, // compat 是核心的超集 \u0026#34;@earendil-works/pi-coding-agent\u0026#34;: _bundledPiCodingAgent, \u0026#34;@mariozechner/pi-*\u0026#34;: ..., // 旧命名空间别名 }; 功能：提供两种模块解析方式：\nBun 二进制模式 (生产)：virtualModules — 预打包的模块直接注入，无文件系统解析 Node.js 开发模式：aliases — 通过 jiti 的 alias 选项映射到 node_modules 路径 两种模式都包含旧命名空间 @mariozechner/* 的兼容映射 getAliases() 函数：\n1 2 3 4 5 6 7 8 9 10 11 12 function getAliases(): Record\u0026lt;string, string\u0026gt; { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const packageIndex = path.resolve(__dirname, \u0026#34;../..\u0026#34;, \u0026#34;index.js\u0026#34;); const packagesRoot = path.resolve(__dirname, \u0026#34;../../../../\u0026#34;); const resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string =\u0026gt; { const workspacePath = path.join(packagesRoot, workspaceRelativePath); if (fs.existsSync(workspacePath)) { return workspacePath; } // monorepo workspace return fileURLToPath(import.meta.resolve(specifier)); // fallback to node_modules }; // ... } 语法要点 — import.meta.resolve()：\n返回模块说明符在运行时的绝对路径（Node.js 21+ / Bun） 与 require.resolve() 功能相似，但用于 ESM 环境 抽象与实现：resolveWorkspaceOrImport() 实现了工作区优先策略 — 在 monorepo 开发环境中优先使用本地构建产物，否则回退到 node_modules。\n段 2：createExtensionRuntime() — 运行时骨架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 export function createExtensionRuntime(): ExtensionRuntime { const notInitialized = () =\u0026gt; { throw new Error(\u0026#34;Extension runtime not initialized\u0026#34;); }; const state: { staleMessage?: string } = {}; const assertActive = () =\u0026gt; { if (state.staleMessage) throw new Error(state.staleMessage); }; const runtime: ExtensionRuntime = { sendMessage: notInitialized, // 抛出错误的桩函数 sendUserMessage: notInitialized, // ... 所有动作都是桩函数 registerProvider: (name, config, extensionPath = \u0026#34;\u0026lt;unknown\u0026gt;\u0026#34;) =\u0026gt; { runtime.pendingProviderRegistrations.push({ name, config, extensionPath }); }, invalidate: (message) =\u0026gt; { state.staleMessage = message; }, // ... }; return runtime; } 设计模式：Two-Phase Initialization（两阶段初始化）：\n加载阶段：createExtensionRuntime() 创建运行时，所有动作方法都是抛出错误的桩函数 绑定阶段：runner.bindCore() 用真实实现替换这些桩函数 这种方式允许扩展在加载期间安全地调用注册方法（如 registerTool()），但动作方法（如 sendMessage()）只能在绑定后调用。\npendingProviderRegistrations 队列的作用：\n加载期间扩展调用 registerProvider() → 入队 bindCore() 时统一刷新 → 调用 modelRegistry.registerProvider() 绑定后 registerProvider 被替换为直接调用 → 立即生效 段 3：createExtensionAPI() — API 包装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function createExtensionAPI(extension: Extension, runtime: ExtensionRuntime, cwd: string, eventBus: EventBus): ExtensionAPI { const api = { on(event: string, handler: HandlerFn): void { runtime.assertActive(); const list = extension.handlers.get(event) ?? []; list.push(handler); extension.handlers.set(event, list); }, registerTool(tool: ToolDefinition): void { runtime.assertActive(); extension.tools.set(tool.name, { definition: tool, sourceInfo: extension.sourceInfo }); runtime.refreshTools(); }, getFlag(name: string): boolean | string | undefined { runtime.assertActive(); if (!extension.flags.has(name)) return undefined; // 安全检查：只能读自己注册的 return runtime.flagValues.get(name); }, // ... }; } 关键安全设计：每个 api 方法调用前都会 runtime.assertActive()，检测当前扩展是否已失效（因会话替换或重载导致）。\n功能边界：registerTool() 写入 extension.tools Map 并调用 runtime.refreshTools() 通知系统更新；sendMessage() 直接委托给运行时；getFlag() 限制在扩展自己注册的标志范围内。\n段 4：loadExtensionModule() — 模块加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function loadExtensionModule(extensionPath: string, cacheToken?: ExtensionCacheToken) { if (isCurrentCacheToken(cacheToken)) { const cachedFactory = extensionCache.get(extensionPath); if (cachedFactory) return cachedFactory; // 缓存命中 } const jiti = createJiti(import.meta.url, { moduleCache: false, ...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }), }); const module = await jiti.import(extensionPath, { default: true }); const factory = module as ExtensionFactory; if (typeof factory !== \u0026#34;function\u0026#34;) return undefined; // 不是有效的扩展 // ... } 语法要点 — jiti 的使用：\ncreateJiti(import.meta.url) — 创建一个能直接运行 TypeScript 的运行时 jiti.import(path, { default: true }) — 加载模块并提取 default 导出 moduleCache: false — 禁用 jiti 的模块缓存，因为 loader 有自己的缓存层 tryNative: false — Bun 模式下禁用原生解析，完全使用 virtualModules 段 5：扩展发现系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function readPiManifest(packageJsonPath: string): PiManifest | null { const pkg = JSON.parse(fs.readFileSync(packageJsonPath, \u0026#34;utf-8\u0026#34;)); return pkg.pi \u0026amp;\u0026amp; typeof pkg.pi === \u0026#34;object\u0026#34; ? pkg.pi as PiManifest : null; } function discoverExtensionsInDir(dir: string): string[] { // 发现规则： // 1. *.ts / *.js 文件 → 直接加载 // 2. 子目录有 index.ts/index.js → 加载 // 3. 子目录有 package.json 含 \u0026#34;pi.extensions\u0026#34; 字段 → 按清单加载 // 不递归超过一层，复杂包必须使用 package.json 清单 } export async function discoverAndLoadExtensions( configuredPaths: string[], cwd: string, agentDir: string ): Promise\u0026lt;LoadExtensionsResult\u0026gt; { // 优先级： // 1. 项目本地: cwd/.pi/extensions/ // 2. 全局: agentDir/extensions/ // 3. 配置路径（逐条解析） // 去重：Set\u0026lt;resolvedPath\u0026gt; } 抽象与实现：扩展发现实现了三层路径合并策略：\n项目本地 → 全局 → 显式配置 去重保护（seen Set）防止路径冲突 discoverExtensionsInDir() 是核心发现函数，支持文件、子目录、package.json 清单三种形式 段 6：缓存机制 1 2 3 4 5 6 7 8 9 10 let extensionCacheCwd: string | undefined; let extensionCacheGeneration = 0; const extensionCache = new Map\u0026lt;string, ExtensionFactory\u0026gt;(); export function clearExtensionCache(): void { extensionCacheGeneration++; } function useExtensionCacheCwd(cwd: string): ExtensionCacheToken { if (extensionCacheCwd !== undefined \u0026amp;\u0026amp; extensionCacheCwd !== resolvedCwd) clearExtensionCache(); // 切换工作目录 → 清空缓存 } 设计：缓存与工作目录绑定。切换工作目录自动清空缓存。extensionCacheGeneration 用作版本戳，配合 isCurrentCacheToken() 检查缓存是否有效。\n文件三：runner.ts — 运行器 (Runtime Layer) 角色与位置 这是扩展系统的心脏。ExtensionRunner 类管理扩展生命周期、事件分发、上下文创建、快捷键解析和工具注册汇总。\n导入分析 1 2 3 4 5 6 import type { AgentMessage } from \u0026#34;@earendil-works/pi-agent-core\u0026#34;; import type { KeyId } from \u0026#34;@earendil-works/pi-tui\u0026#34;; import { type Theme, theme } from \u0026#34;../../modes/interactive/theme/theme.ts\u0026#34;; import type { ModelRegistry } from \u0026#34;../model-registry.ts\u0026#34;; import type { SessionManager } from \u0026#34;../session-manager.ts\u0026#34;; // ... 从同一目录的 types.ts 导入 ~30 个类型 引用关系：runner.ts 大量引用 types.ts 的类型定义，但不引用 loader.ts。这意味着 runner 只关心\u0026quot;已经加载好的扩展\u0026quot;，不关心它们是如何加载的。\n逐段解读 段 1：快捷键冲突检测 1 2 3 4 5 const RESERVED_KEYBINDINGS_FOR_EXTENSION_CONFLICTS = [ \u0026#34;app.interrupt\u0026#34;, \u0026#34;app.clear\u0026#34;, \u0026#34;app.exit\u0026#34;, \u0026#34;app.suspend\u0026#34;, \u0026#34;tui.input.submit\u0026#34;, ... ] as const; type BuiltInKeyBindings = Partial\u0026lt;Record\u0026lt;KeyId, { keybinding: string; restrictOverride: boolean }\u0026gt;\u0026gt;; 功能：定义一组保留快捷键，扩展不能覆盖。这防止了扩展劫持核心功能（如 Ctrl+C 中断、提交输入等）。\nbuildBuiltinKeybindings() 将 KeybindingsConfig 解析为按键查找表：\n1 2 3 4 5 for (const key of keyList) { const existing = builtinKeybindings[normalizedKey]; if (existing?.restrictOverride \u0026amp;\u0026amp; !restrictOverride) continue; // 保留的获胜 builtinKeybindings[normalizedKey] = { keybinding, restrictOverride }; } 抽象与实现：当多个 action 绑定同一个键时，保留 action 优先。这通过 restrictOverride 标志实现。\n段 2：ExtensionRunner 类核心结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export class ExtensionRunner { private extensions: Extension[]; private runtime: ExtensionRuntime; private uiContext: ExtensionUIContext; // 默认为 noOpUIContext private mode: ExtensionMode = \u0026#34;print\u0026#34;; private cwd: string; private sessionManager: SessionManager; private modelRegistry: ModelRegistry; // 所有内部函数都可被外部替换（通过 bindCore/bindCommandContext） private getModel: () =\u0026gt; Model\u0026lt;any\u0026gt; | undefined = () =\u0026gt; undefined; private isIdleFn: () =\u0026gt; boolean = () =\u0026gt; true; // ... 约 15 个可注入函数 } 设计模式：Strategy Pattern + Dependency Injection — Runner 的所有行为依赖都是可替换的私有函数字段。外部通过 bindCore()、bindCommandContext()、setUIContext() 注入具体实现。\n段 3：bindCore() — 核心绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bindCore(actions: ExtensionActions, contextActions: ExtensionContextActions, providerActions?): void { // 1. 将 actions 复制到共享 runtime（所有 ExtensionAPI 引用同一个 runtime 对象） this.runtime.sendMessage = actions.sendMessage; this.runtime.sendUserMessage = actions.sendUserMessage; // ... // 2. 设置 context actions（getter 函数） this.getModel = contextActions.getModel; this.isIdleFn = contextActions.isIdle; // ... // 3. 刷新 provider 注册队列 for (const { name, config } of this.runtime.pendingProviderRegistrations) { this.modelRegistry.registerProvider(name, config); } this.runtime.pendingProviderRegistrations = []; // 4. 替换 registerProvider 为立即生效的版本 this.runtime.registerProvider = (name, config) =\u0026gt; { this.modelRegistry.registerProvider(name, config); }; } 关键时序：\n加载阶段：registerProvider() 入队 pendingProviderRegistrations 绑定阶段：bindCore() 统一刷新所有排队注册 绑定后：registerProvider() 被替换为直接调用 modelRegistry.registerProvider()，立即生效 段 4：上下文创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 createContext(): ExtensionContext { const runner = this; return { get ui() { runner.assertActive(); return runner.uiContext; }, get mode() { runner.assertActive(); return runner.mode; }, isIdle: () =\u0026gt; { runner.assertActive(); return runner.isIdleFn(); }, // ... }; } 语法要点 — 惰性 getter (Lazy Property Descriptors)：\nget ui() 是 Object.defineProperty 的简写语法 每次访问都重新求值，反映运行时的最新状态 所有访问都经过 assertActive() 检查 与 createCommandContext() 的区别：\ncreateContext() 返回 ExtensionContext — 用于事件处理和工具执行 createCommandContext() 使用 Object.defineProperties() + Object.getOwnPropertyDescriptors() 继承 createContext() 的所有惰性 getter，再添加命令专用方法（waitForIdle()、newSession() 等） 1 2 3 4 5 6 7 8 9 createCommandContext(): ExtensionCommandContext { const context = Object.defineProperties( {}, Object.getOwnPropertyDescriptors(this.createContext()), ) as ExtensionCommandContext; context.waitForIdle = () =\u0026gt; { ... }; context.newSession = (options) =\u0026gt; { ... }; // ... } 语法要点 — Object.getOwnPropertyDescriptors()：\n复制 createContext() 返回对象的所有属性描述符（包括 getter） 这是正确复制惰性 getter 的唯一方式；普通展开 {...obj} 会求值 getter 段 5：事件分发系统 通用事件分发（emit()）：\n1 2 3 4 5 6 7 8 9 10 11 async emit\u0026lt;TEvent extends RunnerEmitEvent\u0026gt;(event: TEvent): Promise\u0026lt;RunnerEmitResult\u0026lt;TEvent\u0026gt;\u0026gt; { const ctx = this.createContext(); for (const ext of this.extensions) { const handlers = ext.handlers.get(event.type); if (!handlers) continue; for (const handler of handlers) { const handlerResult = await handler(event, ctx); if (this.isSessionBeforeEvent(event) \u0026amp;\u0026amp; handlerResult?.cancel) return; } } } 设计模式：责任链模式 — 每个扩展依次处理事件，session_before_* 事件在处理返回 cancel 时提前终止。\n专用事件分发（类型安全版本）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 async emitMessageEnd(event: MessageEndEvent): Promise\u0026lt;AgentMessage | undefined\u0026gt; { const ctx = this.createContext(); let currentMessage = event.message; let modified = false; for (const ext of this.extensions) { for (const handler of ext.handlers.get(\u0026#34;message_end\u0026#34;) ?? []) { const currentEvent: MessageEndEvent = { ...event, message: currentMessage }; const handlerResult = await handler(currentEvent, ctx) as MessageEndEventResult; if (handlerResult?.message) { if (handlerResult.message.role !== currentMessage.role) { this.emitError({...}); // 角色不变检查 continue; } currentMessage = handlerResult.message; modified = true; } } } return modified ? currentMessage : undefined; } 功能：message_end 处理器可以修改最终消息内容。要求返回的消息必须保持相同角色（role），防止注入攻击。\n1 2 3 4 5 6 7 8 9 10 11 async emitToolResult(event: ToolResultEvent): Promise\u0026lt;ToolResultEventResult | undefined\u0026gt; { // 对原事件对象展开（不会污染原始对象） const currentEvent: ToolResultEvent = { ...event }; for (const handler of handlers) { const handlerResult = await handler(currentEvent, ctx) as ToolResultEventResult; if (handlerResult.content !== undefined) currentEvent.content = handlerResult.content; if (handlerResult.details !== undefined) currentEvent.details = handlerResult.details; if (handlerResult.isError !== undefined) currentEvent.isError = handlerResult.isError; } // 返回修改后的完整结果 } 链式修改：tool_result 处理器可以逐步修改结果，每个处理器在之前处理器修改的基础上继续。多个扩展可以合作修改同一个工具结果。\n1 2 3 4 5 6 7 8 9 async emitToolCall(event: ToolCallEvent): Promise\u0026lt;ToolCallEventResult | undefined\u0026gt; { for (const handler of handlers) { const handlerResult = await handler(event, ctx); if (handlerResult) { result = handlerResult as ToolCallEventResult; if (result.block) return result; // block 立即终止 } } } 阻断机制：tool_call 处理器可以返回 { block: true } 阻止工具执行。event.input 是可变引用，处理器可以原地修改参数。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async emitInput(text, images, source, streamingBehavior?): Promise\u0026lt;InputEventResult\u0026gt; { let currentText = text; let currentImages = images; for (const handler of ext.handlers.get(\u0026#34;input\u0026#34;) ?? []) { const event: InputEvent = { type: \u0026#34;input\u0026#34;, text: currentText, images: currentImages, source, streamingBehavior }; const result = await handler(event, ctx) as InputEventResult; if (result?.action === \u0026#34;handled\u0026#34;) return result; // 短路：扩展完全处理了输入 if (result?.action === \u0026#34;transform\u0026#34;) { // 转换：修改输入文本 currentText = result.text; currentImages = result.images ?? currentImages; } } return currentText !== text ? { action: \u0026#34;transform\u0026#34;, text: currentText, images: currentImages } : { action: \u0026#34;continue\u0026#34; }; } 三态结果：输入事件支持三种结果：\n\u0026quot;continue\u0026quot; — 扩展未修改输入，按原样处理 \u0026quot;transform\u0026quot; — 扩展修改了输入文本或图片 \u0026quot;handled\u0026quot; — 扩展完全处理了输入，后续停止处理 \u0026ldquo;handled\u0026rdquo; 会短路径终止，\u0026ldquo;transform\u0026rdquo; 会链式累积修改。 段 6：工具注册管理 getAllRegisteredTools() — 将各扩展工具按名称去重合并：\n1 2 3 4 5 6 7 8 9 10 11 getAllRegisteredTools(): RegisteredTool[] { const toolsByName = new Map\u0026lt;string, RegisteredTool\u0026gt;(); for (const ext of this.extensions) { for (const tool of ext.tools.values()) { if (!toolsByName.has(tool.definition.name)) { toolsByName.set(tool.definition.name, tool); // 先注册者获胜 } } } return Array.from(toolsByName.values()); } 设计决策：同名工具先注册者获胜。这意味着内置工具优先于扩展工具，第一个加载的扩展优先于后续加载的。\n段 7：命令去重 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private resolveRegisteredCommands(): ResolvedCommand[] { const counts = new Map\u0026lt;string, number\u0026gt;(); for (const ext of this.extensions) { for (const command of ext.commands.values()) { counts.set(command.name, (counts.get(command.name) ?? 0) + 1); } } // 重名命令自动添加数字后缀：command:1, command:2 return commands.map((command) =\u0026gt; { const invocationName = counts.get(command.name) \u0026gt; 1 ? `${command.name}:${occurrence}` : command.name; // ... }); } 抽象与实现：当多个扩展注册同名命令时，invocationName 自动生成带序号的后缀（如 mycommand:1、mycommand:2），避免冲突。\n段 8：失效机制 (Stale Invalidation) 1 2 3 4 5 6 7 8 9 10 11 12 invalidate(message?: string): void { if (!this.staleMessage) { this.staleMessage = message; this.runtime.invalidate(message); // 传播到共享 runtime } } private assertActive(): void { if (this.staleMessage) { throw new Error(this.staleMessage); } } 功能：会话切换、fork、重载后，旧的 ExtensionRunner 实例被标记为 stale。所有后续操作抛出清晰的错误，防止使用已过期的上下文。错误消息指导用户将工作移到 withSession 回调中。\n文件四：wrapper.ts — 包装器 (Adapter Layer) 角色与位置 将扩展定义的 ToolDefinition 转换为 agent-core 可执行的 AgentTool。这是适配器层的唯一文件，只有 30 行。\n完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import type { AgentTool } from \u0026#34;@earendil-works/pi-agent-core\u0026#34;; import { wrapToolDefinition, wrapToolDefinitions } from \u0026#34;../tools/tool-definition-wrapper.ts\u0026#34;; import type { ExtensionRunner } from \u0026#34;./runner.ts\u0026#34;; import type { RegisteredTool } from \u0026#34;./types.ts\u0026#34;; export function wrapRegisteredTool(registeredTool: RegisteredTool, runner: ExtensionRunner): AgentTool { return wrapToolDefinition(registeredTool.definition, () =\u0026gt; runner.createContext()); } export function wrapRegisteredTools(registeredTools: RegisteredTool[], runner: ExtensionRunner): AgentTool[] { return wrapToolDefinitions( registeredTools.map((registeredTool) =\u0026gt; registeredTool.definition), () =\u0026gt; runner.createContext(), ); } 逐行解读 行 代码 含义 1-2 导入 AgentTool agent-core 的类型，表示 LLM 可调用的工具 3 导入 wrapToolDefinition tool-definition-wrapper.ts 中的通用包装函数 4-5 导入 ExtensionRunner 和 RegisteredTool 本模块的类型依赖 7-9 wrapRegisteredTool() 将单个 RegisteredTool 包装为 AgentTool。第二个参数是一个惰性上下文工厂 () =\u0026gt; runner.createContext() 11-16 wrapRegisteredTools() 批量包装。调用 wrapToolDefinitions()（带 s 的复数版本），传入定义数组和共享工厂 关键设计 — 惰性上下文工厂：\n() =\u0026gt; runner.createContext() 不是直接传递上下文对象，而是传递一个在工具执行时才调用的工厂 这确保了 ExtensionContext 在工具实际运行时反映最新状态（如当前工作目录、model、信号等） 也避开了扩展生命周期问题：即使 runner 状态在注册后发生了变化，执行时仍能访问最新状态 引用与被引用 引用 runner.ts 的 ExtensionRunner — 需要 runner 的 createContext() 方法 引用 types.ts 的 RegisteredTool — 包装器的输入类型 引用 ../tools/tool-definition-wrapper.ts — 实际的包装逻辑 被引用 index.ts 重导出两个函数 抽象层次：wrapper.ts 是非常薄的一层适配，核心逻辑在 tool-definition-wrapper.ts 中。这里只做 \u0026ldquo;如何获取上下文\u0026rdquo; 的决策。\n文件五：index.ts — 入口 (Barrel Layer) 角色与位置 作为包的公共入口点，统一重导出所有需要暴露的类型和函数。这是唯一的公共 API 表面。\n代码结构分析 重导出 loader.ts 的函数：\n1 2 3 4 5 6 export { createExtensionRuntime, discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions, } from \u0026#34;./loader.ts\u0026#34;; 注意：loader.ts 的 loadExtensionModule、createExtensionAPI、loadExtensionsInternal 等内部函数未导出，对包使用者不可见。\n重导出 runner.ts 的类和类型：\n1 2 3 4 5 6 7 8 9 10 export { ExtensionRunner, } from \u0026#34;./runner.ts\u0026#34;; export type { ExtensionErrorListener, ForkHandler, NavigateTreeHandler, NewSessionHandler, SwitchSessionHandler, } from \u0026#34;./runner.ts\u0026#34;; 重导出 wrapper.ts 的函数：\n1 export { wrapRegisteredTool, wrapRegisteredTools } from \u0026#34;./wrapper.ts\u0026#34;; 重导出 types.ts：\n这是最庞大的部分，约 70 个类型重导出，涵盖：\n事件类型（SessionStartEvent, AgentStartEvent, ToolCallEvent 等） 结果类型（ContextEventResult, ToolCallEventResult 等） 上下文类型（ExtensionContext, ExtensionCommandContext, ExtensionUIContext 等） API 类型（ExtensionAPI, ExtensionFactory 等） 配置类型（ProviderConfig, ProviderModelConfig 等） 类型守卫（isBashToolResult, isToolCallEventType 等） 额外导出：\n1 2 export type { SlashCommandInfo, SlashCommandSource } from \u0026#34;../slash-commands.ts\u0026#34;; export type { SourceInfo } from \u0026#34;../source-info.ts\u0026#34;; 设计：这些类型本不属于扩展系统，但由于扩展 API 中引用了它们（如 ExtensionAPI.getCommands() 返回 SlashCommandInfo[]），所以在这里重导出，方便扩展作者一次性导入所有需要的类型。\n导出的分层结构 index.ts ├── 类型定义 (types.ts) → 约 70 个 types ├── 加载器函数 (loader.ts) → 4 个导出函数 ├── 运行器类 (runner.ts) → ExtensionRunner (class) + 5 个类型 ├── 包装器函数 (wrapper.ts) → 2 个导出函数 ├── 额外类型 (../slash-commands.ts, ../source-info.ts) → 2 个 type └── 类型守卫 (types.ts) → 8 个类型守卫函数 关系总结 引用链全景 ┌─────────────────────────────────────────┐ │ types.ts │ │ (所有类型定义：事件、API、上下文、工具) │ └────────────┬────────────┬───────────────┘ │ │ ┌───────────────────┘ ┌────────┘ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ │ loader.ts │ │ runner.ts │ │ (加载扩展) │ │ (生命周期管理) │ └───────────────┘ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ wrapper.ts │ │ (工具适配) │ └────────┬────────┘ │ ┌─────────────────────────┘ ▼ ┌───────────────────┐ │ index.ts │ │ (公共 API 入口) │ └───────────────────┘ 核心数据流 [扩展 .ts 文件] │ ▼ jiti.import() loader.ts ────→ ExtensionFactory (default export 的函数) │ │ │ ▼ factory(api) │ Extension 对象 (handlers, tools, commands, etc.) │ │ ▼ ▼ ExtensionRuntime (共享单例, pendingProviderRegistrations 队列) │ ▼ bindCore() runner.ts ────→ 替换 runtime 桩函数 → 刷新 provider 队列 │ ├── createContext() → ExtensionContext (事件处理用) ├── createCommandContext() → ExtensionCommandContext (命令用) ├── emit(event) → 遍历扩展的 handlers 分派事件 ├── getAllRegisteredTools() → 汇总所有工具（去重） └── getShortcuts() → 汇总快捷键（冲突检测） │ ▼ wrapRegisteredTool() wrapper.ts ────→ AgentTool (可被 agent-core 调用) 关键设计模式总结 模式 位置 说明 策略模式 ExtensionUIContext 不同运行模式（TUI/RPC/print）提供不同 UI 实现 两阶段初始化 createExtensionRuntime() + bindCore() 加载期用桩函数，绑定后替换为真实实现 责任链模式 emit() 系列方法 所有扩展依次处理事件，可提前终止 观察者模式 ExtensionAPI.on() 扩展订阅事件，runner 分发 适配器模式 wrapper.ts ToolDefinition → AgentTool 的适配转换 惰性求值 createContext() 的 getter 上下文属性在访问时求值，反映最新状态 工厂模式 createExtensionAPI() 为每个扩展创建独立的 API 包装 抽象层次 高抽象层 ─── index.ts (公共 API 表面) │ runner.ts (事件分发、上下文创建、生命周期) │ loader.ts (模块解析、扩展发现、缓存) │ wrapper.ts (工具适配桥接) │ 低抽象层 ─── types.ts (全部类型契约) 安全机制 安全机制 位置 说明 Stale Invalidation runner.ts + loader.ts 会话切换后自动失效旧实例 assertActive() 整个 API 表面 每次操作前检查有效性 快捷键冲突检测 runner.ts 保留快捷键阻止覆盖（app.interrupt 等） 命令重名自动编号 runner.ts 同名命令生成 name:n 后缀避免冲突 角色检查 emitMessageEnd() 防止扩展修改消息角色 Flag 自检 loader.ts createExtensionAPI() 只能读自己注册的标志 附录：文件一览 文件 路径 行数 角色 types.ts extensions/types.ts 1615 全部类型定义 loader.ts extensions/loader.ts ~500 模块加载与发现 runner.ts extensions/runner.ts ~750 生命周期与事件分发 wrapper.ts extensions/wrapper.ts ~30 工具适配 index.ts extensions/index.ts ~80 公共入口 文档生成时间：2026-06-27 分析对象：F:\\Pi\\packages\\coding-agent\\src\\core\\extensions/\n","tags":["Pi","Extension System","Architecture","TypeScript"],"categories":["Code Analysis"]},{"title":"Game of Chess","permalink":"https://yukar.icu/posts/gametheory/chess/","summary":"Preliminaries of Chess Finite Game Von Neumann\u0026rsquo;s Theorem Theorem. In every two-player, finite game with perfect information in which the set of outcomes is $\\mathcal O=\\{\\text{I wins, II wins, Draw}\\}$, one and only one of the following three alternatives must hold:\nPlayer I has a winning strategy. Player II has a winning strategy. Each of the two players has a strategy that guarantees at least a draw. The proof of the theorem is referred to [1]\n","content":"Preliminaries of Chess Finite Game Von Neumann\u0026rsquo;s Theorem Theorem. In every two-player, finite game with perfect information in which the set of outcomes is $\\mathcal O=\\{\\text{I wins, II wins, Draw}\\}$, one and only one of the following three alternatives must hold:\nPlayer I has a winning strategy. Player II has a winning strategy. Each of the two players has a strategy that guarantees at least a draw. The proof of the theorem is referred to [1]\nThe proof is based on the structure of the game tree\nRemark. Mycielski [1992] proved the theorem for the case of infinite games with perfect information, and the set of outcomes $\\mathcal O$ is finite. The proof is based on transfinite induction.\nZermelo\u0026rsquo;s Theorem Theorem. In chess, one and only one of the following must be true:\nWhite has a winnning strategy. Black has a winning strategy. Each of the two players has a strategy that guarantees at least a draw. It is only an existence theorem, and we still do not know which of the three possibilities is true.\nProof 1 As the game of chess is finite, there must be an upper bound on the number of moves in a game, set as $2K$\nQ.E.D.\nReferences Theory of Games and Economic Behavior by John von Neumann and Oskar Morgenstern Game Theory by Maschler, Michael, Solan, Eilon, and Zamir, Shmuel Winning Ways for your Mathematical Plays by Elwyn R. Berlekamp, John H. Conway, and Richard K. Guy Programming a Computer for Playing Chess by Claude Shannon ","tags":["Game Theory","Notes"],"categories":["Game Theory"]},{"title":"复","permalink":"https://yukar.icu/posts/1/","summary":" hi no ","content":" hi no ","tags":[],"categories":null},{"title":"Hash Table","permalink":"https://yukar.icu/posts/cs/preliminary/","summary":"Hash Dictionary","content":"Hash Dictionary ","tags":null,"categories":null},{"title":"Manifold","permalink":"https://yukar.icu/posts/manifold/1/","summary":"Reference: Introduction to Smooth Manifolds by John M. Lee.\nA topological space $M$ is a topological manifold of dimension $n$ if $M$ is locally Euclidean of dimension $n$, where locally Euclidean means that for every point $p \\in M$, there exists an open neighborhood $U$ of $p$ in $M$ and a homeomorphism $\\varphi: U \\to B^n$, then we obtain a set of such charts $\\{(U_\\alpha, \\varphi_\\alpha)\\}$ called a topological atlas on $M$.\n","content":"Reference: Introduction to Smooth Manifolds by John M. Lee.\nA topological space $M$ is a topological manifold of dimension $n$ if $M$ is locally Euclidean of dimension $n$, where locally Euclidean means that for every point $p \\in M$, there exists an open neighborhood $U$ of $p$ in $M$ and a homeomorphism $\\varphi: U \\to B^n$, then we obtain a set of such charts $\\{(U_\\alpha, \\varphi_\\alpha)\\}$ called a topological atlas on $M$.\nIn most cases, we add two more conditions to the definition of a topological manifold:\n$M$ is Hausdorff. $M$ is second countable. $\\mathbb R^n, \\mathbb P^n$ are topological manifolds. Any open subset or finite product of topological manifolds is a topological manifold.\n","tags":["Manifold","Notes"],"categories":["Manifold"]},{"title":"分析力学","permalink":"https://yukar.icu/posts/analyticalmechanics/1/","summary":" Euler-Lagrange 方程 $$\\dfrac {\\mathrm d}{\\mathrm dt} \\dfrac {\\partial L}{\\partial \\dot q_i} - \\dfrac {\\partial L}{\\partial q_i} = 0.$$ 正则动量 $$p_i = \\dfrac{\\partial L}{\\partial \\dot q_i},\\quad \\dot p_i = \\dfrac {\\partial L}{\\partial q_i}.$$ Hamiltonian，可以对于正则 Lagrangian 定义 $$H(q,p,t) = \\sum_i p_i \\dot q_i - L(q, \\dot q, t)=\\sum_i p_i \\dot q_i - L(q, p, t).$$ Hamilton 方程 $$\\dot q_i = \\dfrac{\\partial H}{\\partial p_i}, \\quad \\dot p_i = -\\dfrac{\\partial H}{\\partial q_i},\\quad \\dfrac{\\partial H}{\\partial t} = -\\dfrac{\\partial L}{\\partial t}.$$ 相空间作用量原理*\n","content":" Euler-Lagrange 方程 $$\\dfrac {\\mathrm d}{\\mathrm dt} \\dfrac {\\partial L}{\\partial \\dot q_i} - \\dfrac {\\partial L}{\\partial q_i} = 0.$$ 正则动量 $$p_i = \\dfrac{\\partial L}{\\partial \\dot q_i},\\quad \\dot p_i = \\dfrac {\\partial L}{\\partial q_i}.$$ Hamiltonian，可以对于正则 Lagrangian 定义 $$H(q,p,t) = \\sum_i p_i \\dot q_i - L(q, \\dot q, t)=\\sum_i p_i \\dot q_i - L(q, p, t).$$ Hamilton 方程 $$\\dot q_i = \\dfrac{\\partial H}{\\partial p_i}, \\quad \\dot p_i = -\\dfrac{\\partial H}{\\partial q_i},\\quad \\dfrac{\\partial H}{\\partial t} = -\\dfrac{\\partial L}{\\partial t}.$$ 相空间作用量原理*\nPoisson 括号 $$\\{f, g\\} = \\sum_i \\left( \\dfrac{\\partial f}{\\partial q_i} \\dfrac{\\partial g}{\\partial p_i} - \\dfrac{\\partial f}{\\partial p_i} \\dfrac{\\partial g}{\\partial q_i} \\right).$$ Poisson 括号与 Hamilton 方程 $$\\dfrac {\\mathrm d f}{\\mathrm dt} = \\{f, H\\} + \\dfrac{\\partial f}{\\partial t}.$$ 正则括号 $$\\{q_i, q_j\\} = 0, \\quad \\{p_i, p_j\\} = 0, \\quad \\{q_i, p_j\\} = \\delta_{ij}.$$ Jacobi 恒等式 $$\\{f, \\{g, h\\}\\} + \\{g, \\{h, f\\}\\} + \\{h, \\{f, g\\}\\} = 0.$$ 对于 $T^*V$ 上的任意一点，存在局部坐标 $(q_1, \\dots, q_n, p_1, \\dots, p_n)$，使得\n","tags":["Analytical Mechanics","Notes"],"categories":["Analytical Mechanics"]},{"title":"Sobolev 空间和弱导数","permalink":"https://yukar.icu/posts/partialdifferentialequations/sobolev/","summary":"$W^{m,p}$ 回忆广义函数空间 $\\mathcal D(\\Omega)$ 上的对偶元素构造。给定任意 $f\\in L_{\\mathrm{loc}}^1(\\Omega)$，存在线性映射 $$T:\\mathcal D(\\Omega)\\to \\mathcal D^*(\\Omega),\\quad \\varphi\\mapsto\\left(T_f:\\varphi\\mapsto \\int_{\\Omega} f\\varphi\\mathrm dx\\right).$$在 Lebesgue 意义下（默认）是单射：\n$T$ 是单射：对任意 $f,g\\in L_{\\mathrm{loc}}^1(\\Omega)$，如果 $T_f=T_g$，则 $f=g$.\n这等价于说明 $T_f=0$，即 $$\\langle T_f,\\varphi\\rangle = \\int_{\\Omega} f\\varphi\\mathrm dx=0,\\quad \\forall \\varphi\\in \\mathcal D(\\Omega),$$ 可以推出 $f=0$，这是变分法基本引理的 Lebesgue 版本。考虑截断空间和 Friedrichs 磨光核，则 $$f*\\rho_\\varepsilon(x)=\\int_{\\Omega} f(y)\\rho_\\varepsilon(x-y)\\mathrm dy=\\langle T_f,\\rho_\\varepsilon(x-\\cdot)\\rangle=0$$ 接下来只需证明，对于任意有界开集 $U\\subseteq\\Omega$，有 $\\|f\\|_{L^1(U)}=0$，这是因为 $$f(x)-f*\\rho_\\varepsilon(x)=\\int_{\\Omega} (f(x)-f(x-y))\\rho_\\varepsilon(y)\\mathrm dy,$$ 因此有 Minkowski 不等式 $$\\|f-f*\\rho_\\varepsilon\\|_{L^1(U)}\\leq \\int_{\\Omega} \\|f(\\cdot)-f(\\cdot-y)\\|_{L^1(U)}\\rho_\\varepsilon(y)\\mathrm dy\\leq \\sup_{|y|\\leq \\varepsilon} \\|f(\\cdot)-f(\\cdot-y)\\|_{L^1(U)}.$$ 由 $L^1$ 的平移连续性，取 $\\varepsilon\\to 0$，结合 $f*\\rho_\\varepsilon=0$ 即可。\n我们还需要指出，$L^p(\\Omega)\\subseteq L_{\\mathrm{loc}}^1(\\Omega)$，对任意 $1\\leq p\\leq \\infty$，这是因为 Hölder 不等式 ","content":"$W^{m,p}$ 回忆广义函数空间 $\\mathcal D(\\Omega)$ 上的对偶元素构造。给定任意 $f\\in L_{\\mathrm{loc}}^1(\\Omega)$，存在线性映射 $$T:\\mathcal D(\\Omega)\\to \\mathcal D^*(\\Omega),\\quad \\varphi\\mapsto\\left(T_f:\\varphi\\mapsto \\int_{\\Omega} f\\varphi\\mathrm dx\\right).$$在 Lebesgue 意义下（默认）是单射：\n$T$ 是单射：对任意 $f,g\\in L_{\\mathrm{loc}}^1(\\Omega)$，如果 $T_f=T_g$，则 $f=g$.\n这等价于说明 $T_f=0$，即 $$\\langle T_f,\\varphi\\rangle = \\int_{\\Omega} f\\varphi\\mathrm dx=0,\\quad \\forall \\varphi\\in \\mathcal D(\\Omega),$$ 可以推出 $f=0$，这是变分法基本引理的 Lebesgue 版本。考虑截断空间和 Friedrichs 磨光核，则 $$f*\\rho_\\varepsilon(x)=\\int_{\\Omega} f(y)\\rho_\\varepsilon(x-y)\\mathrm dy=\\langle T_f,\\rho_\\varepsilon(x-\\cdot)\\rangle=0$$ 接下来只需证明，对于任意有界开集 $U\\subseteq\\Omega$，有 $\\|f\\|_{L^1(U)}=0$，这是因为 $$f(x)-f*\\rho_\\varepsilon(x)=\\int_{\\Omega} (f(x)-f(x-y))\\rho_\\varepsilon(y)\\mathrm dy,$$ 因此有 Minkowski 不等式 $$\\|f-f*\\rho_\\varepsilon\\|_{L^1(U)}\\leq \\int_{\\Omega} \\|f(\\cdot)-f(\\cdot-y)\\|_{L^1(U)}\\rho_\\varepsilon(y)\\mathrm dy\\leq \\sup_{|y|\\leq \\varepsilon} \\|f(\\cdot)-f(\\cdot-y)\\|_{L^1(U)}.$$ 由 $L^1$ 的平移连续性，取 $\\varepsilon\\to 0$，结合 $f*\\rho_\\varepsilon=0$ 即可。\n我们还需要指出，$L^p(\\Omega)\\subseteq L_{\\mathrm{loc}}^1(\\Omega)$，对任意 $1\\leq p\\leq \\infty$，这是因为 Hölder 不等式 $$\\|f\\|_{L^1(U)}\\leq \\|f\\|_{L^p(U)}\\cdot \\|\\chi_U\\|_{L^{p'}(U)}\u003c\\infty,$$ 在更大的空间 $\\mathcal D^*(\\Omega)\\supseteq L_{\\mathrm{loc}}^1(\\Omega)\\supseteq L^p(\\Omega)$ 中，我们已经定义过广义导数 $D_iT\\in \\mathcal D^*(\\Omega)$ $$\\langle D_iT,\\varphi\\rangle:=-\\langle T,\\partial_i \\varphi\\rangle,\\quad \\forall \\varphi\\in \\mathcal D(\\Omega),\\ T\\in \\mathcal D^*(\\Omega).$$ 且与经典导数相容。我们对 $f\\in L^1_{\\mathrm{loc}}(\\Omega)$ 说 $D^\\alpha f\\in L^p(\\Omega)$，指的是存在 $g\\in L^p(\\Omega)$，使得 $D^\\alpha T_f=T_g$，即 $$(-1)^{|\\alpha|}\\int_{\\Omega} f\\partial^\\alpha \\varphi\\mathrm dx=\\int_{\\Omega} g\\varphi\\mathrm dx,\\quad \\forall \\varphi\\in \\mathcal D(\\Omega).$$ 此时 $g$ 被称为 $f$ 的 $\\alpha$ 阶弱导数，记作 $D^\\alpha f$.\n对于开集 $\\Omega\\subseteq \\mathbb R^n$，定义 Sobolev 空间 $$W^{m,p}(\\Omega):=\\{u\\in L^p(\\Omega): D^\\alpha u\\in L^p(\\Omega),\\ \\forall |\\alpha|\\leq m\\},\\quad 1\\leq p\\leq \\infty,$$ 为具有不超过 $m$ 阶的弱导数全体的函数集，配备范数 $$\\|u\\|_{W^{m,p}(\\Omega)}:=\\begin{dcases}\\left[\\sum_{|\\alpha|\\leq m}\\int_{\\Omega} |D^\\alpha u|^p\\mathrm dx\\right]^{\\frac 1p}, \u0026 1\\leq p\u003c\\infty, \\\\[14pt] \\max_{|\\alpha|\\leq m}\\|D^\\alpha u\\|_{L^\\infty(\\Omega)}, \u0026 p=\\infty.\\end{dcases}$$ 特别地，$H^m(\\Omega):=W^{m,2}(\\Omega)$，$W^{0,p}(\\Omega)=L^p(\\Omega)$.\n在 $H^m(\\Omega)$ 中，有内积 $$\\langle u,v\\rangle_{H^m(\\Omega)}:=\\sum_{|\\alpha|\\leq m}\\int_{\\Omega} D^\\alpha u\\cdot D^\\alpha v\\mathrm dx.$$ 只有在 $p=2$ 时，Sobolev 空间才是 Hilbert 空间；对于任意 $1\\leq p\u003c\\infty$，$W^{m,p}(\\Omega)$ 是 Banach 空间。虽然 $$\\|u\\|_{m,p}=\\sum_{|\\alpha|\\leq m}\\|D^\\alpha u\\|_{L^p(\\Omega)},\\quad 1\\leq p\\leq \\infty,$$ 是等价范数，但这与 Hilbert 空间的内积范数不相容。不过考虑这个等价范数的好处是，将多重指标 $\\alpha$ 作为指标，分区控制。具体而言，为了不增加维数，定义不交并空间 $$\\Omega^{(m)}:=\\bigsqcup_{|\\alpha|\\leq m} \\Omega_\\alpha,\\quad \\Omega_\\alpha=\\Omega,$$ 定义映射 $P: W^{m,p}(\\Omega)\\to L^p(\\Omega^{(m)})$，平铺 $u$ 的全体 $|\\alpha|\\leq m$ 阶弱导数 $$P(u)=U,\\ U|_{\\Omega_\\alpha}=D^\\alpha u(x),\\quad \\forall |\\alpha|\\leq m.$$ 所以 $P$ 是等距嵌入，记嵌入像为 $W:=P(W^{m,p}(\\Omega))\\subseteq L^p(\\Omega^{(m)})$。\n$W^{m,p}(\\Omega)$ 的完备性：$W^{m,p}(\\Omega)$ 是 Banach 空间。\n只需证明完备性，对于 $W^{m,p}(\\Omega)$ 中的 Cauchy 列 $\\{u_k\\}$，$\\{D^\\alpha u\\}$ 是 $L^p(\\Omega)$ 中的 Cauchy 列，根据 $L^p(\\Omega)$ 的完备性，存在 $$u_k\\xrightarrow{\\|\\cdot\\|_{L^p(\\Omega)}} u,\\quad D^\\alpha u_k\\xrightarrow{\\|\\cdot\\|_{L^p(\\Omega)}} u_\\alpha,\\quad \\forall |\\alpha|\\leq m.$$ 下面说明 $u_\\alpha$ 是 $u$ 的 $\\alpha$ 阶弱导数，即 $D^\\alpha u=u_\\alpha$，考虑 $$T_{u_\\alpha}(\\varphi)=\\lim_{n\\to\\infty}T_{D^\\alpha u_n}(\\varphi)=\\lim_{n\\to\\infty}(-1)^{|\\alpha|}T_{u_n}(\\partial^\\alpha \\varphi)=(-1)^{|\\alpha|}T_u(\\partial^\\alpha \\varphi)=T_{D^\\alpha u}(\\varphi)$$ 应用 Hölder 不等式说明上述极限交换，从而说明 $$u\\in W^{m,p}(\\Omega),\\quad u_n\\xrightarrow{\\|\\cdot\\|_{W^{m,p}(\\Omega)}} u.$$ 从而，$W$ 是 $L^p(\\Omega^{(m)})$ 的闭子空间。据此，有从 $L^p(\\Omega)$ 推出的基本框架\n$W^{m,p}(\\Omega)$ 的基本性质：\n当 $p=2$ 时，$H^m(\\Omega)$ 是 Hilbert 空间。 $W^{m,p}(\\Omega)$ 是可分空间，当 $1\\leq p\u003c\\infty$ 时；$W^{m,\\infty}(\\Omega)$ 是不可分空间； $W^{m,p}(\\Omega)$ 是自反空间，当 $1","tags":["Partial Differential Equations","Notes"],"categories":["Partial Differential Equations"]},{"title":"Workshop","permalink":"https://yukar.icu/posts/partialdifferentialequations/homework/13---%E5%89%AF%E6%9C%AC/","summary":"传递、混合、压缩、扩张\n设 $f:S\\to S,\\ g:T\\to T$ 是连续映射，如果有同胚 $h:S\\to T$ 使得 $h\\circ f=g\\circ h$，则称 $f,g$ 拓扑共轭，$h$ 是共轭映射；如果除去 $h$ 的同胚条件，只保留满射，则称 $g$ 是 $f$ 的因子，$h$ 是半共轭映射。\n稠密与周期 考虑 $S^1\\cong \\mathbb R/\\mathbb Z$ 上的旋转映射（这等价于） $$x\\mapsto x+\\alpha\\mod 1.$$ 根据有理性分类轨道： $$\\# \\mathcal O(x)=\\begin{cases}\\infty, \u0026 \\alpha\\in \\mathbb R\\setminus \\mathbb Q,\\\\[6pt]\u003c\\infty, \u0026 \\alpha\\in \\mathbb Q.\\end{cases},\\quad \\forall x\\in S^1.$$ 特别地，由 Weyl 定理，$\\overline {\\mathcal O(x)}=S^1$ 当且仅当 $\\alpha\\in \\mathbb R\\setminus \\mathbb Q$.\n设 $f\\in C(X)$，如果存在 $x\\in X$ 使得双向轨道 $\\mathcal O(x)=\\{f^n(x)\\}_{n\\in\\mathbb Z}$ 在 $X$ 中稠密，则称 $f$ 是拓扑传递的；如果对于任意 $x\\in X$，都有 $\\overline {\\mathcal O(x)}=X$，则称 $f$ 是拓扑极小的。其中 $f$ 不一定是同胚，因此 $f^{-n}$ 表示取 $n$ 次原像。\n","content":"传递、混合、压缩、扩张\n设 $f:S\\to S,\\ g:T\\to T$ 是连续映射，如果有同胚 $h:S\\to T$ 使得 $h\\circ f=g\\circ h$，则称 $f,g$ 拓扑共轭，$h$ 是共轭映射；如果除去 $h$ 的同胚条件，只保留满射，则称 $g$ 是 $f$ 的因子，$h$ 是半共轭映射。\n稠密与周期 考虑 $S^1\\cong \\mathbb R/\\mathbb Z$ 上的旋转映射（这等价于） $$x\\mapsto x+\\alpha\\mod 1.$$ 根据有理性分类轨道： $$\\# \\mathcal O(x)=\\begin{cases}\\infty, \u0026 \\alpha\\in \\mathbb R\\setminus \\mathbb Q,\\\\[6pt]\u003c\\infty, \u0026 \\alpha\\in \\mathbb Q.\\end{cases},\\quad \\forall x\\in S^1.$$ 特别地，由 Weyl 定理，$\\overline {\\mathcal O(x)}=S^1$ 当且仅当 $\\alpha\\in \\mathbb R\\setminus \\mathbb Q$.\n设 $f\\in C(X)$，如果存在 $x\\in X$ 使得双向轨道 $\\mathcal O(x)=\\{f^n(x)\\}_{n\\in\\mathbb Z}$ 在 $X$ 中稠密，则称 $f$ 是拓扑传递的；如果对于任意 $x\\in X$，都有 $\\overline {\\mathcal O(x)}=X$，则称 $f$ 是拓扑极小的。其中 $f$ 不一定是同胚，因此 $f^{-n}$ 表示取 $n$ 次原像。\n上述定义基于轨道分类，传递性是对轨道的刻画，极小性类比于不可约分支。所以极小性蕴含传递性，反过来不成立，考虑 $$f: S^1\\times [0,1]\\to S^1\\times [0,1],\\quad (x,y)\\mapsto (x+\\alpha,y).$$ 其中 $[0,1]$ 配备半开拓扑，因此 $\\alpha\\in \\mathbb R\\setminus \\mathbb Q$ 时，$f$ 关于 $(1,0)$ 的轨道稠密，但 $f$ 不极小，同时 $$X=\\bigsqcup_{y\\in [0,1]}\\mathcal O((1,y)),\\quad \\forall \\alpha\\in \\mathbb R\\setminus \\mathbb Q.$$ 全空间可以分解为极小子轨道的（可交）并，这个观察比较无聊。\n注意到 $$f^n(\\mathcal O(x))=\\mathcal O(f^n(x)),\\quad \\forall n\\in \\mathbb Z, x\\in X,$$ 因此定义 $f$ 的不变集 $A\\subseteq X$ 为满足 $f(A)=A$ 的集合。极小性等价于 $X$ 是 $f$ 的唯一非空不变集（用到连续性）。\n保测映射 $T:X\\to X$，如果对任意 $A,B\\subseteq X$ 可测，都有 $$\\lim_{n\\to \\infty}\\mu(T^{-n}(A)\\cap B)=\\mu(A)\\mu(B),$$ 则称 $T$ 是混合的。\nProp. 混合性蕴含遍历性。\nProp. 混合性只需对充分族中的集合验证。\nProof. $A,B\\in\\mathscr A$，其中 $A=\\bigcup^n_{i=1}A_i$，$B=\\bigcup^m_{j=1}B_j$，其中 $A_i,B_j\\in\\mathscr C$，则 $$\\mu(T^{-n}(A)\\cap B)=\\mu\\left(\\bigcup^n_{i=1}\\bigcup^m_{j=1}T^{-n}(A_i)\\cap B_j\\right)=\\sum^n_{i=1}\\sum^m_{j=1}\\mu(T^{-n}(A_i)\\cap B_j),$$ Thm. 给定 $L^2(X)$ 的一组 Schauder 基 $\\Phi$，则 $T$ 混合，当且仅当对任意 $\\phi,\\psi\\in \\Phi$ 都有 $$\\lim_{n\\to \\infty}\\int_X \\varphi(T^nx)\\psi(x)\\mathrm d\\mu=\\int_X \\varphi(x)\\mathrm d\\mu\\int_X \\psi(x)\\mathrm d\\mu.$$ Proof. 充分性：因为 $\\Phi$ 是 Schauder 基，在 $L^2(X)$ 是稠密的，所以成立。必要性，取 $\\varphi=\\chi _A,\\ \\psi=\\chi _B$，特征函数族是 Schauder 基。\n保测变换 $T:X\\to X$，如果对任意 $\\varphi,\\psi\\in L^2(X)$ 都有 $$\\lim_{n\\to\\infty}\\dfrac 1n\\sum^{n-1}_{k=0}\\left(\\int_X (\\varphi\\circ T^k)\\bar \\psi\\mathrm d\\mu-\\int_X \\varphi\\mathrm d\\mu\\int_X \\bar \\psi\\mathrm d\\mu\\right)^2=0,$$ 则称 $T$ 是弱混合的。\nProp. 混合性 $\\implies$ 弱混合性 $\\implies$ 遍历性。\nProp. 几个结果：\n$T_\\gamma: S^1\\to S^1,\\quad x\\mapsto x+\\gamma$，不混合； $E_m$ 混合 ","tags":["Dynamical Systems"],"categories":["Partial Differential Equations"]},{"title":"调和函数与极值原理","permalink":"https://yukar.icu/posts/partialdifferentialequations/harmonic/","summary":"极值原理 弱极值原理 强极值原理 调和函数 设 $\\Omega \\subseteq \\mathbb R^d$ 是开区域，如果 $u\\in C^2(\\Omega)$ 满足 $\\Delta u=0$，则称 $u$ 是 $\\Omega$ 上的调和函数，记作 $u\\in \\mathcal H(\\Omega)$。调和函数是 Laplace 方程的解，继承其性质。\n球面平均性 球面平均性：如果 $u\\in \\mathcal H(\\Omega)$，则对任意 $r\\in (0,R]$ 和 $x\\in \\Omega$ 满足 $B_R(x)\\subseteq \\Omega$，有 $$u(x)=\\frac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} u(y)\\mathrm dS=\\dfrac 1{|B_r(0)|}\\int_{B_r(x)} u(y)\\mathrm dy=: h(x;r).$$ 反过来，如果 $u\\in C^2(\\Omega)$ 在 $x\\in \\Omega$ 满足上述球面平均性，则 $\\Delta u(x)=0$.\n从连续性出发 $$u(x)=\\lim_{r\\to 0}\\frac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} u(y)\\mathrm dS=\\lim_{r\\to 0}\\dfrac 1{|\\partial B_1(0)|}\\int_{\\partial B_1(0)} u(x+ry)\\mathrm dS=:\\lim_{r\\to 0} h(x;r).$$ 考虑含参积分 ","content":"极值原理 弱极值原理 强极值原理 调和函数 设 $\\Omega \\subseteq \\mathbb R^d$ 是开区域，如果 $u\\in C^2(\\Omega)$ 满足 $\\Delta u=0$，则称 $u$ 是 $\\Omega$ 上的调和函数，记作 $u\\in \\mathcal H(\\Omega)$。调和函数是 Laplace 方程的解，继承其性质。\n球面平均性 球面平均性：如果 $u\\in \\mathcal H(\\Omega)$，则对任意 $r\\in (0,R]$ 和 $x\\in \\Omega$ 满足 $B_R(x)\\subseteq \\Omega$，有 $$u(x)=\\frac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} u(y)\\mathrm dS=\\dfrac 1{|B_r(0)|}\\int_{B_r(x)} u(y)\\mathrm dy=: h(x;r).$$ 反过来，如果 $u\\in C^2(\\Omega)$ 在 $x\\in \\Omega$ 满足上述球面平均性，则 $\\Delta u(x)=0$.\n从连续性出发 $$u(x)=\\lim_{r\\to 0}\\frac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} u(y)\\mathrm dS=\\lim_{r\\to 0}\\dfrac 1{|\\partial B_1(0)|}\\int_{\\partial B_1(0)} u(x+ry)\\mathrm dS=:\\lim_{r\\to 0} h(x;r).$$ 考虑含参积分 $$\\begin{darray}{ll}\\partial_r h(x;r)\u0026=\\dfrac 1{|\\partial B_1(0)|}\\int_{\\partial B_1(0)} \\nabla u(x+r\\omega)\\cdot \\omega\\mathrm d S_\\omega\\\\[14pt]\u0026=\\dfrac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} \\dfrac {y-x}{r}\\cdot \\nabla u(y)\\mathrm dS_y\\\\[14pt]\u0026=\\dfrac 1{|\\partial B_r(0)|}\\int_{\\partial B_r(x)} \\dfrac {\\partial u}{\\partial \\mathbf n}(y)\\mathrm dS_y\\\\[14pt]\u0026=\\dfrac 1{|\\partial B_r(0)|}\\int_{B_r(x)} \\mathrm{div}(\\nabla u)(y)\\mathrm dy\\\\[14pt]\u0026=\\dfrac rd\\cdot \\dfrac 1{|B_r(0)|}\\int_{B_r(x)} \\Delta u(y)\\mathrm dy\\end{darray}$$ 其中 $$r|\\partial B_r(0)|=\\int_{\\partial B_r(0)}y\\cdot\\mathbf n\\mathrm dS_y=\\int_{B_r(0)} \\mathrm{div} y\\mathrm dy=\\int_{B_r(0)} d\\mathrm dy=d|B_r(0)|.$$ 如果 $u\\in\\mathcal H(\\Omega)$，则 $$\\begin{darray}{ll}\\dfrac 1{|B_r(0)|}\\int_{B_r(x)} u(y)\\mathrm dy\u0026=\\dfrac d{r^d|\\partial B_1(0)|}\\int_0^r\\int_{\\partial B_\\rho(0)} u(x+y)\\mathrm dS_y\\mathrm d\\rho\\\\[14pt]\u0026=\\dfrac d{r^d|\\partial B_1(0)|}\\int_0^ru(x)\\rho^{d-1}|\\partial B_1(0)|\\mathrm d\\rho\\\\[14pt]\u0026=u(x)\\end{darray}$$ 反过来，如果 $u\\in C^2(\\Omega)$ 满足 $\\partial_r h(x;r)=0$，则 $\\Delta u(x)=0$，这由连续性保证。\nPoisson 公式：如果 $u\\in \\mathcal H(B_R(0))$，则对于任意 $x\\in B_R(0)$，有 $$u(\\xi)=\\dfrac {R^2-|\\xi|^2}{\\omega_n R}\\int_{\\partial B_R(0)} \\dfrac{u(x)}{|\\xi-x|^n}\\mathrm dS_x.$$ 其中 $|\\xi|","tags":["Partial Differential Equations","Notes"],"categories":["Partial Differential Equations"]},{"title":"偏微分方程 习题 12","permalink":"https://yukar.icu/posts/partialdifferentialequations/homework/12/","summary":"参考资料：《数学物理方程讲义》，姜礼尚。\npp212.15 设 $u\\in C^3_0(\\Omega)$ 且满足 $$-\\Delta u=f(x),\\quad x\\in\\Omega,$$ 证明 $$\\sum^n_{i,j=1}\\int_\\Omega u^2_{x_ix_j}\\mathrm dx\\leq n\\int_\\Omega f^2\\mathrm dx.$$解： 考虑到其紧支性，作分部积分，再由光滑性交换偏导次序 $$\\begin{darray}{ll}\\sum^n_{i,j=1}\\int_\\Omega u^2_{x_ix_j}\\mathrm dx\u0026=\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_j}u_{x_ix_j}\\mathrm dx\\\\[14pt]\u0026=-\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_jx_j}u_{x_i}\\mathrm dx\\\\[14pt]\u0026=\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_i}u_{x_jx_j}\\mathrm dx\\\\[14pt]\u0026=\\int_\\Omega \\Delta^2 u\\mathrm dx\\\\[14pt]\u0026=\\int_\\Omega f^2\\mathrm dx.\\end{darray}$$这应更严格。\npp212.16 设 $\\Omega=\\{(x,y):0","content":"参考资料：《数学物理方程讲义》，姜礼尚。\npp212.15 设 $u\\in C^3_0(\\Omega)$ 且满足 $$-\\Delta u=f(x),\\quad x\\in\\Omega,$$ 证明 $$\\sum^n_{i,j=1}\\int_\\Omega u^2_{x_ix_j}\\mathrm dx\\leq n\\int_\\Omega f^2\\mathrm dx.$$解： 考虑到其紧支性，作分部积分，再由光滑性交换偏导次序 $$\\begin{darray}{ll}\\sum^n_{i,j=1}\\int_\\Omega u^2_{x_ix_j}\\mathrm dx\u0026=\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_j}u_{x_ix_j}\\mathrm dx\\\\[14pt]\u0026=-\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_jx_j}u_{x_i}\\mathrm dx\\\\[14pt]\u0026=\\sum^n_{i,j=1}\\int_\\Omega u_{x_ix_i}u_{x_jx_j}\\mathrm dx\\\\[14pt]\u0026=\\int_\\Omega \\Delta^2 u\\mathrm dx\\\\[14pt]\u0026=\\int_\\Omega f^2\\mathrm dx.\\end{darray}$$这应更严格。\npp212.16 设 $\\Omega=\\{(x,y):0","tags":["Partial Differential Equations","Homework"],"categories":["Partial Differential Equations"]},{"title":"分析力学 习题 10","permalink":"https://yukar.icu/posts/analyticalmechanics/homework/10/","summary":"问题 1：对称陀螺的 Liouville 可积性 考虑一个绕固定点自由旋转的对称陀螺（无平动，仅含转动自由度）。设固定点为 $O$，陀螺关于其对称轴的转动惯量为 $I_3$，关于垂直于对称轴的两个主轴的转动惯量相等，$I_1=I_2$。用 Euler 角 $(\\phi,\\theta,\\psi)$ 描述陀螺的取向，其中 $\\phi$ 为进动角，$\\theta$ 为章动角，$\\psi$ 为自转角。在 Euler 角下，对称陀螺的 Hamiltonian 为 $$H(\\phi,\\theta,\\psi,p_\\phi,p_\\theta,p_\\psi) = \\frac{p_\\theta^2}{2I_1} + \\frac{(p_\\phi - p_\\psi \\cos\\theta)^2}{2I_1 \\sin^2\\theta} + \\frac{p_\\psi^2}{2I_3},$$ 其中正则动量为 $$p_\\phi = \\dfrac {\\partial L}{\\partial \\dot \\phi},\\quad p_\\theta=\\dfrac {\\partial L}{\\partial \\dot \\theta},\\quad p_\\psi=\\dfrac {\\partial L}{\\partial \\dot \\psi}.$$(a) 指出 $H$ 中不出现的循环坐标，并写出相应的守恒量。验证它们确实是运动常数。\n(b) 写出该系统的三个守恒量 $F_1,F_2,F_3$（其中一个为 $H$ 本身）。显式计算它们两两之间的 Poisson 括号，证明它们相互对合： $$\\{F_i,F_j\\}=0,\\quad i,j=1,2,3.$$(c) 论证这三个守恒量是函数独立的，从而得出结论：该对称陀螺是 Liouville 可积的。\n解： (a) Hamiltonian $H$ 中显式不出现的循环坐标是 $\\phi,\\psi$，对应的守恒量为 $p_\\phi,p_\\psi$，现在用 Poisson 括号验证它们是运动常数，这里标号 $\\phi,\\psi,\\theta$ 分别为 $1,2,3$，则 ","content":"问题 1：对称陀螺的 Liouville 可积性 考虑一个绕固定点自由旋转的对称陀螺（无平动，仅含转动自由度）。设固定点为 $O$，陀螺关于其对称轴的转动惯量为 $I_3$，关于垂直于对称轴的两个主轴的转动惯量相等，$I_1=I_2$。用 Euler 角 $(\\phi,\\theta,\\psi)$ 描述陀螺的取向，其中 $\\phi$ 为进动角，$\\theta$ 为章动角，$\\psi$ 为自转角。在 Euler 角下，对称陀螺的 Hamiltonian 为 $$H(\\phi,\\theta,\\psi,p_\\phi,p_\\theta,p_\\psi) = \\frac{p_\\theta^2}{2I_1} + \\frac{(p_\\phi - p_\\psi \\cos\\theta)^2}{2I_1 \\sin^2\\theta} + \\frac{p_\\psi^2}{2I_3},$$ 其中正则动量为 $$p_\\phi = \\dfrac {\\partial L}{\\partial \\dot \\phi},\\quad p_\\theta=\\dfrac {\\partial L}{\\partial \\dot \\theta},\\quad p_\\psi=\\dfrac {\\partial L}{\\partial \\dot \\psi}.$$(a) 指出 $H$ 中不出现的循环坐标，并写出相应的守恒量。验证它们确实是运动常数。\n(b) 写出该系统的三个守恒量 $F_1,F_2,F_3$（其中一个为 $H$ 本身）。显式计算它们两两之间的 Poisson 括号，证明它们相互对合： $$\\{F_i,F_j\\}=0,\\quad i,j=1,2,3.$$(c) 论证这三个守恒量是函数独立的，从而得出结论：该对称陀螺是 Liouville 可积的。\n解： (a) Hamiltonian $H$ 中显式不出现的循环坐标是 $\\phi,\\psi$，对应的守恒量为 $p_\\phi,p_\\psi$，现在用 Poisson 括号验证它们是运动常数，这里标号 $\\phi,\\psi,\\theta$ 分别为 $1,2,3$，则 $$\\{p_\\phi,H\\}=\\sum_{i=1}^3 \\left(\\frac{\\partial p_1}{\\partial q_i}\\frac{\\partial H}{\\partial p_i}-\\frac{\\partial p_1}{\\partial p_i}\\frac{\\partial H}{\\partial q_i}\\right)=-\\frac{\\partial H}{\\partial \\phi}=0,$$ 另一个是同理的。\n(b) 得到 $F_1=p_\\phi,F_2=p_\\psi,F_3=H$，和 $H$ 有关的 Poisson 括号已经验证，而计算 $\\{p_\\phi,p_\\psi\\}=0$ 只需注意到 $$\\dfrac {\\partial p_i}{\\partial q_j}=0,\\quad \\dfrac {\\partial p_i}{\\partial p_j}=\\delta_{ij}.$$(c)\n问题 2：用 Hamilton-Jacobi 方程求解对称陀螺 继续考虑问题 1 中的对称陀螺。该系统具有 $n=3$ 个自由度，其完全积分需依赖于 $3$ 个独立常数，可自然取为 $(\\alpha_1,\\alpha_2,\\alpha_3)=(\\alpha_\\phi,\\alpha_\\psi,E)$。\n(a) 写出不含时 Hamilton-Jacobi 方程，设 $$S(\\phi,\\theta,\\psi,\\alpha_\\phi,\\alpha_\\psi,E,t)=W(\\phi,\\theta,\\psi,\\alpha_\\phi,\\alpha_\\psi,E)-Et,$$ 并写出 $W$ 所满足的方程。\n(b)\n问题 3：Kepler 问题的作用量-角变量 ","tags":["Analytical Mechanics","Homework"],"categories":["Analytical Mechanics"]},{"title":"Pi Agent 包源码逐行解读 — agent-loop.ts \u0026 agent.ts","permalink":"https://yukar.icu/posts/pi-architecture/agent-loop-code-walkthrough/","summary":"Pi Agent 包源码逐行解读 本文对 packages/agent/src/ 下的 agent-loop.ts（~700 行）和 agent.ts（~540 行）做逐行级的代码解读，涵盖每个函数的设计意图、类型系统、控制流和错误处理策略。\n目录 文件概览 agent-loop.ts 逐行解读 类型与导入 agentLoop / agentLoopContinue — 公开入口 runAgentLoop / runAgentLoopContinue — 异步底层 runLoop — 核心双层循环 streamAssistantResponse — LLM 流式调用 executeToolCalls — 工具调度 executeToolCallsSequential / Parallel — 串行与并行 prepareToolCall — 前置管道 executePreparedToolCall — 工具执行 finalizeExecutedToolCall — 后处理 辅助函数 agent.ts 逐行解读 类型与导入 MutableAgentState 与 createMutableAgentState PendingMessageQueue Agent 类 — 构造器 subscribe — 事件订阅 prompt — 发起对话 continue — 继续对话 steer / followUp — 消息队列 abort / waitForIdle / reset — 生命周期控制 私有方法：runPromptMessages / runContinuation runWithLifecycle — 并发控制 handleRunFailure — 失败处理 finishRun — 运行终结 processEvents — 事件归约 createContextSnapshot / createLoopConfig — 适配层 设计模式总结 文件概览 packages/agent/src/ ├── agent-loop.ts # 无状态循环引擎（~700 行，纯函数） ├── agent.ts # 有状态封装层（~540 行，Agent 类） ├── types.ts # 类型定义（~400 行） ├── proxy.ts # HTTP 代理（~320 行） ├── index.ts # 导出入口（~8 行） ├── agent-learn.ts # 学习相关 ├── agent-loop-learn.ts └── README.md 核心分界线：\n","content":"Pi Agent 包源码逐行解读 本文对 packages/agent/src/ 下的 agent-loop.ts（~700 行）和 agent.ts（~540 行）做逐行级的代码解读，涵盖每个函数的设计意图、类型系统、控制流和错误处理策略。\n目录 文件概览 agent-loop.ts 逐行解读 类型与导入 agentLoop / agentLoopContinue — 公开入口 runAgentLoop / runAgentLoopContinue — 异步底层 runLoop — 核心双层循环 streamAssistantResponse — LLM 流式调用 executeToolCalls — 工具调度 executeToolCallsSequential / Parallel — 串行与并行 prepareToolCall — 前置管道 executePreparedToolCall — 工具执行 finalizeExecutedToolCall — 后处理 辅助函数 agent.ts 逐行解读 类型与导入 MutableAgentState 与 createMutableAgentState PendingMessageQueue Agent 类 — 构造器 subscribe — 事件订阅 prompt — 发起对话 continue — 继续对话 steer / followUp — 消息队列 abort / waitForIdle / reset — 生命周期控制 私有方法：runPromptMessages / runContinuation runWithLifecycle — 并发控制 handleRunFailure — 失败处理 finishRun — 运行终结 processEvents — 事件归约 createContextSnapshot / createLoopConfig — 适配层 设计模式总结 文件概览 packages/agent/src/ ├── agent-loop.ts # 无状态循环引擎（~700 行，纯函数） ├── agent.ts # 有状态封装层（~540 行，Agent 类） ├── types.ts # 类型定义（~400 行） ├── proxy.ts # HTTP 代理（~320 行） ├── index.ts # 导出入口（~8 行） ├── agent-learn.ts # 学习相关 ├── agent-loop-learn.ts └── README.md 核心分界线：\n层面 文件 有无状态 职责 引擎层 agent-loop.ts 无状态（纯函数） 双层循环、LLM 调用、工具执行、事件发射 管理层 agent.ts 有状态（Agent 类） 状态管理、并发控制、消息队列、事件分发 pi-ai 核心类型体系 agent-loop.ts 和 agent.ts 大量依赖 @earendil-works/pi-ai（位于 packages/ai/）的核心类型。这些类型构成了 LLM 调用、流式事件、消息格式和工具系统的基石。\nMessage — 统一消息类型 1 type Message = UserMessage | AssistantMessage | ToolResultMessage; 三种消息角色构成了 LLM 对话的基本单元：\nUserMessage 1 2 3 4 5 interface UserMessage { role: \u0026#34;user\u0026#34;; content: string | (TextContent | ImageContent)[]; timestamp: number; } 字段 说明 content 字符串（自动转为 text content）或 content 数组（支持文本+图片多模态） timestamp Unix 毫秒时间戳，用于排序和上下文管理 AssistantMessage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface AssistantMessage { role: \u0026#34;assistant\u0026#34;; content: (TextContent | ThinkingContent | ToolCall)[]; api: Api; provider: Provider; model: string; responseModel?: string; responseId?: string; diagnostics?: AssistantMessageDiagnostic[]; usage: Usage; stopReason: StopReason; errorMessage?: string; timestamp: number; } 字段 说明 content 可混合文本、思考过程和工具调用 api / provider / model 标识响应的来源 responseModel 实际处理请求的模型（如 OpenRouter 路由后可能与请求模型不同） responseId Provider 返回的响应标识符 usage Token 用量（含 cost 估算） stopReason 终止原因：\u0026quot;stop\u0026quot; / \u0026quot;length\u0026quot; / \u0026quot;toolUse\u0026quot; / \u0026quot;error\u0026quot; / \u0026quot;aborted\u0026quot; errorMessage 仅在 stopReason 为 error/aborted 时存在 stopReason 语义：\n值 含义 \u0026quot;stop\u0026quot; LLM 正常结束 \u0026quot;length\u0026quot; 达到 maxTokens 截断 \u0026quot;toolUse\u0026quot; LLM 要求调用工具 \u0026quot;error\u0026quot; LLM 调用失败或被 engine catch 到异常后转换 \u0026quot;aborted\u0026quot; 外部调用 abortController.abort() ToolResultMessage 1 2 3 4 5 6 7 8 9 interface ToolResultMessage\u0026lt;TDetails = any\u0026gt; { role: \u0026#34;toolResult\u0026#34;; toolCallId: string; toolName: string; content: (TextContent | ImageContent)[]; details?: TDetails; isError: boolean; timestamp: number; } 字段 说明 toolCallId 与 AssistantMessage 中的 ToolCall.id 对应 toolName 工具名称，用于路由和日志 isError 工具执行是否出错 Content 类型 TextContent 1 interface TextContent { type: \u0026#34;text\u0026#34;; text: string; textSignature?: string; } ThinkingContent 1 2 3 4 5 6 interface ThinkingContent { type: \u0026#34;thinking\u0026#34;; thinking: string; thinkingSignature?: string; redacted?: boolean; // 安全过滤导致内容被隐去 } ImageContent 1 interface ImageContent { type: \u0026#34;image\u0026#34;; data: string; mimeType: string; } ToolCall 1 2 3 4 5 6 7 interface ToolCall { type: \u0026#34;toolCall\u0026#34;; id: string; name: string; arguments: Record\u0026lt;string, any\u0026gt;; thoughtSignature?: string; } Context — LLM 上下文 1 2 3 4 5 interface Context { systemPrompt?: string; messages: Message[]; tools?: Tool[]; } 字段 说明 systemPrompt 可选，系统提示词 messages 对话历史（Message[]，即 UserMessage / AssistantMessage / ToolResultMessage） tools 可选，工具定义 Agent 上下文 vs LLM 上下文的区别：\nAgentContext（Agent 层） Context（LLM 层） systemPrompt: string systemPrompt?: string messages: AgentMessage[] --convertToLlm--\u0026gt; messages: Message[] tools: AgentTool[] tools?: Tool[] AgentMessage 是 Agent 层的扩展消息（支持自定义角色），而 Message 是 LLM 层的标准消息。\nTool — 工具定义 1 2 3 4 5 interface Tool\u0026lt;TParameters extends TSchema = TSchema\u0026gt; { name: string; description: string; parameters: TParameters; // JSON Schema (TypeBox) } 使用 TypeBox 的 TSchema 定义参数类型，支持 JSON Schema 校验。\nModel — 模型定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface Model\u0026lt;TApi extends Api\u0026gt; { id: string; name: string; api: TApi; provider: Provider; baseUrl: string; reasoning: boolean; thinkingLevelMap?: ThinkingLevelMap; input: (\u0026#34;text\u0026#34; | \u0026#34;image\u0026#34;)[]; cost: { input: number; output: number; cacheRead: number; cacheWrite: number; }; contextWindow: number; maxTokens: number; compat?: OpenAICompletionsCompat | OpenAIResponsesCompat | AnthropicMessagesCompat; } 字段 说明 api 已知 API 类型（如 \u0026quot;openai-completions\u0026quot;, \u0026quot;anthropic-messages\u0026quot; 等 30+ 种） provider 已知 Provider 名称（如 \u0026quot;openai\u0026quot;, \u0026quot;anthropic\u0026quot;, \u0026quot;bedrock\u0026quot; 等 30+ 种） reasoning 是否支持推理 cost 每百万 token 的价格（用于 cost 估算） compat API 兼容性覆盖 AssistantMessageEvent — 流式事件协议 共 13 种子事件：\n1 2 3 4 5 6 7 8 9 10 11 12 13 type AssistantMessageEvent = | { type: \u0026#34;start\u0026#34;; partial } | { type: \u0026#34;text_start\u0026#34;; contentIndex; partial } | { type: \u0026#34;text_delta\u0026#34;; contentIndex; delta; partial } | { type: \u0026#34;text_end\u0026#34;; contentIndex; content; partial } | { type: \u0026#34;thinking_start\u0026#34;; contentIndex; partial } | { type: \u0026#34;thinking_delta\u0026#34;; contentIndex; delta; partial } | { type: \u0026#34;thinking_end\u0026#34;; contentIndex; content; partial } | { type: \u0026#34;toolcall_start\u0026#34;; contentIndex; partial } | { type: \u0026#34;toolcall_delta\u0026#34;; contentIndex; delta; partial } | { type: \u0026#34;toolcall_end\u0026#34;; contentIndex; toolCall; partial } | { type: \u0026#34;done\u0026#34;; reason; message } | { type: \u0026#34;error\u0026#34;; reason; error } 状态机：\nstart -\u0026gt; text_start -\u0026gt; text_delta* -\u0026gt; text_end -\u0026gt; done -\u0026gt; thinking_start -\u0026gt; thinking_delta* -\u0026gt; thinking_end -\u0026gt; -\u0026gt; toolcall_start -\u0026gt; toolcall_delta* -\u0026gt; toolcall_end -\u0026gt; -\u0026gt; error (任意时刻) EventStream — 通用流式事件容器 1 2 3 4 5 6 7 class EventStream\u0026lt;T, R = T\u0026gt; implements AsyncIterable\u0026lt;T\u0026gt; { constructor(isComplete: (event: T) =\u0026gt; boolean, extractResult: (event: T) =\u0026gt; R) {} push(event: T): void end(result?: R): void [Symbol.asyncIterator](): AsyncIterator\u0026lt;T\u0026gt; result(): Promise\u0026lt;R\u0026gt; } 三要素：\n方法 用途 调用方 push(event) 发射事件 生产者 for await (const e of stream) 消费事件 消费者 result() 获取最终结果 调用方 内部机制：\n1 2 3 4 5 6 7 8 9 10 11 12 push(event: T): void { if (this.isComplete(event)) { this.done = true; this.resolveFinalResult(this.extractResult(event)); } const waiter = this.waiting.shift(); if (waiter) { waiter({ value: event, done: false }); // 直接交付等待者 } else { this.queue.push(event); // 无人消费则入队 } } 生产者-消费者模式：事件要么被 for-await 消费者立即取走，要么入队等待 isComplete 判定流终止，extractResult 提取最终结果 AssistantMessageEventStream 1 2 3 4 5 6 7 8 9 10 11 class AssistantMessageEventStream extends EventStream\u0026lt;AssistantMessageEvent, AssistantMessage\u0026gt; { constructor() { super( (event) =\u0026gt; event.type === \u0026#34;done\u0026#34; || event.type === \u0026#34;error\u0026#34;, (event) =\u0026gt; { if (event.type === \u0026#34;done\u0026#34;) return event.message; if (event.type === \u0026#34;error\u0026#34;) return event.error; }, ); } } LLM 返回的流。done 或 error 事件触发流结束。\nStreamFunction — 流式函数签名 1 2 3 4 5 type StreamFunction = ( model: Model, context: Context, options?: StreamOptions, ) =\u0026gt; AssistantMessageEventStream; 契约：\n必须返回 AssistantMessageEventStream 错误应编码在流中（error 事件），而非抛异常 错误终止必须产生 stopReason: \u0026quot;error\u0026quot; | \u0026quot;aborted\u0026quot; 的 AssistantMessage StreamOptions / SimpleStreamOptions — 流式选项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface StreamOptions { temperature?: number; maxTokens?: number; signal?: AbortSignal; apiKey?: string; transport?: Transport; // \u0026#34;sse\u0026#34; | \u0026#34;websocket\u0026#34; | \u0026#34;websocket-cached\u0026#34; | \u0026#34;auto\u0026#34; cacheRetention?: CacheRetention; sessionId?: string; onPayload?: (payload, model) =\u0026gt; unknown; onResponse?: (response, model) =\u0026gt; void; timeoutMs?: number; maxRetries?: number; maxRetryDelayMs?: number; } interface SimpleStreamOptions extends StreamOptions { reasoning?: ThinkingLevel; thinkingBudgets?: ThinkingBudgets; } 类型关系总览 graph TB subgraph \"LLM 核心库\" Message[\"Message\"] AM[\"AssistantMessagestopReason / usage\"] Context[\"Context\"] Tool[\"Tool\"] Event[\"AssistantMessageEvent13 种\"] Stream[\"EventStream\"] end subgraph \"Agent 包\" AgentMsg[\"AgentMessage\"] AgentCtx[\"AgentContext\"] AgentTool[\"AgentTool\"] AgentEvent[\"AgentEvent\"] end Message --\u003e AgentMsg Context -.-\u003e|convertToLlm| AgentCtx Tool --\u003e AgentTool ## agent-loop.ts 逐行解读 ### 类型与导入 ```typescript import { type AssistantMessage, type Context, EventStream, streamSimple, type ToolResultMessage, validateToolArguments, } from \"@earendil-works/pi-ai\"; import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentTool, AgentToolCall, AgentToolResult, StreamFn, } from \"./types.js\"; 导入 来源 用途 AssistantMessage pi-ai LLM 返回的 assistant 消息类型 Context pi-ai LLM 调用所需的上下文（systemPrompt + messages + tools） EventStream pi-ai 流式事件流，支持 push/end 模式 streamSimple pi-ai 默认的 LLM 流式调用函数 ToolResultMessage pi-ai 工具执行返回的消息类型 validateToolArguments pi-ai 工具参数的 JSON Schema 校验 AgentContext ./types Agent 层的上下文（systemPrompt + AgentMessage[] + tools） AgentEvent ./types Agent 生命周期事件的联合类型（13 种） AgentLoopConfig ./types 引擎配置（含回调钩子） AgentMessage ./types Agent 统一消息类型（可扩展） AgentTool ./types Agent 工具定义 AgentToolCall ./types LLM 发出的工具调用请求 AgentToolResult ./types 工具执行结果 StreamFn ./types 流式函数类型签名 设计要点： agent-loop.ts 不直接依赖 Agent 类，它通过 AgentEventSink（emit 回调）与上层通信。这是依赖倒置的体现——引擎层定义接口，管理层实现接口。\nAgentEventSink — 事件接收器类型 1 export type AgentEventSink = (event: AgentEvent) =\u0026gt; Promise\u0026lt;void\u0026gt; | void; 同步或异步均可 引擎层 await 每次 emit 调用，保证事件顺序 这是引擎层与管理层之间的唯一通信通道 agentLoop() / agentLoopContinue() — 公开入口 1 2 3 4 5 6 7 export function agentLoop( prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn, ): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; 逐行解读：\n1 const stream = createAgentStream(); 创建 EventStream 实例，内部实现了终止判断和结果提取 1 2 3 4 5 void runAgentLoop( prompts, context, config, async (event) =\u0026gt; { stream.push(event); }, signal, streamFn, ).then((messages) =\u0026gt; { stream.end(messages); }); void 关键字：显式表达我们不 await 这个 Promise（由 EventStream 管理生命周期） stream.push(event)：每产生一个 AgentEvent，立即推入流 stream.end(messages)：流结束时，AgentMessage[] 作为最终结果写入流 1 return stream; 调用方通过 for await (const event of stream) 消费事件 最终通过 stream.result() 获取 AgentMessage[] agentLoopContinue() 的额外校验：\n1 2 3 4 5 6 if (context.messages.length === 0) { throw new Error(\u0026#34;Cannot continue: no messages in context\u0026#34;); } if (context.messages[context.messages.length - 1].role === \u0026#34;assistant\u0026#34;) { throw new Error(\u0026#34;Cannot continue from message role: assistant\u0026#34;); } 空上下文 → 无法继续 最后一条是 assistant → LLM 的 context 最后一条必须是 user 或 toolResult，否则 LLM provider 会拒绝请求 runAgentLoop() / runAgentLoopContinue() — 异步底层 1 2 3 4 5 6 7 8 export async function runAgentLoop( prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, emit: AgentEventSink, signal?: AbortSignal, streamFn?: StreamFn, ): Promise\u0026lt;AgentMessage[]\u0026gt; 逐行解读：\n1 const newMessages: AgentMessage[] = [...prompts]; newMessages 是返回值，记录本轮新增的所有消息（prompt + assistant response + tool results） 1 2 3 4 const currentContext: AgentContext = { ...context, messages: [...context.messages, ...prompts], }; 浅拷贝 context，并在末尾追加 prompt 消息 注意 ...context 是浅拷贝，但 messages: [...context.messages, ...prompts] 是新数组，防止引擎层篡改外部引用 1 2 3 4 5 6 await emit({ type: \u0026#34;agent_start\u0026#34; }); await emit({ type: \u0026#34;turn_start\u0026#34; }); for (const prompt of prompts) { await emit({ type: \u0026#34;message_start\u0026#34;, message: prompt }); await emit({ type: \u0026#34;message_end\u0026#34;, message: prompt }); } 发射生命周期事件：agent_start → turn_start → 每个 prompt 的 message_start/end prompt 消息的 message_start 和 message_end 连续发射（中间没有 update 事件，因为 prompt 是已确定的消息） 1 2 await runLoop(currentContext, newMessages, config, signal, emit, streamFn); return newMessages; 进入核心双层循环 返回本轮新增的所有消息 runAgentLoopContinue() 与 runAgentLoop() 的差异：\nnewMessages 初始化为 []（没有新 prompt） currentContext 直接 { ...context }（不追加消息） 不发射 prompt 的 message_start/end 事件 createAgentStream() — EventStream 工厂 1 2 3 4 5 6 function createAgentStream(): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; { return new EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt;( (event: AgentEvent) =\u0026gt; event.type === \u0026#34;agent_end\u0026#34;, (event: AgentEvent) =\u0026gt; (event.type === \u0026#34;agent_end\u0026#34; ? event.messages : []), ); } 第一个参数（终止判定）：当 event.type === \u0026ldquo;agent_end\u0026rdquo; 时流结束 第二个参数（结果提取）：从 agent_end 事件中提取 messages 作为最终结果 调用方通过 stream.result() 获取 AgentMessage[] runLoop() — 核心双层循环 1 2 3 4 5 6 7 8 async function runLoop( currentContext: AgentContext, newMessages: AgentMessage[], config: AgentLoopConfig, signal: AbortSignal | undefined, emit: AgentEventSink, streamFn?: StreamFn, ): Promise\u0026lt;void\u0026gt; 这是整个 Agent 包最核心的函数，约 80 行。\n初始化 1 2 let firstTurn = true; let pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || []; 变量 用途 firstTurn 首个 turn 已在入口函数中发射了 turn_start，后续 turn 需要自行发射 pendingMessages 两层循环的桥梁：内层消费，外层（follow-up）生产 外层循环 1 2 while (true) { let hasMoreToolCalls = true; hasMoreToolCalls 每轮外层循环重置为 true（进入内层至少一次，检查 steering 消息） 内层循环条件 1 while (hasMoreToolCalls || pendingMessages.length \u0026gt; 0) { 内层持续的条件：\nhasMoreToolCalls === true：当前轮次有 tool call，且未全部 terminate pendingMessages.length \u0026gt; 0：steering 队列中有待处理消息 Turn 管理 1 2 3 4 5 if (!firstTurn) { await emit({ type: \u0026#34;turn_start\u0026#34; }); } else { firstTurn = false; } 首个 turn 不发射（已在入口函数中发射） 后续 turn（steering 注入后、follow-up 注入后）各自发射 turn_start 处理 pending 消息 1 2 3 4 5 6 7 8 9 if (pendingMessages.length \u0026gt; 0) { for (const message of pendingMessages) { await emit({ type: \u0026#34;message_start\u0026#34;, message }); await emit({ type: \u0026#34;message_end\u0026#34;, message }); currentContext.messages.push(message); newMessages.push(message); } pendingMessages = []; } 把 steering 队列中的消息以 message_start → message_end 的事件序列注入 同时追加到 currentContext（LLM 调用用）和 newMessages（返回值用） 清空 pendingMessages LLM 调用 1 2 const message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn); newMessages.push(message); 返回 AssistantMessage（LLM 的完整回复） 推入 newMessages 错误/中止检测 1 2 3 4 5 if (message.stopReason === \u0026#34;error\u0026#34; || message.stopReason === \u0026#34;aborted\u0026#34;) { await emit({ type: \u0026#34;turn_end\u0026#34;, message, toolResults: [] }); await emit({ type: \u0026#34;agent_end\u0026#34;, messages: newMessages }); return; } stopReason 为 error 或 aborted → 立即终止整个 agent 先发射 turn_end（空 toolResults），再发射 agent_end 工具调用检测与执行 1 2 3 4 5 6 7 8 9 10 11 12 13 const toolCalls = message.content.filter((c) =\u0026gt; c.type === \u0026#34;toolCall\u0026#34;); const toolResults: ToolResultMessage[] = []; hasMoreToolCalls = false; if (toolCalls.length \u0026gt; 0) { const executedToolBatch = await executeToolCalls(currentContext, message, config, signal, emit); toolResults.push(...executedToolBatch.messages); hasMoreToolCalls = !executedToolBatch.terminate; for (const result of toolResults) { currentContext.messages.push(result); newMessages.push(result); } } hasMoreToolCalls = !executedToolBatch.terminate：当所有工具都返回 terminate: true 时，内层循环终止 tool result 同时推入 currentContext（LLM 下一轮上下文）和 newMessages Turn 结束与停止判定 1 2 3 4 5 6 await emit({ type: \u0026#34;turn_end\u0026#34;, message, toolResults }); if (await config.shouldStopAfterTurn?.({ message, toolResults, context: currentContext, newMessages })) { await emit({ type: \u0026#34;agent_end\u0026#34;, messages: newMessages }); return; } shouldStopAfterTurn 是可选回调，调用方可以自定义停止条件（如：基于消息数量、工具调用次数等） 若返回 true → agent_end 获取下一轮 steering 消息 1 pendingMessages = (await config.getSteeringMessages?.()) || []; 内层循环末尾拉取 steering 消息，如果有 → 继续内层 外层 follow-up 机制 1 2 3 4 5 6 const followUpMessages = (await config.getFollowUpMessages?.()) || []; if (followUpMessages.length \u0026gt; 0) { pendingMessages = followUpMessages; continue; } break; 内层结束（无更多 tool call 和 steering 消息） 检查 follow-up 队列，有消息 → 设为 pending → continue 外层循环 无消息 → break 退出 1 await emit({ type: \u0026#34;agent_end\u0026#34;, messages: newMessages }); 最终发射 agent_end streamAssistantResponse() — LLM 流式调用 1 2 3 4 5 6 7 async function streamAssistantResponse( context: AgentContext, config: AgentLoopConfig, signal: AbortSignal | undefined, emit: AgentEventSink, streamFn?: StreamFn, ): Promise\u0026lt;AssistantMessage\u0026gt; 第一步：上下文变换（可选） 1 2 3 4 let messages = context.messages; if (config.transformContext) { messages = await config.transformContext(messages, signal); } transformContext 允许对整个消息列表做预处理 典型用途：过滤、重排序、注入系统消息、合并上下文窗口 第二步：消息转换 1 const llmMessages = await config.convertToLlm(messages); AgentMessage[] → Message[]：这是 Agent 世界与 LLM 世界的边界 默认实现：只保留 user、assistant、toolResult 角色，过滤掉自定义消息 第三步：构造 LLM Context 1 2 3 4 5 const llmContext: Context = { systemPrompt: context.systemPrompt, messages: llmMessages, tools: context.tools, }; 第四步：选择流函数 1 const streamFunction = streamFn || streamSimple; 第五步：解析 API Key 1 2 const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey; 支持动态获取 API key（如短期 OAuth token） 先尝试 getApiKey 回调，fallback 到 config.apiKey 短路求值：若 config.getApiKey 未定义，跳过调用 第六步：发起 LLM 调用 1 2 3 4 5 const response = await streamFunction(config.model, llmContext, { ...config, apiKey: resolvedApiKey, signal, }); 将 AgentLoopConfig 的所有属性透传给 LLM 核心库（model、reasoning、sessionId、onPayload、transport、thinkingBudgets 等） apiKey 覆盖传入 第七步：事件循环 1 2 let partialMessage: AssistantMessage | null = null; let addedPartial = false; 变量 用途 partialMessage 当前正在构建中的消息引用 addedPartial 标记是否已推入占位消息到 context.messages 1 2 for await (const event of response) { switch (event.type) { case \u0026ldquo;start\u0026rdquo; 1 2 3 4 5 6 case \u0026#34;start\u0026#34;: partialMessage = event.partial; context.messages.push(partialMessage); addedPartial = true; await emit({ type: \u0026#34;message_start\u0026#34;, message: { ...partialMessage } }); break; 将 partial 消息推入 context.messages 末尾作为占位 浅拷贝后发射 message_start，防止 listener 篡改引用 case text/thinking/toolcall delta 1 2 3 4 5 6 7 8 9 case \u0026#34;text_delta\u0026#34;: case \u0026#34;toolcall_delta\u0026#34;: // ... 等 11 种子事件类型 if (partialMessage) { partialMessage = event.partial; context.messages[context.messages.length - 1] = partialMessage; await emit({ type: \u0026#34;message_update\u0026#34;, assistantMessageEvent: event, message: { ...partialMessage } }); } break; partialMessage = event.partial：变量指向新的引用（事件对象中的更新版本） context.messages[last] = partialMessage：原地替换占位消息 关键设计：使用引用更新，避免每次 delta 都深拷贝完整消息 case \u0026ldquo;done\u0026rdquo; / \u0026ldquo;error\u0026rdquo; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 case \u0026#34;done\u0026#34;: case \u0026#34;error\u0026#34;: { const finalMessage = await response.result(); if (addedPartial) { context.messages[context.messages.length - 1] = finalMessage; } else { context.messages.push(finalMessage); } if (!addedPartial) { await emit({ type: \u0026#34;message_start\u0026#34;, message: { ...finalMessage } }); } await emit({ type: \u0026#34;message_end\u0026#34;, message: finalMessage }); return finalMessage; } 分支 A：addedPartial === true → 替换占位 分支 B：addedPartial === false → 没有 start 事件（LLM 直接返回完整结果），推入并补发 message_start 防线：for-await 正常结束（无 done/error 事件） 1 2 3 4 5 6 7 8 9 const finalMessage = await response.result(); if (addedPartial) { context.messages[context.messages.length - 1] = finalMessage; } else { context.messages.push(finalMessage); await emit({ type: \u0026#34;message_start\u0026#34;, message: { ...finalMessage } }); } await emit({ type: \u0026#34;message_end\u0026#34;, message: finalMessage }); return finalMessage; 与 done/error 分支逻辑相同 此分支作为 for-await 循环正常结束的兜底 executeToolCalls() — 工具调度 1 2 3 async function executeToolCalls( currentContext, assistantMessage, config, signal, emit ): Promise\u0026lt;ExecutedToolCallBatch\u0026gt; 调度逻辑（仅 7 行）：\n1 const toolCalls = assistantMessage.content.filter((c) =\u0026gt; c.type === \u0026#34;toolCall\u0026#34;); 从 assistant 消息中提取所有 tool call 1 2 3 const hasSequentialToolCall = toolCalls.some( (tc) =\u0026gt; currentContext.tools?.find((t) =\u0026gt; t.name === tc.name)?.executionMode === \u0026#34;sequential\u0026#34;, ); 检查是否有工具声明了串行模式 1 2 3 4 if (config.toolExecution === \u0026#34;sequential\u0026#34; || hasSequentialToolCall) { return executeToolCallsSequential(...); } return executeToolCallsParallel(...); 全局配置或单个工具声明 → 串行；否则并行 executeToolCallsSequential() — 串行执行 1 2 3 4 5 async function executeToolCallsSequential(...): Promise\u0026lt;ExecutedToolCallBatch\u0026gt; { const finalizedCalls: FinalizedToolCallOutcome[] = []; const messages: ToolResultMessage[] = []; for (const toolCall of toolCalls) { 每个 tool call 的完整生命周期：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 1. 发射 tool_execution_start await emit({ type: \u0026#34;tool_execution_start\u0026#34;, toolCallId, toolName, args }); // 2. 前置处理 const preparation = await prepareToolCall(...); // 3. immediate → 直接记录错误 if (preparation.kind === \u0026#34;immediate\u0026#34;) { finalized = { toolCall, result: preparation.result, isError: preparation.isError }; } else { // 4. 执行 const executed = await executePreparedToolCall(preparation, signal, emit); // 5. 后处理 finalized = await finalizeExecutedToolCall(...); } // 6. 发射 tool_execution_end await emitToolExecutionEnd(finalized, emit); // 7. 构建 ToolResultMessage 并发射 const toolResultMessage = createToolResultMessage(finalized); await emitToolResultMessage(toolResultMessage, emit); 串行的关键： for...of 确保逐个处理，一个完成才到下一个。\nexecuteToolCallsParallel() — 并行执行 两阶段设计：\nPhase 1：串行 prepare 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 for (const toolCall of toolCalls) { await emit({ type: \u0026#34;tool_execution_start\u0026#34;, ... }); const preparation = await prepareToolCall(...); if (preparation.kind === \u0026#34;immediate\u0026#34;) { // 直接记入结果（不是 thunk） finalizedCalls.push(finalized); continue; } // 包装为 lazy thunk finalizedCalls.push(async () =\u0026gt; { const executed = await executePreparedToolCall(preparation, signal, emit); const finalized = await finalizeExecutedToolCall(...); await emitToolExecutionEnd(finalized, emit); return finalized; }); } kind 处理方式 \u0026quot;immediate\u0026quot;（工具未找到 / 校验失败 / beforeToolCall 阻拦） 直接记入 finalizedCalls 数组 \u0026quot;prepared\u0026quot; 包装为 () =\u0026gt; Promise\u0026lt;FinalizedToolCallOutcome\u0026gt; 的 thunk FinalizedToolCallEntry 类型：\n1 type FinalizedToolCallEntry = FinalizedToolCallOutcome | (() =\u0026gt; Promise\u0026lt;FinalizedToolCallOutcome\u0026gt;); 即：要么是直接结果（immediate），要么是延迟计算的 thunk（prepared）。\nPhase 2：并发 execute 1 2 3 const orderedFinalizedCalls = await Promise.all( finalizedCalls.map((entry) =\u0026gt; (typeof entry === \u0026#34;function\u0026#34; ? entry() : Promise.resolve(entry))), ); Promise.all() 并发执行所有 thunk typeof entry === \u0026quot;function\u0026quot; 区分 direct result 和 thunk orderedFinalizedCalls 保持原始 tool call 顺序（因为 finalizedCalls 的顺序与 Phase 1 遍历顺序一致） 1 2 3 4 5 6 const messages: ToolResultMessage[] = []; for (const finalized of orderedFinalizedCalls) { const toolResultMessage = createToolResultMessage(finalized); await emitToolResultMessage(toolResultMessage, emit); messages.push(toolResultMessage); } 按原始顺序构建消息 1 2 3 4 return { messages, terminate: shouldTerminateToolBatch(orderedFinalizedCalls), }; prepareToolCall() — 前置管道 1 async function prepareToolCall(...): Promise\u0026lt;PreparedToolCall | ImmediateToolCallOutcome\u0026gt; 4 步检查管道：\n步骤 1：查找工具定义 1 2 3 4 5 6 7 8 const tool = currentContext.tools?.find((t) =\u0026gt; t.name === toolCall.name); if (!tool) { return { kind: \u0026#34;immediate\u0026#34;, result: createErrorToolResult(`Tool ${toolCall.name} not found`), isError: true, }; } 不在 tools 数组中？→ 立即返回错误 步骤 2：参数兼容 1 const preparedToolCall = prepareToolCallArguments(tool, toolCall); 若工具定义了 prepareArguments，允许对 LLM 生成的参数做运行时转换 典型用途：字符串 ID → 对象引用、格式转换 步骤 3：Schema 校验 1 const validatedArgs = validateToolArguments(tool, preparedToolCall); 由 @earendil-works/pi-ai 的 validateToolArguments 根据工具的 JSON Schema 校验 校验失败会抛异常，被外层的 try/catch 捕获 步骤 4：beforeToolCall 钩子 1 2 3 4 5 6 7 8 9 10 if (config.beforeToolCall) { const beforeResult = await config.beforeToolCall({ assistantMessage, toolCall, args: validatedArgs, context }, signal); if (beforeResult?.block) { return { kind: \u0026#34;immediate\u0026#34;, result: createErrorToolResult(beforeResult.reason || \u0026#34;Tool execution was blocked\u0026#34;), isError: true, }; } } 钩子可阻止工具执行（返回 { block: true }） 应用场景：权限检查、限流、内容审核 整个管道的异常兜底 1 2 3 4 5 6 7 } catch (error) { return { kind: \u0026#34;immediate\u0026#34;, result: createErrorToolResult(error instanceof Error ? error.message : String(error)), isError: true, }; } 任何步骤抛异常 → catch → 返回 error tool result 这是第一层容错：工具调用前置阶段的异常不会扩散到主循环 executePreparedToolCall() — 工具执行 1 2 3 4 5 async function executePreparedToolCall( prepared: PreparedToolCall, signal: AbortSignal | undefined, emit: AgentEventSink, ): Promise\u0026lt;ExecutedToolCallOutcome\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const updateEvents: Promise\u0026lt;void\u0026gt;[] = []; try { const result = await prepared.tool.execute( prepared.toolCall.id, prepared.args as never, signal, (partialResult) =\u0026gt; { updateEvents.push( Promise.resolve( emit({ type: \u0026#34;tool_execution_update\u0026#34;, ... }) ), ); }, ); await Promise.all(updateEvents); return { result, isError: false }; } catch (error) { await Promise.all(updateEvents); return { result: createErrorToolResult(error instanceof Error ? error.message : String(error)), isError: true, }; } 设计要点：\n特性 说明 signal 传入 AbortSignal，工具应监听取消信号 onProgress 工具可多次调用来发射 tool_execution_update 事件 updateEvents 收集所有 update 发射的 Promise，执行完后统一 await 异常处 catch 后生成 error tool result，不崩溃 finalizeExecutedToolCall() — 后处理 1 async function finalizeExecutedToolCall(...): Promise\u0026lt;FinalizedToolCallOutcome\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let result = executed.result; let isError = executed.isError; if (config.afterToolCall) { try { const afterResult = await config.afterToolCall({ assistantMessage, toolCall, args, result, isError, context }, signal); if (afterResult) { result = { content: afterResult.content ?? result.content, details: afterResult.details ?? result.details, terminate: afterResult.terminate ?? result.terminate, }; isError = afterResult.isError ?? isError; } } catch (error) { result = createErrorToolResult(error instanceof Error ? error.message : String(error)); isError = true; } } return { toolCall: prepared.toolCall, result, isError }; afterToolCall 钩子可覆盖的字段：\n字段 覆盖方式 用途 content afterResult.content ?? result.content 修改返回内容（脱敏、格式化） details afterResult.details ?? result.details 追加额外信息 terminate afterResult.terminate ?? result.terminate 强制终止或解除终止 isError afterResult.isError ?? isError 标记为错误 第二层容错： 钩子自身抛异常 → catch 后覆盖为 error tool result（防止钩子崩溃波及主流程）。\n辅助函数 shouldTerminateToolBatch() 1 2 3 function shouldTerminateToolBatch(finalizedCalls: FinalizedToolCallOutcome[]): boolean { return finalizedCalls.length \u0026gt; 0 \u0026amp;\u0026amp; finalizedCalls.every((finalized) =\u0026gt; finalized.result.terminate === true); } 仅当所有工具都要求终止时才返回 true 空数组 → false（没有 tool call 时不应终止） 设计选择：防止单个工具意外终止整个 agent prepareToolCallArguments() 1 2 3 4 5 6 function prepareToolCallArguments(tool: AgentTool\u0026lt;any\u0026gt;, toolCall: AgentToolCall): AgentToolCall { if (!tool.prepareArguments) return toolCall; const preparedArguments = tool.prepareArguments(toolCall.arguments); if (preparedArguments === toolCall.arguments) return toolCall; return { ...toolCall, arguments: preparedArguments as Record\u0026lt;string, any\u0026gt; }; } 若工具定义了 prepareArguments，对参数做兼容转换 === 引用比较：若返回值与输入相同，跳过属性复制（性能优化） createErrorToolResult() 1 2 3 function createErrorToolResult(message: string): AgentToolResult\u0026lt;any\u0026gt; { return { content: [{ type: \u0026#34;text\u0026#34;, text: message }], details: {} }; } 统一错误格式：纯文本 content + 空 details emitToolExecutionEnd() 1 2 3 async function emitToolExecutionEnd(finalized: FinalizedToolCallOutcome, emit: AgentEventSink): Promise\u0026lt;void\u0026gt; { await emit({ type: \u0026#34;tool_execution_end\u0026#34;, toolCallId, toolName, result, isError }); } createToolResultMessage() 1 2 3 4 5 6 7 8 9 10 11 function createToolResultMessage(finalized: FinalizedToolCallOutcome): ToolResultMessage { return { role: \u0026#34;toolResult\u0026#34;, toolCallId: finalized.toolCall.id, toolName: finalized.toolCall.name, content: finalized.result.content, details: finalized.result.details, isError: finalized.isError, timestamp: Date.now(), }; } emitToolResultMessage() 1 2 3 4 async function emitToolResultMessage(toolResultMessage: ToolResultMessage, emit: AgentEventSink): Promise\u0026lt;void\u0026gt; { await emit({ type: \u0026#34;message_start\u0026#34;, message: toolResultMessage }); await emit({ type: \u0026#34;message_end\u0026#34;, message: toolResultMessage }); } tool result 以完整的 message_start/end 事件序列发射 agent.ts 逐行解读 类型与导入 1 2 3 4 5 6 7 8 9 10 11 import { type ImageContent, type Message, type Model, type SimpleStreamOptions, streamSimple, type TextContent, type ThinkingBudgets, type Transport, } from \u0026#34;@earendil-works/pi-ai\u0026#34;; import { runAgentLoop, runAgentLoopContinue } from \u0026#34;./agent-loop.js\u0026#34;; import type { AfterToolCallContext, AfterToolCallResult, AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, BeforeToolCallContext, BeforeToolCallResult, StreamFn, ToolExecutionMode, } from \u0026#34;./types.js\u0026#34;; 关键导入： runAgentLoop 和 runAgentLoopContinue 是 agent-loop.ts 的两个核心函数，agent.ts 将它们封装为有状态的 API。\ndefaultConvertToLlm() — 默认消息转换 1 2 3 4 5 function defaultConvertToLlm(messages: AgentMessage[]): Message[] { return messages.filter( (message) =\u0026gt; message.role === \u0026#34;user\u0026#34; || message.role === \u0026#34;assistant\u0026#34; || message.role === \u0026#34;toolResult\u0026#34;, ); } 只保留 LLM 能识别的三种角色 过滤掉自定义消息（artifact、notification 等） 常量 1 2 const EMPTY_USAGE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { ... } }; const DEFAULT_MODEL = { id: \u0026#34;unknown\u0026#34;, name: \u0026#34;unknown\u0026#34;, api: \u0026#34;unknown\u0026#34;, ... }; EMPTY_USAGE：用于失败消息的空用量（避免 undefined） DEFAULT_MODEL：fallback 模型（用户未指定 initialState.model 时使用） MutableAgentState 与 createMutableAgentState() 1 2 3 4 5 6 type MutableAgentState = Omit\u0026lt;AgentState, \u0026#34;isStreaming\u0026#34; | \u0026#34;streamingMessage\u0026#34; | \u0026#34;pendingToolCalls\u0026#34; | \u0026#34;errorMessage\u0026#34;\u0026gt; \u0026amp; { isStreaming: boolean; streamingMessage?: AgentMessage; pendingToolCalls: Set\u0026lt;string\u0026gt;; errorMessage?: string; }; AgentState vs MutableAgentState：\n字段 AgentState（对外只读） MutableAgentState（内部可写） isStreaming readonly boolean boolean streamingMessage readonly 可写 pendingToolCalls ReadonlySet\u0026lt;string\u0026gt; Set\u0026lt;string\u0026gt; errorMessage readonly 可写 Getter/Setter 技巧：\n1 2 3 4 5 6 7 return { get tools() { return tools; }, set tools(nextTools: AgentTool\u0026lt;any\u0026gt;[]) { tools = nextTools.slice(); }, get messages() { return messages; }, set messages(nextMessages: AgentMessage[]) { messages = nextMessages.slice(); }, // ... }; getter 返回内部数组引用 setter 自动 slice() 拷贝，防止外部通过 .push() 等操作篡改内部状态 pendingToolCalls 在 processEvents 中每次更新都新建 Set PendingMessageQueue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class PendingMessageQueue { private messages: AgentMessage[] = []; constructor(public mode: QueueMode) {} enqueue(message: AgentMessage): void { this.messages.push(message); } hasItems(): boolean { return this.messages.length \u0026gt; 0; } drain(): AgentMessage[] { if (this.mode === \u0026#34;all\u0026#34;) { const drained = this.messages.slice(); this.messages = []; return drained; // 返回全部并清空 } const first = this.messages[0]; this.messages = this.messages.slice(1); return [first]; // 只返回第一条 } clear(): void { this.messages = []; } } QueueMode 控制 drain 行为：\n模式 drain 场景 \u0026quot;one-at-a-time\u0026quot; 返回第一条，队列保留其余 用户快速打字，逐条处理 \u0026quot;all\u0026quot; 返回全部并清空 批量注入，合并处理 设计要点：\nclear() 方法用于 reset() hasItems() 被 hasQueuedMessages() 调用 drain() 不阻塞——队列为空时返回 [] ActiveRun 类型 1 2 3 4 5 type ActiveRun = { promise: Promise\u0026lt;void\u0026gt;; resolve: () =\u0026gt; void; abortController: AbortController; }; 字段 用途 promise waitForIdle() 返回此 Promise resolve finishRun() 中调用，兑现 promise abortController abort() 时调用 .abort() Agent 类 — 构造器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export class Agent { private _state: MutableAgentState; private readonly listeners = new Set\u0026lt;(event: AgentEvent, signal: AbortSignal) =\u0026gt; Promise\u0026lt;void\u0026gt; | void\u0026gt;(); private readonly steeringQueue: PendingMessageQueue; private readonly followUpQueue: PendingMessageQueue; // 公共可配置属性 public convertToLlm: (messages: AgentMessage[]) =\u0026gt; Message[] | Promise\u0026lt;Message[]\u0026gt;; public transformContext?: ... public streamFn: StreamFn; public getApiKey?: ... public onPayload?: ... public onResponse?: ... public beforeToolCall?: ... public afterToolCall?: ... private activeRun?: ActiveRun; public sessionId?: string; public thinkingBudgets?: ThinkingBudgets; public transport: Transport; public maxRetryDelayMs?: number; public toolExecution: ToolExecutionMode; 构造器初始化：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 constructor(options: AgentOptions = {}) { this._state = createMutableAgentState(options.initialState); this.convertToLlm = options.convertToLlm ?? defaultConvertToLlm; this.transformContext = options.transformContext; this.streamFn = options.streamFn ?? streamSimple; this.getApiKey = options.getApiKey; this.onPayload = options.onPayload; this.onResponse = options.onResponse; this.beforeToolCall = options.beforeToolCall; this.afterToolCall = options.afterToolCall; this.steeringQueue = new PendingMessageQueue(options.steeringMode ?? \u0026#34;one-at-a-time\u0026#34;); this.followUpQueue = new PendingMessageQueue(options.followUpMode ?? \u0026#34;one-at-a-time\u0026#34;); this.sessionId = options.sessionId; this.thinkingBudgets = options.thinkingBudgets; this.transport = options.transport ?? \u0026#34;auto\u0026#34;; this.maxRetryDelayMs = options.maxRetryDelayMs; this.toolExecution = options.toolExecution ?? \u0026#34;parallel\u0026#34;; } 默认值策略：\n选项 默认值 说明 convertToLlm defaultConvertToLlm 过滤非标准角色 streamFn streamSimple 直接调用 LLM steeringMode \u0026quot;one-at-a-time\u0026quot; 逐条处理 steering 消息 followUpMode \u0026quot;one-at-a-time\u0026quot; 逐条处理 follow-up transport \u0026quot;auto\u0026quot; 自动选择传输方式 toolExecution \u0026quot;parallel\u0026quot; 默认并行执行工具 subscribe() — 事件订阅 1 2 3 4 subscribe(listener: (event: AgentEvent, signal: AbortSignal) =\u0026gt; Promise\u0026lt;void\u0026gt; | void): () =\u0026gt; void { this.listeners.add(listener); return () =\u0026gt; this.listeners.delete(listener); } 返回解注册函数 内部 processEvents 中按订阅顺序 await 每个 listener signal 参数允许 listener 响应取消 state — 状态访问器 1 2 3 get state(): AgentState { return this._state; } 返回 MutableAgentState，但 TypeScript 类型为 AgentState（只读视图） 外部不能直接修改 isStreaming、streamingMessage 等只读字段 steer() / followUp() — 消息队列 1 2 steer(message: AgentMessage): void { this.steeringQueue.enqueue(message); } followUp(message: AgentMessage): void { this.followUpQueue.enqueue(message); } 注入时机差异：\n方法 入队 被消费的时机 steer() steeringQueue runLoop 内层每次迭代前（getSteeringMessages） followUp() followUpQueue runLoop 内层结束后、agent 停止前（getFollowUpMessages） 应用场景：\nsteer() → 用户打字\u0026quot;等一下、我换个问题\u0026quot;，agent 在当前上下文注入新消息，立即处理 followUp() → 用户等 agent 完成后说\u0026quot;还有一件事\u0026quot;，agent 做新一轮处理 prompt() — 发起对话 1 2 async prompt(message: AgentMessage | AgentMessage[]): Promise\u0026lt;void\u0026gt;; async prompt(input: string, images?: ImageContent[]): Promise\u0026lt;void\u0026gt;; 重载实现：\n1 2 3 4 5 6 7 async prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise\u0026lt;void\u0026gt; { if (this.activeRun) { throw new Error(\u0026#34;Agent is already processing a prompt. Use steer() or followUp() to queue messages.\u0026#34;); } const messages = this.normalizePromptInput(input, images); await this.runPromptMessages(messages); } 并发保护： activeRun 存在时抛异常，不允许并发 prompt normalizePromptInput 统一输入格式 normalizePromptInput() 1 2 3 4 5 6 7 private normalizePromptInput(input, images?): AgentMessage[] { if (Array.isArray(input)) return input; if (typeof input !== \u0026#34;string\u0026#34;) return [input]; const content: Array\u0026lt;TextContent | ImageContent\u0026gt; = [{ type: \u0026#34;text\u0026#34;, text: input }]; if (images \u0026amp;\u0026amp; images.length \u0026gt; 0) content.push(...images); return [{ role: \u0026#34;user\u0026#34;, content, timestamp: Date.now() }]; } 输入 输出 \u0026quot;Hello\u0026quot; [{ role: \u0026quot;user\u0026quot;, content: [{ type: \u0026quot;text\u0026quot;, text: \u0026quot;Hello\u0026quot; }], timestamp }] \u0026quot;Hello\u0026quot; + images 同上，content 追加 image 数组 AgentMessage [message] AgentMessage[] 透传 continue() — 继续对话 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 async continue(): Promise\u0026lt;void\u0026gt; { if (this.activeRun) { throw new Error(\u0026#34;Agent is already processing. Wait for completion before continuing.\u0026#34;); } const lastMessage = this._state.messages[this._state.messages.length - 1]; if (!lastMessage) throw new Error(\u0026#34;No messages to continue from\u0026#34;); if (lastMessage.role === \u0026#34;assistant\u0026#34;) { // 最后是 assistant → 尝试从队列取消息 const queuedSteering = this.steeringQueue.drain(); if (queuedSteering.length \u0026gt; 0) { await this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true }); return; } const queuedFollowUps = this.followUpQueue.drain(); if (queuedFollowUps.length \u0026gt; 0) { await this.runPromptMessages(queuedFollowUps); return; } throw new Error(\u0026#34;Cannot continue from message role: assistant\u0026#34;); } await this.runContinuation(); } 分支逻辑：\ncontinue() → activeRun 存在？→ throw → 无消息？→ throw → 最后是 assistant？ → steeringQueue 有？→ drain → runPromptMessages(skipInitialSteeringPoll=true) → followUpQueue 有？→ drain → runPromptMessages → 都没有？→ throw → 最后是 user/toolResult？ → runContinuation() → runAgentLoopContinue() 设计意图： 避免\u0026quot;最后是 assistant\u0026quot;时直接调 LLM 导致重复响应。只有当我们有新的 user/toolResult 消息时才应该触发新一轮 LLM 调用。\nabort() / waitForIdle() / reset() — 生命周期控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 abort(): void { this.activeRun?.abortController.abort(); } waitForIdle(): Promise\u0026lt;void\u0026gt; { return this.activeRun?.promise ?? Promise.resolve(); } reset(): void { this._state.messages = []; this._state.isStreaming = false; this._state.streamingMessage = undefined; this._state.pendingToolCalls = new Set\u0026lt;string\u0026gt;(); this._state.errorMessage = undefined; this.clearFollowUpQueue(); this.clearSteeringQueue(); } 方法 行为 abort() 触发 AbortController → LLM 和工具执行收到取消信号 waitForIdle() 返回 Promise，在 finishRun() 中 resolve reset() 清空 transcript 和运行时状态 私有方法：runPromptMessages() / runContinuation() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private async runPromptMessages( messages: AgentMessage[], options: { skipInitialSteeringPoll?: boolean } = {}, ): Promise\u0026lt;void\u0026gt; { await this.runWithLifecycle(async (signal) =\u0026gt; { await runAgentLoop( messages, this.createContextSnapshot(), this.createLoopConfig(options), (event) =\u0026gt; this.processEvents(event), signal, this.streamFn, ); }); } private async runContinuation(): Promise\u0026lt;void\u0026gt; { await this.runWithLifecycle(async (signal) =\u0026gt; { await runAgentLoopContinue( this.createContextSnapshot(), this.createLoopConfig(), (event) =\u0026gt; this.processEvents(event), signal, this.streamFn, ); }); } 方法 调用引擎函数 调用者 runPromptMessages() runAgentLoop() prompt()、continue()（有队列消息时） runContinuation() runAgentLoopContinue() continue()（最后一条非 assistant） options.skipInitialSteeringPoll： 当 continue() 从 steeringQueue drain 消息后调用 runPromptMessages 时，跳过首次 steering 轮询，避免刚 drain 的消息又被 agent-loop 取走。\ncreateContextSnapshot() — 上下文快照 1 2 3 4 5 6 7 private createContextSnapshot(): AgentContext { return { systemPrompt: this._state.systemPrompt, messages: this._state.messages.slice(), tools: this._state.tools.slice(), }; } slice() 浅拷贝，防止引擎层篡改 _state 的引用 每次 run 创建新快照，保证多个 run 之间的隔离 createLoopConfig() — 配置适配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig { let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true; return { model: this._state.model, reasoning: this._state.thinkingLevel === \u0026#34;off\u0026#34; ? undefined : this._state.thinkingLevel, sessionId: this.sessionId, onPayload: this.onPayload, onResponse: this.onResponse, transport: this.transport, thinkingBudgets: this.thinkingBudgets, maxRetryDelayMs: this.maxRetryDelayMs, toolExecution: this.toolExecution, beforeToolCall: this.beforeToolCall, afterToolCall: this.afterToolCall, convertToLlm: this.convertToLlm, transformContext: this.transformContext, getApiKey: this.getApiKey, getSteeringMessages: async () =\u0026gt; { if (skipInitialSteeringPoll) { skipInitialSteeringPoll = false; return []; } return this.steeringQueue.drain(); }, getFollowUpMessages: async () =\u0026gt; this.followUpQueue.drain(), }; } 关键转换：\nAgent 属性 AgentLoopConfig 字段 steeringQueue 包装为 getSteeringMessages 回调 followUpQueue 包装为 getFollowUpMessages 回调 thinkingLevel 转换为 reasoning（\u0026quot;off\u0026quot; → undefined） beforeToolCall 透传 afterToolCall 透传 convertToLlm 透传 runWithLifecycle() — 并发控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private async runWithLifecycle(executor: (signal: AbortSignal) =\u0026gt; Promise\u0026lt;void\u0026gt;): Promise\u0026lt;void\u0026gt; { if (this.activeRun) { throw new Error(\u0026#34;Agent is already processing.\u0026#34;); } const abortController = new AbortController(); let resolvePromise = () =\u0026gt; {}; const promise = new Promise\u0026lt;void\u0026gt;((resolve) =\u0026gt; { resolvePromise = resolve; }); this.activeRun = { promise, resolve: resolvePromise, abortController }; this._state.isStreaming = true; this._state.streamingMessage = undefined; this._state.errorMessage = undefined; try { await executor(abortController.signal); } catch (error) { await this.handleRunFailure(error, abortController.signal.aborted); } finally { this.finishRun(); } } 完整生命周期：\nrunWithLifecycle(executor) │ ├─ activeRun 存在？→ throw（并发保护） │ ├─ 创建 AbortController + Promise ├─ activeRun = { promise, resolve, abortController } ├─ isStreaming = true │ ├─ try { executor(signal) } │ ├─ 成功 → finishRun() │ └─ 失败 → handleRunFailure(error, aborted) → finishRun() │ └─ finally { finishRun() } ← 确保始终执行 handleRunFailure() — 失败处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private async handleRunFailure(error: unknown, aborted: boolean): Promise\u0026lt;void\u0026gt; { const failureMessage = { role: \u0026#34;assistant\u0026#34;, content: [{ type: \u0026#34;text\u0026#34;, text: \u0026#34;\u0026#34; }], api: this._state.model.api, provider: this._state.model.provider, model: this._state.model.id, usage: EMPTY_USAGE, stopReason: aborted ? \u0026#34;aborted\u0026#34; : \u0026#34;error\u0026#34;, errorMessage: error instanceof Error ? error.message : String(error), timestamp: Date.now(), } satisfies AgentMessage; this._state.messages.push(failureMessage); this._state.errorMessage = failureMessage.errorMessage; await this.processEvents({ type: \u0026#34;agent_end\u0026#34;, messages: [failureMessage] }); } 将错误编码为正常消息：\n场景 stopReason 说明 外部调用 abort() \u0026quot;aborted\u0026quot; 用户主动取消 LLM 调用抛错 \u0026quot;error\u0026quot; 网络故障、API 错误 工具执行抛错 \u0026quot;error\u0026quot; 工具内部异常 transformContext 抛错 \u0026quot;error\u0026quot; 预处理失败 convertToLlm 抛错 \u0026quot;error\u0026quot; 消息转换失败 哲学：永不崩溃，所有失败都编码为事件。\nfinishRun() — 运行终结 1 2 3 4 5 6 7 private finishRun(): void { this._state.isStreaming = false; this._state.streamingMessage = undefined; this._state.pendingToolCalls = new Set\u0026lt;string\u0026gt;(); this.activeRun?.resolve(); this.activeRun = undefined; } 5 步清理：\n语句 效果 isStreaming = false 外部可感知 run 已结束 streamingMessage = undefined 清除流式占位 pendingToolCalls = new Set() 清除工具跟踪 activeRun.resolve() waitForIdle() Promise 兑现 activeRun = undefined 允许下一次 prompt() / continue() processEvents() — 事件归约 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 private async processEvents(event: AgentEvent): Promise\u0026lt;void\u0026gt; { switch (event.type) { case \u0026#34;message_start\u0026#34;: this._state.streamingMessage = event.message; break; case \u0026#34;message_update\u0026#34;: this._state.streamingMessage = event.message; break; case \u0026#34;message_end\u0026#34;: this._state.streamingMessage = undefined; this._state.messages.push(event.message); break; case \u0026#34;tool_execution_start\u0026#34;: { const pendingToolCalls = new Set(this._state.pendingToolCalls); pendingToolCalls.add(event.toolCallId); this._state.pendingToolCalls = pendingToolCalls; break; } case \u0026#34;tool_execution_end\u0026#34;: { const pendingToolCalls = new Set(this._state.pendingToolCalls); pendingToolCalls.delete(event.toolCallId); this._state.pendingToolCalls = pendingToolCalls; break; } case \u0026#34;turn_end\u0026#34;: if (event.message.role === \u0026#34;assistant\u0026#34; \u0026amp;\u0026amp; event.message.errorMessage) { this._state.errorMessage = event.message.errorMessage; } break; case \u0026#34;agent_end\u0026#34;: this._state.streamingMessage = undefined; break; } const signal = this.activeRun?.abortController.signal; if (!signal) throw new Error(\u0026#34;Agent listener invoked outside active run\u0026#34;); for (const listener of this.listeners) { await listener(event, signal); } } 状态转换表：\n事件类型 streamingMessage messages pendingToolCalls errorMessage message_start 设置为 event.message — — — message_update 更新为最新 — — — message_end undefined push event.message — — tool_execution_start — — 添加 toolCallId — tool_execution_end — — 删除 toolCallId — turn_end — — — 若有 error 则更新 agent_end undefined — — — 不可变性策略： pendingToolCalls 每次更新都新建 Set，对外暴露 ReadonlySet。\n1 2 3 4 5 6 // 过程：每次更新都新建 Set const pendingToolCalls = new Set(this._state.pendingToolCalls); pendingToolCalls.add(event.toolCallId); this._state.pendingToolCalls = pendingToolCalls; // 效果：外部持有的旧引用不会被篡改 设计模式总结 1. 分层架构（Layered Architecture） 应用层 ↓ prompt() / subscribe() Agent 类（有状态管理层） ↓ runAgentLoop() / emit() 无状态引擎层（agent-loop.ts） ↓ streamSimple() LLM 核心库（pi-ai） 2. 依赖倒置（Dependency Inversion） 引擎层定义 AgentEventSink 接口 管理层实现该接口（processEvents） 引擎层不依赖管理层 3. 事件溯源（Event Sourcing） processEvents 类似 Redux reducer 每次事件驱动状态变更，状态变更可追溯 AgentEvent 是唯一的日志来源 4. 两阶段执行（Parallel Tool Execution） Phase 1：串行 prepare（含 beforeToolCall 钩子） Phase 2：并发 execute（Promise.all） 兼顾钩子顺序可预测性和执行性能 5. 错误边界（Error Boundaries） 层级 错误处理 LLM 调用层 stopReason: \u0026quot;error\u0026quot; 工具 prepare 层 catch → immediate error tool result 工具 execute 层 catch → error tool result afterToolCall 钩子 catch → error tool result Agent 管理层 handleRunFailure → 失败消息 6. 不可变状态（Immutable State） pendingToolCalls 每次更新新建 Set messages 通过 setter 自动 slice context 和 newMessages 通过引用传递（引擎层无需拷贝，因为引擎层是纯函数） ","tags":["Artificial Intelligence","Agent","Source Code"],"categories":["Pi Architecture"]},{"title":"Pi-Architecture: Agent","permalink":"https://yukar.icu/posts/pi-architecture/agent-loop/","summary":"Pi Agent 包架构 packages/agent/src/ 实现了 Pi 框架中的 Agent 运行时，用于构建工具增强型 LLM 对话代理（Tool-Augmented LLM Agent）。\n整个包由五个模块组成：\n文件 职责 行数 types.ts 类型定义层 ~400 agent-loop.ts 无状态循环引擎 ~700 agent.ts 有状态封装层 ~540 proxy.ts 代理流式函数 ~320 index.ts 导出入口 8 架构总览 graph TD App[外部应用] subgraph \"Agent 包\" Index[index.ts导出入口] subgraph \"类型系统\" Types[types.tsAgentMessage / AgentEventAgentLoopConfig / AgentTool] end subgraph \"管理层 agent.ts\" Agent[Agent 类状态管理 / 队列 / 并发 / 事件] Queue[PendingMessageQueuesteering / follow-up 队列] State[MutableAgentState状态归约] end subgraph \"引擎层 agent-loop.ts\" Engine[LoopEnginerunAgentLoop / runLoop] LLMCall[streamAssistantResponseLLM 调用] ToolExec[executeToolCalls工具执行] end subgraph \"HTTP 代理 proxy.ts\" Proxy[streamProxy代理流式函数] end end subgraph \"LLM 核心库 @earendil-works/pi-ai\" AI[streamSimple / ModelMessage / Tool] end App --\u003e Agent Agent --\u003e Engine Agent --\u003e Queue Agent --\u003e State Engine --\u003e LLMCall Engine --\u003e ToolExec LLMCall --\u003e AI Proxy -- 替代 streamFn --\u003e Agent Agent -.-\u003e Types Engine -.-\u003e Types 分层设计 无状态引擎 vs 有状态封装 Agent 包的核心设计思想是将引擎逻辑与状态管理分离：\n","content":"Pi Agent 包架构 packages/agent/src/ 实现了 Pi 框架中的 Agent 运行时，用于构建工具增强型 LLM 对话代理（Tool-Augmented LLM Agent）。\n整个包由五个模块组成：\n文件 职责 行数 types.ts 类型定义层 ~400 agent-loop.ts 无状态循环引擎 ~700 agent.ts 有状态封装层 ~540 proxy.ts 代理流式函数 ~320 index.ts 导出入口 8 架构总览 graph TD App[外部应用] subgraph \"Agent 包\" Index[index.ts导出入口] subgraph \"类型系统\" Types[types.tsAgentMessage / AgentEventAgentLoopConfig / AgentTool] end subgraph \"管理层 agent.ts\" Agent[Agent 类状态管理 / 队列 / 并发 / 事件] Queue[PendingMessageQueuesteering / follow-up 队列] State[MutableAgentState状态归约] end subgraph \"引擎层 agent-loop.ts\" Engine[LoopEnginerunAgentLoop / runLoop] LLMCall[streamAssistantResponseLLM 调用] ToolExec[executeToolCalls工具执行] end subgraph \"HTTP 代理 proxy.ts\" Proxy[streamProxy代理流式函数] end end subgraph \"LLM 核心库 @earendil-works/pi-ai\" AI[streamSimple / ModelMessage / Tool] end App --\u003e Agent Agent --\u003e Engine Agent --\u003e Queue Agent --\u003e State Engine --\u003e LLMCall Engine --\u003e ToolExec LLMCall --\u003e AI Proxy -- 替代 streamFn --\u003e Agent Agent -.-\u003e Types Engine -.-\u003e Types 分层设计 无状态引擎 vs 有状态封装 Agent 包的核心设计思想是将引擎逻辑与状态管理分离：\ngraph LR subgraph \"agent.ts（管理层）\" A1[持有 state / listeners / queues] A2[并发控制 ActiveRun] A3[事件归约 processEvents] A4[API: prompt / steer / abort] end subgraph \"agent-loop.ts（引擎层）\" E1[纯函数] E2[双层循环 runLoop] E3[LLM 调用 + 工具执行] E4[通过 emit 输出事件] end A1 -.-\u003e|createContextSnapshot| E1 A4 --\u003e|runPromptMessages| E2 E4 --\u003e|emit event| A3 维度 agent.ts agent-loop.ts 有无状态 有状态（类实例） 无状态（纯函数） 并发控制 ActiveRun + AbortController 通过 signal? 支持取消 消息注入 PendingMessageQueue 队列 getSteeringMessages 回调 事件消费 processEvents 归约状态 + 通知 listener emit 发出，不关心谁收 工具钩子 持有 beforeToolCall / afterToolCall 在 prepare / finalize 中调用 复用性 需实例化，单次运行 可任意并发调用 类型系统（types.ts） 类型系统定义了五个核心类别：\n1. 消息体系 graph TD Message[\"@pi-ai Messageuser / assistant / toolResult\"] subgraph \"AgentMessage（统一消息）\" AM[AgentMessage] CUSTOM[\"CustomAgentMessages[key]（通过 declaration merging 扩展）\"] end Message --\u003e AM CUSTOM --\u003e AM AgentMessage 通过 TypeScript 的 declaration merging 实现扩展：\n1 2 3 4 5 6 7 8 // 应用层扩展自定义消息类型 declare module \u0026#34;@mariozechner/agent\u0026#34; { interface CustomAgentMessages { artifact: ArtifactMessage; notification: NotificationMessage; } } // AgentMessage 自动包含 Message | ArtifactMessage | NotificationMessage 2. 事件体系（13 种） graph LR subgraph \"Agent 生命周期\" AS[agent_start] AE[agent_end] end subgraph \"Turn 生命周期\" TS[turn_start] TE[turn_end] end subgraph \"消息生命周期\" MS[message_start] MU[message_update] ME[message_end] end subgraph \"工具执行生命周期\" TES[tool_execution_start] TEU[tool_execution_update] TEE[tool_execution_end] end AS --\u003e TS --\u003e MS --\u003e MU --\u003e ME --\u003e TES --\u003e TEU --\u003e TEE --\u003e TE --\u003e AE 3. 配置体系（AgentLoopConfig） 继承自 SimpleStreamOptions，增加：\n消息转换：convertToLlm（AgentMessage[] → Message[]）、transformContext（上下文变换） 队列回调：getSteeringMessages、getFollowUpMessages 工具钩子：beforeToolCall（可阻拦执行）、afterToolCall（可覆盖结果） 停止判定：shouldStopAfterTurn 动态认证：getApiKey（用于短期 OAuth token） 4. 工具体系（AgentTool） 1 2 3 4 5 6 interface AgentTool\u0026lt;TParameters, TDetails\u0026gt; extends Tool\u0026lt;TParameters\u0026gt; { label: string; // UI 显示名称 prepareArguments?: (args: unknown) =\u0026gt; Static\u0026lt;TParameters\u0026gt;; // 参数兼容 execute: (...) =\u0026gt; Promise\u0026lt;AgentToolResult\u0026lt;TDetails\u0026gt;\u0026gt;; // 执行 executionMode?: \u0026#34;sequential\u0026#34; | \u0026#34;parallel\u0026#34;; // 单工具执行模式覆盖 } 5. 状态体系（AgentState） 1 2 3 4 5 6 7 8 9 10 11 interface AgentState { systemPrompt: string; // 系统提示词 model: Model\u0026lt;any\u0026gt;; // 当前模型 thinkingLevel: ThinkingLevel; // 推理级别 tools: AgentTool\u0026lt;any\u0026gt;[]; // 工具列表（setter 自动 slice 拷贝） messages: AgentMessage[]; // 对话记录（setter 自动 slice 拷贝） readonly isStreaming: boolean; // 是否正在运行 readonly streamingMessage?: AgentMessage; // 当前流式消息 readonly pendingToolCalls: ReadonlySet\u0026lt;string\u0026gt;; // 执行中的工具 readonly errorMessage?: string; // 最近错误 } 无状态循环引擎（agent-loop.ts） 导出接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 新 prompt 入口 → EventStream（流式封装） function agentLoop(prompts, context, config, signal?, streamFn?): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; // 继续入口 → EventStream function agentLoopContinue(context, config, signal?, streamFn?): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; // 底层异步版（被 agent.ts 调用） function runAgentLoop(prompts, context, config, emit, signal?, streamFn?): Promise\u0026lt;AgentMessage[]\u0026gt; function runAgentLoopContinue(context, config, emit, signal?, streamFn?): Promise\u0026lt;AgentMessage[]\u0026gt; 核心：双层循环（runLoop） 这是整个 Agent 包最核心的函数。\nflowchart TD Start([\"runLoop 开始\"]) --\u003e OuterCond{\"外层循环while true\"} OuterCond --\u003e|true| InnerCond{\"内层循环while hasMoreToolCalls|| pendingMessages\"} InnerCond --\u003e|true| Steering[\"1. 注入 steering 消息getSteeringMessages()\"] Steering --\u003e LLM[\"2. streamAssistantResponse()调 LLM\"] LLM --\u003e StopCheck{\"stopReasonerror / aborted?\"} StopCheck --\u003e|是| EmitAgentEnd[\"emit agent_endreturn\"] StopCheck --\u003e|否| ToolCheck{\"3. 有 tool calls?\"} ToolCheck --\u003e|有| ToolExec[\"4. executeToolCalls()串行或并行执行工具\"] ToolExec --\u003e Results[\"推入 tool result 到上下文\"] Results --\u003e TurnEnd[\"emit turn_end\"] ToolCheck --\u003e|无| TurnEnd TurnEnd --\u003e StopHook{\"5. shouldStopAfterTurn?\"} StopHook --\u003e|true| EmitAgentEnd StopHook --\u003e|false| SteeringPoll[\"6. 取 steering 消息\"] SteeringPoll --\u003e InnerCond InnerCond --\u003e|false| FollowUp{\"外层:getFollowUpMessages()有消息?\"} FollowUp --\u003e|有| SetPending[\"pendingMessages = followUp\"] SetPending --\u003e OuterCond FollowUp --\u003e|无| EmitAgentEnd2[\"emit agent_endreturn\"] 内层循环条件 内层持续的条件：有工具调用待处理 或 有 steering 消息待注入。\nhasMoreToolCalls：当前 assistant 消息有 tool call，且并非全部工具都要求终止 pendingMessages：steering 队列中有消息 外层循环条件 内层结束后检查 follow-up 队列，有则设为 pending 继续外层。\nLLM 调用（streamAssistantResponse） flowchart LR subgraph \"消息转换链\" AM[\"AgentMessage[]\"] TC[\"transformContext()（可选）\"] CL[\"convertToLlm()\"] M[\"Message[]\"] end subgraph \"LLM 调用\" LLM[\"streamSimple / streamFn\"] Stream[\"流式事件流\"] end subgraph \"响应处理\" Events[\"start / text_delta /toolcall_delta / done / error\"] Partial[\"构建 partial message逐帧更新 context.messages\"] end 函数逐项解读 以下逐一解读 agent-loop.ts 中各函数的设计细节。\nagentLoop() / agentLoopContinue() — 公开入口（EventStream 封装） 1 2 function agentLoop(prompts, context, config, signal?, streamFn?): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; function agentLoopContinue(context, config, signal?, streamFn?): EventStream\u0026lt;AgentEvent, AgentMessage[]\u0026gt; 方面 说明 职责 为外部调用者提供流式封装，避免直接处理 emit 回调 实现 内部创建 EventStream，启动 runAgentLoop / runAgentLoopContinue，通过 stream.push(event) 输出事件，最终 stream.end(messages) 终止条件 createAgentStream() 指定 agent_end 事件作为流结束标志，此时 messages 作为最终结果 agentLoopContinue 校验 空上下文 / 最后一条为 assistant 角色 → 直接抛异常 runAgentLoop() / runAgentLoopContinue() — 异步底层入口 方面 runAgentLoop runAgentLoopContinue 启动方式 新 prompt，复制上下文并在末尾追加 prompt 消息 复用当前上下文，不追加新消息 初始化事件 agent_start → turn_start → 逐一 message_start/end 发射 prompt agent_start → turn_start，无 prompt 事件 用途 agent.prompt() 调用 agent.continue() 调用 两者最终都汇聚到 runLoop()。\nrunLoop() — 核心双层循环 已在前面用流程图详细展示，这里补充源码级要点：\n关键设计点：\n要点 说明 变量位于闭包中 currentContext 和 newMessages 是引用类型，runLoop 内部修改直接反映到调用方 pendingMessages 作为内外层桥梁 内层每次迭代末尾通过 getSteeringMessages() 拉取；外层通过 getFollowUpMessages() 拉取后赋值给 pendingMessages 并 continue 外层 firstTurn 标志 首个 turn 不发射 turn_start（已在入口函数中发射）；后续 turn 需发射 终止路径 ① stopReason 为 error/aborted → 立即终止；② shouldStopAfterTurn 返回 true → 终止；③ 外层无 follow-up → 正常退出 streamAssistantResponse() — LLM 调用与流式处理 处理流水线：\n阶段 步骤 说明 1. 上下文变换 config.transformContext? 可选，对整个 AgentMessage[] 做预处理 2. 消息转换 config.convertToLlm(messages) Agent 世界 → LLM 世界的边界 3. 构造 LLM context { systemPrompt, messages, tools } 拼装成 Context 类型 4. 选择流函数 streamFn ?? streamSimple 允许外部注入自定义流函数（proxy、mock 等） 5. 解析 API key config.getApiKey? 支持短期 OAuth token 动态获取 6. 发起 LLM 调用 streamFunction(model, context, options) 返回 EventStream 7. 事件循环 for await (const event of response) 逐帧处理流式事件 事件循环核心逻辑：\nstart -\u0026gt; 推入 partialMessage 到 context.messages 末尾（占位），emit message_start delta -\u0026gt; 原地替换 context.messages 最后一条，emit message_update done/error -\u0026gt; 取最终消息替换占位，emit message_end 设计要点：\npartialMessage 是引用，每次 delta 事件原地更新同一对象，避免频繁深拷贝 context.messages 最后一条被用作\u0026quot;占位\u0026quot;：start 推入，update 替换，end 确定 若 start 事件从未到达（如空响应），则直接 push 最终消息 executeToolCalls() — 工具执行调度 调度逻辑：\n从 assistantMessage.content 中过滤出所有 type === \u0026quot;toolCall\u0026quot; 检查是否有工具标记了 executionMode: \u0026quot;sequential\u0026quot; config.toolExecution === \u0026quot;sequential\u0026quot; 或存在串行工具 -\u0026gt; executeToolCallsSequential 否则 -\u0026gt; executeToolCallsParallel executeToolCallsSequential() — 串行执行 对每个 tool call 逐个： emit tool_execution_start -\u0026gt; prepareToolCall() -\u0026gt; 若 immediate：直接记 error result -\u0026gt; 若 prepared：executePreparedToolCall() -\u0026gt; finalizeExecutedToolCall() -\u0026gt; emit tool_execution_end -\u0026gt; createToolResultMessage + emit message_start/end -\u0026gt; 全部完毕后计算 shouldTerminateToolBatch 每一步都严格串行：prepare -\u0026gt; execute -\u0026gt; finalize -\u0026gt; 下一个 executeToolCallsParallel() — 并行执行 两阶段设计：\n阶段 行为 Phase 1（串行 prepare） 逐个遍历 tool calls，执行 prepareToolCall()。immediate 结果直接记入；prepared 结果包装为 lazy thunk Phase 2（并发 execute） Promise.all() 并发执行所有 thunk（execute + finalize） 结果排序 按 assistant 原始 tool call 顺序排序（非完成顺序） 终止判定 全部完成后统一计算 shouldTerminateToolBatch prepareToolCall() — 工具调用前置管道 toolCall -\u0026gt; 1. 查找工具定义（context.tools.find()） -\u0026gt; 不存在 -\u0026gt; return { kind: \u0026#39;immediate\u0026#39;, error } -\u0026gt; 找到 -\u0026gt; 2. prepareArguments() 参数兼容转换 -\u0026gt; 3. validateToolArguments() Schema 校验 -\u0026gt; 4. beforeToolCall 钩子 -\u0026gt; block -\u0026gt; return { kind: \u0026#39;immediate\u0026#39;, error } -\u0026gt; 允许 -\u0026gt; return { kind: \u0026#39;prepared\u0026#39;, tool, args } 步骤 错误处理 工具不存在 error: \u0026ldquo;Tool xxx not found\u0026rdquo; prepareArguments() 抛错 catch -\u0026gt; immediate error validateToolArguments() 抛错 catch -\u0026gt; immediate error beforeToolCall 返回 block immediate error，原因由钩子指定 executePreparedToolCall() — 工具执行 调用工具自带的 execute()，传入 signal 支持取消 第四个参数是 onProgress 回调，工具可在执行中多次调用来发射 tool_execution_update 执行抛错 -\u0026gt; catch 后生成 error tool result（永不崩溃） finalizeExecutedToolCall() — 工具后处理 afterToolCall 钩子可以覆盖工具执行的任何输出字段（content / details / terminate / isError） 钩子本身抛错 -\u0026gt; catch 后覆盖为 error tool result（防止钩子崩溃波及主流程） 辅助函数一览 函数 职责 createAgentStream() 创建 EventStream，以 agent_end 为终止信号，提取 messages 为最终结果 createErrorToolResult(msg) 生成标准错误格式的 AgentToolResult shouldTerminateToolBatch(calls) 所有 finalized call 的 result.terminate === true 时才返回 true prepareToolCallArguments(tool, tc) 若工具定义 prepareArguments，对参数做兼容转换 createToolResultMessage(finalized) 从 finalized outcome 构建 ToolResultMessage emitToolExecutionEnd(finalized, emit) 发射 tool_execution_end 事件 emitToolResultMessage(msg, emit) 将 tool result 作为消息发射 message_start/end AM --\u0026gt; TC --\u0026gt; CL --\u0026gt; M --\u0026gt; LLM --\u0026gt; Stream --\u0026gt; Events --\u0026gt; Partial ### 工具执行（executeToolCalls） ```mermaid flowchart TD TC[tool calls 列表] --\u0026gt; Mode{\u0026#34;配置 + 工具标记\u0026lt;br/\u0026gt;sequential 还是 parallel?\u0026#34;} Mode --\u0026gt;|sequential| Seq[\u0026#34;executeToolCallsSequential()\u0026lt;br/\u0026gt;逐个执行\u0026#34;] Mode --\u0026gt;|parallel| Par[\u0026#34;executeToolCallsParallel()\u0026lt;br/\u0026gt;prepare 串行 → execute 并发\u0026#34;] subgraph \u0026#34;每个工具的完整生命周期\u0026#34; Prep[\u0026#34;prepareToolCall()\u0026#34;] Exec[\u0026#34;executePreparedToolCall()\u0026#34;] Final[\u0026#34;finalizeExecutedToolCall()\u0026#34;] end Prep --\u0026gt; Exec --\u0026gt; Final subgraph \u0026#34;prepareToolCall 细节\u0026#34; Find[\u0026#34;1. 查找工具定义\u0026#34;] PrepArgs[\u0026#34;2. prepareArguments()\u0026lt;br/\u0026gt;参数兼容\u0026#34;] Validate[\u0026#34;3. validateToolArguments()\u0026lt;br/\u0026gt;Schema 校验\u0026#34;] Before[\u0026#34;4. beforeToolCall 钩子\u0026lt;br/\u0026gt;可 block 执行\u0026#34;] end Find --\u0026gt; PrepArgs --\u0026gt; Validate --\u0026gt; Before 串行 vs 并行策略 方面 串行（sequential） 并行（parallel） preflight 逐工具 prepare → execute → finalize 全部 prepare（串行），然后全部 execute（并发） 结果顺序 按执行顺序 按 assistant 原始 tool call 顺序 终止判定 每个工具独立判定 批量判定（全部 terminate 才终止） 适用场景 工具间有依赖 / executionMode: \u0026quot;sequential\u0026quot; 工具间无依赖，追求速度 终止语义（terminate） 仅当批次内所有工具的 result.terminate === true 时，循环才会终止 单一工具返回 terminate: true 不会立即终止 这是设计选择：避免一个工具意外终止整个 agent 有状态封装层（agent.ts） Agent 类结构 classDiagram class Agent { -MutableAgentState _state -Set~Listener~ listeners -PendingMessageQueue steeringQueue -PendingMessageQueue followUpQueue -ActiveRun activeRun +StreamFn streamFn +convertToLlm() +beforeToolCall() +afterToolCall() +subscribe(listener) +prompt(input) +continue() +steer(msg) +followUp(msg) +abort() +reset() +waitForIdle() -runPromptMessages() -runContinuation() -createLoopConfig() -runWithLifecycle() -processEvents(event) -handleRunFailure(error) -finishRun() } class PendingMessageQueue { -AgentMessage[] messages +mode: QueueMode +enqueue(msg) +drain() AgentMessage[] +clear() } class ActiveRun { +Promise~void~ promise +resolve() +AbortController abortController } Agent *-- PendingMessageQueue Agent *-- ActiveRun 消息队列设计 flowchart LR subgraph \"steer() 注入\" S1[\"用户输入中间消息\"] S2[\"外部事件推送\"] end subgraph \"followUp() 注入\" F1[\"用户最终确认\"] F2[\"后台任务结果\"] end subgraph \"PendingMessageQueue\" Q1[\"steeringQueuemode: one-at-a-time / all\"] Q2[\"followUpQueuemode: one-at-a-time / all\"] end subgraph \"agent-loop 回调\" GS[\"getSteeringMessages()→ 内层每次迭代\"] GF[\"getFollowUpMessages()→ 内层结束后\"] end S1 --\u003e Q1 S2 --\u003e Q1 F1 --\u003e Q2 F2 --\u003e Q2 Q1 --\u003e GS Q2 --\u003e GF drain() 模式：\n模式 drain() 行为 场景 \u0026quot;one-at-a-time\u0026quot;（默认） 返回队列第一条，从队列移除 用户快速打字，每条消息独立处理 \u0026quot;all\u0026quot; 返回全部并清空 批量注入，多条消息合并处理 事件归约（processEvents） 类似 Redux reducer，接收 agent-loop 的 emit 事件，更新 _state：\nstateDiagram-v2 [*] --\u003e 空闲: reset / finishRun 空闲 --\u003e 流式: message_start 流式 --\u003e 流式: message_update 流式 --\u003e 有消息: message_end 流式 --\u003e 有消息: agent_end 有消息 --\u003e 执行工具: tool_execution_start 执行工具 --\u003e 执行工具: tool_execution_update 执行工具 --\u003e 有消息: tool_execution_end 有消息 --\u003e 空闲: turn_end + shouldStop 有消息 --\u003e 流式: 继续下一轮 事件 状态变更 message_start streamingMessage = event.message message_update streamingMessage = updated message_end streamingMessage = undefined，推入 messages[] tool_execution_start 新建 Set，添加 toolCallId tool_execution_end 新建 Set，删除 toolCallId turn_end 如有 errorMessage，更新 _state.errorMessage agent_end streamingMessage = undefined 函数逐项解读 以下逐一解读 agent.ts 中各成员的设计细节。\nsubscribe() — 事件订阅系统 特性 说明 存储 Set\u0026lt;(event, signal) =\u0026gt; void\u0026gt;，保证去重 执行顺序 按订阅顺序依次 await（同步执行 + 异步推进） 并发信号 回调接收当前 run 的 AbortSignal，可用于响应取消 解注册 返回 () =\u0026gt; void 取消函数 生命周期 agent_end 是最后一个事件，但 agent 不会 idle 直到所有 listener 处理完 agent_end 内部调用方式（在 processEvents 中）：\n1 2 3 for (const listener of this.listeners) { await listener(event, signal); } prompt() — 发起对话 1 2 3 // 三种重载 async prompt(message: AgentMessage | AgentMessage[]): Promise\u0026lt;void\u0026gt; async prompt(input: string, images?: ImageContent[]): Promise\u0026lt;void\u0026gt; 流程：\nprompt(input, images?) -\u0026gt; normalizePromptInput() 统一成 AgentMessage[] -\u0026gt; 检查 activeRun？存在 -\u0026gt; throw Error -\u0026gt; runPromptMessages(messages) -\u0026gt; runWithLifecycle() -\u0026gt; createContextSnapshot() -\u0026gt; createLoopConfig() -\u0026gt; runAgentLoop(..., emit -\u0026gt; processEvents) normalizePromptInput() 规则：\n输入 输出 AgentMessage[] 透传 AgentMessage [message] string [{ role: \u0026quot;user\u0026quot;, content: [{ type: \u0026quot;text\u0026quot;, text: input }], timestamp }] string + images 同上，content 追加 images continue() — 继续对话 逻辑分支：\ncontinue() -\u0026gt; activeRun 存在？ -\u0026gt; throw Error -\u0026gt; 最后一条消息角色？ -\u0026gt; 无消息 -\u0026gt; throw Error -\u0026gt; assistant -\u0026gt; steeringQueue 有消息？ -\u0026gt; 有 -\u0026gt; drain() -\u0026gt; runPromptMessages(steering, skipInitialSteeringPoll=true) -\u0026gt; 无 -\u0026gt; followUpQueue 有消息？ -\u0026gt; 有 -\u0026gt; drain() -\u0026gt; runPromptMessages(followUps) -\u0026gt; 无 -\u0026gt; throw Error \u0026#34;Cannot continue from assistant\u0026#34; -\u0026gt; user/toolResult -\u0026gt; runContinuation() -\u0026gt; runAgentLoopContinue() steer() / followUp() — 消息队列 方法 注入时机 对应回调 用途 steer() 内层每次迭代前 getSteeringMessages() 用户想\u0026quot;插话\u0026quot;、外部事件推送 followUp() 内层结束后、agent 停止前 getFollowUpMessages() 用户确认、后台任务结果 PendingMessageQueue 的 drain() 模式通过 QueueMode 控制：\n模式 drain() 行为 场景 \u0026quot;one-at-a-time\u0026quot;（默认） 返回队列第一条，从队列移除 用户快速打字，每条消息独立处理 \u0026quot;all\u0026quot; 返回全部并清空 批量注入，多条消息合并处理 abort() / waitForIdle() / reset() — 生命周期控制 方法 行为 abort() activeRun.abortController.abort() -\u0026gt; LLM 调用和工具执行收到取消信号 waitForIdle() 返回 activeRun.promise，在 finishRun() 中被 resolve reset() 清空 messages / isStreaming / pendingToolCalls / errorMessage，并清空两个队列 runWithLifecycle() — 并发控制核心 runWithLifecycle(executor) -\u0026gt; activeRun 存在？ -\u0026gt; throw Error -\u0026gt; 创建 AbortController + Promise -\u0026gt; activeRun = { promise, resolve, abortController } -\u0026gt; isStreaming = true -\u0026gt; try { executor(signal) } -\u0026gt; 成功 -\u0026gt; finally { finishRun() } -\u0026gt; 失败 -\u0026gt; handleRunFailure(error, aborted) -\u0026gt; finally { finishRun() } 状态变更 时机 isStreaming = true run 开始时 activeRun = { promise, resolve, abortController } run 开始时 isStreaming = false finishRun() 中 activeRun = undefined finishRun() 中 handleRunFailure() — 失败处理 将错误编码为一条 stopReason: \u0026quot;error\u0026quot; / \u0026quot;aborted\u0026quot; 的 AgentMessage 并推入对话历史：\n场景 stopReason 外部调用 abort() \u0026quot;aborted\u0026quot; LLM 调用抛错、工具执行抛错等 \u0026quot;error\u0026quot; 1 2 3 4 5 6 7 const failureMessage = { role: \u0026#34;assistant\u0026#34;, content: [{ type: \u0026#34;text\u0026#34;, text: \u0026#34;\u0026#34; }], stopReason: aborted ? \u0026#34;aborted\u0026#34; : \u0026#34;error\u0026#34;, errorMessage: error instanceof Error ? error.message : String(error), }; this._state.messages.push(failureMessage); await this.processEvents({ type: \u0026#34;agent_end\u0026#34;, messages: [failureMessage] }); 所有失败都被编码为正常的 AgentMessage 推入对话历史，应用层可通过 subscribe() 监听到 agent_end 事件。\nfinishRun() — 运行终结 1 2 3 4 5 6 7 private finishRun(): void { this._state.isStreaming = false; this._state.streamingMessage = undefined; this._state.pendingToolCalls = new Set\u0026lt;string\u0026gt;(); this.activeRun?.resolve(); this.activeRun = undefined; } 清理 isStreaming、streamingMessage、pendingToolCalls，兑现 waitForIdle() 的 Promise，清除 activeRun 以允许下一次调用。\nprocessEvents() — 事件归约（状态机） 类似 Redux reducer，接收 AgentEvent 并更新 _state：\n事件 状态变更 message_start streamingMessage = event.message message_update streamingMessage = updated message_end streamingMessage = undefined，推入 messages[] tool_execution_start 新建 Set，添加 toolCallId tool_execution_end 新建 Set，删除 toolCallId turn_end 如有 errorMessage，更新 _state.errorMessage agent_end streamingMessage = undefined 然后按订阅顺序依次 await 所有 listener。\n不可变性保证： pendingToolCalls 每次更新都新建 Set，外部通过 AgentState.pendingToolCalls 只读到 ReadonlySet，防止篡改。\ncreateContextSnapshot() / createLoopConfig() — 适配层 函数 作用 createContextSnapshot() 将 _state 转换为 AgentContext（无状态引擎入参），messages 和 tools 做浅拷贝 createLoopConfig() 将 Agent 的配置和钩子映射为 AgentLoopConfig，同时将 steeringQueue / followUpQueue 包装为 getSteeringMessages / getFollowUpMessages 回调 然后按订阅顺序依次 await 所有 listener。\n并发控制（ActiveRun） sequenceDiagram participant App as 应用 participant Agent as Agent participant Engine as agent-loop App-\u003e\u003eAgent: prompt(\"Hello\") Agent-\u003e\u003eAgent: 检查 activeRun Note over Agent: activeRun 不存在 → 继续 Agent-\u003e\u003eAgent: 创建 AbortController Agent-\u003e\u003eAgent: isStreaming = true Agent-\u003e\u003eAgent: activeRun = { promise, resolve, abortController } Agent-\u003e\u003eEngine: runAgentLoop(...) alt 正常完成 Engine--\u003e\u003eAgent: 返回 Agent-\u003e\u003eAgent: finishRun() Note over Agent: isStreaming = falseresolve promise Agent--\u003e\u003eApp: waitForIdle 完成 else 异常 Engine--\u003e\u003eAgent: throw error Agent-\u003e\u003eAgent: handleRunFailure(error) Note over Agent: 生成 error 消息emit agent_end Agent-\u003e\u003eAgent: finishRun() else 外部取消 App-\u003e\u003eAgent: abort() Agent-\u003e\u003eAgent: abortController.abort() Engine--\u003e\u003eAgent: 检测 signal.aborted Agent-\u003e\u003eAgent: handleRunFailure(error, aborted=true) end 完整调用链 sequenceDiagram participant App as 应用 participant A as agent.ts participant AL as agent-loop.ts participant LLM as LLM Provider App-\u003e\u003eA: agent.prompt(\"Hello\") A-\u003e\u003eA: normalizePromptInput() A-\u003e\u003eA: runWithLifecycle() Note over A: 设 isStreaming = true A-\u003e\u003eA: createContextSnapshot() A-\u003e\u003eA: createLoopConfig() A-\u003e\u003eAL: runAgentLoop(prompts, ctx, config, emit) AL-\u003e\u003eAL: agent_start / turn_start AL-\u003e\u003eAL: message_start/end for prompt loop 内层循环 AL-\u003e\u003eAL: getSteeringMessages() AL-\u003e\u003eAL: streamAssistantResponse() AL-\u003e\u003eAL: transformContext() AL-\u003e\u003eAL: convertToLlm() AL-\u003e\u003eLLM: streamSimple(model, context) LLM--\u003e\u003eAL: text_delta / toolcall_delta AL--\u003e\u003eA: emit(message_start/update/end) alt 有 tool calls AL-\u003e\u003eAL: executeToolCalls() AL--\u003e\u003eA: emit(tool_execution_start) AL-\u003e\u003eAL: prepareToolCall() AL-\u003e\u003eAL: beforeToolCall 钩子 AL-\u003e\u003eAL: executePreparedToolCall() AL-\u003e\u003eAL: finalizeExecutedToolCall() AL-\u003e\u003eAL: afterToolCall 钩子 AL--\u003e\u003eA: emit(tool_execution_end) end AL--\u003e\u003eA: emit(turn_end) AL-\u003e\u003eAL: shouldStopAfterTurn? end loop 外层循环 AL-\u003e\u003eAL: getFollowUpMessages() end AL--\u003e\u003eA: emit(agent_end) A-\u003e\u003eA: processEvents() 更新 state A-\u003e\u003eA: 通知 listeners A-\u003e\u003eA: finishRun() Note over A: isStreaming = false HTTP 代理（proxy.ts） streamProxy 是一个替代 streamSimple 的流式函数，用于需要经过代理服务器的场景。\nsequenceDiagram participant Agent as Agent participant Proxy as streamProxy participant Server as 代理服务器 participant LLM as LLM Provider Agent-\u003e\u003eProxy: streamProxy(model, context, options) Proxy-\u003e\u003eProxy: 创建 partial message Proxy-\u003e\u003eServer: POST /api/streamAuthorization: Bearer {token}Body: { model, context, options } Server-\u003e\u003eLLM: 转发请求 loop SSE 流式响应 LLM--\u003e\u003eServer: text_delta / toolcall_delta Server--\u003e\u003eProxy: data: { type: \"text_delta\", delta: \"...\" } Note over Proxy: 重建 partial message Proxy--\u003e\u003eAgent: push(text_delta, partial) end Server--\u003e\u003eProxy: data: { type: \"done\", reason: \"stop\", usage } Proxy--\u003e\u003eAgent: push(done, finalMessage) Proxy--\u003e\u003eAgent: end() 关键设计：\n服务器端去掉 partial 字段以减少带宽 客户端根据事件序列重建完整的 AssistantMessage 支持 text / thinking / toolcall 所有三种内容类型 异常时生成 stopReason: \u0026quot;error\u0026quot; / \u0026quot;aborted\u0026quot; 的消息，不抛异常 容错设计 Agent 包的设计哲学是永不崩溃——所有可能的失败都被捕获并编码为正常事件流：\n失败场景 处理方式 LLM 调用失败 stopReason: \u0026quot;error\u0026quot;, errorMessage: 描述 工具不存在 生成 error tool result: \u0026quot;Tool xxx not found\u0026quot; 参数校验失败 catch 后生成 error tool result beforeToolCall 阻拦 生成 error tool result: \u0026quot;Tool execution was blocked\u0026quot; afterToolCall 抛错 catch 后覆盖为 error tool result transformContext 抛错 通过 runWithLifecycle 的 catch → handleRunFailure convertToLlm 抛错 同上 外部取消 stopReason: \u0026quot;aborted\u0026quot; 工具执行抛错 catch → error tool result 所有错误最终都以 AgentEvent 的形式通过 emit 输出，应用层可以通过 subscribe() 统一处理。\n使用示例 基础用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import { Agent } from \u0026#34;@mariozechner/agent\u0026#34;; const agent = new Agent({ initialState: { systemPrompt: \u0026#34;你是一个有用的助手。\u0026#34;, model: myModel, }, }); // 订阅事件（更新 UI） agent.subscribe((event) =\u0026gt; { switch (event.type) { case \u0026#34;message_update\u0026#34;: updateUI(event.message); break; case \u0026#34;turn_end\u0026#34;: updateToolPanel(event.toolResults); break; } }); // 发起对话 await agent.prompt(\u0026#34;Hello!\u0026#34;); // 注入中间消息（不打断当前 LLM 调用） agent.steer({ role: \u0026#34;user\u0026#34;, content: [{ type: \u0026#34;text\u0026#34;, text: \u0026#34;等一下...\u0026#34; }] }); 自定义消息类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 扩展自定义消息 declare module \u0026#34;@mariozechner/agent\u0026#34; { interface CustomAgentMessages { artifact: { role: \u0026#34;artifact\u0026#34;; content: ArtifactContent[]; timestamp: number }; } } // 自定义转换函数 const agent = new Agent({ convertToLlm: (messages) =\u0026gt; messages.flatMap((m) =\u0026gt; { if (m.role === \u0026#34;artifact\u0026#34;) return []; // 过滤掉 artifact 消息 return [m]; }), }); 使用代理 1 2 3 4 5 6 7 8 const agent = new Agent({ streamFn: (model, context, options) =\u0026gt; streamProxy(model, context, { ...options, authToken: await getAuthToken(), proxyUrl: \u0026#34;https://genai.example.com\u0026#34;, }), }); 设计决策 为什么分离引擎层和管理层？ 可测试性：agent-loop.ts 是纯函数，无需 mock 状态即可单元测试 复用性：无状态引擎可以被不同的管理层复用 关注点分离：引擎只管循环逻辑，管理只管状态和并发 为什么用双层循环（steering + follow-up）？ steering：用户想在 agent 工作过程中\u0026quot;插话\u0026quot;，需要立即处理 follow-up：用户想等 agent 完成后再补充，不打断当前工作流 双层结构让这两种场景各得其所 为什么工具终止需要全部一致？ 防止一个工具意外终止整个 agent 只有所有工具都明确要求终止时才生效 调用方可以通过 afterToolCall 钩子调整 terminate 标志 为什么用 Set + 替换而非直接 mutable？ AgentState.pendingToolCalls 对外是 ReadonlySet，保证状态不可变性 内部每次更新都新建 Set，防止外部持有引用后篡改 ","tags":["Artificial Intelligence","Agent"],"categories":["Pi Architecture"]},{"title":"分析力学 习题 9","permalink":"https://yukar.icu/posts/analyticalmechanics/homework/9/","summary":"问题 1：两粒子系统与质心运动 考虑一维空间中质量分别为 $m_1$ 和 $m_2$ 的两个粒子，它们通过仅依赖于相对距离的势能相互作用： $$H(q_1, q_2, p_1, p_2) = \\frac{p_1^2}{2m_1} + \\frac{p_2^2}{2m_2} + V(q_1 - q_2),$$(a) 通过计算 $\\{G,H\\}$，证明总动量 $$G=p_1 + p_2$$ 是一个守恒量。\n(b) 质心坐标定义为 $$Q_{\\mathrm{cm}}=\\dfrac {m_1 q_1 + m_2 q_2}{m_1 + m_2}.$$ 证明量 $$G'=(m_1+m_2)Q_{\\mathrm{cm}}-(p_1 + p_2)t$$ 也是守恒的。\n(c) 解释 $G'$ 守恒的物理意义。\n解： (a) $H$ 作为 Hamiltonian，满足 $$\\dfrac {\\partial H}{\\partial p_1} = \\dfrac{p_1}{m_1}, \\quad \\dfrac{\\partial H}{\\partial p_2} = \\dfrac{p_2}{m_2},\\quad \\dfrac{\\partial H}{\\partial q_1} = V'(q_1 - q_2), \\quad \\dfrac{\\partial H}{\\partial q_2} = -V'(q_1 - q_2).$$ 所以 ","content":"问题 1：两粒子系统与质心运动 考虑一维空间中质量分别为 $m_1$ 和 $m_2$ 的两个粒子，它们通过仅依赖于相对距离的势能相互作用： $$H(q_1, q_2, p_1, p_2) = \\frac{p_1^2}{2m_1} + \\frac{p_2^2}{2m_2} + V(q_1 - q_2),$$(a) 通过计算 $\\{G,H\\}$，证明总动量 $$G=p_1 + p_2$$ 是一个守恒量。\n(b) 质心坐标定义为 $$Q_{\\mathrm{cm}}=\\dfrac {m_1 q_1 + m_2 q_2}{m_1 + m_2}.$$ 证明量 $$G'=(m_1+m_2)Q_{\\mathrm{cm}}-(p_1 + p_2)t$$ 也是守恒的。\n(c) 解释 $G'$ 守恒的物理意义。\n解： (a) $H$ 作为 Hamiltonian，满足 $$\\dfrac {\\partial H}{\\partial p_1} = \\dfrac{p_1}{m_1}, \\quad \\dfrac{\\partial H}{\\partial p_2} = \\dfrac{p_2}{m_2},\\quad \\dfrac{\\partial H}{\\partial q_1} = V'(q_1 - q_2), \\quad \\dfrac{\\partial H}{\\partial q_2} = -V'(q_1 - q_2).$$ 所以 $$\\{G,H\\} = -\\dfrac {\\partial H}{\\partial q_1} - \\dfrac{\\partial H}{\\partial q_2} = -V'(q_1 - q_2) + V'(q_1 - q_2) = 0.$$ 考虑到结论 $$\\dfrac {\\mathrm dG}{\\mathrm dt}=\\dfrac {\\partial G}{\\partial t}+\\sum \\left(\\frac{\\partial G}{\\partial q_i} \\dot q_i + \\frac{\\partial G}{\\partial p_i} \\dot p_i\\right) = \\{G,H\\} + \\dfrac {\\partial G}{\\partial t} = 0,$$ 因此 $G$ 是守恒量。\n(b) 类似地，计算 $\\{G',H\\}$，同上公式 $$\\begin{darray}{ll}\\dfrac {\\mathrm dG'}{\\mathrm dt}\u0026= -(p_1+p_2) + \\{G',H\\} \\\\[14pt]\u0026=-(p_1+p_2)+m_1\\cdot \\dfrac {p_1}{m_1} + m_2\\cdot \\dfrac {p_2}{m_2} +V'(q_1-q_2)-V'(q_1-q_2) \\\\[14pt]\u0026= 0.\\end{darray}$$(c) 将质量因子相除 $$\\dfrac {G'}{m_1+m_2}=Q_{\\mathrm{cm}}-\\dfrac {p_1+p_2}{m_1+m_2}t,$$ 第一项是质心位置，第二项是质心位置随时间的线性变化。$G'$ 的守恒意味着质心以恒定速度运动，这反映了系统的平移对称性。\n问题 2：含时守恒量 考虑质量为 $m$ 的粒子在均匀重力场中运动，Hamiltonian 为 $$H(q,p)=\\dfrac {p^2}{2m}+mgq,$$ 其中 $q$ 为高度（向上为正），$g$ 为重力加速度。\n(a) 证明量 $$G(q,p,t)=p+mgt$$ 是运动常数。\n(b) 直接求解 Hamilton 方程得到 $q(t)$ 和 $p(t)$，并验证在轨迹上取值的 $G(q(t),p(t),t)$ 确实为常数。\n(c) 给出 $G$ 的物理解释。该守恒量对应于系统的何种对称性。\n解： (a) 计算 $$\\dfrac {\\mathrm dG}{\\mathrm dt} = \\dfrac {\\partial G}{\\partial t} + \\{G,H\\} = mg + \\{p+mgt, H\\}=mg - mg = 0,$$ 所以 $G(q,p,t)$ 是守恒量。\n(b) 根据 Hamilton 方程 $$\\dot q = \\dfrac {\\partial H}{\\partial p} = \\dfrac {p}{m}, \\quad \\dot p = -\\dfrac {\\partial H}{\\partial q} = -mg,$$ 由此，常微分方程的解为 $$p(t) = p(0) - mgt, \\quad q(t) = q(0) + \\dfrac {p(0)}{m}t - \\dfrac {1}{2}gt^2.$$ 将 $p(t)$ 代入 $G$ 中 $$G(q(t),p(t),t) = p(0) - mgt + mgt = p(0),$$ 确实为常数。\n(c) $\\dot G$ 的物理意义是粒子受到的合外力等于重力，$G$ 的物理意义是粒子的速度在重力作用下的线性变化。这对应系统的时间平移对称性。\n问题 3：三个耦合谐振子 考虑三个相等质量 $m$ 由弹簧（劲度系数均为 $k$）连接成线性链。令 $q_1,q_2,q_3$ 为偏离平衡位置的位移。Hamiltonian 为 $$H=\\dfrac {1}{2m}(p_1^2+p_2^2+p_3^2)+\\dfrac {k}{2}[(q_1-q_2)^2+(q_2-q_3)^2+(q_3-q_1)^2].$$(a) 计算 $\\{p_1+p_2+p_3,H\\}$ 并解释结果。\n(b) 考虑量 $$G=p_1q_2-p_2q_1+p_2q_3-p_3q_2+p_3q_1-p_1q_3.$$ 计算 $\\{G,H\\}$ 并判断 $G$ 是否守恒。\n(c) 你能否确定 $G$ 对应于何种物理对称性？\n解： (a) 计算 $$\\{p_1+p_2+p_3,H\\} = -\\dfrac {\\partial H}{\\partial q_1} - \\dfrac {\\partial H}{\\partial q_2} - \\dfrac {\\partial H}{\\partial q_3} = k\\sum_{i=1}^3 \\sum_{j=1}^3 (q_i - q_j) = 0,$$ 因此 $p_1+p_2+p_3$ 是守恒量。这反映了系统的平移对称性，因为系统受到的合外力为零，所以质心保持静止。\n(b) 计算，其中 $\\{i,j,k\\}=\\{1,2,3\\}$，则（注意系数 $k$ 和指标 $k$ 无关） $$\\dfrac {\\partial H}{\\partial q_i}= k(2q_i - q_j - q_k), \\quad \\dfrac {\\partial H}{\\partial p_i} = \\dfrac {p_i}{m},$$ 容易计算 $$\\dfrac {\\partial G}{\\partial q_i} = -\\varepsilon_{ijk}(p_j - p_k), \\quad \\dfrac {\\partial G}{\\partial p_i} = \\varepsilon_{ijk}(q_j - q_k),$$ 进一步计算 $$\\begin{darray}{ll}\\{G,H\\}\u0026=\\sum_{i=1}^3 \\left[\\dfrac {\\partial G}{\\partial q_i} \\dfrac {\\partial H}{\\partial p_i} - \\dfrac {\\partial G}{\\partial p_i} \\dfrac {\\partial H}{\\partial q_i}\\right] \\\\[14pt]\u0026=\\sum^3_{i=1}(-\\varepsilon_{ijk})\\left[\\dfrac {p_i(p_j-p_k)}{m}+k(q_j-q_k)[(q_i-q_j)-(q_k-q_i)]\\right] \\end{darray}$$ 考虑 $p$ 项，等式相当于对 $i,j,k$ 做循环求和 $(1,2,3),(2,3,1),(3,1,2)$，不难看出等于 $0$；同理，对 $q$ 项，求和后也等于 $0$。因此 $\\{G,H\\}=0$，$G$ 是守恒量。\n(c) $G$ 的形式可以写为 $$\\mathbf p=\\begin{pmatrix}p_1 \\\\ p_2 \\\\ p_3\\end{pmatrix}, \\quad \\mathbf q=\\begin{pmatrix}q_1 \\\\ q_2 \\\\ q_3\\end{pmatrix},\\quad \\mathbf p\\times \\mathbf q = \\begin{pmatrix}p_2q_3 - p_3q_2 \\\\ p_3q_1 - p_1q_3 \\\\ p_1q_2 - p_2q_1\\end{pmatrix},$$ 进一步，考虑法向量 $\\mathbf n = (1,1,1)^\\top$，则 $$G = \\mathbf n \\cdot (\\mathbf p \\times \\mathbf q)=-(\\mathbf q \\times \\mathbf p)\\cdot \\mathbf n,$$ 这说明系统在 $\\mathbf n$ 方向的投影的角动量是守恒的。这对应于系统绕 $\\mathbf n$ 轴的旋转对称性。\n问题 4：两个守恒量的 Poisson 括号 Poisson 定理指的是：若 $F$ 和 $G$ 是 Hamilton 系统的两个守恒量（即 $\\dot F=\\dot G=0$），则它们的 Poisson 括号 $\\{F,G\\}$ 也是一个守恒量。\n(a) 利用 Jacobi 恒等式证明 Poisson 定理。\n(b) 将该定理应用于中心势粒子的角动量分量 $L_x,L_y$。已知 $L_x,L_y$ 各自守恒，该定理对 $\\{L_x,L_y\\}$ 有何预测？显式计算 $\\{L_x,L_y\\}$ 并验证其确实守恒（注意 $L_z=\\{L_x,L_y\\}$ 是守恒的）。\n解： (a) Jacobi 恒等式表明对于任意三个函数 $F,G,H$，有 $$\\{F,\\{G,H\\}\\} + \\{G,\\{H,F\\}\\} + \\{H,\\{F,G\\}\\} = 0.$$ 这里取 $H$ 为 Hamiltonian，则 $\\dot F=\\dot G=0$ 意味着 $$\\{F,H\\}+\\dfrac {\\partial F}{\\partial t} = 0, \\quad \\{G,H\\}+\\dfrac {\\partial G}{\\partial t} = 0.$$ 将上述等式代入 Jacobi 恒等式中，得到 $$-\\{F,G_t\\} + \\{G,F_t\\}-\\dfrac {\\partial \\{F,G\\}}{\\partial t} + \\{H,\\{F,G\\}\\}+\\dfrac {\\partial \\{F,G\\}}{\\partial t} = 0,$$ 事实上 $$\\dfrac {\\partial \\{F,G\\}}{\\partial t} = \\{F_t,G\\} + \\{F,G_t\\}=-\\{G,F_t\\} + \\{F,G_t\\},$$ 因此综上可以说明 $\\{F,G\\}$ 是守恒量，证明了 Poisson 定理。\n(b) 该定理表明 $\\{L_x,L_y\\}$ 也是守恒量。回忆第 7 次作业问题 2，我们已经给出 $$\\{L_i,L_j\\} = \\varepsilon_{ijk}L_k,$$ 的证明，这里略去。对 $i=x,j=y,k=z$，两侧取 $t$ 的全导数，得证。\n","tags":["Analytical Mechanics","Homework"],"categories":["Analytical Mechanics"]},{"title":"位势方程与 Green 公式","permalink":"https://yukar.icu/posts/partialdifferentialequations/green/","summary":"位势方程 一些基本的结果：\n基本解：在 $\\mathbb R^n$ 上，位势方程 $-\\Delta u=f(x)$ 的基本解 $-\\Delta E=\\delta(x)$ 满足 $$E(x)=\\begin{dcases} -\\frac{1}{2\\pi}\\log|x|,\u0026n=2,\\\\[14pt] \\frac{1}{(n-2)S_{n-1}|x|^{n-2}},\u0026n\\geq 3, \\end{dcases}$$ 尽管基本解 $E$ 在 $x=0$ 处有奇点，但通过球坐标变换 $$\\int_{|x|\u003c\\varepsilon} E(x)\\mathrm dx=\\int_0^\\varepsilon\\int_{\\Theta}E(r)J(\\Theta)r^{n-1}\\mathrm d\\Theta\\mathrm dr\\to 0,\\quad \\varepsilon\\to 0,$$ 其中 $J(\\Theta)$ 是球坐标变换的 Jacobian 的角度部分。因此 $E\\in L^1_{\\mathrm{loc}}(\\mathbb R^n)$；另外 $$\\nabla E(x)=-\\dfrac 1{S_{n-1}}\\dfrac x{|x|^n},\\quad \\forall n\\geq 2,$$ 这推出 $\\nabla E\\in L^1_{\\mathrm{loc}}(\\mathbb R^n)$，因为 $$\\int_{|x|\u003c\\varepsilon} |\\nabla E(x)|\\mathrm dx=\\int_0^\\varepsilon\\int_{\\Theta}\\dfrac {J(\\Theta)}{S_{n-1}}\\mathrm d\\Theta\\mathrm dr=\\int_0^\\varepsilon\\mathrm dr=\\varepsilon,$$ 如果我们进一步求二阶微分 $$|\\nabla^2 E(x)|=\\dfrac 1{S_{n-1}}\\left|\\dfrac n{|x|^n}\\dfrac {xx^T}{|x|^2}-\\dfrac 1{|x|^n}I\\right|\\sim \\dfrac 1{|x|^n},$$ 这推出 $\\nabla^2 E\\notin L^1_{\\mathrm{loc}}(\\mathbb R^n)$，因为 ","content":"位势方程 一些基本的结果：\n基本解：在 $\\mathbb R^n$ 上，位势方程 $-\\Delta u=f(x)$ 的基本解 $-\\Delta E=\\delta(x)$ 满足 $$E(x)=\\begin{dcases} -\\frac{1}{2\\pi}\\log|x|,\u0026n=2,\\\\[14pt] \\frac{1}{(n-2)S_{n-1}|x|^{n-2}},\u0026n\\geq 3, \\end{dcases}$$ 尽管基本解 $E$ 在 $x=0$ 处有奇点，但通过球坐标变换 $$\\int_{|x|\u003c\\varepsilon} E(x)\\mathrm dx=\\int_0^\\varepsilon\\int_{\\Theta}E(r)J(\\Theta)r^{n-1}\\mathrm d\\Theta\\mathrm dr\\to 0,\\quad \\varepsilon\\to 0,$$ 其中 $J(\\Theta)$ 是球坐标变换的 Jacobian 的角度部分。因此 $E\\in L^1_{\\mathrm{loc}}(\\mathbb R^n)$；另外 $$\\nabla E(x)=-\\dfrac 1{S_{n-1}}\\dfrac x{|x|^n},\\quad \\forall n\\geq 2,$$ 这推出 $\\nabla E\\in L^1_{\\mathrm{loc}}(\\mathbb R^n)$，因为 $$\\int_{|x|\u003c\\varepsilon} |\\nabla E(x)|\\mathrm dx=\\int_0^\\varepsilon\\int_{\\Theta}\\dfrac {J(\\Theta)}{S_{n-1}}\\mathrm d\\Theta\\mathrm dr=\\int_0^\\varepsilon\\mathrm dr=\\varepsilon,$$ 如果我们进一步求二阶微分 $$|\\nabla^2 E(x)|=\\dfrac 1{S_{n-1}}\\left|\\dfrac n{|x|^n}\\dfrac {xx^T}{|x|^2}-\\dfrac 1{|x|^n}I\\right|\\sim \\dfrac 1{|x|^n},$$ 这推出 $\\nabla^2 E\\notin L^1_{\\mathrm{loc}}(\\mathbb R^n)$，因为 $$\\int_{|x|\u003c\\varepsilon} |\\nabla^2 E(x)|\\mathrm dx\\sim \\int_{|x|\u003c\\varepsilon} \\dfrac 1{|x|^n}\\mathrm dx\\sim \\int_0^\\varepsilon\\dfrac 1r\\mathrm dr=\\infty.$$ 所以我们不能再向其索要光滑性了，并且这是一个临界情形。\n全空间经典解：如果 $f\\in C_0^2(\\mathbb R^n)$，则位势方程 $-\\Delta u=f$ 在 $\\mathbb R^n$ 上的解为 $$u=E*f=\\int_{\\mathbb R^n} E(x-y)f(y)\\mathrm dy.$$ 考虑差商和 Lebesgue 控制收敛，可以说明经典性（连续性略去）： $$\\begin{darray}{rl}\\partial_i u(x)= \\lim_{h\\to 0}\\frac{u(x+h e_i)-u(x)}{h}=\u0026\\lim_{h\\to 0} E*\\left[\\frac{f(\\cdot+h e_i)-f}{h}\\right](x)\\\\[14pt]\\xlongequal{\\text{Lebesgue}}\u0026 E*\\left[\\lim_{h\\to 0}\\frac{f(\\cdot+h e_i)-f}{h}\\right](x)\\\\[14pt]=\u0026 E*(e_i\\cdot\\nabla f)(x)\\\\[14pt]=\u0026 \\int_{\\mathbb R^n} E(x-y)\\nabla f(y)\\mathrm dy\\cdot e_i\\end{darray}$$ 二次微分同理，结果是 $$\\partial_{ij}(x)=\\int_{\\mathbb R^n} E(y)\\partial_{ij}f(x-y)\\mathrm dy.$$ 如果减弱条件 $f\\in C_0^2(\\mathbb R^n)$ 为 $f\\in C^1_0(\\mathbb R^n)$，则 $u$ 仍然是经典解，因为 $$\\partial_{ij}u(x)=\\int_{\\mathbb R^n} \\partial_i E(y)\\partial_j f(x-y)\\mathrm dy,$$Stokes 公式告诉我们\n在有界开集 $\\Omega\\subseteq \\mathbb R^n$ 上有分片光滑的边界 $\\partial \\Omega$（可以是多连通区域），$w=(w_1,\\cdots,w_n)\\in C(\\overline \\Omega)\\cap C^1(\\Omega)$，则 $$\\int_\\Omega \\mathrm{div} w\\mathrm dx=\\int_{\\partial \\Omega} w\\cdot \\mathbf n\\mathrm dS,$$ 其中 $\\mathbf n$ 是 $\\partial \\Omega$ 的外法向量，$\\mathrm dS$ 是 $\\partial \\Omega$ 上的面积微元，$\\mathrm dx$ 是 $\\Omega$ 上的体积微元。\n作为推论，有 Green 第一、二公式\n如果 $u,v\\in C^1(\\overline \\Omega)\\cap C^2(\\Omega)$，则 $$\\int_\\Omega \\nabla u\\cdot \\nabla v\\mathrm dx+\\int_\\Omega v\\Delta u\\mathrm dx=\\int_{\\partial \\Omega} v\\frac{\\partial u}{\\partial \\mathbf n}\\mathrm dS$$ 这是取 $$w_j=v\\frac{\\partial u}{\\partial x_j},\\quad j=1,\\cdots,n$$ 的结果。特别地 $$\\int _\\Omega |\\nabla u|^2\\mathrm dx+\\int_\\Omega u\\Delta u\\mathrm dx=\\int_{\\partial \\Omega} u\\frac{\\partial u}{\\partial \\mathbf n}\\mathrm dS.$$ 这可以证明 $u\\in C^1(\\overline \\Omega)\\cap C^2(\\Omega)$ 的意义下，位势方程的第一、三类边界值问题 $$\\begin{dcases} -\\Delta u=f(x),\u0026x\\in\\Omega,\\\\[14pt] u|_{\\partial \\Omega}=g(x),\u0026x\\in\\partial \\Omega, \\end{dcases},\\quad \\begin{dcases} -\\Delta u=f(x),\u0026x\\in\\Omega,\\\\[14pt] \\left[\\frac{\\partial u}{\\partial \\mathbf n}+\\alpha(x)u\\right]|_{\\partial \\Omega}=g(x),\u0026x\\in\\partial \\Omega, \\end{dcases}$$ 的解的唯一性，其中 $\\alpha(x)\\geq 0$.\n如果 $u,v\\in C^1(\\overline \\Omega)\\cap C^2(\\Omega)$，则 $$\\int_\\Omega [u\\Delta v-v\\Delta u]\\mathrm dx=\\int_{\\partial \\Omega} \\left[u\\frac{\\partial v}{\\partial \\mathbf n}-v\\frac{\\partial u}{\\partial \\mathbf n}\\right]\\mathrm dS.$$ 这是在第一公式的基础上交换相减得到的。其中 $v$ 可以作为测试函数。回忆位势方程的基本解 $$E(x)=\\begin{dcases} -\\frac{1}{2\\pi}\\log|x|,\u0026n=2,\\\\[14pt] \\frac{1}{(2-n)S_{n-1}|x|^{n-2}},\u0026n\\geq 3, \\end{dcases}$$Green 函数 ","tags":["Partial Differential Equations","Notes"],"categories":["Partial Differential Equations"]},{"title":"复旦 2021 推免考试","permalink":"https://yukar.icu/posts/a/fdu21/","summary":"1 2 设 $\\varepsilon\\in (0,1)$，则 $$\\begin{darray}{ll}\\lim_{n\\to\\infty}\\int^\\infty_0\\cos x^n\\mathrm dx\u0026=\\lim_{n\\to\\infty}\\int^\\infty_{1}\\cos x^n\\mathrm dx+\\lim_{n\\to\\infty}\\int^{1-\\varepsilon}_0\\cos x^n\\mathrm dx+\\lim_{n\\to\\infty}\\int^{1}_{1-\\varepsilon}\\cos x^n\\mathrm dx.\\end{darray}$$","content":"1 2 设 $\\varepsilon\\in (0,1)$，则 $$\\begin{darray}{ll}\\lim_{n\\to\\infty}\\int^\\infty_0\\cos x^n\\mathrm dx\u0026=\\lim_{n\\to\\infty}\\int^\\infty_{1}\\cos x^n\\mathrm dx+\\lim_{n\\to\\infty}\\int^{1-\\varepsilon}_0\\cos x^n\\mathrm dx+\\lim_{n\\to\\infty}\\int^{1}_{1-\\varepsilon}\\cos x^n\\mathrm dx.\\end{darray}$$","tags":[],"categories":["FDU"]},{"title":"复旦 2022 推免考试","permalink":"https://yukar.icu/posts/a/fdu22/","summary":"1 (1) 二阶连续可微的下凸函数，满足 $f''(x)\\geq 0$，这说明 $f'(x)$ 单调递增，而如果存在 $x_0$ 使得对任意 $x\u003ex_0$ 都有 $f(x)\\geq f(x_0)\u003e0$，那么 $f(x)$ 无界，矛盾。所以 $f'(x)\\leq 0$ 恒成立，从而 $f(x)$ 是有界单调递减函数，存在极限。\n(2) 计算反常积分，先形式地有 $$\\begin{darray}{ll}\\int^\\infty_0 xf''(x)\\mathrm dx\u0026=\\int^\\infty_0x\\mathrm df'(x)\\\\[6pt]\u0026=xf'(x)\\big|^\\infty_0-\\int^\\infty_0f'(x)\\mathrm dx\\\\[6pt]\u0026=\\lim_{x\\to\\infty}xf'(x)-\\lim_{x\\to \\infty}f(x)+f(0)\\\\[6pt]\u0026=\\lim_{x\\to\\infty}xf'(x)+f(0)-A\\end{darray}$$ 此外注意到，$f'(x)$ 是单调递增函数，所以 $$f(2x)-f(x)=\\int^{2x}_xf'(t)\\mathrm dt\\leq xf'(x)\\leq 0$$ 取极限即可，说明最终结果为 $f(0)-A$.\n2 (1) 因为 $f'(x)\\neq 0$，分类讨论。当 $f'(x)\u003e0$ 时，$f(x)$ 单调递增，所以 $$f(x)","content":"1 (1) 二阶连续可微的下凸函数，满足 $f''(x)\\geq 0$，这说明 $f'(x)$ 单调递增，而如果存在 $x_0$ 使得对任意 $x\u003ex_0$ 都有 $f(x)\\geq f(x_0)\u003e0$，那么 $f(x)$ 无界，矛盾。所以 $f'(x)\\leq 0$ 恒成立，从而 $f(x)$ 是有界单调递减函数，存在极限。\n(2) 计算反常积分，先形式地有 $$\\begin{darray}{ll}\\int^\\infty_0 xf''(x)\\mathrm dx\u0026=\\int^\\infty_0x\\mathrm df'(x)\\\\[6pt]\u0026=xf'(x)\\big|^\\infty_0-\\int^\\infty_0f'(x)\\mathrm dx\\\\[6pt]\u0026=\\lim_{x\\to\\infty}xf'(x)-\\lim_{x\\to \\infty}f(x)+f(0)\\\\[6pt]\u0026=\\lim_{x\\to\\infty}xf'(x)+f(0)-A\\end{darray}$$ 此外注意到，$f'(x)$ 是单调递增函数，所以 $$f(2x)-f(x)=\\int^{2x}_xf'(t)\\mathrm dt\\leq xf'(x)\\leq 0$$ 取极限即可，说明最终结果为 $f(0)-A$.\n2 (1) 因为 $f'(x)\\neq 0$，分类讨论。当 $f'(x)\u003e0$ 时，$f(x)$ 单调递增，所以 $$f(x)","tags":[],"categories":["FDU"]},{"title":"Cayley-Hamilton 定理","permalink":"https://yukar.icu/posts/linearalgebra/cayley-hamilton/","summary":"介绍三种 Cayley-Hamilton 的证明方法。在正式开始之前，先指出一类错误的证明思路。\n考虑到 $f(\\lambda)=\\det (\\lambda I-A)$ 的形式，将 $\\lambda$ 替换成 $A$，就得到所谓 $$f(A)=\\det (AI-A)=\\det O=0$$ 这混淆了标量和矩阵的关系，上述代换应该表示为 Kronecker 乘积的形式，与本定理并无关系。\n伴随矩阵方法（代数方法） 对于 $f(\\lambda)=\\det (\\lambda I-A)$，设 $\\lambda I-A=B$，则根据行列式的计算，有 $$f(\\lambda )I=\\det B\\cdot I=BB^*$$ 而伴随 $B^*$ 的定义要求它至多是 $n-1$ 次的 $\\lambda$ 的多项式，所以 $B^*$ 可以表示为 $$B^*=\\sum^{n-1}_{k=0}C_k\\lambda^k$$ 所以代入 $$\\sum^{n}_{k=0}a_k\\lambda^k I=f(\\lambda)I=\\sum^{n-1}_{k=0}(\\lambda I-A)C_k\\lambda^k$$ 从而比较系数 $$\\begin{matrix}a_0\u0026 =\u0026 -AC_0\u0026\\\\ a_1\u0026 =\u0026 C_0\u0026-AC_1\\\\ a_2\u0026 =\u0026\u0026 C_1\u0026-AC_2\\\\ \\vdots\u0026 \u0026\u0026\u0026\\ddots\u0026\\\\ a_{n-1}\u0026 =\u0026\u0026\u0026 C_{n-2}\u0026-AC_{n-1}\\\\ a_n\u0026 =\u0026\u0026\u0026\u0026 C_{n-1}\\end{matrix}$$ 累加即可。\n对角矩阵方法（分析方法） 这个方法只适用于 $\\mathbb F=\\mathbb R,\\mathbb C$ 上的矩阵。\n先考虑 $A$ 可对角化的情形，则此时存在可逆矩阵 $P$ 使得，对于任意多项式 $f$，都有 ","content":"介绍三种 Cayley-Hamilton 的证明方法。在正式开始之前，先指出一类错误的证明思路。\n考虑到 $f(\\lambda)=\\det (\\lambda I-A)$ 的形式，将 $\\lambda$ 替换成 $A$，就得到所谓 $$f(A)=\\det (AI-A)=\\det O=0$$ 这混淆了标量和矩阵的关系，上述代换应该表示为 Kronecker 乘积的形式，与本定理并无关系。\n伴随矩阵方法（代数方法） 对于 $f(\\lambda)=\\det (\\lambda I-A)$，设 $\\lambda I-A=B$，则根据行列式的计算，有 $$f(\\lambda )I=\\det B\\cdot I=BB^*$$ 而伴随 $B^*$ 的定义要求它至多是 $n-1$ 次的 $\\lambda$ 的多项式，所以 $B^*$ 可以表示为 $$B^*=\\sum^{n-1}_{k=0}C_k\\lambda^k$$ 所以代入 $$\\sum^{n}_{k=0}a_k\\lambda^k I=f(\\lambda)I=\\sum^{n-1}_{k=0}(\\lambda I-A)C_k\\lambda^k$$ 从而比较系数 $$\\begin{matrix}a_0\u0026 =\u0026 -AC_0\u0026\\\\ a_1\u0026 =\u0026 C_0\u0026-AC_1\\\\ a_2\u0026 =\u0026\u0026 C_1\u0026-AC_2\\\\ \\vdots\u0026 \u0026\u0026\u0026\\ddots\u0026\\\\ a_{n-1}\u0026 =\u0026\u0026\u0026 C_{n-2}\u0026-AC_{n-1}\\\\ a_n\u0026 =\u0026\u0026\u0026\u0026 C_{n-1}\\end{matrix}$$ 累加即可。\n对角矩阵方法（分析方法） 这个方法只适用于 $\\mathbb F=\\mathbb R,\\mathbb C$ 上的矩阵。\n先考虑 $A$ 可对角化的情形，则此时存在可逆矩阵 $P$ 使得，对于任意多项式 $f$，都有 $$Pf(A)P^{-1}=\\mathrm{diag}(f(\\lambda_1),f(\\lambda_2),\\cdots,f(\\lambda_n))$$ 所以特别地，$A$ 适合于多项式 $$f(\\lambda)=\\prod^n_{i=1}(\\lambda -\\lambda_i)=\\det (\\lambda I-A)$$ 对于一般的不可对角化的 $A$，考虑扰动 $B(\\varepsilon)$，其中 $$B(\\varepsilon)=\\mathrm {diag}(\\varepsilon_1,\\varepsilon_2,\\cdots,\\varepsilon_n),\\quad \\|B(\\varepsilon)\\|_\\infty\u003c\\varepsilon$$ 则 $$f_\\varepsilon(\\lambda):=\\det (\\lambda I-(A+ B))=\\det (\\lambda I-A)+o(\\varepsilon)=:f(\\lambda)+o(\\varepsilon)$$ 可对角化矩阵在 $(M_n(\\mathbb F),\\|\\cdot\\|_\\infty)$ 中稠密，所以 $$O=f_\\varepsilon(A+B)=f(A+ B)+o(\\varepsilon)= f(A)+o(\\varepsilon)\\to f(A)$$ 上述极限是关于 $\\varepsilon$，显然是连续的，所以极限成立。关于稠密性，考虑复矩阵的 Schur 引理，进行上三角化。\n上三角化（几何方法） Schur 引理或 Jordan 标准型和根子空间。略。同样只适用于 $\\mathbb F=\\mathbb R,\\mathbb C$ 上的矩阵。\n","tags":["Linear Algebra"],"categories":["Linear Algebra"]},{"title":"根子空间","permalink":"https://yukar.icu/posts/linearalgebra/linearoperator/","summary":"185\n199-28,29,30,31,32,33\nOksendal - Introduction to Stochastic\n","content":"185\n199-28,29,30,31,32,33\nOksendal - Introduction to Stochastic\n","tags":["Linear Algebra"],"categories":["Linear Algebra"]},{"title":"根子空间","permalink":"https://yukar.icu/posts/linearalgebra/rootsubspace/","summary":"线性算子 $T:\\mathbb F^n\\to\\mathbb F^n$ 作用在向量 $\\alpha \\in\\mathbb F^n$ 上，我们关心它的不变性。所以有不变子空间的概念，例如 $V\\subseteq \\mathbb F^n$ 是 $T$ 的不变子空间，意味着选取 $V$ 的一组基底进行线性扩张，则能得到以下表示矩阵 $$\\begin{pmatrix}A\u0026*\\\\ 0\u0026B\\end{pmatrix}$$ 其中 $*$ 不一定为零元，这说明 $V$ 是 $T$ 最终的收缩核。特别地，如果 $\\dim V=1$，则 $\\beta$ 是 $T$ 的特征向量，其中 $V=\\mathrm {span}\\{\\beta\\}$，具体地 $$\\exists \\lambda \\in \\mathbb F,\\text{ s.t. }T\\beta=\\lambda \\beta$$ 将这些特征向量元组 $(\\lambda ,\\beta)$ 收集起来，记线性算子 $T$ 的 $\\lambda$-特征子空间为 $$V_\\lambda =\\{\\beta \\in \\mathbb F^n:T\\beta=\\lambda \\beta\\}=:\\{\\beta \\in\\mathbb F^n:T_\\lambda \\beta=0\\}$$ 其中 $T_\\lambda :=T-\\lambda I$。这个特征子空间只不过是多个一维不变子空间（一维收缩核）的直和。那么作为收缩核，其核空间会最终平稳 $$\\ker T_\\lambda \\subseteq \\ker T^2_\\lambda \\subseteq \\cdots \\subseteq \\ker T^N_\\lambda =\\ker T^{N+1}_\\lambda =\\cdots$$ 于是 $\\ker T^N_\\lambda$ 中的元素只要作用 $T_\\lambda$ 就会最终收缩到 $\\ker T_\\lambda$ 中的一个特征向量上。而具体落在哪一个特征向量上，是由 $\\ker T^N_\\lambda$ 中元素的链条决定的：对于任意 $\\alpha \\in \\ker T^N_\\lambda$，存在最小的 $k\\in\\{0,1,\\cdots,N\\}$ 使得 ","content":"线性算子 $T:\\mathbb F^n\\to\\mathbb F^n$ 作用在向量 $\\alpha \\in\\mathbb F^n$ 上，我们关心它的不变性。所以有不变子空间的概念，例如 $V\\subseteq \\mathbb F^n$ 是 $T$ 的不变子空间，意味着选取 $V$ 的一组基底进行线性扩张，则能得到以下表示矩阵 $$\\begin{pmatrix}A\u0026*\\\\ 0\u0026B\\end{pmatrix}$$ 其中 $*$ 不一定为零元，这说明 $V$ 是 $T$ 最终的收缩核。特别地，如果 $\\dim V=1$，则 $\\beta$ 是 $T$ 的特征向量，其中 $V=\\mathrm {span}\\{\\beta\\}$，具体地 $$\\exists \\lambda \\in \\mathbb F,\\text{ s.t. }T\\beta=\\lambda \\beta$$ 将这些特征向量元组 $(\\lambda ,\\beta)$ 收集起来，记线性算子 $T$ 的 $\\lambda$-特征子空间为 $$V_\\lambda =\\{\\beta \\in \\mathbb F^n:T\\beta=\\lambda \\beta\\}=:\\{\\beta \\in\\mathbb F^n:T_\\lambda \\beta=0\\}$$ 其中 $T_\\lambda :=T-\\lambda I$。这个特征子空间只不过是多个一维不变子空间（一维收缩核）的直和。那么作为收缩核，其核空间会最终平稳 $$\\ker T_\\lambda \\subseteq \\ker T^2_\\lambda \\subseteq \\cdots \\subseteq \\ker T^N_\\lambda =\\ker T^{N+1}_\\lambda =\\cdots$$ 于是 $\\ker T^N_\\lambda$ 中的元素只要作用 $T_\\lambda$ 就会最终收缩到 $\\ker T_\\lambda$ 中的一个特征向量上。而具体落在哪一个特征向量上，是由 $\\ker T^N_\\lambda$ 中元素的链条决定的：对于任意 $\\alpha \\in \\ker T^N_\\lambda$，存在最小的 $k\\in\\{0,1,\\cdots,N\\}$ 使得 $$T^i_\\lambda \\alpha \\notin V_\\lambda ,\\forall i","tags":["Linear Algebra"],"categories":["Linear Algebra"]},{"title":"若干分解","permalink":"https://yukar.icu/posts/linearalgebra/decomposition/","summary":" Schur Decomposition：对于任意 $A\\in M_n(\\mathbb C)$，存在酉矩阵 $U$ 和上三角矩阵 $T$ 使得 $A=UTU^*$；对于任意 $A\\in M_n(\\mathbb R)$，且其特征值均为实数，则存在正交矩阵 $Q$ 和上三角矩阵 $R$ 使得 $A=QRQ^\\top$，其中 $R$ 的对角线元素为 $A$ 的特征值。\n作为推论，半正定实对称方阵可以开 $n$ 次方根（也是半正定实对称方阵）。\nSingular Value Decomposition：对于任意 $A\\in M_{m\\times n}(\\mathbb C)$，存在酉矩阵 $U$ 和 $V$ 以及对角矩阵 $\\Sigma$ 使得 $A=U\\Sigma V^*$；对于任意 $A\\in M_{m\\times n}(\\mathbb R)$，存在正交矩阵 $Q$ 和 $P$ 以及对角矩阵 $\\Sigma$ 使得 $A=Q\\Sigma P^\\top$，其中 $\\Sigma$ 的对角线元素为 $A$ 的奇异值。\n其中 $$\\Sigma=\\begin{pmatrix}\\mathrm{diag}(\\sigma_1,\\sigma_2,\\cdots,\\sigma_r) \u0026 0\\\\ 0\u00260\\end{pmatrix},\\quad \\Sigma^{-1}:=\\begin{pmatrix}\\mathrm{diag}(\\sigma^{-1}_1,\\sigma^{-1}_2,\\cdots,\\sigma^{-1}_r) \u0026 0\\\\ 0\u00260\\end{pmatrix}$$作为推论，可以定义任意矩阵 $A$ 的 Moore-Penrose 逆 $A^{-}$，满足 ","content":" Schur Decomposition：对于任意 $A\\in M_n(\\mathbb C)$，存在酉矩阵 $U$ 和上三角矩阵 $T$ 使得 $A=UTU^*$；对于任意 $A\\in M_n(\\mathbb R)$，且其特征值均为实数，则存在正交矩阵 $Q$ 和上三角矩阵 $R$ 使得 $A=QRQ^\\top$，其中 $R$ 的对角线元素为 $A$ 的特征值。\n作为推论，半正定实对称方阵可以开 $n$ 次方根（也是半正定实对称方阵）。\nSingular Value Decomposition：对于任意 $A\\in M_{m\\times n}(\\mathbb C)$，存在酉矩阵 $U$ 和 $V$ 以及对角矩阵 $\\Sigma$ 使得 $A=U\\Sigma V^*$；对于任意 $A\\in M_{m\\times n}(\\mathbb R)$，存在正交矩阵 $Q$ 和 $P$ 以及对角矩阵 $\\Sigma$ 使得 $A=Q\\Sigma P^\\top$，其中 $\\Sigma$ 的对角线元素为 $A$ 的奇异值。\n其中 $$\\Sigma=\\begin{pmatrix}\\mathrm{diag}(\\sigma_1,\\sigma_2,\\cdots,\\sigma_r) \u0026 0\\\\ 0\u00260\\end{pmatrix},\\quad \\Sigma^{-1}:=\\begin{pmatrix}\\mathrm{diag}(\\sigma^{-1}_1,\\sigma^{-1}_2,\\cdots,\\sigma^{-1}_r) \u0026 0\\\\ 0\u00260\\end{pmatrix}$$作为推论，可以定义任意矩阵 $A$ 的 Moore-Penrose 逆 $A^{-}$，满足 $$AA^{-}A=A,\\quad A^{-}=P\\Sigma^{-1}Q^\\top$$ 对于满足 $AXA=A$ 的矩阵 $X$ 都称为 $A$ 的广义逆，形如 $$\\Sigma=\\begin{pmatrix}\\mathrm{diag}(\\sigma_1,\\sigma_2,\\cdots,\\sigma_r) \u0026 *\\\\ *\u0026*\\end{pmatrix}$$ 与可逆矩阵的逆相容。Moore-Penrose 逆是一例广义逆。\n作为 Gram-Schmidt 的推论\nQR Decomposition：对于任意 $A\\in M_{m\\times n}(\\mathbb C)$，存在酉矩阵 $Q$ 和上三角矩阵 $R$ 使得 $A=QR$；对于任意 $A\\in M_{m\\times n}(\\mathbb R)$，存在正交矩阵 $Q$ 和对角元非负的上三角矩阵 $R$ 使得 $A=QR$。\n","tags":["Linear Algebra"],"categories":["Linear Algebra"]},{"title":"复旦 2023 推免考试","permalink":"https://yukar.icu/posts/a/fdu23/","summary":"1 对于任意 $x\\in \\mathbb R$，不妨 $x\\in (-M-1,M+1)$，其中 $M\u003e0$ 是对于 $x$ 取定的常数。此时题设级数中通项的分子是有界的，由 Weierstrass 判别法知道一致收敛，而部分和通项都是连续的，所以级数和也是连续的。\n对于 $\\mathbb R\\setminus [0,1]$ 上的任意点 $x$，只需要考虑其邻域的结果，不妨设 $x\\in B(x,\\delta)\\subseteq \\overline {B(x,\\delta)}\\subseteq \\mathbb R\\setminus [0,1]$，考虑形式导函数 $$f'(x):=\\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot (x-r_n)^{-\\frac 23}$$ 根据 $x$ 的取法 $|x-r_n|\\geq \\delta$，所以 $f'$ 在 $B(x,\\delta)$ 上一致收敛，因此 $f$ 在 $B(x,\\delta)$ 上可导，且 $f'$ 是 $f$ 的导函数。\n观察上述形式导函数，只需控制 $|x-r_n|^{-\\frac 23}$，考虑在原先给定的 $\\{r_n\\}$ 上重排构造 $\\{r'_n\\}$，按照以下方式进行（显然是双射）：对于第 $n$ 项 $r_n'$ 的选取，从 $\\{r_n\\}$ 中选取指标最小的元素，满足其不属于 $\\{r'_1,r'_2,\\cdots,r'_{n-1}\\}$，且满足 $|x-r_n'|\\geq 2^{-\\frac n2}$，从而 $$\\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot |x-r'_n|^{-\\frac 23}\\leq \\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot 2^{\\frac n3}\u003c\\infty$$ 计算重排后的差商 ","content":"1 对于任意 $x\\in \\mathbb R$，不妨 $x\\in (-M-1,M+1)$，其中 $M\u003e0$ 是对于 $x$ 取定的常数。此时题设级数中通项的分子是有界的，由 Weierstrass 判别法知道一致收敛，而部分和通项都是连续的，所以级数和也是连续的。\n对于 $\\mathbb R\\setminus [0,1]$ 上的任意点 $x$，只需要考虑其邻域的结果，不妨设 $x\\in B(x,\\delta)\\subseteq \\overline {B(x,\\delta)}\\subseteq \\mathbb R\\setminus [0,1]$，考虑形式导函数 $$f'(x):=\\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot (x-r_n)^{-\\frac 23}$$ 根据 $x$ 的取法 $|x-r_n|\\geq \\delta$，所以 $f'$ 在 $B(x,\\delta)$ 上一致收敛，因此 $f$ 在 $B(x,\\delta)$ 上可导，且 $f'$ 是 $f$ 的导函数。\n观察上述形式导函数，只需控制 $|x-r_n|^{-\\frac 23}$，考虑在原先给定的 $\\{r_n\\}$ 上重排构造 $\\{r'_n\\}$，按照以下方式进行（显然是双射）：对于第 $n$ 项 $r_n'$ 的选取，从 $\\{r_n\\}$ 中选取指标最小的元素，满足其不属于 $\\{r'_1,r'_2,\\cdots,r'_{n-1}\\}$，且满足 $|x-r_n'|\\geq 2^{-\\frac n2}$，从而 $$\\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot |x-r'_n|^{-\\frac 23}\\leq \\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot 2^{\\frac n3}\u003c\\infty$$ 计算重排后的差商 $$\\lim_{h\\to\\infty} \\dfrac {f(x+h)-f(x)}{h}=\\lim_{h\\to 0}\\sum^\\infty_{n=1} \\dfrac {1}{3\\cdot 2^n}\\cdot \\dfrac {(x+h-r_n)^{\\frac 13}-(x-r_n)^{\\frac 13}}{h}$$ 此级数通过 Weierstrass 判别法关于 $h$ 一致收敛，所以可以交换极限和级数的次序。\n2 这是柱状结构，以 $y$ 轴作为高度轴，则三个点绕轴旋转得到的曲线分别是 $$\\{y=0,x^2+z^2=1\\},\\quad \\{y=1,x^2+z^2=5\\},\\quad \\{y=-1,x^2+z^2=5\\}$$ 以它们为底面轮廓得到的 $$\\{|y|\\leq 1,x^2+z^2\\leq 5\\}$$ 是一个圆柱体所去掉两个台体 $$\\{|y|\\leq 1, x^2+z^2\\leq 1+4|y|\\}$$ 的部分，所以体积为\n$x_0$ 到 $V$ 的最短距离，由旋转对称性和平面对称性，只需要考虑部分截面，显然 $d=1$，最近点为 $(2,0,0)$.\n3 对于题设积分，由于其是恒正的，所以考虑 $$\\begin{darray}{ll}\\iint_{\\mathbb R^2}e^{k(x^2+y^2)}\\mathrm dx\\mathrm dy\u0026=\\lim_{R\\to \\infty}\\iint_{x^2+y^2\\leq R}e^{k(x^2+y^2)}\\mathrm dx\\mathrm dy\\\\[6pt]\u0026=\\lim_{R\\to \\infty}\\int^R_0\\int^{2\\pi}_0 e^{kr^2}r\\mathrm d\\theta \\mathrm dr\\\\[6pt]\u0026=\\pi \\lim_{R\\to \\infty}\\dfrac {e^{kR^2}-1}{k}\\\\[6pt]\u0026=-\\dfrac {\\pi}k\\end{darray}$$ 为了保证上述积分存在，必须 $k\u003c0$，解得 $k=-\\pi$.\n4 这里指出，原题应是 $\\mathscr TA$ 可逆当且仅当 $A$ 可逆。此时考虑扰动 $$f(z):=\\det (A-zI),\\quad g(z):=\\det (\\mathscr T(A-zI))=\\det (\\mathscr TA-z\\mathscr TI)$$ 如果 $z$ 是 $f$ 的零点，则 $A-zI$ 不可逆，所以 $\\mathscr T(A-zI)$ 不可逆，所以 $g(z)=0$，由等价性知道 $f,g$ 的零点集相同。首先考虑 $A$ 无重特征根的情况，此时 $f\\sim g$，所以存在 $c\\in\\mathbb C$ 使得 $$f(z)=cg(z)$$ 取 $z=0$ 即可说明 $$\\det A=c\\cdot \\det \\mathscr TA$$ 其次考虑 $A$ 有重特征根的情况，因为无重特征根的矩阵在 $M_n(\\mathbb C)$ 中稠密，而 $\\det A$ 和 $\\det \\mathscr TA$ 都是连续函数，所以结论同样成立。\n5 对于复方阵，上述级数在相似意义下不变，所以不妨设 $A$ 已经是 Jordan 标准型，则 $$ \\det (e^A)=\\left|\\begin{pmatrix}e^{\\lambda_1}\u0026* \u0026\\cdots \u0026*\\\\ 0\u0026e^{\\lambda_2}\u0026\\cdots \u0026*\\\\ \\vdots \u0026\\vdots \u0026\\ddots \u0026\\vdots \\\\ 0\u00260\u0026\\cdots \u0026e^{\\lambda_n}\\end{pmatrix}\\right|=e^{\\lambda_1+\\lambda_2+\\cdots +\\lambda_n}=e^{\\mathrm{tr}A}$$ 注意到 $e^A$ 作为矩阵函数，不改变对应后的特征值的代数重数和几何重数，所以结果是显然的。对于任意行列式为 $1$ 的 $2$ 阶实方阵 $$A=\\begin{pmatrix}a\u0026b\\\\ c\u0026d\\end{pmatrix}$$ 考虑特征多项式，其特征值为 $$\\lambda_{1,2}=\\frac {a+d\\pm \\sqrt{(a-d)^2+4bc}}2,\\quad \\lambda_1\\lambda_2=ad-bc=1$$ 如果是不相等的两实根，则存在 $P\\in M_2(\\mathbb R)$ 使得 $$PAP^{-1}=\\begin{pmatrix}\\lambda_1\u00260\\\\ 0\u0026\\lambda_2\\end{pmatrix}=\\exp\\begin{pmatrix}\\ln \\lambda_1\u00260\\\\ 0\u0026\\ln \\lambda_2\\end{pmatrix}$$ 上述对 $\\lambda_1,\\lambda_2\u003e0$ 成立。如果 $\\lambda_1,\\lambda_2\u003c0$，则右侧的对角元应为两个共轭复数，考虑其实标准型即可。如果是共轭复根，同样考虑实标准型即可。如果是重根，则 $\\lambda_1=\\lambda_2=1$，只考虑几何重数为 $1$ 的情况，此时 $$\\exp\\begin{pmatrix}0\u00261\\\\ 0\u00260\\end{pmatrix}=\\begin{pmatrix}e^0\u0026e^0\\\\ 0\u0026e^0\\end{pmatrix}=\\begin{pmatrix}1\u00261\\\\ 0\u00261\\end{pmatrix}$$6 由于 $Q^\\top Q=I_m$，所以是列满秩且单位正交，考虑将这一组列向量延拓成 $n$ 维空间的一组标准正交基 $(Q,Q_\\perp)$，则 $$(Q,Q_\\perp)^\\top A(Q,Q_\\perp)=\\begin{pmatrix}Q^\\top AQ\u0026Q^\\top AQ_\\perp\\\\ Q_\\perp^\\top AQ\u0026Q_\\perp^\\top AQ_\\perp\\end{pmatrix}=\\begin{pmatrix}A_{11}\u0026A_{12}\\\\ A_{21}\u0026A_{22}\\end{pmatrix}$$ 由于 $A$ 是实正定矩阵，所以右侧的矩阵的主子式也都是正定的，另一方面取逆 $$(Q,Q_\\perp)^{-1}A^{-1}(Q,Q_\\perp)^{-\\top}=(Q,Q_\\perp)^\\top A^{-1}(Q,Q_\\perp) =\\begin{pmatrix}Q^\\top A^{-1}Q\u0026Q^\\top A^{-1}Q_\\perp\\\\ Q_\\perp^\\top A^{-1}Q\u0026Q_\\perp^\\top A^{-1}Q_\\perp\\end{pmatrix}$$ 则综合以上两式 $$Q^\\top A^{-1}Q=(A_{11}-A_{12}A_{22}^{-1}A_{21})^{-1}$$ 则 $$\\begin{darray}{ll}\u0026Q^\\top A^{-1}Q-(Q^\\top AQ)^{-1}=(A_{11}-A_{12}A_{22}^{-1}A_{21})^{-1}-A_{11}^{-1}\\succeq 0\\\\[6pt]\\iff \u0026 A_{11}-A_{12}A_{22}^{-1}A_{21}\\preceq A_{11}\\\\[6pt]\\iff \u0026 A_{12}A_{22}^{-1}A_{21}\\succeq 0\\\\[6pt] \\impliedby \u0026 A_{22}\\succeq 0\\end{darray}$$","tags":[],"categories":["FDU"]},{"title":"Pin","permalink":"https://yukar.icu/posts/mathematicalanalysis/pin/","summary":"Problems in Mathematical Analysis\n作为注记\n71-\nAbel \u0026amp; Dirichlet Tests Abel 求和\nDirichlet 判别法（级数版本） Abel 判别法（级数版本）\n（函数列版本）\n（含参积分版本）\n76-\npp.76.ex (1) 考虑 $(-1)^n$ 求和有界，$\\frac 1n\\cdot \\left(1+\\frac 1n\\right)^n$ 单调趋于 $0$；\n(2) 考虑 $(-1)^n$ 求和有界，$\\frac 1n\\cdot \\cos \\frac 1n$ 单调趋于 $0$（考察连续函数的单调性）。\npp.77.ex.1 $T^r$ 是压缩映射，所以存在一个不动点 $x^*$，接下来说明这是 $T$ 的不动点。因为 $$x^*,Tx^*,T^2x^*,\\cdots,T^{r-1}x^*$$ 是 $T$ 作用在 $x^*$ 上的所有像，也都是 $T^r$ 的不动点，所以这个 $r$ 个点都相等，即 $x^*=Tx^*$。\npp.77.ex.2 当 $x-y\u003e0$ 取定时，其像之间的距离受到 $e^{-x}$ 的缩放 $$d(e^{-x}, e^{-y})=e^{-x}|1-e^{x-y}|\\geq e^{-x}d(x,y)$$ 所以 $x\\mapsto e^{-x}$ 不是压缩映射。但是 $$d(e^{-e^{-x}}, e^{-e^{-y}})=e^{-e^{-\\xi}}e^{-\\xi}d(x,y)\\leq d(x,y)\\max_{t\u003e0}\\frac {t}{e^t}$$ 所以 $x\\mapsto e^{-e^{-x}}$ 是压缩映射。\n","content":"Problems in Mathematical Analysis\n作为注记\n71-\nAbel \u0026amp; Dirichlet Tests Abel 求和\nDirichlet 判别法（级数版本） Abel 判别法（级数版本）\n（函数列版本）\n（含参积分版本）\n76-\npp.76.ex (1) 考虑 $(-1)^n$ 求和有界，$\\frac 1n\\cdot \\left(1+\\frac 1n\\right)^n$ 单调趋于 $0$；\n(2) 考虑 $(-1)^n$ 求和有界，$\\frac 1n\\cdot \\cos \\frac 1n$ 单调趋于 $0$（考察连续函数的单调性）。\npp.77.ex.1 $T^r$ 是压缩映射，所以存在一个不动点 $x^*$，接下来说明这是 $T$ 的不动点。因为 $$x^*,Tx^*,T^2x^*,\\cdots,T^{r-1}x^*$$ 是 $T$ 作用在 $x^*$ 上的所有像，也都是 $T^r$ 的不动点，所以这个 $r$ 个点都相等，即 $x^*=Tx^*$。\npp.77.ex.2 当 $x-y\u003e0$ 取定时，其像之间的距离受到 $e^{-x}$ 的缩放 $$d(e^{-x}, e^{-y})=e^{-x}|1-e^{x-y}|\\geq e^{-x}d(x,y)$$ 所以 $x\\mapsto e^{-x}$ 不是压缩映射。但是 $$d(e^{-e^{-x}}, e^{-e^{-y}})=e^{-e^{-\\xi}}e^{-\\xi}d(x,y)\\leq d(x,y)\\max_{t\u003e0}\\frac {t}{e^t}$$ 所以 $x\\mapsto e^{-e^{-x}}$ 是压缩映射。\npp.80.ex 在运算过程中只出现 $I,A$，所以是交换的 $$e^{sA}e^{tA}=\\sum_{n=0}^\\infty \\frac {(sA)^n}{n!}\\sum_{m=0}^\\infty \\frac {(tA)^m}{m!}=\\sum_{n=0}^\\infty\\sum^n_{m=0}\\frac {s^mt^{n-m}}{m!(n-m)!}A^n=\\sum_{n=0}^\\infty \\frac {(s+t)^n}{n!}A^n=e^{(s+t)A}$$ 其中第二项的两个因子都是绝对收敛的，从而可以重排成第三项，再用二项式定理得到第四项。\npp.87.ex.1 只证明充分性，如果对于任意收敛到 $x_0$ 的数列 $\\{x_n\\}$，根据其与 $x_0$ 的大小关系分为两个子列，至少有一个是无限集。这个无限集是子列，这个子列的极限对应 $f$ 的单边极限。而这两个子列（如果都是无限集）对应的极限相等，那么容易证明 $\\{f(x_n)\\}$ 是 Cauchy 列，从而收敛。\npp.87.ex.2 连续等价于极限存在，且值等于在该点的函数值。\npp.94.ex 考虑 Dirichlet 函数 $D(x)$，定义 $f(x)=2D(x)-1$，则 $|f|\\equiv 1$，但 $f$ 在任意点都不连续。\npp.100.ex 定义函数 $$F(x,y)=[f(x)-f(y)]\\cdot (x-y)$$ 对于固定的 $y\\in (a,b)$，$F(x,y)$ 是 $x$ 的连续函数。$x$ 在区间 $[a,y)$ 上（不妨）设 $f(x)\u003ef(y)$ 恒成立，那么由单射进一步推出 $f(x)","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"偏微分方程 习题 11","permalink":"https://yukar.icu/posts/partialdifferentialequations/homework/11/","summary":"参考资料：《数学物理方程讲义》，姜礼尚；《数学物理方程》（第三版），谷超豪。\nJ.pp162.5(1)(3)(5) 证明在 $\\mathcal D'(\\mathbb R)$ 意义下：\n$\\varphi(x)\\delta(x)=\\varphi(0)\\delta(x)$； $x\\delta^{(m)}(x)=-m\\delta^{(m-1)}(x)$； $(H(x)\\rho(x))'=\\delta(x)\\rho(0)+H(x)\\rho'(x)$ 其中 $H(x)$ 是 Heaviside 函数，$\\rho,\\varphi\\in C^\\infty(\\mathbb R)$.\n解： (1) 这里 $\\varphi\\in\\mathcal D(\\mathbb R)$，作为乘子作用在 $\\delta$ 上。取任意 $\\psi\\in \\mathcal D(\\mathbb R)$，则 $$\\langle \\varphi(x)\\delta(x),\\psi\\rangle =\\langle \\delta(x),\\varphi(x)\\psi\\rangle =\\varphi(0)\\psi(0)=\\langle \\varphi(0)\\delta(x),\\psi\\rangle.$$ (3) 对于任意 $\\psi\\in \\mathcal D(\\mathbb R)$，这里用到算子导数： $$\\begin{darray}{ll}\\langle x\\delta^{(m)}(x),\\psi\\rangle \u0026=\\langle \\delta^{(m)}(x),x\\psi\\rangle \\\\[12pt]\u0026=(-1)^{m}\\langle \\delta(x),(x\\psi)^{(m)}\\rangle \\\\[12pt]\u0026=(-1)^{m}\\langle \\delta(x),\\psi^{(m)}x+m\\psi^{(m-1)}\\rangle \\\\[12pt]\u0026=(-1)^{m}m\\langle \\delta(x),\\psi^{(m-1)}\\rangle \\\\[12pt]\u0026=\\langle -m\\delta^{(m-1)}(x),\\psi\\rangle.\\end{darray}$$ (5) 对于任意 $\\psi\\in \\mathcal D(\\mathbb R)$，同上（乘子是交换的） $$\\begin{darray}{ll}\\langle (H(x)\\rho(x))',\\psi\\rangle \u0026= \\langle (\\rho(x)H(x))',\\psi\\rangle \\\\[12pt]\u0026= -\\langle H(x),\\rho(x)\\psi'\\rangle \\\\[12pt]\u0026= -\\int_0^\\infty \\rho(x)\\psi'(x)\\mathrm dx\\\\[12pt]\u0026= -\\psi(0)\\rho(0)+\\int_0^\\infty \\rho'(x)\\psi(x)\\mathrm dx\\\\[12pt]\u0026= \\langle \\delta(x)\\rho(0)+H(x)\\rho'(x),\\psi\\rangle.\\end{darray}$$J.pp162.6 计算\n","content":"参考资料：《数学物理方程讲义》，姜礼尚；《数学物理方程》（第三版），谷超豪。\nJ.pp162.5(1)(3)(5) 证明在 $\\mathcal D'(\\mathbb R)$ 意义下：\n$\\varphi(x)\\delta(x)=\\varphi(0)\\delta(x)$； $x\\delta^{(m)}(x)=-m\\delta^{(m-1)}(x)$； $(H(x)\\rho(x))'=\\delta(x)\\rho(0)+H(x)\\rho'(x)$ 其中 $H(x)$ 是 Heaviside 函数，$\\rho,\\varphi\\in C^\\infty(\\mathbb R)$.\n解： (1) 这里 $\\varphi\\in\\mathcal D(\\mathbb R)$，作为乘子作用在 $\\delta$ 上。取任意 $\\psi\\in \\mathcal D(\\mathbb R)$，则 $$\\langle \\varphi(x)\\delta(x),\\psi\\rangle =\\langle \\delta(x),\\varphi(x)\\psi\\rangle =\\varphi(0)\\psi(0)=\\langle \\varphi(0)\\delta(x),\\psi\\rangle.$$ (3) 对于任意 $\\psi\\in \\mathcal D(\\mathbb R)$，这里用到算子导数： $$\\begin{darray}{ll}\\langle x\\delta^{(m)}(x),\\psi\\rangle \u0026=\\langle \\delta^{(m)}(x),x\\psi\\rangle \\\\[12pt]\u0026=(-1)^{m}\\langle \\delta(x),(x\\psi)^{(m)}\\rangle \\\\[12pt]\u0026=(-1)^{m}\\langle \\delta(x),\\psi^{(m)}x+m\\psi^{(m-1)}\\rangle \\\\[12pt]\u0026=(-1)^{m}m\\langle \\delta(x),\\psi^{(m-1)}\\rangle \\\\[12pt]\u0026=\\langle -m\\delta^{(m-1)}(x),\\psi\\rangle.\\end{darray}$$ (5) 对于任意 $\\psi\\in \\mathcal D(\\mathbb R)$，同上（乘子是交换的） $$\\begin{darray}{ll}\\langle (H(x)\\rho(x))',\\psi\\rangle \u0026= \\langle (\\rho(x)H(x))',\\psi\\rangle \\\\[12pt]\u0026= -\\langle H(x),\\rho(x)\\psi'\\rangle \\\\[12pt]\u0026= -\\int_0^\\infty \\rho(x)\\psi'(x)\\mathrm dx\\\\[12pt]\u0026= -\\psi(0)\\rho(0)+\\int_0^\\infty \\rho'(x)\\psi(x)\\mathrm dx\\\\[12pt]\u0026= \\langle \\delta(x)\\rho(0)+H(x)\\rho'(x),\\psi\\rangle.\\end{darray}$$J.pp162.6 计算\n$(|x|)^{(m)},\\ m\\in \\mathbb N$； $(H(x)\\sin x)'$； $(H(x)e^{ax})''$. 解： 本题的结论可以参考 J.pp162.5(1)(3)(5) 的结果。\n(1) 在 $\\mathcal D'(\\mathbb R)$ 意义下，因为 $|x|\\in\\mathcal L_{\\mathrm{loc}}(\\mathbb R)$，所以 $|x|$ 可以看成一个广义函数。对于任意 $\\psi\\in \\mathcal D(\\mathbb R)$，考虑到 $$|x|=x(H(x)-H(-x))=2xH(x)-x,$$ 所以由算子的 Leibniz 公式，且 $H(x)'=\\delta(x)$，有 $$\\begin{darray}{ll}(|x|)^{(m)}\u0026=2\\sum_{k=0}^m \\binom{m}{k} x^{(k)}H(x)^{(m-k)}-x^{(m)}\\\\[6pt]\u0026\\displaystyle =\\begin{dcases} 2H(x)-1=\\mathrm{sgn}(x), \u0026 m=1 \\\\[6pt] 2x\\delta^{(m-1)}+2m\\delta^{(m-2)}(x)=2\\delta^{(m-2)}(x), \u0026 m\\geq 2 \\end{dcases}\\end{darray}$$(2) 直接计算，用 Leibniz 公式 $$(H(x)\\sin x)'=\\delta(x)\\sin x+H(x)\\cos x=H(x)\\cos x.$$(3) 同上 $$(H(x)e^{ax})''=a^2 H(x)e^{ax}+2a\\delta(x)e^{ax}+\\delta'(x)e^{ax}=\\delta'(x)+2a\\delta(x)+a^2 H(x)e^{ax}.$$J.pp162.7(3) 求广义函数 $f'(x)$，其中\n$f(x)=\\begin{cases}x^2, \u0026-1\\leq x\\leq 1\\\\0, \u0026|x|\u003e1\\end{cases}$. 解： 对于广义函数， $$f(x)=x^2H(1-|x|)$$ 参考 J.pp162.6 的结果和 Leibniz 公式，得到 $$(f)'=2xH(1-|x|)-x^2\\delta(1-|x|)(|x|)'=2xH(1-|x|).$$ 注意，$\\delta(1-|x|)$ 的支撑是 $\\pm 1$，而 $|x|'=\\mathrm{sgn}(x)$ 在 $\\pm 1$ 处的值分别为 $\\pm 1$，这可以用泛函的定义来严格说明。\nG.pp163.1 证明：若 $\\varphi_\\nu(x)\\in C^\\infty_c(\\mathbb R_x^n),\\ \\psi(y)\\in C^\\infty_c(\\mathbb R_y^m)$，则 $\\varphi_\\nu(x)\\psi(y)\\in C^\\infty_c(\\mathbb R_x^n\\times \\mathbb R_y^m)$，且当 $\\varphi_\\nu(x)\\to 0(C^\\infty_c(\\mathbb R_x^n))$ 时，$\\varphi_\\nu(x)\\psi(y)\\to 0(C^\\infty_c(\\mathbb R_x^n\\times \\mathbb R_y^m))$.\n解： 在乘积空间中，$\\mathrm{supp}(\\varphi_\\nu(x)\\psi(y))\\subseteq \\mathrm{supp}(\\varphi_\\nu)\\times \\mathrm{supp}(\\psi)$，后者是一个紧集。另一方面，乘积空间的偏微分算子作用在 $\\varphi_\\nu(x)\\psi(y)$ 上是分离的，这只依赖于 $\\varphi_\\nu$ 和 $\\psi$ 的光滑性。下面证明 $\\mathcal D(\\mathbb R^n)$ 拓扑下的收敛结论，根据定义， $$\\varphi_\\nu(x)\\psi(y)\\to 0$$ 等价于存在一致紧支撑 $K\\subset\\subset \\Omega$，并且对于任意指标 $\\alpha$ 有 $$\\lim_{\\nu\\to\\infty}\\max_{x\\in K} |D^\\alpha (\\varphi_\\nu(x)\\psi(y))|=0.$$ 根据已知条件，$\\varphi_\\nu(x)$ 有一致紧支撑 $\\hat K$ $$\\mathrm{supp}(\\varphi_\\nu(x)\\psi(y))\\subseteq \\mathrm{supp}(\\varphi_\\nu)\\times \\mathrm{supp}(\\psi)\\subseteq \\hat K\\times \\mathrm{supp}(\\psi)=:K.$$ 对于任意指标 $\\alpha$，用 Leibniz 公式， $$D^\\alpha (\\varphi_\\nu(x)\\psi(y))=\\sum_{\\beta\\leq \\alpha} \\binom{\\alpha}{\\beta} D^\\beta \\varphi_\\nu(x) D^{\\alpha-\\beta}\\psi(y).$$ 由于 $\\alpha$ 是给定且有限的，所以 $$\\max_{\\beta\\le\\alpha}\\max_{x\\in K} |D^{\\alpha-\\beta} \\psi(y)|\u003c\\infty.$$ 因而 $$\\max_{x\\in K} |D^\\alpha (\\varphi_\\nu(x)\\psi(y))|\\le \\max_{\\beta\\le\\alpha}\\max_{x\\in K} |D^{\\alpha-\\beta} \\psi(y)| \\cdot \\max_{\\beta\\le\\alpha}\\max_{x\\in K} |D^\\beta \\varphi_\\nu(x)|\\cdot 2^{|\\alpha|}.$$ $\\beta$ 的个数有限，所以当 $\\nu\\to\\infty$ 时，不等式右端是一致趋于 $0$ 的。\nG.pp163.5 若 $P(x),Q(x)$ 为常系数多项式，$Q(\\partial)$ 为将 $Q(x)$ 中的 $x_i$ 用 $\\frac {\\partial}{\\partial x_i}$ 代替后所得到的偏微分算子。试证 $\\nu\\to\\infty$ 时下列命题等价：\n$\\varphi_\\nu(x)\\to 0(\\mathcal S(\\mathbb R^n))$； 对任意给定的 $P(x),Q(x)$，$P(x)Q(\\partial)\\varphi_\\nu(x)\\to 0$ 在 $\\mathbb R^n$ 上一致成立； 对任意给定的 $P(x),Q(x)$，$Q(\\partial)(P(x)\\varphi_\\nu(x))\\to 0$ 在 $\\mathbb R^n$ 上一致成立。 解： (1) 在 $\\mathcal S(\\mathbb R^n)$ 上，这等价于对于任意指标 $\\alpha,\\beta$，有 $$\\lim_{\\nu\\to\\infty}\\max_{x\\in \\mathbb R^n} |x^\\alpha D^\\beta \\varphi_\\nu(x)|=0.$$(2) 已知 (1)，可以看出 $Q(\\partial)$ 是多个混合偏微分算子的线性组合 $q_iQ_i$，$P(x)$ 也是多个单项式 $p_iP_i$ 的线性组合，在取定后，它们是有限的，取 $\\max$，或者 $$\\max_{x\\in\\mathbb R^n}|P(x)Q(\\partial)\\varphi_\\nu(x)| \\leq\\sum_{i,j}\\max_{x\\in \\mathbb R^n} |p_i q_j P_i(x) Q_j(\\partial)\\varphi_\\nu(x)|\\to 0.$$ 求和是有限个，所以这是一致趋于 $0$ 的。反过来，(1) 显然是 (2) 的特例。\n(3) 同上，考虑 Leibniz 公式，$Q(\\partial)(P(x)\\varphi_\\nu(x))$ 最后可以展开成有限项 $x^\\alpha D^\\beta \\varphi_\\nu(x)$ 的线性组合，所以可以用 (1) 来说明 (3) 的结论。反过来，已知 (3)，考虑 $Q(\\partial)=D^\\beta$ 和 $P(x)=x^\\alpha$ 的特例，得到 $$D^\\beta (x^\\alpha \\varphi_\\nu(x))=x^\\alpha D^\\beta \\varphi_\\nu(x)+\\sum_{0\u003c\\gamma\\leq \\beta} \\binom{\\beta}{\\gamma} D^\\gamma x^\\alpha D^{\\beta-\\gamma}\\varphi_\\nu(x).$$ 右侧第一项是 (1) 中需要估计的项，我们知道左侧是趋于 $0$ 的，右侧第二项是有限项的线性组合，且 $D^\\gamma x^\\alpha$ 的每个单项式的指标都小于 $\\alpha$。不妨对 $x$ 的指标归纳，当 $\\alpha=0$ 时， $$D^\\beta \\varphi_\\nu(x)=x^0 D^\\beta \\varphi_\\nu(x)\\to 0.$$ 每次只在 $x$ 的一个指标上加 $1$，假设 $|\\alpha|\\leq k$ 时收敛结论成立，那么当 $|\\alpha|=k+1$ 时， $$D^\\beta (x^\\alpha \\varphi_\\nu(x))=x^\\alpha D^\\beta \\varphi_\\nu(x)+\\sum_{0\u003c\\gamma\\leq \\beta} \\binom{\\beta}{\\gamma} D^\\gamma x^\\alpha D^{\\beta-\\gamma}\\varphi_\\nu(x),$$ 求和项中每个单项式的 $x$ 的指标都不大于 $k$，所以根据归纳假设，这些项是一致趋于 $0$ 的，所以第二项也趋于 $0$。归纳完成。从而 (3) 推出 (1)。\nG.pp163.7 判断下列一元函数属于哪些广义函数空间？\n$\\sin x$； $x$； $e^{x^2}$； $f(x)=\\begin{cases}1, \u0026|x|\\le 1\\\\0, \u0026|x|\u003e1\\end{cases}$； 解： 有三类广义函数空间 $\\mathcal E'\\subseteq \\mathcal S'\\subseteq \\mathcal D'$。题目中的函数都属于 $\\mathcal L_{\\mathrm{loc}}(\\mathbb R)$，所以自然可以看作是 $\\mathcal D'(\\mathbb R)$ 的广义函数，满足广义函数的定义 $$\\langle g,\\varphi\\rangle =\\int_{\\mathbb R} g(x)\\varphi(x)\\mathrm dx, \\quad \\forall \\varphi\\in \\mathcal D(\\mathbb R).$$ 此外，考虑序列 $\\varphi_n\\in \\mathcal D(\\mathbb R)$ 是趋于 $0$ 的函数列，且一致紧支撑，则对于常义积分，极限交换 $$\\lim_{n\\to\\infty} \\langle g,\\varphi_n\\rangle =\\lim_{n\\to\\infty} \\int_{\\mathbb R} g(x)\\varphi_n(x)\\mathrm dx=0=\\int_{\\mathbb R} g(x)\\lim_{n\\to\\infty} \\varphi_n(x)\\mathrm dx=0.$$ 所以以上一元函数都属于 $\\mathcal D'(\\mathbb R)$。\n另一方面，对于任意 $\\varphi,\\psi\\in \\mathcal S(\\mathbb R)$，有 $$\\langle g,\\varphi+\\psi\\rangle =\\int_{\\mathbb R} \\dfrac {g(x)}{1+|x|^4}(1+|x|^4)(\\varphi(x)+\\psi(x))\\mathrm dx.$$ 当 $g$ 取 $\\sin x,x,f(x)$ 时，$\\frac {g(x)}{1+|x|^4}$ 绝对可积，而且 $(1+|x|^4)\\varphi(x)\\in \\mathcal S(\\mathbb R)$，所以上述算子是良定义的线性算子，以下结果说明连续性：对于任意 $\\varphi_n\\in \\mathcal S(\\mathbb R)$ 是趋于 $0$ 的函数列 $$\\begin{darray}{ll}\\lim_{n\\to\\infty} \\langle g,\\varphi_n\\rangle \u0026=\\lim_{n\\to\\infty} \\int_{\\mathbb R} \\dfrac {g(x)}{1+|x|^4}(1+|x|^4)\\varphi_n(x)\\mathrm dx\\\\[12pt]\u0026\\leq \\lim_{n\\to\\infty}\\int_{\\mathbb R} \\left|\\dfrac {g(x)}{1+|x|^4}\\right|\\max_{x\\in \\mathbb R} |(1+|x|^4)\\varphi_n(x)|\\mathrm dx\\\\[12pt]\u0026\\leq \\lim_{n\\to\\infty} \\int_{\\mathbb R} \\dfrac {1}{1+|x|^2}\\cdot \\max_{x\\in \\mathbb R} |(1+|x|^4)\\varphi_n(x)|\\mathrm dx = 0.\\end{darray}$$ 最后一步实际上是函数列积分，这是根据绝对可积，这是可以换序的。所以 $\\sin x,x$ 和 $f(x)$ 也属于 $\\mathcal S'(\\mathbb R)$。而以下结果说明 $e^{x^2}\\notin \\mathcal S'(\\mathbb R)$： $$\\langle e^{x^2},e^{-x^2}\\rangle =\\int_{\\mathbb R} e^{x^2}e^{-x^2}\\mathrm dx=\\int_{\\mathbb R} 1\\mathrm dx=\\infty.$$ 严谨地，构造速降函数列 $e^{-x^2}*\\chi_{[-n,n]}(x)\\to e^{-x^2}$，这可以说明上述算子并不连续。\n最后，考虑任意 $\\varphi\\in \\mathcal E(\\mathbb R)$。考虑构造 $$\\varphi_n(x)=\\chi_{[-1,1]}*\\chi_{[0,\\frac \\pi2]}(x-2n\\pi)$$ 在 $\\mathcal E(\\mathbb R)$ 的意义下，$\\varphi_n\\to 0$，但是 $$\\langle \\sin x,\\varphi_n\\rangle =\\int_{\\mathbb R} \\sin x \\chi_{[-1,1]}*\\chi_{[0,\\frac \\pi2]}(x-2n\\pi)\\mathrm dx\u003e\\int_0^{\\frac \\pi2} \\sin x\\mathrm dx\u003e0.$$ 所以 $\\sin x\\notin \\mathcal E'(\\mathbb R)$。同上，$x\\notin \\mathcal E'(\\mathbb R)$。此外，$e^{x^2}\\notin \\mathcal S'(\\mathbb R)\\supseteq \\mathcal E'(\\mathbb R)$。最后，$f(x)$ 本身是紧支撑的，根据定义，对任意 $C^\\infty(\\mathbb R)\\ni \\varphi_n\\to 0$，这推出对于任意指标 $\\alpha \\in \\mathbb Z$，有 $$\\lim_{n\\to\\infty}\\max_{x\\in [-1,1]} |D^\\alpha \\varphi_n(x)|=\\lim_{n\\to\\infty}\\max_{x\\in [-1,1]} |f(x)\\cdot D^\\alpha \\varphi_n(x)|=0.$$ 所以 $$\\langle f,\\varphi_n\\rangle =\\int_{-1}^1 f(x)\\varphi_n(x)\\mathrm dx\\leq 2\\max_{x\\in [-1,1]} |f(x)\\cdot \\varphi_n(x)|\\mathrm dx\\to 0.$$ 所以只有 $f(x)$ 属于 $\\mathcal E'(\\mathbb R)$。\nG.pp167.2 证明：若 $T$ 为 $\\mathcal D'$ 广义函数，$\\alpha$ 为 $\\mathcal D'(\\mathbb R^n)$ 的乘子，则 $$\\partial _j(\\alpha T)=\\alpha\\cdot \\partial _j T+\\partial _j \\alpha\\cdot T.$$解： 对于任意 $\\varphi\\in \\mathcal D(\\mathbb R^n)$ $$\\begin{darray}{ll}\\langle \\partial _j(\\alpha T),\\varphi\\rangle \u0026=-\\langle \\alpha T,\\partial_j \\varphi\\rangle \\\\[12pt]\u0026=-\\langle T,\\alpha \\partial_j \\varphi\\rangle \\\\[12pt]\u0026=-\\langle T,\\partial_j (\\alpha \\varphi)\\rangle +\\langle T,(\\partial_j \\alpha)\\varphi\\rangle \\\\[12pt]\u0026=\\langle \\partial_j T,\\alpha \\varphi\\rangle +\\langle T,(\\partial_j \\alpha)\\varphi\\rangle \\\\[12pt]\u0026=\\langle \\alpha\\cdot \\partial _j T+\\partial _j \\alpha\\cdot T,\\varphi\\rangle.\\end{darray}$$G.pp167.4 用 $$\\left\\langle \\dfrac {\\partial T}{\\partial x_k},\\varphi \\right\\rangle =-\\left\\langle T,\\dfrac {\\partial \\varphi}{\\partial x_k} \\right\\rangle,\\quad \\forall \\varphi\\in \\mathcal S(\\mathbb R^n)$$ 来定义 $\\mathcal S'(\\mathbb R^n)$ 上的广义函数的导数。试证 $\\frac {\\partial}{\\partial x_k}$ 是 $\\mathcal S'(\\mathbb R^n)$ 上的一个线性连续映射。\n解： 这里简记 $\\partial_k=\\frac {\\partial}{\\partial x_k}$。在 $\\mathcal S(\\mathbb R^n)$ 中，考虑任意趋于 $0$ 的函数列 $\\varphi_n\\to 0$，这等价于对于任意指标 $\\alpha,\\beta$，有 $$\\lim_{n\\to\\infty}\\max_{x\\in \\mathbb R^n} |x^\\alpha D^\\beta \\varphi_n(x)|=0.$$ 从而，根据 $T\\in \\mathcal S'(\\mathbb R^n)$ 是连续线性算子，且 $\\partial_k \\varphi_n$ 也是趋于 $0$ 的函数列，所以 $$\\lim_{n\\to\\infty}\\langle \\partial_k T,\\varphi_n\\rangle =\\lim_{n\\to\\infty} -\\langle T,\\partial_k \\varphi_n\\rangle =-\\lim_{n\\to\\infty} \\langle T,\\partial_k \\varphi_n\\rangle =0.$$ 于是 $\\partial_k T\\in\\mathcal S'(\\mathbb R^n)$，这说明 $\\partial_k$ 是 $\\mathcal S'(\\mathbb R^n)$ 上的一个映射。\n证明线性性，对于任意 $T_1,T_2\\in \\mathcal S'(\\mathbb R^n)$ 和 $\\varphi\\in \\mathcal S(\\mathbb R^n)$，有 $$\\begin{darray}{ll}\\langle \\partial_k (T_1+T_2),\\varphi\\rangle \u0026=-\\langle T_1+T_2,\\partial_k \\varphi\\rangle \\\\[12pt]\u0026=-\\langle T_1,\\partial_k \\varphi\\rangle -\\langle T_2,\\partial_k \\varphi\\rangle \\\\[12pt]\u0026=\\langle \\partial_k T_1,\\varphi\\rangle +\\langle \\partial_k T_2,\\varphi\\rangle \\\\[12pt]\u0026=\\langle \\partial_k T_1+\\partial_k T_2,\\varphi\\rangle.\\end{darray}$$ 数乘是显然的，于是线性性得证。\n证明连续性，考虑任意 $T_n\\in \\mathcal S'(\\mathbb R^n)$ 是趋于 $0$ 的广义函数列，这等价于对于任意 $\\varphi\\in \\mathcal S(\\mathbb R^n)$，有 $$\\lim_{n\\to\\infty} \\langle T_n,\\varphi\\rangle =0.$$ 那么 $$\\lim_{n\\to\\infty} \\langle \\partial_k T_n,\\varphi\\rangle =\\lim_{n\\to\\infty} -\\langle T_n,\\partial_k \\varphi\\rangle =-\\lim_{n\\to\\infty} \\langle T_n,\\partial_k \\varphi\\rangle =0.$$ 因此 $\\partial_k T_n\\to 0$，$\\partial_k$ 是连续的。\nG.pp167.8(2) 试证下列函数作为广义函数弱收敛于 $\\delta(x)$：\n$\\dfrac 1\\pi \\dfrac {\\alpha}{\\alpha^2+x^2}$，当 $\\alpha\\to 0$ 时； 解： 作为广义函数 $\\frac 1\\pi \\frac {\\alpha}{\\alpha^2+x^2}\\in \\mathcal D'(\\mathbb R)$，这里认为弱收敛是弱 -$\\ast$ 收敛，即对于任意 $\\varphi\\in \\mathcal D(\\mathbb R)$，有 $$\\begin{darray}{ll}\\lim_{\\alpha\\to 0} \\left\\langle \\dfrac 1\\pi \\dfrac {\\alpha}{\\alpha^2+x^2},\\varphi\\right\\rangle \u0026=\\lim_{\\alpha\\to 0} \\int_{\\mathbb R} \\dfrac 1\\pi \\dfrac {\\alpha}{\\alpha^2+x^2}\\varphi(x)\\mathrm dx\\\\[12pt]\u0026=\\lim_{\\alpha\\to 0} \\mathrm{sgn}(\\alpha)\\int_{\\mathbb R} \\dfrac 1\\pi \\dfrac {1}{1+t^2}\\varphi(\\alpha t)\\mathrm dt\\\\[12pt]\u0026=\\int_{\\mathbb R}\\dfrac 1\\pi \\dfrac {1}{1+t^2}\\varphi(0)\\mathrm dt\\\\[12pt]\u0026=\\varphi(0)=\\langle \\delta(x),\\varphi\\rangle.\\end{darray}$$换序是由一致收敛保证的。\nExtra 证明 Fourier 变换是速降函数空间到自身的线性连续变换。\n解： 回顾 Fourier 变换的定义： $$F[f](\\xi) =\\dfrac 1{(2\\pi )^{d/2}}\\int_{\\mathbb{R}^d} e^{-ix\\cdot \\xi}f(x)\\mathrm dx.$$ 显然这是线性的。下面证明 $F:\\mathcal{S}(\\mathbb{R}^d)\\to \\mathcal{S}(\\mathbb{R}^d)$ 是连续变换。对于 $f\\in \\mathcal{S}(\\mathbb{R}^d)\\subseteq C^\\infty (\\mathbb{R}^d)$， $$\\lim_{|x|\\to \\infty} |x^\\alpha D^\\beta f(x)|=0, \\quad \\forall \\alpha, \\beta.$$ 因此，由速降函数的性质，以下积分对 $\\xi$ 是一致收敛的，所以可以交换微分和积分的次序： $$\\begin{darray}{ll}\\xi^\\alpha D^\\beta F[f](\\xi) \u0026= \\dfrac {\\xi^\\alpha}{(2\\pi )^{d/2}}D^\\beta \\int_{\\mathbb{R}^d} e^{-ix\\cdot \\xi}f(x)\\mathrm dx \\\\[12pt]\u0026=\\dfrac {(-i)^{|\\beta|}}{(2\\pi )^{d/2}}\\int_{\\mathbb{R}^d} e^{-ix\\cdot \\xi}\\xi^{\\alpha} x^{\\beta} f(x)\\mathrm dx\\\\[12pt]\u0026=\\dfrac {(-i)^{|\\beta|}}{(2\\pi )^{d/2}}\\int_{\\mathbb{R}^d} x^\\beta f(x)(-i)^{-\\alpha}D^\\alpha ( e^{-ix\\cdot \\xi})\\mathrm dx\\\\[12pt]\u0026=\\dfrac {(-i)^{|\\beta|+|\\alpha|}}{(2\\pi )^{d/2}}\\int_{\\mathbb{R}^d} D^\\alpha (x^{\\beta} f(x)) e^{-ix\\cdot \\xi}\\mathrm dx.\\end{darray}$$ 由 Riemann-Lebesgue 定理，因为 $D^\\alpha (x^{\\beta} f(x))\\in\\mathcal L^1_{}(\\mathbb{R}^d)$，所以对上述取模长，再取 $|\\xi|\\to \\infty$，得到 $$\\lim_{|\\xi|\\to \\infty} |\\xi^\\alpha D^\\beta F[f](\\xi)|=0.$$ 因此 $F[f]\\in \\mathcal{S}(\\mathbb{R}^d)$。由模长不等式，展开 $$\\begin{darray}{ll}|\\xi^\\alpha D^\\beta F[f](\\xi)|\u0026\\leq \\dfrac 1{(2\\pi )^{d/2}} \\sum_{\\gamma\\leq \\alpha} \\binom{\\alpha}{\\gamma} \\int_{\\mathbb{R}^d}|D^\\gamma x^\\beta D^{\\alpha-\\gamma} f(x)| \\mathrm dx\\\\[12pt]\u0026\\leq \\dfrac 1{(2\\pi )^{d/2}} \\sum_{\\gamma\\leq \\alpha} \\binom{\\alpha}{\\gamma} \\int_{\\mathbb R^d}\\dfrac {1}{1+|x|^2}(1+|x|^2)|D^\\gamma x^\\beta D^{\\alpha-\\gamma} f(x)| \\mathrm dx \\end{darray}$$ 进一步，根据 $\\mathcal S(\\mathbb R^n)$ 中的连续性定义，考虑任意趋于 $0$ 的函数列 $f_n\\to 0$，这显然推出上面的不等式右端关于 $n$ 是一致趋于 $0$ 的，所以 $$\\lim_{n\\to\\infty} \\max_{\\xi\\in \\mathbb{R}^d} |\\xi^\\alpha D^\\beta F[f_n](\\xi)|=0.\\quad \\forall \\alpha,\\beta.$$ 综上，我们证明了 Fourier 变换是 $\\mathcal{S}(\\mathbb{R}^d)$ 上的线性连续变换。\n","tags":["Partial Differential Equations","Homework"],"categories":["Partial Differential Equations"]},{"title":"复旦 2024 推免考试","permalink":"https://yukar.icu/posts/a/fdu24/","summary":"填空题 (1) 二次型矩阵为 $$\\begin{pmatrix}1\u0026t \u0026-1\\\\ t\u00264 \u00262 \\\\-1\u00262 \u00264\\end{pmatrix}\\sim \\begin{pmatrix}1\u00260 \u00260\\\\ 0\u00264-t^2 \u00262+t \\\\0\u00262+t \u00263\\end{pmatrix}\\sim \\begin{pmatrix}1\u00260 \u00260\\\\ 0\u00263 \u00260 \\\\ 0\u00260\u0026\\frac 43\\cdot (t+2)(1-t)\\end{pmatrix}$$ 所以当 $t\\in (-2,1)$ 时是正定的。\n(2) 求特征多项式 $$|\\lambda I-A|=(\\lambda -1)^2(\\lambda +5)+16(b+1)-8b(\\lambda +5)+8(\\lambda -1)$$(3) 可见 $A$ 满足多项式 $f(x)=x^2$，特征值只能为 $0$，$\\mathrm{rank}(A)=3$ 说明 $A$ 的 Jordan 标准型有 $6-3=3$ 个 $0$-Jordan 块，且每个块的大小不超过 $2$，所以 $A$ 的 Jordan 标准型为 $$\\begin{pmatrix}0\u00261\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00261\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00261\\\\ 0\u00260\u00260\u00260\u00260\u00260\\end{pmatrix}$$计算题 (1) $-x^2\\sin \\frac 1x+2\\int ^x_0t\\sin\\frac t1\\mathrm dt +C$，在原点为 $C$.\n(2) $-\\frac {\\pi^2}2\\ln 2$\n","content":"填空题 (1) 二次型矩阵为 $$\\begin{pmatrix}1\u0026t \u0026-1\\\\ t\u00264 \u00262 \\\\-1\u00262 \u00264\\end{pmatrix}\\sim \\begin{pmatrix}1\u00260 \u00260\\\\ 0\u00264-t^2 \u00262+t \\\\0\u00262+t \u00263\\end{pmatrix}\\sim \\begin{pmatrix}1\u00260 \u00260\\\\ 0\u00263 \u00260 \\\\ 0\u00260\u0026\\frac 43\\cdot (t+2)(1-t)\\end{pmatrix}$$ 所以当 $t\\in (-2,1)$ 时是正定的。\n(2) 求特征多项式 $$|\\lambda I-A|=(\\lambda -1)^2(\\lambda +5)+16(b+1)-8b(\\lambda +5)+8(\\lambda -1)$$(3) 可见 $A$ 满足多项式 $f(x)=x^2$，特征值只能为 $0$，$\\mathrm{rank}(A)=3$ 说明 $A$ 的 Jordan 标准型有 $6-3=3$ 个 $0$-Jordan 块，且每个块的大小不超过 $2$，所以 $A$ 的 Jordan 标准型为 $$\\begin{pmatrix}0\u00261\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00261\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00260\\\\ 0\u00260\u00260\u00260\u00260\u00261\\\\ 0\u00260\u00260\u00260\u00260\u00260\\end{pmatrix}$$计算题 (1) $-x^2\\sin \\frac 1x+2\\int ^x_0t\\sin\\frac t1\\mathrm dt +C$，在原点为 $C$.\n(2) $-\\frac {\\pi^2}2\\ln 2$\n(3) 考虑幂级数展开 $$-\\ln(1-x)=\\sum^\\infty_{n=1}\\frac {x^n}n=:\\sum^\\infty_{n=0}b_nx^n$$ 由于 $\\lim na_n=\\lim a_n/b_n=1$，所以对于任意 $\\varepsilon\u003e0$，存在 $N$ 使得当 $n\\geq N$ 时 $$(1-\\varepsilon)b_n\u003c a_n\u003c(1+\\varepsilon)b_n$$ 从而累加得到 $$(1-\\varepsilon)\\sum^\\infty_{n=N} b_nx^n \u003c \\sum^\\infty_{n=N} a_nx^n \u003c (1+\\varepsilon)\\sum^\\infty_{n=N} b_nx^n$$ 则估计 $\\sum a_nx^n$ 有 $$\\dfrac {\\sum^\\infty_{n=0} a_nx^n}{-\\ln(1-x)}=\\dfrac {\\sum^\\infty_{n=N} a_nx^n}{-\\ln (1-x)}+\\dfrac {\\sum^{N-1}_{n=0} a_nx^n}{-\\ln(1-x)}\\to \\dfrac {\\sum^\\infty_{n=N} a_nx^n}{-\\ln (1-x)}\\in [1-\\varepsilon,1+\\varepsilon]$$ 最后由双边控制得到。由 $\\varepsilon$ 的任意性，结果为 $-1$.\n解答题 (1) 考虑 Cauchy 不等式 $$\\int ^1_0(1+x^2)f^2(x)\\mathrm dx\\geq \\dfrac {\\left(\\int ^1_0 |f(x)|\\mathrm dx\\right)^2}{\\int ^1_0(1+x^2)^{-1}\\mathrm dx}\\leq \\dfrac 4\\pi \\left(\\int ^1_0 f(x)\\mathrm dx\\right)^2=\\frac 4\\pi$$ 此时取等，当且仅当 $$f(x)=\\frac {C}{1+x^2}=\\frac 4{\\pi (1+x^2)}$$ 几乎处处成立（而 $f(x)\\in C[0,1]$，所以必须完全相等），通过条件解得 $C$.\n(2) 考虑辅助函数 $$F(x)=e^{-2024x}[f(x)-f(0)]$$ 则其导数为 $$F'(x)=e^{-2024x}[f'(x)-2024f(x)+2024f(0)]$$ 因此题目是在证明存在 $\\xi \\in (0,2)$ 使得 $F'(\\xi)=0$. 首先 $$F(0)=0,\\quad F(1)=e^{-2024}[f(1)-f(0)],$$ 当 $f(1)=f(0)$ 时，Rolle 中值定理推出。当 $f(1)\u003ef(0)$，由于 $$F'(1)=-2024e^{-2024}[f(1)-f(0)]\u003c0$$ 所以连续函数 $F$ 的最大值在内部取到，在极值点处得证。当 $f(1)","tags":[],"categories":["FDU"]},{"title":"Homework 1","permalink":"https://yukar.icu/posts/mathematicalanalysis/pinhomework/1/","summary":"参考资料：《数学分析之课程讲义》，于品。\nA A1) B (1) $\\dfrac 12$,\n(2) $\\dfrac 12$,\n(3) $1$,\n","content":"参考资料：《数学分析之课程讲义》，于品。\nA A1) B (1) $\\dfrac 12$,\n(2) $\\dfrac 12$,\n(3) $1$,\n","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"Homework 3","permalink":"https://yukar.icu/posts/mathematicalanalysis/pinhomework/3/","summary":"参考资料：《数学分析之课程讲义》，于品。\nA A1 先证明必要性，存在极限，则 $$|f(x_1)-f(x_2)|\\leq |f(x_1)-\\lim_{x\\to x_0}f(x)|+|\\lim_{x\\to x_0}f(x)-f(x_2)|$$ 再证明充分性，反证，假设不存在极限，则存在 $\\varepsilon\u003e0$ 和两个数列 $x_n,y_n$ 都趋于 $x_0$，使得 $|f(x_n)-f(y_n)|\\geq \\varepsilon$，矛盾。\nA2 构造函数列 $\\chi_{[10n,10n+1]}* \\rho$，其中 $\\rho$ 是 Friedrich 磨光核。这是 $\\mathbb R$-线性无关集。\nA3 对任意 $x_n\\to x_0$，有 $f(x_n)\\to f(x_0)$，从而 $g\\circ f(x_n)\\to g\\circ f(x_0)$，所以 $g\\circ f$ 在 $x_0$ 处连续。\nA4 对任意 $x_n\\xrightarrow{d_X}x_0$，有正实数 $c_1,c_2$ 使得 $$c_1d_X(x_n,x_0)\\leq d'_X(x_n,x_0)\\leq c_2d_X(x_n,x_0)$$ 所以极限性质是等价的。\nA5 在 $\\mathbb R^n$ 上的 $L^p$ 范数等价。\nA6 考虑 $$\\|[f\\pm g](x)-[f\\pm g](x_0)\\|\\leq \\|f(x)-f(x_0)\\|+\\|g(x)-g(x_0)\\|$$ 从而证明第一个命题。考虑 $$\\|[f\\cdot g](x)-[f\\cdot g](x_0)\\|\\leq \\|f\\|\\|g(x)-g(x_0)\\|\\leq \\|f\\|\\|g\\|\\|x-x_0\\|$$ 从而证明第二个命题。考虑 ","content":"参考资料：《数学分析之课程讲义》，于品。\nA A1 先证明必要性，存在极限，则 $$|f(x_1)-f(x_2)|\\leq |f(x_1)-\\lim_{x\\to x_0}f(x)|+|\\lim_{x\\to x_0}f(x)-f(x_2)|$$ 再证明充分性，反证，假设不存在极限，则存在 $\\varepsilon\u003e0$ 和两个数列 $x_n,y_n$ 都趋于 $x_0$，使得 $|f(x_n)-f(y_n)|\\geq \\varepsilon$，矛盾。\nA2 构造函数列 $\\chi_{[10n,10n+1]}* \\rho$，其中 $\\rho$ 是 Friedrich 磨光核。这是 $\\mathbb R$-线性无关集。\nA3 对任意 $x_n\\to x_0$，有 $f(x_n)\\to f(x_0)$，从而 $g\\circ f(x_n)\\to g\\circ f(x_0)$，所以 $g\\circ f$ 在 $x_0$ 处连续。\nA4 对任意 $x_n\\xrightarrow{d_X}x_0$，有正实数 $c_1,c_2$ 使得 $$c_1d_X(x_n,x_0)\\leq d'_X(x_n,x_0)\\leq c_2d_X(x_n,x_0)$$ 所以极限性质是等价的。\nA5 在 $\\mathbb R^n$ 上的 $L^p$ 范数等价。\nA6 考虑 $$\\|[f\\pm g](x)-[f\\pm g](x_0)\\|\\leq \\|f(x)-f(x_0)\\|+\\|g(x)-g(x_0)\\|$$ 从而证明第一个命题。考虑 $$\\|[f\\cdot g](x)-[f\\cdot g](x_0)\\|\\leq \\|f\\|\\|g(x)-g(x_0)\\|\\leq \\|f\\|\\|g\\|\\|x-x_0\\|$$ 从而证明第二个命题。考虑 $$\\left|\\dfrac {f(x_0)}{g(x_0)}-\\dfrac {f(x)}{g(x)}\\right|\\leq \\left|\\dfrac 1{g(x_0)}\\right|\\left|f(x_0)-f(x)\\right|+\\left|\\dfrac {f(x)}{g(x)g(x_0)}\\right|\\left|g(x)-g(x_0)\\right|$$ 从而证明第三个命题。\nA7 首先 $f(0)=1$，对于非零有理数 $\\frac pq$，根据 Dirichlet 原理，总是可以找到 $\\frac rs$ 使得 $$|f(\\tfrac pq)-f(\\tfrac rs)|\\geq \\tfrac 1{2q}$$ 所以 $f$ 在有理数处不连续。对于无理点，考虑到有理数分母的间隙，因而是连续的。\nA8 根据定义 $$\\lim_{x\\to 0}\\dfrac {e^x-1}x=\\lim_{x\\to 0}\\dfrac 1x\\cdot \\sum^\\infty_{n=1}\\dfrac {x^n}{n!}=1+\\lim_{x\\to 0}\\sum^\\infty_{n=2}\\dfrac {x^{n-1}}{n!}=1$$ 这是因为 $$\\lim_{x\\to 0}\\left|\\sum^\\infty_{n=2}\\dfrac {x^{n-1}}{n!}\\right|\\leq \\lim_{x\\to 0}\\sum^\\infty_{n=2}\\dfrac {|x|^{n-1}}{(n-1)!}=0$$A9 考虑任意 $x\\geq 2$，有 $$\\left(1+\\dfrac 1{[x]+1}\\right)^{[x]}\\leq \\left(1+\\dfrac 1x\\right)^{[x]}\\leq \\left(1+\\dfrac 1x\\right)^x\\leq \\left(1+\\dfrac 1x\\right)^{[x]+1}\\leq \\left(1+\\dfrac 1{[x]}\\right)^{[x]+1}$$ 左右的极限是存在的，取双边控制即可，得到 $e$.\nA10 考虑 $$\\lim_{x\\to -\\infty}\\left(1+\\dfrac 1x\\right)^x=\\lim_{t\\to \\infty}\\left(1-\\dfrac 1t\\right)^{-t}=\\lim_{t\\to \\infty}\\left(1+\\dfrac 1{t-1}\\right)^t=e$$B B1 (1) $1$.\n(2) $\\dfrac 12$.\n(3) $\\dfrac 14$.\n(4) 考虑到 $\\tan$ 的公式，尝试裂项相消 $$\\tan(\\arctan \\alpha -\\arctan \\beta)=\\dfrac {\\alpha-\\beta}{1+\\alpha\\beta}$$ 所以 $$\\arctan\\dfrac {(n+1)-n}{1+(n+1)n}=\\arctan \\dfrac 1{n^2+n+1}=\\arctan (n+1)-\\arctan n$$ 从而得到 $\\arctan 2$.\n(5) $\\dfrac {15}4$.\n(6) $\\dfrac 34$.\n(7) $\\dfrac 23$.\n(8) $3$.\n(9) $1$.\n(10) $1-\\sqrt 2$.\n(11) $\\log 2$，严格来写，应该需要计算部分和的极限。\n(12) $\\dfrac 1m\\displaystyle \\sum^m_{n=1}\\dfrac 1n$.\nB2 (1) 发散，考虑 $\\displaystyle \\sum n^{-1/2}$.\n(2) 收敛，考虑 $\\displaystyle \\sum n^{-3/2}$.\n(3) 收敛，考虑 $\\sqrt [n]n-1\\sim n^{-1}{\\log n}=O(n^{-1/2})$（根式判别也可以）。\n(4) 当 $|x|\\leq 1$ 时，发散。当 $|x|\u003e1$ 时，收敛，几何级数。\n(5) 收敛。几何级数。\n(6) 收敛，根式判别。\n(7) 发散，考虑 $\\displaystyle \\sum (2n)^{-1}$.\n(8) 收敛，实际上等价于分析 $n^{-\\log\\log n}$，考虑 $\\displaystyle \\sum n^{-2}$.\n(9) 发散，通项的极限为 $1$.\n(10) 条件收敛，考虑 Dirichlet 判别法。\n(11) 发散，通项极限不为 $0$.\n(12) 收敛。由于调和级数的渐进性质 $$\\sum^n_{k=1}\\dfrac 1k=\\log n+\\gamma+o(1),\\quad n\\to \\infty$$ 此时通项可以写为 $$A_n=\\dfrac {\\log n+\\gamma}n\\cdot \\sin nx+ o(1)\\cdot \\dfrac {\\sin nx}n$$ 第一项应用 Dirichlet 判别法，第二项先对 $\\{\\frac {\\sin nx}n\\}$ 应用 Dirichlet 判别法，说明求和收敛，再用 Abel 判别法说明 $\\{o(1)\\cdot \\frac {\\sin nx}n\\}$ 的求和也收敛。这对于所有的 $x\\in\\mathbb R$ 都收敛。但是至少对 $x=\\pi/2$，而言级数不是绝对收敛的。\nB3 (1) 条件收敛。由 Dirichlet 判别法说明收敛，积分判别法说明不绝对收敛。\n(2) 条件收敛。由 Dirichlet 判别法说明收敛，和调和级数比较说明不绝对收敛。\n(3) 条件收敛。\n(4) 绝对收敛。\nC C2 注意到等价的不等式 $$(b-1)b^{k-1}f(b^k)\\leq \\sum^{b^k-1}_{j=b^{k-1}}f(j)\\leq (b-1)b^{k-1}f(b^{k-1})\\leq b\\sum^{b^{k-1}-1}_{j=b^{k-2}}f(j)$$ 将最右边的系数 $b$ 看作常数倍数，应用比较判别即可。\nC3 取 $b=2$，就得到 $$\\dfrac 1{2k\\log 2}\\leq 2^{k-1}f(2^k)\\leq \\sum^{2^k-1}_{j=2^{k-1}}f(j)$$C4 取 $b=100$，就得到 $$\\dfrac 1{k\\log k}\\sim \\dfrac {99}{100}\\dfrac 1{k\\log 100}\\dfrac 1{\\log k+\\log \\log 100}\\leq \\sum^{100^k-1}_{j=100^{k-1}}f(j)$$C5 取 $b=2$，就得到 $$\\dfrac 1{2^{k(s-1)+1}}\\leq \\sum^{2^k-1}_{j=2^{k-1}}f(j)\\leq \\dfrac 1{2^{(k-1)(s-1)}}$$ 和几何级数比较。\nC6 同 C3，取 $b=2$ $$\\dfrac 1{2k^s\\log^s2}\\leq \\sum^{2^k-1}_{j=2^{k-1}}f(j)\\leq \\dfrac 1{(k-1)^s\\log^s 2}$$ 另一个也同理。\nD D3 对于无穷集，如果是有界的，则应用 Bolzano-Weierstrass 定理得到一个收敛子列，从而得到有界聚点。如果是无界的，则可以找到一个趋于无穷的数列，从而得到无穷远点。\nD5 由于对称性，只证明上确界情形。由上确界的定义，存在可重的聚点序列 $\\alpha_n\\to \\sup E$ 满足 $$|\\alpha_n-\\sup E|\u003c\\dfrac 1n$$ 根据聚点的定义，可以对应选取 $\\{a_n\\}$ 的子列满足 $$|\\alpha_n-a_{k_n}|\u003c\\dfrac 1n$$ 于是 $$\\limsup_{n\\to\\infty}a_n\\geq \\limsup_{k\\to\\infty}a_{n_k}=\\lim_{k\\to\\infty}a_{n_k}=\\sup E$$ 根据上极限的定义，左端属于 $E$，所以严格取等。\nD6 D5 已经构造了这样的子列。这相当于在说，给定间隙 $A :=(x+a^*)/2$，集合 $\\{a_n\\}\\cap (A,\\infty)$ 是有限集。否则上述集合存在聚点 $\\alpha$，满足 $\\alpha \\geq A\\geq a^*$，矛盾。\nD7 按照对角线排列的 $\\mathbb Q$，无穷远点也是它的聚点。\nD8 同上。\nE 考虑部分和 $$1+\\sum_{p\\in\\mathcal P, p\\leq n}p^{-s}\\leq \\prod_{p\\in\\mathcal P, p\\leq n}\\left(1+p^{-s}\\right)\\leq \\zeta(s)$$ 则 $s\u003e1$ 时，代求级数部分和是有界单调的，所以收敛。当 $0","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"Homework 3","permalink":"https://yukar.icu/posts/mathematicalanalysis/pinhomework/4/","summary":"参考资料：《数学分析之课程讲义》，于品。\nA B C D (1) 当 $n=m$ 时，结果为 $a_0/b_0$；当 $m\u003en$ 时，结果为 $0$；当 $m","content":"参考资料：《数学分析之课程讲义》，于品。\nA B C D (1) 当 $n=m$ 时，结果为 $a_0/b_0$；当 $m\u003en$ 时，结果为 $0$；当 $m","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"Homework 3","permalink":"https://yukar.icu/posts/mathematicalanalysis/pinhomework/5/","summary":"参考资料：《数学分析之课程讲义》，于品。\nA A1 考虑当 $x\u003ey$ 时 $$|e^x-e^y|=e^y|e^{x-y}-1|\\geq e^y(x-y)$$ 所以在 $\\mathbb R$ 上不是一致连续的。而对于足够小的 $\\delta$，当 $|x-y|\u003c\\delta$ 时 $$|e^x-e^y|=e^y|e^{x-y}-1|\\leq e^y\\cdot 2\\delta\\leq 2\\delta$$ 在 $(-\\infty,0]$ 上是一致连续的。\nB B1 有估计 $$\\sqrt [3]{x+\\varepsilon}-\\sqrt [3]{x}=\\frac {\\varepsilon}{\\sqrt [3]{(x+\\varepsilon)^2}+\\sqrt [3]{(x+\\varepsilon)x}+\\sqrt [3]{x^2}}\\leq \\varepsilon^{\\frac 13}.$$ 从而一致连续。\nB2 考虑 $$\\log \\frac 2n-\\log \\frac 1n=\\log 2$$ 但 $\\frac 2n-\\frac 1n\\to 0$，所以不一致连续。\nB3 考虑 $$\\cos \\frac 1{\\frac {1}{2n\\pi +\\frac \\pi 2}}-\\cos \\frac 1{\\frac {1}{2n\\pi}}=-1$$ 但 $\\frac {1}{2n\\pi +\\frac \\pi 2}-\\frac {1}{2n\\pi}\\to 0$，所以不一致连续。\nB4 分拆成两个区间 $(0,1]\\cup [\\frac 12,+\\infty)$，对于 $(0,1]$，考虑补充定义 $f(0)=0$，则由 Cantor 定理，$f$ 在 $[0,1]$ 上一致连续；对于 $[\\frac 12,+\\infty)$，求导即可，或者 ","content":"参考资料：《数学分析之课程讲义》，于品。\nA A1 考虑当 $x\u003ey$ 时 $$|e^x-e^y|=e^y|e^{x-y}-1|\\geq e^y(x-y)$$ 所以在 $\\mathbb R$ 上不是一致连续的。而对于足够小的 $\\delta$，当 $|x-y|\u003c\\delta$ 时 $$|e^x-e^y|=e^y|e^{x-y}-1|\\leq e^y\\cdot 2\\delta\\leq 2\\delta$$ 在 $(-\\infty,0]$ 上是一致连续的。\nB B1 有估计 $$\\sqrt [3]{x+\\varepsilon}-\\sqrt [3]{x}=\\frac {\\varepsilon}{\\sqrt [3]{(x+\\varepsilon)^2}+\\sqrt [3]{(x+\\varepsilon)x}+\\sqrt [3]{x^2}}\\leq \\varepsilon^{\\frac 13}.$$ 从而一致连续。\nB2 考虑 $$\\log \\frac 2n-\\log \\frac 1n=\\log 2$$ 但 $\\frac 2n-\\frac 1n\\to 0$，所以不一致连续。\nB3 考虑 $$\\cos \\frac 1{\\frac {1}{2n\\pi +\\frac \\pi 2}}-\\cos \\frac 1{\\frac {1}{2n\\pi}}=-1$$ 但 $\\frac {1}{2n\\pi +\\frac \\pi 2}-\\frac {1}{2n\\pi}\\to 0$，所以不一致连续。\nB4 分拆成两个区间 $(0,1]\\cup [\\frac 12,+\\infty)$，对于 $(0,1]$，考虑补充定义 $f(0)=0$，则由 Cantor 定理，$f$ 在 $[0,1]$ 上一致连续；对于 $[\\frac 12,+\\infty)$，求导即可，或者 $$(x+\\varepsilon)\\cos\\dfrac 1{x+\\varepsilon}-x\\cos \\dfrac 1x\\leq (x+\\varepsilon)-x\\cos\\dfrac 1x\\leq \\varepsilon +x\\left(1-\\cos \\dfrac 1x\\right)\\leq \\varepsilon +2x\\sin^2\\dfrac 1{2x}\\to \\varepsilon$$C C1 $$\\lim_{\\varepsilon\\to 0}\\dfrac {\\ln (1+\\varepsilon)}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\dfrac {\\varepsilon}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\varepsilon^{1-\\alpha}$$C2 $$\\lim_{\\varepsilon\\to 0}\\dfrac {e(e^\\varepsilon -1)}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\dfrac {e\\varepsilon}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}e\\varepsilon^{1-\\alpha}$$C3 $$\\lim_{\\varepsilon\\to 0}\\dfrac {(1+\\varepsilon)^{1+\\varepsilon}-1}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\dfrac {e^{(1+\\varepsilon)\\ln (1+\\varepsilon)}-1}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\dfrac {(1+\\varepsilon)\\ln (1+\\varepsilon)}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\dfrac {(1+\\varepsilon)\\varepsilon}{\\varepsilon^\\alpha}=\\lim_{\\varepsilon\\to 0}\\varepsilon^{1-\\alpha}$$","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"偏微分方程 习题 10","permalink":"https://yukar.icu/posts/partialdifferentialequations/homework/10/","summary":"参考资料：《数学物理方程讲义》，姜礼尚。\npp160.4 应用 Fourier 变换求解以下定解问题：\n$\\begin{cases}u_t-a^2u_{xx}-bu_x-cu=f(x,t),\u0026x\\in\\mathbb R,t\u003e0,\\$$14pt] u(x,0)=\\varphi(x),\u0026x\\in\\mathbb R.\\end{cases}$ 其中 $a,b,c$ 是常数；\n$\\begin{cases}u_{xx}+u_{yy}=0,\u0026x\\in\\mathbb R,y\u003e0,\\\\[14pt] u(x,0)=\\varphi(x),\u0026x\\in\\mathbb R.\\end{cases}$ 设 $\\varphi(x)$ 连续有界，求问题的有界解。\n解： 求解是在速降空间 $S(\\mathbb R)$ 中进行的。\n(1) 应用 Fourier 变换，得到 [\\begin{darray}{ll}Ff\u0026amp;=Fu_t-a^2Fu_{xx}-bFu_x-cFu\\[14pt] \u0026amp;=\\frac{\\mathrm d}{\\mathrm dt}Fu+a^2\\xi^2Fu-ib\\xi Fu-cFu\\[14pt] \u0026amp;=\\frac{\\mathrm d}{\\mathrm dt}Fu+\\bigl(a^2\\xi^2-ib\\xi-c\\bigr)Fu.\\end{darray}\n$$ 将 $F[u](\\xi)$ 看作 $t$ 的函数（$\\xi$ 为参数）$F[u](\\xi)=F[u](\\xi,t)$，得到一个常系数线性微分方程，解为（Duhamel 原理） $$Fu=e^{-(a^2\\xi^2-ib\\xi-c)t}\\left(F\\varphi+\\int^t_0e^{(a^2\\xi^2-ib\\xi-c)\\tau}Ff\\mathrm d\\tau\\right).\n$$ 再应用 Fourier 反演，得到 $$u(x,t)=F^{-1}[e^{-(a^2\\xi^2-ib\\xi-c)t}F\\varphi](x)+F^{-1}\\left[\\int^t_0e^{-(a^2\\xi^2-ib\\xi-c)(t-\\tau)}Ff\\mathrm d\\tau\\right](x).\n$$ 首先求解热核 $$F^{-1}e^{-(a^2\\xi^2-ib\\xi-c)t}=\\frac 1{\\sqrt {2\\pi}}\\int^\\infty_{-\\infty}e^{-(a^2\\xi^2-ib\\xi-c)t}e^{ix\\xi}\\mathrm d\\xi=\\frac {e^{-\\frac {(x+bt)^2}{4a^2t}+ct}}{a\\sqrt {2t}}:=G(x,t).\n$$ 由卷积公式 $$F^{-1}\\hat G\\cdot \\hat \\varphi=G*\\varphi(x)=\\int^\\infty_{-\\infty}G(x-y,t)\\varphi(y)\\mathrm dy=\\int^\\infty_{-\\infty}\\frac {e^{ct}}{a\\sqrt {2t}}e^{-\\frac {(x-y+bt)^2}{4a^2t}}\\varphi(y)\\mathrm dy.\n","content":"参考资料：《数学物理方程讲义》，姜礼尚。\npp160.4 应用 Fourier 变换求解以下定解问题：\n$\\begin{cases}u_t-a^2u_{xx}-bu_x-cu=f(x,t),\u0026x\\in\\mathbb R,t\u003e0,\\$$14pt] u(x,0)=\\varphi(x),\u0026x\\in\\mathbb R.\\end{cases}$ 其中 $a,b,c$ 是常数；\n$\\begin{cases}u_{xx}+u_{yy}=0,\u0026x\\in\\mathbb R,y\u003e0,\\\\[14pt] u(x,0)=\\varphi(x),\u0026x\\in\\mathbb R.\\end{cases}$ 设 $\\varphi(x)$ 连续有界，求问题的有界解。\n解： 求解是在速降空间 $S(\\mathbb R)$ 中进行的。\n(1) 应用 Fourier 变换，得到 [\\begin{darray}{ll}Ff\u0026amp;=Fu_t-a^2Fu_{xx}-bFu_x-cFu\\[14pt] \u0026amp;=\\frac{\\mathrm d}{\\mathrm dt}Fu+a^2\\xi^2Fu-ib\\xi Fu-cFu\\[14pt] \u0026amp;=\\frac{\\mathrm d}{\\mathrm dt}Fu+\\bigl(a^2\\xi^2-ib\\xi-c\\bigr)Fu.\\end{darray}\n$$ 将 $F[u](\\xi)$ 看作 $t$ 的函数（$\\xi$ 为参数）$F[u](\\xi)=F[u](\\xi,t)$，得到一个常系数线性微分方程，解为（Duhamel 原理） $$Fu=e^{-(a^2\\xi^2-ib\\xi-c)t}\\left(F\\varphi+\\int^t_0e^{(a^2\\xi^2-ib\\xi-c)\\tau}Ff\\mathrm d\\tau\\right).\n$$ 再应用 Fourier 反演，得到 $$u(x,t)=F^{-1}[e^{-(a^2\\xi^2-ib\\xi-c)t}F\\varphi](x)+F^{-1}\\left[\\int^t_0e^{-(a^2\\xi^2-ib\\xi-c)(t-\\tau)}Ff\\mathrm d\\tau\\right](x).\n$$ 首先求解热核 $$F^{-1}e^{-(a^2\\xi^2-ib\\xi-c)t}=\\frac 1{\\sqrt {2\\pi}}\\int^\\infty_{-\\infty}e^{-(a^2\\xi^2-ib\\xi-c)t}e^{ix\\xi}\\mathrm d\\xi=\\frac {e^{-\\frac {(x+bt)^2}{4a^2t}+ct}}{a\\sqrt {2t}}:=G(x,t).\n$$ 由卷积公式 $$F^{-1}\\hat G\\cdot \\hat \\varphi=G*\\varphi(x)=\\int^\\infty_{-\\infty}G(x-y,t)\\varphi(y)\\mathrm dy=\\int^\\infty_{-\\infty}\\frac {e^{ct}}{a\\sqrt {2t}}e^{-\\frac {(x-y+bt)^2}{4a^2t}}\\varphi(y)\\mathrm dy.\n$$ 由于反演公式的线性性，第二项与 $t$ 的积分可以交换，得到 $$\\begin{darray}{ll}\u0026amp;\\displaystyle F^{-1}\\left[\\int^t_0e^{-(a^2\\xi^2-ib\\xi-c)(t-\\tau)}Ff\\mathrm d\\tau\\right](x)\\[14pt] =\u0026amp;\\displaystyle\\int^t_0F^{-1}[\\hat G(x,t-\\tau)Ff](x)\\mathrm d\\tau\\[14pt] =\u0026amp;\\displaystyle \\int^t_0\\int^\\infty_{-\\infty}G(x-y,t-\\tau)f(y,\\tau)\\mathrm dy\\mathrm d\\tau\\[14pt] =\u0026amp;\\displaystyle\\int^t_0\\int^\\infty_{-\\infty}\\frac {e^{c(t-\\tau)}}{a\\sqrt {2(t-\\tau)}}e^{-\\frac {(x-y+ b(t-\\tau))^2}{4a^2(t-\\tau)}}f(y,\\tau)\\mathrm dy\\mathrm d\\tau.\\end{darray}\n$$ 综上所述，问题的解为 $$u(x,t)=\\int^\\infty_{-\\infty}\\frac {e^{ct}}{a\\sqrt {2t}}e^{-\\frac {(x-y+bt)^2}{4a^2t}}\\varphi(y)\\mathrm dy+\\int^t_0\\int^\\infty_{-\\infty}\\frac {e^{c(t-\\tau)}}{a\\sqrt {2(t-\\tau)}}e^{-\\frac {(x-y+ b(t-\\tau))^2}{4a^2(t-\\tau)}}f(y,\\tau)\\mathrm dy\\mathrm d\\tau.$$\n(2) 这里并不能看作波动方程（相差一个负号）。于是这里考虑 Fourier 变换，以 $y$ 为主元，$x$ 为参数，得到 $$F[u_{xx}+u_{yy}](x,\\xi)=F[u_{xx}](x,\\xi)+F[u_{yy}](x,\\xi)=-\\xi^2F[u](x,\\xi)+\\frac{\\mathrm d^2}{\\mathrm dx^2}F[u](x,\\xi).$$ 于是 $F[u](x,\\xi)$ 满足常系数二阶线性微分方程，外力项为零，解为（这里取特征根为 $\\pm|\\xi|$ 是为了保证解的有界性） $$F[u](x,\\xi)=A(\\xi)e^{|\\xi|x}+B(\\xi)e^{-|\\xi|x}.$$ 由于 $\\varphi(x)$ 连续有界，所以 $$\\varphi(x)=F^{-1}[F[u](x,\\xi)](0)=\\frac 1{\\sqrt {2\\pi}}\\int^\\infty_{-\\infty}[A(\\xi)e^{|\\xi|x}+B(\\xi)e^{-|\\xi|x}]e^{i\\xi y}\\mathrm d\\xi.$$ 考虑 $x\\to\\infty$，由有界性推出 $A(\\xi)=0$。于是 $$F[u](x,\\xi)=B(\\xi)e^{-|\\xi|x}=F[u](0,\\xi)e^{-|\\xi|x}=F[\\varphi](\\xi)e^{-|\\xi|x}.$$ 上述过程还对边界条件 $y=0$ 进行了 Fourier 变换。反演需要考虑 Poisson 核 $$\\begin{darray}{ll}F^{-1}[e^{-|\\xi|x}](y)\u0026=\\displaystyle \\frac 1{\\sqrt {2\\pi}}\\int^\\infty_{-\\infty}e^{-|\\xi|x}e^{i\\xi y}\\mathrm d\\xi\\\\[14pt]\u0026=\\displaystyle \\frac 1{\\sqrt {2\\pi}}\\int^\\infty_{-\\infty}e^{-|\\xi|x}\\cos(\\xi y)\\mathrm d\\xi\\\\[14pt]\u0026=\\displaystyle \\sqrt{\\frac 2\\pi}\\int^\\infty_0e^{-\\xi x}\\cos(\\xi y)\\mathrm d\\xi\\\\[14pt]\u0026=\\displaystyle \\sqrt{\\frac 2\\pi}\\frac x{x^2+y^2}=\\displaystyle P(x,y).\\end{darray}$$ 则 $$u(x,y)=F^{-1}[\\hat \\varphi \\cdot \\hat P](y)=\\varphi *P(x,y)=\\int^\\infty_{-\\infty}\\frac x{\\pi(x^2+(y-\\xi)^2)}\\varphi(\\xi)\\mathrm d\\xi.$$pp160.8(2)(3) 利用函数 $$\\Phi(z)=\\frac 2{\\sqrt \\pi}\\int^z_0e^{-\\xi^2}\\mathrm d\\xi$$ 求解以下半无界问题;\n$\\begin{cases}u_t-a^2u_{xx}=0,\u0026x\u003e0,t\u003e0,\\$$14pt] u(x,0)=\\begin{cases}U_0,\u00260\\leq x\\leq 1,\\\\ 0,\u0026x\u003e1,\\end{cases}\u0026\\\\[20pt] u_x(0,t)=0,\u0026t\u003e0.\\end{cases}$\n$\\begin{cases}u_t-a^2u_{xx}=0,\u0026x\u003e0,t\u003e0,\\\\[14pt] u(x,0)=U_0\u0026 x\\geq 0\\\\[14pt] (-u_x+hu)|_{x=0}=0,\u0026t\u003e0.\\end{cases}$\n在上述各题中 $U_0,q,h$ 是常数，且 $h\u003e0$.\n解： (2) 这是半无界问题，首先考虑偶延拓，得到延拓问题 [\\begin{cases}u_t-a^2u_{xx}=0,\u0026amp;x\\in\\mathbb R,t\u0026gt;0,\\[14pt] u(x,0)=\\begin{cases}U_0,\u0026amp;|x|\\leq 1,\\ 0,\u0026amp;|x|\u0026gt;1.\\end{cases}\u0026amp;\\end{cases}\n$$ 此时再应用 Fourier 变换，得到 $$\\dfrac {\\mathrm d}{\\mathrm dt}Fu+a^2\\xi^2Fu=0,\n$$ 结合初值条件，得到 $$\\begin{darray}{ll}Fu\u0026amp;=Fue^{-a^2\\xi^2t}\\[14pt]\u0026amp;\\displaystyle =\\dfrac 1{\\sqrt {2\\pi}}\\int_{\\mathbb R}u(x,0)e^{-i\\xi x}\\mathrm dx\\cdot e^{-a^2\\xi^2t}\\[14pt]\u0026amp;=\\displaystyle\\dfrac 1{\\sqrt {2\\pi}}\\int^1_{-1}U_0e^{-i\\xi x}\\mathrm dx\\cdot e^{-a^2\\xi^2t}\\[14pt]\u0026amp;=\\displaystyle\\dfrac{2U_0}{\\sqrt {2\\pi}}\\frac{\\sin\\xi}{\\xi}e^{-a^2\\xi^2t}.\\end{darray}\n$$ 此时再应用 Fourier 反演，得到 $$u(x,t)=\\dfrac {U_0}{\\pi}\\int ^\\infty_{-\\infty}\\frac{\\sin\\xi}{\\xi}e^{-a^2\\xi^2t}e^{i\\xi x}\\mathrm d\\xi=\\displaystyle\\dfrac{U_0}{\\pi}\\int^\\infty_{0}\\frac{\\sin(\\xi(1+x))+\\sin(\\xi(1-x))}{\\xi}e^{-a^2\\xi^2t}\\mathrm d\\xi.\n$$ 这只需要算以下函数 $$I(k)=\\int^\\infty_0\\frac {\\sin k\\xi}\\xi e^{-a^2\\xi^2t}\\mathrm d\\xi.\n$$ 这是一致有界（不论 $I(k)$ 还是 $I'(k)$）的，所以可以交换积分与求导，得到 Gauss 积分 $$I\u0026rsquo;(k)=\\int^\\infty_0\\cos(k\\xi)e^{-a^2\\xi^2t}\\mathrm d\\xi=\\frac{\\sqrt \\pi}{2a\\sqrt t}e^{-\\frac {k^2}{4a^2t}}.\n$$ 再积分得到 $$I(k)=\\int^k_0I\u0026rsquo;(s)\\mathrm ds=\\frac{\\sqrt \\pi}{2a\\sqrt t}\\int^k_0e^{-\\frac {s^2}{4a^2t}}\\mathrm ds=\\dfrac \\pi 2\\Phi\\left(\\frac k{2a\\sqrt t}\\right).\n$$ 综上所述，问题的解为 $$u(x,t)=\\frac{U_0}{2}\\left[\\Phi\\left(\\frac{1+x}{2a\\sqrt t}\\right)+\\Phi\\left(\\frac{1-x}{2a\\sqrt t}\\right)\\right].$$ 限制在 $x\u003e0$ 上，得到问题的解。\n(3) 对于混合边界条件，换元 $v(x,t)=e^{-hx}u(x,t)$ 会破坏对称性。因此考虑如下换元 $$v(x,t)=u_x(x,t)-hu(x,t).$$ 则 $v$ 满足热传导方程（这里用到了 $(\\partial_x-h)$ 与 $(\\partial_t-a^2\\partial^2_x)$ 交换） $$\\begin{cases}v_t-a^2v_{xx}=0,\u0026x\u003e0,t\u003e0,\\\\[14pt] v(x,0)=-hU_0,\u0026x\u003e0,\\\\[14pt] v(0,t)=0,\u0026t\u003e0.\\end{cases}$$ 此时可以用奇延拓的方法，得到延拓问题 $$\\begin{cases}v_t-a^2v_{xx}=0,\u0026x\\in\\mathbb R,t\u003e0,\\\\[14pt] v(x,0)=\\begin{cases}-hU_0,\u0026x\u003e0,\\\\ hU_0,\u0026x\u003c0,\\end{cases}\u0026\\end{cases}$$ 应用 Fourier 变换，得到 $$F[v](\\xi,t)=F[v](\\xi,0)e^{-a^2\\xi^2t}=\\dfrac {1}{\\sqrt {2\\pi}}\\int_{\\mathbb R}v(x,0)e^{-i\\xi x}\\mathrm dx\\cdot e^{-a^2\\xi^2t}=\\dfrac {2ihU_0}{\\sqrt {2\\pi}}\\int^\\infty_0\\sin(\\xi x)\\mathrm dx\\cdot e^{-a^2\\xi^2t}$$ 这里考虑如下因子，由于一致收敛，可以交换积分与极限，得到 $$\\int^\\infty_0\\sin (\\xi x)\\mathrm dx=\\lim_{\\varepsilon \\to 0^+}\\int^\\infty_0e^{-\\varepsilon x}\\sin(\\xi x)\\mathrm dx=\\lim_{\\varepsilon \\to 0^+}\\frac{\\xi}{\\varepsilon^2+\\xi^2}=\\frac 1\\xi.$$ 所以 $$F[v](\\xi,t)=\\dfrac{2ihU_0}{\\sqrt {2\\pi}}\\frac 1\\xi e^{-a^2\\xi^2t}.$$ 再应用 Fourier 反演，得到 $$v(x,t)=-\\dfrac {2hU_0}{\\pi}\\int^\\infty_0\\frac {\\sin \\xi x}\\xi e^{-a^2\\xi^2t}\\mathrm d\\xi=-hU_0\\Phi\\left(\\frac x{2a\\sqrt t}\\right).$$ 由换元关系，得到 $$\\lim_{s\\to\\infty}e^{-hs}u(s,t)-e^{-hx}u(x,t)=-hU_0\\int^\\infty_x\\Phi\\left(\\frac{\\xi}{2a\\sqrt t}\\right)e^{-h\\xi}\\mathrm d\\xi.$$ 由于 $u(x,0)=U_0$ 有界，所以 $$u(s,t)=U_0+\\int^t_0a^2u_{xx}(s,\\tau)\\mathrm d\\tau=U_0+\\int^t_0a^2[v_x(s,\\tau)+hv(s,\\tau)+h^2u(s,\\tau)]\\mathrm d\\tau.$$ 所以设 $\\int^t_0e^{-hs}u(s,\\tau)\\mathrm d\\tau=F_s(t)$，有 $F_s(0)\\equiv 0$，且 $$\\lim_{s\\to\\infty}\\left[F_s'(t)-a^2h^2F_s(t)\\right]=0.$$ 从而记 $|F'_s(t)-a^2h^2F_s(t)|=M(s,t)$， $$|F_s'(t)|\\leq a^2h^2|F_s(t)|+M(s,t).$$ 由 Gronwall 不等式，得到 $$e^{-hs}|u(s,t)|=|F_s'(t)|\\leq e^{a^2h^2t}M(s,t).$$ 由于 $M(s,t)\\to 0$，所以 $\\lim_{s\\to\\infty}e^{-hs}u(s,t)=0$。综上所述，问题的解为 $$\\begin{darray}{ll}u(x,t)\u0026\\displaystyle =hU_0e^{hx}\\int^\\infty_x\\Phi\\left(\\frac{\\xi}{2a\\sqrt t}\\right)e^{-h\\xi}\\mathrm d\\xi\\\\[14pt]\u0026\\displaystyle =hU_0e^{hx}\\left[\\Phi\\left(\\frac{\\xi}{2a\\sqrt t}\\right)\\dfrac {e^{-h\\xi}}{-h}\\Big|^\\infty_x+\\int^\\infty_x\\dfrac {e^{-h\\xi}}{h}\\cdot\\frac 1{2a\\sqrt t}\\cdot\\frac 2{\\sqrt \\pi}e^{-\\frac {\\xi^2}{4a^2t}}\\mathrm d\\xi\\right]\\\\[14pt]\u0026\\displaystyle =U_0\\Phi\\left(\\frac x{2a\\sqrt t}\\right)+\\frac{U_0e^{hx+a^2h^2t}}{a\\sqrt {\\pi t}}\\int^\\infty_xe^{-(\\xi\\frac 1{2a\\sqrt t}+{a\\sqrt t h})^2}\\mathrm d\\xi\\\\[14pt]\u0026\\displaystyle =U_0\\Phi\\left(\\frac x{2a\\sqrt t}\\right)+U_0e^{hx+a^2h^2t}\\left[1-\\Phi\\left(\\frac x{2a\\sqrt t}+a\\sqrt t h\\right)\\right].\\end{darray}$$pp101.9 求解并证明初值问题\n$$\\begin{cases}u_{tt}-a^2u_{xx}+cu=f(x,t),\u0026x\\in\\mathbb R,t\u003e0,\\\\[14pt] u(x,0)=\\varphi(x), \u0026x\\in\\mathbb R, \\\\[14pt] u_t(x,0)=\\psi(x),\u0026x\\in\\mathbb R.\\end{cases}$$解的唯一性，其中 $c$ 为常数。\n解： 应用 Fourier 变换，得到 $$\\begin{dcases}\\frac{\\mathrm d^2}{\\mathrm dt^2}F[u](\\xi,t)+(a^2\\xi^2+c)F[u](\\xi,t)=F[f](\\xi,t),\\\\[14pt] F[u](\\xi,0)=F[\\varphi](\\xi),\\\\[14pt] F[u_t](\\xi,0)=F[\\psi](\\xi).\\end{dcases}$$ 这是关于 $t$ 的常系数二阶线性微分方程，解为（Duhamel 原理），记号 $a^2\\xi^2+c=h$，则 $$F[u](\\xi,t)=F[\\varphi](\\xi)\\cos(\\sqrt h t)+\\dfrac {F[\\psi](\\xi)}{\\sqrt h}\\sin(\\sqrt h t)+\\int^t_0\\frac{\\sin(\\sqrt h(t-\\tau))}{\\sqrt h}F[f](\\xi,\\tau)\\mathrm d\\tau.$$ 考虑 Fourier 反演，这需要计算核，结果与 Bessel 函数有关，这里略去。最终得到 $$u(x,t)=\\varphi*F^{-1}[\\cos(\\sqrt h t)](x)+\\psi*F^{-1}\\left[\\frac {\\sin(\\sqrt h t)}{\\sqrt h}\\right](x)+\\int^t_0f(\\cdot,\\tau)*F^{-1}\\left[\\frac{\\sin(\\sqrt h(t-\\tau))}{\\sqrt h}\\right](x)\\mathrm d\\tau.$$ 这是解，由 Fourier 变换在 $\\mathcal S(\\mathbb R)$ 是同构的，解的唯一性得证。\n","tags":["Partial Differential Equations","Homework"],"categories":["Partial Differential Equations"]},{"title":"分析力学 习题 8","permalink":"https://yukar.icu/posts/analyticalmechanics/homework/8/","summary":"问题 1：周期 Toda 链（$A_1$ 型） $A_1$ 型周期 Toda 链（最简单的秩一情形）描述两个在圆周上运动、通过指数型最近邻势相互作用的粒子。设 $q_1,q_2$ 为它们的坐标，$p_1,p_2$ 为对应的共轭动量。Hamilton 量为 $$H(q_1,q_2,p_1,p_2) =\\frac {p_1^2} {2} + \\frac {p_2^2} {2} + e^{q_2 - q_1} + e^{q_1 - q_2}.$$(a) 写出 Hamilton 的正则方程 $$\\dot{q}_i = \\frac{\\partial H}{\\partial p_i}, \\quad \\dot{p}_i = -\\frac{\\partial H}{\\partial q_i}.$$(b) 证明总动量 $P = p_1 + p_2$ 守恒。\n(c) 引入质心与相对坐标 $$Q = \\frac{q_1 + q_2}{2}, \\quad q = q_1 - q_2,\\quad P = p_1 + p_2, \\quad p = \\frac{p_1 - p_2}{2}.$$ 用这些新变量写出 Hamilton 量，并求出运动方程。\n","content":"问题 1：周期 Toda 链（$A_1$ 型） $A_1$ 型周期 Toda 链（最简单的秩一情形）描述两个在圆周上运动、通过指数型最近邻势相互作用的粒子。设 $q_1,q_2$ 为它们的坐标，$p_1,p_2$ 为对应的共轭动量。Hamilton 量为 $$H(q_1,q_2,p_1,p_2) =\\frac {p_1^2} {2} + \\frac {p_2^2} {2} + e^{q_2 - q_1} + e^{q_1 - q_2}.$$(a) 写出 Hamilton 的正则方程 $$\\dot{q}_i = \\frac{\\partial H}{\\partial p_i}, \\quad \\dot{p}_i = -\\frac{\\partial H}{\\partial q_i}.$$(b) 证明总动量 $P = p_1 + p_2$ 守恒。\n(c) 引入质心与相对坐标 $$Q = \\frac{q_1 + q_2}{2}, \\quad q = q_1 - q_2,\\quad P = p_1 + p_2, \\quad p = \\frac{p_1 - p_2}{2}.$$ 用这些新变量写出 Hamilton 量，并求出运动方程。\n解： (a) 展开 Hamilton 的正则方程为 $$\\dot{q}_1 = p_1, \\quad \\dot{q}_2 = p_2,\\quad \\dot{p}_1 = e^{q_2 - q_1} - e^{q_1 - q_2}, \\quad \\dot{p}_2 = e^{q_1 - q_2} - e^{q_2 - q_1}.$$ (b) 根据守恒的定义，代入 (a) 的结果 $$\\dot P =p_i\\dot{p}_i + p_1(e^{q_1-q_2} - e^{q_2 - q_1}) + p_2(e^{q_2 - q_1} - e^{q_1 - q_2}) = 0.$$ (c) 换元即可 $$H(Q,q,P,p) = \\frac{P^2}{4} + p^2 + 2\\cosh q.$$ 对应的 Hamilton 方程为 $$\\dot{Q} = \\frac{P}{2}, \\quad \\dot{q} = 2p, \\quad \\dot{P} = 0, \\quad \\dot{p} = -2\\sinh q.$$问题 2：三维自由粒子 考虑质量为 $m$ 的粒子在 $\\mathbb R^3$ 中自由运动。其相空间坐标为 $(x_i,p_i)$，Hamilton 量为 $$H(\\mathbf r,\\mathbf p) = \\frac {\\mathbf p^2} {2m} = \\frac{p_1^2 + p_2^2 + p_3^2}{2m}.$$ 角动量矢量为 $\\mathbf L = \\mathbf r \\times \\mathbf p$，即 $L_i = \\varepsilon_{ijk} x_j p_k.$\n(a) 利用 Poisson 括号 $$\\{F,G\\} = \\sum_{i=1}^3 \\left( \\frac{\\partial F}{\\partial x_i} \\frac{\\partial G}{\\partial p_i} - \\frac{\\partial F}{\\partial p_i} \\frac{\\partial G}{\\partial x_i} \\right)$$ 计算 $i=1,2,3$ 时的 $\\{L_i,H\\}.$\n(b) 验证 $\\{L_i,L_j\\} = \\varepsilon_{ijk} L_k.$\n(c) 解释 $\\{L_i,H\\} = 0$ 的物理意义。\n解： (a) 直接计算 $$\\{L_i,H\\}=\\varepsilon_{ijk}\\delta_{ij}p_kp_im^{-1}=0.$$ (b) 同样直接计算 $$\\begin{array}{ll}\\{L_i,L_j\\}\u0026 = \\varepsilon_{imn}\\varepsilon_{jst} \\{x_m p_n, x_s p_t\\}\\\\[6pt] \u0026= \\varepsilon_{imn}\\varepsilon_{jst} (\\delta_{mr}\\delta_{tr}p_n x_s - \\delta_{nr}\\delta_{sr}x_m p_t)\\\\[6pt] \u0026= \\delta^r_{m}\\varepsilon_{imn}\\varepsilon_{jsm}x_sp_n-\\delta^r_{n}\\varepsilon_{imn}\\varepsilon_{jnt}x_mp_t\\\\[6pt] \u0026= \\delta^r_m\\varepsilon_{mni}\\varepsilon_{mjs}x_sp_n-\\delta^r_n\\varepsilon_{nim}\\varepsilon_{ntj}x_mp_t\\\\[6pt] \u0026= \\delta^r_m(\\delta^n_j\\delta^i_s-\\delta^n_s\\delta^i_j)x_sp_n-\\delta^r_n(\\delta^m_j\\delta^i_t-\\delta^m_t\\delta^i_j)x_mp_t\\\\[6pt] \u0026= \\delta^r_m\\delta^i_jx_np_n+\\delta^r_mx_ip_j-\\delta^r_n x_jp_i+\\delta^r_n\\delta^i_jx_mp_m\\\\[6pt] \u0026= x_ip_j-x_jp_i\\\\[6pt] \u0026= \\varepsilon_{ijk}L_k.\\end{array}$$ (c) 方程等价于 $$\\frac{dL_i}{dt} = \\{L_i,H\\} + \\frac{\\partial L_i}{\\partial t} = 0.$$ 这说明自由粒子运动时角动量守恒。\n问题 3：第四类生成函数 设 $(p,q)$ 为旧正则坐标，$(Q,P)$ 为新正则坐标。第四类生成函数的形式为 $F_4(p,P,t)$，其中旧动量 $p$ 和新动量 $P$ 被取为独立变量。\n(a) 写出生成函数的微分： $$\\mathrm dF_4(p,P,t)=\\dfrac {\\partial F_4}{\\partial p}\\mathrm dp + \\dfrac {\\partial F_4}{\\partial P}\\mathrm dP + \\dfrac {\\partial F_4}{\\partial t}\\mathrm dt.$$ 通过比较旧作用量形式和新作用量形式 $(p_i\\mathrm dq_i - H\\mathrm dt)$ 与 $(P_i\\mathrm dQ_i - K\\mathrm dt)$，推导出正则变换关系 $$q_i = -\\frac{\\partial F_4}{\\partial p_i}, \\quad Q_i = \\frac{\\partial F_4}{\\partial P_i}, \\quad K = H + \\frac{\\partial F_4}{\\partial t},$$ 其中 $K(Q,P,t)$ 是新 Hamilton 量。\n(b) 作为例子，取 $$F_4(p,P,t)=-p_i P_i + \\varepsilon p_ig_i(P,t),$$ 其中 $\\varepsilon$ 为无穷小量。证明它生成如下无穷小正则变换 $$Q_i=-p_i + \\varepsilon \\frac {\\partial g_j}{\\partial q_i}p_j+\\cdots , \\quad P_i=q_i + \\varepsilon g_i(q,t)+\\cdots,$$ 并确定该变换的生成元。\n解： (a) 由正则变换的定义 $$p_i\\mathrm dq_i - H\\mathrm dt = P_i\\mathrm dQ_i - K\\mathrm dt + \\mathrm dF$$ 由于题目中 $p,P$ 被取为独立变量，所以通过 Legendre 变换 $$\\mathrm d(F-pq+PQ)=Q_i\\mathrm dP_i - q_i\\mathrm dp_i + (K-H)\\mathrm dt,$$ 此时的微分形式才对应第四类生成函数，则 $$\\frac {\\partial F_4}{\\partial p}\\mathrm dp+\\frac {\\partial F_4}{\\partial P}\\mathrm dP + \\frac {\\partial F_4}{\\partial t}\\mathrm dt = Q_i\\mathrm dP_i - q_i\\mathrm dp_i + (K-H)\\mathrm dt.$$ 比较系数即可。\n(b) 同上，在 (a) 的基础上 $$\\mathrm dF_4=-p_i\\mathrm dP_i - P_i\\mathrm dp_i + \\varepsilon \\left( p_i\\frac {\\partial g_i}{\\partial P_j}\\mathrm dP_j + g_i\\mathrm dp_i + p_i\\frac {\\partial g_i}{\\partial t}\\mathrm dt \\right).$$ 比较系数得到 $$P_i=q_i + \\varepsilon g_i(P,t),\\quad Q_i=-p_i + \\varepsilon \\frac {\\partial g_j}{\\partial P_i}p_j.$$ 由于这是无穷小变换，所以 $P-q=O(\\varepsilon)$，因此关于 $g$ 作展开 $$P_i=q_i+\\varepsilon g_i(q+O(\\varepsilon),t)=q_i+\\varepsilon g_i(q,t)+\\cdots.$$ 同理，对于 $Q$ 也作展开 $$Q_i=-p_i + \\varepsilon \\frac {\\partial g_j}{\\partial P_i}(q+O(\\varepsilon),t)p_j=-p_i + \\varepsilon \\frac {\\partial g_j}{\\partial q_i}p_j+\\cdots.$$ 相空间坐标的改变量可以写为 $$\\delta Q_i=\\varepsilon \\frac {\\partial g_j}{\\partial q_i}p_j, \\quad \\delta P_i=\\varepsilon g_i(q,t).$$ 所以该变换的生成元为 $$G(q,p,t) = g_i(q,t)p_i.$$问题 4：Laplace-Runge-Lenz 矢量 质量为 $m$ 的粒子在三维空间中的中心势 $V(r)$ 中运动。在平面极坐标下，Lagrange 量为 $$L(r,\\theta,\\dot{r},\\dot{\\theta}) = \\frac{1}{2}m(\\dot{r}^2 + r^2\\dot{\\theta}^2) - V(r).$$ (a) 通过 Legendre 变换构造 Hamilton 量 $H(r,\\theta,p_r,p_\\theta)$。\n(b) Laplace-Runge-Lenz 矢量定义为 $$\\mathbf A = \\mathbf p \\times \\mathbf L - m \\alpha\\dfrac {\\mathbf r}{r},$$ 其中 $\\mathbf L=\\mathbf r \\times \\mathbf p$ 为角动量，$\\alpha$ 为常数。用 $(x_i,p_i)$ 写出其直角分量 $A_i$。\n(c) 对一般中心势 $V(r)=-\\frac kr$ （Kepler 问题，$\\alpha=k$），计算 Poisson 括号 $\\{A_i,H\\}$.\n(d) 证明在 Kelper 势中 LRL 矢量守恒，即 $\\{A_i,H\\}=0$。简要解释为什么这一守恒律是 $1/r$ 势所特有的。\n解： (a) 根据定义 $H=p_r\\dot{r}+p_\\theta\\dot{\\theta}-L$，其中 $$p_r = \\frac{\\partial L}{\\partial \\dot{r}} = m\\dot{r}, \\quad p_\\theta = \\frac{\\partial L}{\\partial \\dot{\\theta}} = mr^2\\dot{\\theta}.$$ 所以 Hamilton 量为 $$H(r,\\theta,p_r,p_\\theta) = \\frac{p_r^2}{2m} + \\frac{p_\\theta^2}{2mr^2} + V(r)=\\frac {\\mathbf p^2}{2m} + V(r).$$ (b) 沿用记号 $L_k=\\varepsilon_{klm}x_lp_m$，则 $$\\begin{array}{ll} (\\mathbf p \\times \\mathbf L)_i\u0026=\\varepsilon_{ijk}p_jL_k\\\\[6pt]\u0026=\\varepsilon_{ijk}\\varepsilon_{klm}p_jx_lp_m\\\\[6pt]\u0026=(\\delta_{il}\\delta_{jm}-\\delta_{im}\\delta_{jl})p_jp_mx_l\\\\[6pt]\u0026=\\sum^3_{j=1}x_ip_j^2-p_ip_jx_j\\\\[6pt]\u0026=\\mathbf p^2x_i-(\\mathbf r\\cdot \\mathbf p)p_i\\end{array}$$ 所以 $$A_i = \\mathbf p^2x_i-(\\mathbf r\\cdot \\mathbf p)p_i - m\\alpha \\frac{x_i}{r}.$$ (c) 根据 Hamilton 量在 Poisson 括号中的性质，先计算每一项的 Poisson 括号（直角坐标） $$\\begin{array}{ll}\\{\\mathbf p^2x_i,H\\}\u0026=\\mathbf p^2\\{x_i,H\\}+\\{ \\mathbf p^2,H\\}x_i\\\\[6pt]\u0026=\\mathbf p^2\\frac{\\partial H}{\\partial p_i}-\\frac{\\partial H}{\\partial x_j}\\frac{\\partial \\mathbf p^2}{\\partial p_j}x_i\\\\[6pt]\u0026=\\mathbf p^2\\frac{p_i}{m}-2x_i\\mathbf p\\cdot \\nabla V\\\\[6pt]\u0026=\\frac {\\mathbf p^2p_i}m - 2V'(r)\\frac {\\mathbf r\\cdot \\mathbf p}{r}x_i\\end{array}$$ 其次， $$\\begin{array}{ll}\\{(\\mathbf r\\cdot \\mathbf p)p_i,H\\}\u0026=(\\mathbf r\\cdot \\mathbf p)\\{p_i,H\\}+\\{ \\mathbf r\\cdot \\mathbf p,H\\}p_i\\\\[6pt]\u0026=-(\\mathbf r\\cdot \\mathbf p)\\frac{\\partial H}{\\partial x_i}+\\left( \\{x_j,H\\}p_j + x_j\\{p_j,H\\} \\right)p_i\\\\[6pt]\u0026=-(\\mathbf r\\cdot \\mathbf p)\\frac{\\partial H}{\\partial x_i}+\\left( \\frac{\\partial H}{\\partial p_j}p_j - x_j\\frac{\\partial H}{\\partial x_j} \\right)p_i\\\\[6pt]\u0026=-V'(r)\\frac {\\mathbf r\\cdot \\mathbf p}{r}x_i + p_i(\\frac {\\mathbf p^2}m - V'(r)r).\\end{array}$$ 第三项， $$\\begin{array}{ll}\\{m\\alpha \\frac{x_i}{r},H\\}=m\\alpha \\{\\frac {x_i}r,\\frac {\\mathbf p^2}{2m}\\}=\\alpha (\\frac {\\delta_{ij}}r-\\frac {x_ix_j}{r^3})p_j\\end{array}$$ 综上， $$\\begin{array}{ll}\\{A_i,H\\}\u0026=(rV'(r)-\\frac \\alpha r)p_i-(\\frac {V'(r)}{r}-\\frac \\alpha{r^3})(\\mathbf r\\cdot \\mathbf p)x_i\\end{array}$$ (d) 在 Kepler 势中 $V(r)=-\\frac kr$，所以 $V'(r)=\\tfrac k{r^2}$，因此 $$\\{A_i,H\\}=(\\tfrac kr-\\tfrac \\alpha r)p_i-(\\tfrac k{r^3}-\\tfrac \\alpha{r^3})(\\mathbf r\\cdot \\mathbf p)x_i=0.$$ 可以反解 $\\{A_i,H\\}=0$ 的条件，得到 $$V'(r)=\\frac \\alpha{r^2},$$ 这只能是 $1/r$ 势的特例。\n","tags":["Analytical Mechanics","Homework"],"categories":["Analytical Mechanics"]},{"title":"数列极限","permalink":"https://yukar.icu/posts/mathematicalanalysis/1/","summary":"参考资料：《数学分析习题课讲义》（第 2 版），谢惠民。\n","content":"参考资料：《数学分析习题课讲义》（第 2 版），谢惠民。\n","tags":["Mathematical Analysis","Homework"],"categories":["Mathematical Analysis"]},{"title":"无界区域的热传导方程","permalink":"https://yukar.icu/posts/partialdifferentialequations/8/","summary":"","content":"","tags":["Partial Differential Equations","Notes"],"categories":["Partial Differential Equations"]},{"title":"Hello World","permalink":"https://yukar.icu/posts/hello-world/","summary":"欢迎来到我的博客 这是我的第一篇博客！使用 Hugo + PaperMod 主题搭建，部署在 GitHub Pages 上。\n关于我 一个热爱技术和分享的开发者。\n接下来 我会在这里分享技术文章、学习笔记和各种有趣的内容。\n","content":"欢迎来到我的博客 这是我的第一篇博客！使用 Hugo + PaperMod 主题搭建，部署在 GitHub Pages 上。\n关于我 一个热爱技术和分享的开发者。\n接下来 我会在这里分享技术文章、学习笔记和各种有趣的内容。\n","tags":["first","blog"],"categories":null},{"title":"Archives","permalink":"https://yukar.icu/archives/","summary":"archives","content":"","tags":null,"categories":null},{"title":"搜索","permalink":"https://yukar.icu/search/","summary":"search","content":"","tags":null,"categories":null}]