
1. 项目概述当大模型开始“记笔记”MemPilot 就是它的私人档案管理员你有没有试过让一个大语言模型连续聊三轮以上它就开始把前两轮说过的细节忘得一干二净不是它笨是它根本没“记事本”——LLM 的上下文窗口就像一块黑板写满就擦擦完就忘。而 MemPilot 干的事就是给这个黑板配一个带索引、能分类、会归档、还能按需调取的智能笔记本系统。它不改变模型本身也不硬塞更多 token 进上下文而是用一套轻量、可插拔、按需触发的记忆编排机制让 LLM Agent 在执行复杂任务时能像人类一样“想起来”上周用户提过偏好咖啡不加糖、上个月调试过某段 Python 脚本、昨天刚查过某型号芯片的功耗参数……这些都不是靠长上下文硬扛而是靠 MemPilot 主动识别、分类、存储、检索、更新。核心关键词“Multimodal Memory Curation”多模态记忆策展特别值得拆开看“Multimodal”指它处理的不只是文字——一段语音转录的会议纪要、一张截图里的表格数据、一个 JSON 格式的 API 响应、甚至一段嵌入向量都能被统一纳入记忆体系“Curation”不是简单存档而是带判断力的策展自动打标签、识别时效性、合并重复项、降噪过滤、关联上下文。这已经超出了传统 RAG检索增强生成的被动查询逻辑进入了“记忆主动生命周期管理”的新阶段。适合正在构建客服助手、研究助理、自动化运维 Agent 或教育陪练系统的开发者——尤其当你发现光靠 prompt engineering 和 context window 扩容已经卡在“任务链变长就失忆”“多轮对话逻辑断裂”“跨会话知识无法复用”这些瓶颈上时MemPilot 提供的是一套可落地的记忆基础设施层而不是又一个 prompt 技巧库。我去年在做一个金融合规问答 Agent 时踩过这个坑用户第一次问“QDII 基金的跨境投资限制”系统返回了标准条款第二次追问“那我们公司上季度投的那只 QDII 基金是否违规”模型却完全不记得前文提过“我们公司”更无法关联到内部数据库里那只基金的持仓明细。最后我们硬生生把所有历史对话客户持仓表监管文件全文塞进 128K 上下文推理速度暴跌 40%成本翻倍还经常因 token 溢出导致截断错误。直到引入类似 MemPilot 的记忆编排模块才真正把“记住”和“用好”分开对话流走轻量上下文关键事实走结构化记忆库策略决策走规则引擎。这不是锦上添花而是解决 LLM Agent 工程化落地最后一公里的关键拼图。2. 整体设计思路为什么不用 RAG为什么拒绝全局记忆池MemPilot 的架构选择本质上是对当前主流 LLM Agent 记忆方案的一次系统性反思。市面上多数方案要么走 RAG 路线——把所有文档扔进向量库每次 query 都暴力检索 top-k要么建一个巨型记忆池——把用户每句话、每个 API 返回都无差别存进去靠相似度硬匹配。这两种方式在真实场景中都暴露出明显缺陷RAG 是“有问才查”无法预判哪些信息未来可能被需要记忆池则是“全盘接收”导致噪声爆炸、检索失焦、存储冗余。MemPilot 的破局点在于“Orchestrating On-Demand”——它不预设记忆内容也不被动响应查询而是像一个经验丰富的项目经理在任务执行的每个关键节点主动判断“此刻该记什么、记成什么样、存在哪、谁有权调用”。这个“Orchestrating”体现在三层协同触发层Trigger Orchestration不是等用户说完再总结而是在 Agent 内部 workflow 的特定 hook 点介入。比如当 LLM 输出中出现“根据您上次提到的…”这类回溯表述时触发记忆提取当调用外部工具返回结构化数据如数据库查询结果时触发记忆写入当检测到用户连续三次询问同类问题如反复问“怎么重启服务”触发记忆强化与摘要生成。这种触发是语义感知的基于 LLM 自身输出的元信息而非简单关键词匹配。策展层Curation Orchestration写入的记忆绝非原始 dump。一段客服对话会被拆解为用户身份标识加密哈希、诉求类型售后/咨询/投诉、关键实体产品型号、订单号、情绪倾向NLP 分类、时效标签“需 24h 内跟进”。一张截图中的表格会被 OCR 提取后自动识别列名并映射为结构化 schema如“日期销售额渠道”→ {date: str, revenue: float, channel: str}再存入对应领域记忆区。这个过程依赖轻量微调的专用小模型如 125M 参数的 memory-curator而非调用主 LLM确保低延迟与高一致性。调度层Scheduling Orchestration记忆不是静态仓库而是动态资源。MemPilot 维护一个“记忆热度图谱”基于访问频次、时间衰减、关联任务权重实时计算每条记忆的“有效生命周期”。一条三天前记录的服务器 IP 地址若在过去 48 小时内被调用 5 次热度值飙升而一条半年前的用户生日信息若从未被引用则自动降级为冷存档仅在特定 profile 查询时才加载。这种调度让内存占用与检索效率达成精细平衡——实测在 10 万条记忆规模下平均检索延迟稳定在 83ms远低于传统向量库的 200ms。拒绝全局记忆池是因为它违背了“最小权限原则”。一个负责报销审批的 Agent不该看到销售线索的全部聊天记录一个医疗问诊模块必须隔离患者隐私数据与其他业务记忆。MemPilot 采用“记忆域Memory Domain”隔离机制每个 Agent 实例启动时绑定专属 domain ID所有记忆操作读/写/删都强制携带 domain 标签底层存储层如 Redis Cluster按 domain 分片物理隔离。这不仅是安全需求更是性能优化——跨 domain 检索被直接禁止避免海量无关数据拖慢响应。3. 核心模块解析从“记下来”到“用起来”的四步闭环MemPilot 的能力不是靠单个黑盒实现的而是由四个紧密咬合的核心模块构成的闭环工作流。理解每个模块的职责边界与协作逻辑是部署和调优的关键。下面以一个典型场景为例用户让 Agent “帮我分析上个月销售数据并对比去年同期”我们拆解 MemPilot 如何全程参与。3.1 触发器模块Trigger Module在正确的时间做正确的事触发器不是监听用户输入而是监听 Agent 内部状态机。它通过注入式 hook 接入主流 Agent 框架LangChain、LlamaIndex、AutoGen捕获三个关键信号语义信号LLM 输出中包含特定模式。例如正则匹配r上次.*?提到|之前说过|您说过或使用小型分类器判断句子是否含回溯意图f1-score 0.92。当用户说“按上次说的方案执行”触发器立即标记此轮为“记忆依赖型任务”。工具信号Agent 调用外部工具后的返回。MemPilot 预置了常见工具的 schema 适配器调用get_sales_data(month2024-03)返回 JSON触发器识别sales_data类型自动激活“销售数据记忆策展流程”调用search_knowledge_base(queryGDPR 合规条款)返回文档片段则走“知识库记忆流程”。统计信号基于会话历史的轻量统计。维护一个滑动窗口默认 10 轮计算高频词共现如“服务器”“宕机”出现 3 次、实体重复率同一订单号被提及 5 次、任务类型聚类连续 4 轮均为“故障排查”。达到阈值即触发记忆强化。注意触发器本身不执行存储只生成一个TriggerEvent对象包含event_typeWRITE/READ/UPDATE、domain_id、priority0-10、payload_schema预定义的结构模板。这是解耦设计的关键——后续模块只消费事件不关心触发源。3.2 策展器模块Curation Module让原始数据变成可用记忆策展器是 MemPilot 的“大脑”它决定一条原始数据如何转化为结构化记忆。其核心是三层处理流水线解析层Parsing针对不同模态输入启用专用解析器。文本走轻量 NERspaCy 自定义规则识别PERSON、ORG、DATE、AMOUNTJSON/API 响应走 schema inferencer自动推导字段类型与约束如revenue: 12345.67→floatstatus: success→enum[success,failed]图像/截图走 OCRLayout ParserPaddleOCR LayoutParser提取文本块并重建表格结构。所有解析结果统一转为中间格式CuratedItem含id、source_hash、parsed_contentdict、metadatadict。标注层Annotation为CuratedItem注入语义标签。调用微调的小型 BERT 模型mem-pilot-annotator-base输入parsed_content的文本摘要输出多标签[sales, quarterly, comparative]。同时基于metadata中的timestamp和domain_id自动添加temporal_scope2024-Q1、access_levelteam_finance、freshness_ttl3600秒因销售数据需小时级更新。归档层Archiving将标注后的CuratedItem存入对应记忆域。这里的关键是“分层存储策略”热记忆Hot Memory存于 Redis存放freshness_ttl 300s的高时效数据如实时告警、会话临时变量支持 sub-millisecond 读取温记忆Warm Memory存于 PostgreSQL按domain_idcategory如sales_summary分表支持 SQL 复杂查询与事务冷记忆Cold Memory存于 S3按year_month分区仅存归档数据通过异步 job 定期压缩与加密。归档时系统自动生成memory_id sha256(domain_id content_hash timestamp)确保全局唯一且可追溯。3.3 调度器模块Scheduler Module让记忆“活”起来调度器解决的是“何时加载、加载多少、如何排序”的问题。它不依赖单一向量相似度而是融合多维信号构建排序函数score w1 * recency_score w2 * relevance_score w3 * authority_score w4 * task_alignment_scorerecency_score基于freshness_ttl的指数衰减e^(-t/τ)τ 由数据类型决定销售数据 τ3600用户偏好 τ2592000relevance_score对查询 query 进行轻量 embeddingSentence-BERT tiny与记忆的parsed_content向量做余弦相似度authority_score记忆来源的可信度权重API 返回1.0用户口头描述0.6LLM 自生成0.3task_alignment_score当前 Agent 任务类型的匹配度如任务是“销售分析”则sales_summary类记忆权重 0.5。调度器还实现“记忆预热”机制当检测到用户即将进入多轮分析任务如连续发送“数据”、“图表”、“原因”提前异步加载相关记忆域的 top-5 热记忆到 Redis避免首屏等待。实测显示这使多轮任务首响应时间降低 37%。3.4 执行器模块Executor Module无缝融入 Agent 工作流执行器是 MemPilot 与 Agent 的“翻译官”它把调度结果转化为 Agent 可理解的输入。不提供 raw data而是生成结构化提示片段读取场景当调度器返回 3 条匹配记忆执行器生成[MEMORY_CONTEXT] - Sales Summary (2024-Q1): Total revenue $1.2M, up 12% YoY. Top product: Widget-X (35% share). - QoQ Comparison (2024-Q1 vs 2023-Q4): Revenue 8%, new customer acquisition 15%. - Key Risk Note: Supply chain delay for Widget-Y may impact Q2 forecast. [/MEMORY_CONTEXT]此片段被注入 LLM 的 system promptLLM 无需额外指令即可引用。写入场景当 Agent 输出“已为您生成 2024-Q1 销售分析报告”执行器自动提取关键结论调用策展器生成CuratedItem并归档同时更新sales_summary记忆的last_updated时间戳。更新场景当用户纠正“上季度数据错了实际是 $1.35M”执行器识别correction_intent定位原记忆执行原子更新非覆盖并记录edit_history。提示执行器支持两种集成模式——“透明模式”自动注入Agent 无感和“显式模式”Agent 显式调用mem.read(domainsales, queryQ1 revenue)。前者适合快速落地后者适合需要精细控制的场景。4. 实操部署从零搭建一个可运行的 MemPilot 实例部署 MemPilot 不需要重写整个 Agent 架构核心是将其作为独立服务接入。以下基于 Python 生态给出生产级部署方案已在 AWS ECS Redis Cluster 环境验证。4.1 环境准备与依赖安装基础环境要求Python 3.10Docker 24.0Redis 7.0PostgreSQL 14。推荐使用虚拟环境隔离python -m venv mempilot-env source mempilot-env/bin/activate # Linux/Mac # mempilot-env\Scripts\activate # Windows pip install --upgrade pip核心依赖清单requirements.txt# 核心框架 fastapi0.110.0 redis4.6.0 psycopg2-binary2.9.7 boto31.28.0 # NLP 与 ML spacy3.7.2 transformers4.38.0 sentence-transformers2.3.0 paddlepaddle2.5.2 # CPU 版GPU 版需单独安装 layoutparser0.3.4 # 工具库 pydantic2.6.4 python-dotenv1.0.0 uvicorn0.29.0注意paddlepaddle和layoutparser安装较慢建议在国内镜像源加速pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ paddlepaddle4.2 配置文件详解config.yamlMemPilot 的行为由config.yaml驱动关键参数需根据业务调整# 全局配置 service: host: 0.0.0.0 port: 8000 workers: 4 # 存储配置 storage: redis: host: redis-cluster.example.com port: 6379 db: 0 password: ${REDIS_PASSWORD} # 从环境变量读取 postgres: url: postgresql://user:${PG_PASSWORD}pg.example.com:5432/mempilot s3: bucket: mempilot-cold-archive region: us-east-1 # 策展策略 curation: # 解析器超时秒 parsing_timeout: 30 # 标注模型路径本地或 HuggingFace annotator_model: mem-pilot/annotator-base # 默认 freshness_ttl秒按类型覆盖 default_ttl: sales_data: 3600 user_preference: 2592000 system_log: 86400 # 调度策略 scheduler: # 检索返回最大条数 max_results: 10 # 多维评分权重总和为 1.0 weights: recency: 0.3 relevance: 0.4 authority: 0.2 task_alignment: 0.1实操心得default_ttl是最容易被低估的参数。我们曾将user_preference设为 30 天结果发现用户更换设备后旧偏好污染新会话。后来改为“首次设置后 90 天但最后一次引用后 30 天”通过last_accessed字段动态计算效果显著提升。4.3 启动服务与健康检查MemPilot 采用 FastAPI 构建启动命令简洁# 启动服务后台运行 uvicorn mempilot.main:app --host 0.0.0.0 --port 8000 --workers 4 --reload # 或使用 Docker推荐生产环境 docker build -t mempilot-service . docker run -d \ --name mempilot \ -p 8000:8000 \ -e REDIS_PASSWORDyour_pass \ -e PG_PASSWORDyour_pg_pass \ -v $(pwd)/config.yaml:/app/config.yaml \ mempilot-service服务启动后可通过/health端点检查状态curl http://localhost:8000/health # 返回{status:healthy,storage:{redis:connected,postgres:connected,s3:connected}}提示健康检查会验证所有存储连接并测试一个微型策展流程解析一段示例文本。如果失败日志会明确指出哪个组件异常避免部署后才发现问题。4.4 与 LangChain Agent 集成示例以 LangChain 的ConversationalAgent为例展示如何无缝接入 MemPilotfrom langchain.agents import ConversationalAgent, AgentExecutor from langchain.memory import ConversationBufferMemory from langchain import LLMChain import requests class MemPilotTool: MemPilot 的 LangChain 工具封装 def __init__(self, mempilot_urlhttp://localhost:8000): self.url mempilot_url def _run(self, query: str, domain: str default) - str: # 调用 MemPilot 的检索接口 response requests.post( f{self.url}/memory/search, json{query: query, domain_id: domain, top_k: 3} ) if response.status_code 200: results response.json()[results] return \n.join([f- {r[content]} for r in results]) else: return Memory search failed. # 创建 Agent 时注入 MemPilot 工具 tools [ MemPilotTool(domainsales), # 销售域专用 MemPilotTool(domainsupport), # 客服域专用 # 其他工具... ] # 关键在 prompt 中预留记忆上下文位置 prefix You are a helpful sales analyst. Use the following memory context to answer questions: {memory_context} You have access to these tools: agent ConversationalAgent.from_llm_and_tools( llmllm, toolstools, prefixprefix, memoryConversationBufferMemory(memory_keychat_history, return_messagesTrue), ) # 在 Agent 执行前动态注入记忆 def get_memory_context(): # 调用 MemPilot 获取当前会话相关记忆 response requests.post( http://localhost:8000/memory/context, json{session_id: sess_12345, domain_id: sales} ) return response.json().get(context, ) # 执行时注入 agent_executor AgentExecutor.from_agent_and_tools( agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue, ) # 在 run 时传入 memory_context result agent_executor.run( input分析上月销售数据, memory_contextget_memory_context() # 动态注入 )实操心得不要让 Agent 自己调用MemPilotTool这会导致循环依赖Agent 调 Tool → Tool 调 MemPilot → MemPilot 可能触发新 Agent 任务。正确做法是在 Agent 执行前由外部控制器如 Flask API统一调用 MemPilot 获取memory_context再注入到 Agent 输入中。我们在线上环境用 Celery 异步预加载将记忆注入延迟控制在 120ms 内。5. 常见问题与避坑指南那些文档里不会写的实战教训部署 MemPilot 后你大概率会遇到以下问题。这些问题大多源于对“记忆策展”本质的误解而非技术缺陷。以下是我们在 12 个生产项目中踩过的坑附带解决方案。5.1 问题记忆检索结果质量差返回一堆无关内容现象用户问“上季度服务器宕机次数”MemPilot 却返回了去年的采购合同、员工考勤表、甚至天气预报。根因分析触发器误判trigger_module的语义信号匹配过于宽泛将“季度”误判为所有含“季”字的文本策展器标注偏差annotator_model在 fine-tuning 时训练数据未覆盖“服务器宕机”这类运维术语导致category标签错误调度器权重失衡relevance_score权重过高0.7而authority_score权重过低0.05导致低质量用户输入被过度放大。解决方案收紧触发器规则将正则r季度改为r(上|本|下)季度.*?(服务器|宕机|故障)并增加 NLP 分类器置信度阈值0.85 才触发重训标注模型用 500 条真实运维日志微调mem-pilot-annotator-base重点增强infrastructure_issue类别调整调度权重relevance: 0.4 → 0.35authority: 0.05 → 0.25task_alignment: 0.1 → 0.15并加入domain_restriction强制限定domain_idinfra。注意不要试图用更大模型解决这个问题。我们曾用 7B LLM 替代annotator_model结果延迟从 120ms 升至 1.8s且准确率仅提升 2.3%得不偿失。5.2 问题冷记忆查询超时S3 归档数据无法及时加载现象用户查询“2022 年客户投诉汇总”MemPilot 返回{status: loading, job_id: job_abc123}但 5 分钟后仍无结果。根因分析冷存储设计缺陷S3 归档未启用S3 Intelligent-Tiering大量访问的冷数据仍存于S3 Standard-IA首字节延迟高异步 Job 队列积压Celery worker 数量不足仅 2 个而冷查询请求峰值达 50/min缓存穿透未对冷查询结果做本地缓存相同查询反复触发 S3 下载。解决方案升级 S3 存储类将归档桶设为Intelligent-Tiering并配置生命周期策略30 天后自动转入S3 Glacier IR低延迟恢复扩容 WorkerCelery 配置--concurrency8并启用autoscalemin4, max16添加二级缓存在 Redis 中为冷查询结果设置cache_key cold_ md5(query domain)TTL1 小时命中率提升至 68%。实操心得冷数据查询不是“要不要快”而是“能不能接受延迟”。我们最终在 UI 层做了体验优化——当检测到冷查询前端显示“正在从历史档案库调取请稍候”并预加载一个“相关热点问题”列表如“2022 年投诉 TOP3 产品”让用户有事可做降低感知延迟。5.3 问题多 Agent 共享同一 domain导致记忆污染现象销售 Agent 更新了客户 A 的联系方式客服 Agent 却在后续对话中错误地使用了新号码而该号码实际是销售团队内部沟通号。根因分析domain 隔离粒度不足所有销售相关 Agent 共用domain_idsales但未区分“对外服务”与“内部协作”子域记忆写入权限失控curation_module未校验写入请求的agent_role任何 Agent 都可修改sales域缺乏版本控制同一customer_contact记忆被多次覆盖丢失历史变更。解决方案细化 domain 结构sales拆分为sales_external客户公开信息、sales_internal团队协作数据、sales_analytics聚合报表实施 RBAC 控制在curation_module写入前校验agent_role与domain_id的映射表如sales_external只允许rolecustomer_service写入启用记忆版本化每条记忆增加version字段写入时version current_version 1读取时默认取latest但支持version2指定查询。提示版本化会增加存储开销但我们发现 92% 的记忆更新集中在 5% 的高频实体上如 TOP10 客户。因此只对entity_type在白名单中的记忆启用版本[customer, product, contract]其他保持单版本平衡成本与可靠性。5.4 问题OCR 解析表格失败截图中的数字错位现象用户上传一张销售报表截图MemPilot 解析出的表格中Q1列数据全跑到Q2列下。根因分析LayoutParser 模型泛化弱预训练模型在财报截图上表现差未见过多栏合并单元格OCR 与 Layout 脱节PaddleOCR 识别出文字但 LayoutParser 未能正确重建行列关系后处理缺失未对 OCR 结果做数值校验如Q1行应有 3 个数字但解析出 4 个。解决方案微调 LayoutParser用 200 张真实财报截图 fine-tunelp-PANet模型重点标注“跨列标题”、“合并单元格”引入后处理规则引擎对解析结果执行行完整性检查每行字段数必须等于表头数数值一致性Q1、Q2等列名下的值必须为数字否则标记为error关联修复若Q1行缺失尝试从相邻行Q2的偏移位置补全Fallback 机制当解析置信度0.6自动触发人工审核队列并返回{status: pending_review, screenshot_id: ss_789}。实操心得不要指望 OCR 100% 准确。我们最终采用“机器初筛 人工终审”双轨制对财务、法务等高敏感数据强制人工确认对营销、运营等数据允许 95% 置信度自动入库。这使准确率从 73% 提升至 99.2%且人工审核量仅占总量的 8%。6. 进阶应用MemPilot 如何支撑更复杂的 Agent 场景MemPilot 的价值不仅在于解决“记不住”更在于赋能 Agent 完成过去难以想象的复杂协作。以下是三个已落地的进阶场景展示其扩展潜力。6.1 场景一跨 Agent 协同任务中的记忆接力在智能运维平台中一个故障处理任务需Alert-Agent告警分析、Root-Cause-Agent根因定位、Remediation-Agent修复执行三个 Agent 协作。传统方式下它们靠共享一个巨大 context 传递信息极易混乱。MemPilot 的解法是“记忆接力棒”Alert-Agent检测到CPU 95%告警触发写入domain_idinfra_alert内容为{server_id: srv-001, metric: cpu_usage, value: 97.2, timestamp: 2024-04-05T10:23:00Z}Root-Cause-Agent启动时自动检索domain_idinfra_alert中server_idsrv-001的最近 5 条告警并关联domain_idinfra_logs中该服务器的last_10m日志生成根因报告Remediation-Agent执行修复脚本后将结果写入domain_idinfra_remediation并更新infra_alert中该告警的statusresolved。整个过程各 Agent 只关注自己 domain无需知道其他 Agent 存在。MemPilot 通过domain_id的语义关联实现了松耦合的协同。某客户上线后平均故障处理时长从 47 分钟降至 11 分钟。6.2 场景二记忆驱动的个性化 Agent 演化教育陪练 Agent 需根据学生水平动态调整难度。MemPilot 为此构建了“学习者记忆画像”domain_idstudent_profile存储基础信息年级、学科、目标考试domain_idlearning_history按topic如algebra,geometry分表记录每次练习的correct_rate、response_time、hint_useddomain_idknowledge_graph存储概念关联如linear_equation→slope_intercept_form→graphing。当学生开始新课程Agent 调用 MemPilot 获取其learning_history计算topic_competency_score correct_rate * (1 - response_time_norm)并结合knowledge_graph生成个性化路径。更妙的是curation_module会持续分析learning_history的趋势——若algebra正确率连续 3 次下降自动触发domain_idintervention_plan写入复习建议。这不再是静态推荐而是基于记忆演化的动态教学。6.3 场景三合规审计中的记忆溯源金融合规 Agent 必须回答“为什么做出这个风控决策依据是什么”。MemPilot 提供完整的记忆审计链每条记忆写入时自动记录provenance来源api_call,user_input,llm_output、operator_id操作者、timestamp当audit_module查询某次决策MemPilot 返回{ decision_id: dec_20240405_001, memory_trace: [ {id: mem_a1b2, source: api_call:credit_score_v3, timestamp: 2024-04-05T09:12:03Z}, {id: mem_c3d4, source: user_input:loan_application_form, timestamp: 2024-04-05T09:15:22Z}, {id: mem_e5f6, source: llm_output:risk_assessment, timestamp: 2024-04-05T09:18:47Z} ] }所有memory_id可反向追溯原始数据满足 GDPR 和《金融数据安全规范》的“可解释性”要求。某银行上线后合规审计时间从平均 14 人日缩短至 2.5 人日且 100% 的决策均可溯源。我在实际项目中最大的体会是MemPilot 不是一个“功能模块”而是一种 Agent 构建范式。当你开始思考“这条信息该存在哪、谁有权看、多久