我给 AI 接上知识库和工具后,它终于不只是“陪聊”了
最近一段时间,我看了不少 AI 项目。
有些项目界面做得很漂亮,打开之后却还是熟悉的流程:输入一个问题,模型返回一段文字。刚开始确实觉得新鲜,但用不了多久就会发现,它和普通聊天工具没有太大区别。
我一直想做一个更实用的东西:能不能让 AI 自己查资料、看日志,发现问题后顺手创建一张工单?
于是我动手做了一个简单的技术助手。
整个项目并不复杂,但做完之后,我对 AI 应用开发有了一个比较明显的感受:大模型真正有价值的地方,可能不是“知道很多”,而是能够接入我们已有的系统。
先说说这个助手能做什么
我给它设计了一个很常见的场景。
用户输入:
订单服务今天经常超时,帮我看看是什么原因。
如果确认是线上故障,再创建一张工单。
普通聊天机器人只能根据经验猜测:
可能是数据库压力过大,也可能是网络延迟,建议检查日志。
这类回答不能说错,但基本等于没说。
我希望助手完成的是下面这些事情:
- 从内部知识库中查询订单服务的相关文档;
- 调用日志接口,读取最近一段时间的错误日志;
- 根据真实日志判断问题原因;
- 判断是否达到线上故障标准;
- 符合条件时调用工单接口;
- 把分析过程和处理结果告诉用户。
实际运行起来,大致是这样的:
收到问题
↓
查询相关技术文档
↓
读取订单服务日志
↓
发现数据库连接池耗尽
↓
判断故障等级
↓
创建工单
↓
返回结果
做到这一步,AI 才算从“给建议”变成了“帮忙处理问题”。
第一步:先让 AI 看得懂内部资料
大模型知道很多公开知识,但它不可能知道我们项目里的接口设计、部署方式和历史故障。
我最开始犯过一个错误:直接把大量项目文档塞进提示词。
文档少的时候还能用,内容一多,响应速度和效果都会变差。更麻烦的是,模型经常抓不住真正重要的内容。
后来我换成了 RAG,也就是检索增强生成。
这个名字听起来有点复杂,实际思路非常直白:
不要一次性把所有资料都交给模型。先找到与问题有关的几段内容,再让模型阅读。
处理流程如下:
用户问题
↓
把问题转换成向量
↓
从知识库中找出语义相近的文档
↓
把相关文档和问题一起发给模型
例如,知识库中有下面几段内容:
documents = [
"订单服务使用 Redis 保存热点商品的库存信息。",
"订单接口出现大量超时时,需要优先检查数据库连接池。",
"线上故障达到 P1 级别后,需要立即创建故障工单。"
]
写入知识库时,先把文字转换成向量:
for document in documents:
vector = embedding_model.embed(document)
vector_database.insert({
"content": document,
"vector": vector
})
用户提问后,用相同的方式生成问题向量:
def search_knowledge(question):
question_vector = embedding_model.embed(question)
return vector_database.search(
vector=question_vector,
top_k=3
)
向量检索的好处是,它不要求用户必须说出文档中的原词。
用户说“下单特别慢”,文档中写的是“订单接口超时”,两句话不完全一样,但语义接近,仍然有机会被检索出来。
当然,RAG也不是接上向量数据库就万事大吉。
我在实际测试时发现,文档怎么切分非常重要。切得太长,模型会收到很多无关信息;切得太短,上下文又容易断掉。
目前我的做法是:
- 按标题和段落切分,尽量不破坏完整语义;
- 每段保留来源、更新时间和所属项目;
- 检索后再进行一次相关性排序;
- 找不到可靠资料时,明确告诉用户,而不是让模型自己补充。
最后一点尤其重要。AI 最麻烦的情况不是“不知道”,而是不知道的时候仍然给出一个看起来很确定的答案。
第二步:让 AI 学会调用工具
接入知识库后,助手能够阅读内部资料了,但它仍然无法查询实时日志,也不能创建工单。
这时候就需要 Function Calling。
可以把它理解成:我们先告诉模型“你有哪些工具可以使用”,然后由模型判断什么时候使用、传入哪些参数。
例如,我给助手提供了一个日志查询工具:
tools = [
{
"name": "query_service_logs",
"description": "查询指定服务最近一段时间的错误日志",
"parameters": {
"type": "object",
"properties": {
"service_name": {
"type": "string",
"description": "需要查询的服务名称"
},
"minutes": {
"type": "integer",
"description": "查询最近多少分钟的日志"
}
},
"required": ["service_name", "minutes"]
}
}
]
模型分析用户的问题后,可能返回这样的调用参数:
{
"name": "query_service_logs",
"arguments": {
"service_name": "order-service",
"minutes": 30
}
}
需要注意的是,模型并没有直接访问日志系统。
它只是告诉我们的程序:“我认为现在应该调用这个工具,参数是这些。”真正执行查询的,仍然是我们自己编写的代码:
def query_service_logs(service_name, minutes):
# 实际项目中,这里会请求日志平台
return {
"service": service_name,
"error_count": 236,
"main_error": "Database connection pool exhausted",
"average_latency": "4.8s"
}
程序再把查询结果交还给模型,模型继续分析:
订单服务最近30分钟出现236次错误,
主要错误为数据库连接池耗尽,
接口平均响应时间为4.8秒。
这样一来,模型的判断就不再完全依赖猜测,而是有了真实数据。
真正费脑筋的地方:让它连续完成任务
一开始,我以为给模型配置几个工具就结束了。
真正写起来才发现,工具调用只是第一步。一个任务通常不可能通过一次调用完成。
比如处理订单服务超时,需要经历:
查知识库 → 查日志 → 分析结果 → 判断等级 → 创建工单
每一步都依赖上一步的结果。
于是还需要给它加一个执行循环:
def run_agent(user_input):
messages = [
{
"role": "system",
"content": """
你是一名运维助手。
请结合知识库和可用工具处理问题。
不能根据现有信息确认的内容,需要明确说明。
"""
},
{
"role": "user",
"content": user_input
}
]
for step in range(8):
response = llm.chat(
messages=messages,
tools=tools
)
if response.has_tool_call():
tool_name = response.tool_name
arguments = response.arguments
tool_result = execute_tool(
tool_name,
arguments
)
messages.append(response.to_message())
messages.append({
"role": "tool",
"name": tool_name,
"content": json.dumps(tool_result)
})
continue
return response.content
return "执行步骤过多,任务已经停止。"
这段代码做的事情可以概括为:
- 模型查看当前已有的信息;
- 模型决定下一步要不要调用工具;
- 程序执行工具;
- 执行结果交回模型;
- 模型根据新结果继续判断;
- 没有工具需要调用时,输出最终答案。
这种“看结果,再决定下一步”的循环,才是整个智能体比较关键的部分。
不过,也正是从这里开始,问题逐渐多了起来。
AI 有时候会“太勤快”
测试过程中,我遇到过一个很有意思的情况。
我让助手查询一次日志,它查询完之后觉得信息不够,又查了一次。第二次结果回来后,它换了一个时间范围继续查。再不限制,它很可能一直查下去。
还有一次,模型连续两次调用了创建工单接口,差点生成两张完全相同的工单。
所以,智能体不能只考虑“能不能完成任务”,还要防止它把事情做过头。
我最后加了几层限制。
限制最大执行次数
MAX_AGENT_STEPS = 8
超过最大步骤后直接停止,避免模型陷入循环。
防止重复调用
execution_key = hash(
tool_name + json.dumps(arguments, sort_keys=True)
)
if execution_key in executed_operations:
raise RuntimeError("检测到重复操作,本次调用已停止")
校验模型生成的参数
def validate_tool_arguments(tool_name, arguments):
if tool_name == "query_service_logs":
minutes = arguments["minutes"]
if minutes < 1:
raise ValueError("查询时间必须大于0")
if minutes > 1440:
raise ValueError("单次最多查询24小时")
模型生成了参数,并不代表参数一定合理。
工具的参数校验、权限校验和业务规则,仍然应该写在正常的程序代码里。不能因为接入了大模型,就把原来的安全机制全部交给它。
涉及真实操作时,别让 AI 自己拍板
查询文档和读取日志属于低风险操作,可以自动执行。
但重启服务、删除数据、发送外部邮件就完全不同了。
我的处理方式是给工具划分风险等级:
HIGH_RISK_TOOLS = {
"delete_database_record",
"restart_production_service",
"send_external_email"
}
当模型准备调用高风险工具时,程序不会立即执行,而是返回确认信息:
if tool_name in HIGH_RISK_TOOLS:
return {
"status": "waiting_confirmation",
"message": "该操作风险较高,请确认后继续执行。"
}
也就是说,AI 可以提出建议,可以准备好参数,但最后一步由人确认。
这个过程可能让自动化看起来没有那么“炫”,却更适合真实项目。
毕竟线上系统最怕的不是 AI 不够聪明,而是它非常自信地做错了一件事。
我还加了一个专门“挑刺”的 AI
为了减少错误,我做了一个小尝试:让另一个模型专门检查第一个模型的结果。
第一个智能体负责处理任务:
查资料、调工具、分析问题、生成方案
第二个智能体不执行操作,只负责检查:
证据是否充分?
结论是否和日志一致?
有没有遗漏风险?
是否执行了用户没有授权的操作?
简化后的代码如下:
solution = executor_agent.run(task)
review = reviewer_agent.run({
"task": task,
"solution": solution,
"check_items": [
"结论是否有日志或文档支持",
"是否把推测当成了事实",
"是否遗漏潜在风险",
"是否执行了未经授权的操作"
]
})
if review["passed"]:
return solution
return executor_agent.revise(
original_solution=solution,
review=review
)
这个设计并不能保证结果百分之百正确,第二个模型同样可能判断失误。
但在我的测试中,它确实能发现一些问题,比如:
- 日志只显示连接池不足,回答却直接认定是慢 SQL;
- 工具调用失败了,最终回答却说工单已经创建;
- 知识库文档已经过期,回答时没有提醒用户;
- 模型给出了结论,却没有说明依据。
它有点像开发过程中的 Code Review:不一定能拦住所有问题,但多一道检查通常比没有好。
关于“记忆”,我没有保存全部聊天记录
很多 AI 产品都在强调长期记忆。我最初也想把所有对话全部保存下来,后来发现这样做不太合适。
聊天记录越来越多以后,会出现几个问题:
- 发送给模型的内容太长;
- 旧信息可能已经失效;
- 用户偶然提到的一句话,不一定代表长期偏好;
- 保存过多内容还会带来隐私问题。
所以,我只保存相对稳定、确实对后续任务有用的信息,例如:
{
"preferred_language": "Python",
"deployment_environment": "Docker + Linux",
"answer_style": "先给结论,再说明原因"
}
历史问题则以摘要形式保存:
{
"problem": "订单服务数据库连接池耗尽",
"solution": "排查慢查询并调整连接池配置",
"result": "服务恢复",
"recorded_at": "2026-07-20"
}
下次遇到类似问题时,通过向量检索找出相关记录,不需要把全部历史对话重新发送一遍。
我的理解是,AI 记忆不是保存得越多越好,而是要知道什么值得记、什么时候应该忘。
项目结构不要全塞进一个文件
这种项目刚开始可能只有几百行代码,很容易把提示词、工具和模型调用全部写进一个文件。
工具增加后,维护起来会非常痛苦。
我后来按照职责做了拆分:
ai-agent/
├── api/ # 对外接口
├── agents/ # 智能体执行逻辑
│ ├── executor.py
│ └── reviewer.py
├── tools/ # 日志、工单等工具
│ ├── log_tool.py
│ └── ticket_tool.py
├── rag/ # 知识库检索
│ ├── embedding.py
│ └── retriever.py
├── memory/ # 用户偏好和历史经验
├── prompts/ # 提示词
├── security/ # 权限、参数和风险校验
└── main.py
提示词最好也单独管理。
提示词在 AI 项目中的作用有点像代码,但又不像普通代码那么稳定。模型升级、工具变化或者业务规则调整,都可能影响最终效果。把它散落在业务代码中,后面很难测试和修改。
做完之后,我对 AI 编程有了一点不同的看法
以前我觉得 AI 应用开发主要是“研究提示词”。
真正做了一个能调用工具的项目后,我发现提示词只占其中一部分。大量工作仍然是传统软件开发:
- 接口设计;
- 参数校验;
- 权限控制;
- 异常处理;
- 日志记录;
- 重试和超时;
- 调用成本控制;
- 结果评估。
不同之处在于,传统程序的执行路径通常由开发者提前写好:
if error_count > 100:
create_ticket()
而智能体会根据上下文、工具结果和任务目标,动态决定下一步做什么。
这种灵活性很有吸引力,但也会带来不确定性。我们需要做的,不是完全消除这种不确定性——那几乎不现实——而是把它限制在一个可接受的范围内。
最后
如果你也想做一个 AI 项目,我不太建议停留在简单的聊天页面。
可以尝试给模型接入一个真实场景:
- 读取项目文档;
- 查询数据库;
- 分析运行日志;
- 生成测试用例;
- 整理代码审查意见;
- 调用工单或消息接口。
哪怕只接入一两个工具,项目带来的体验也会完全不同。
这个技术助手目前还有不少问题,例如复杂任务成功率不稳定、长流程调用成本较高、知识库内容需要持续维护。但至少它已经不只是生成一段“正确但没什么用”的文字了。
它开始能够根据真实信息,帮我完成一些具体工作。
对我来说,这才是 AI Agent 最值得继续折腾的地方,体验点击下方下载,软件比较大安装比较慢。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)