聊聊我在家撸代码时折腾AI Agent的那些事儿
作者:一个在医疗软件公司写Python的远程打工人
发布时间:2025年7月14日 凌晨1:23
标签:
PythonAI Agent技术探索远程办公
现在是凌晨一点半,窗外安静得只剩下空调的嗡嗡声。我合上笔记本电脑,伸了个懒腰,决定趁脑子还清醒,把这段时间折腾技术的一些心得记录下来。
先简单介绍一下自己吧——我在一家做医疗信息化软件的公司当Python开发,主要搞HIS(医院信息系统)相关的后端服务。去年公司开始推远程办公,我就成了"居家撸码"大军的一员。说实话,远程办公爽是爽,不用挤地铁了,但有时候也挺孤独的,写技术博客算是我给自己找的一个出口吧。
最近工作之余一直在研究Rust,那所有权机制和生命周期搞得我头秃,但确实有意思。不过今天不聊Rust,聊聊我这段时间在AI Agent方向的一些探索和实践,主要用到了智谱清言和CrewAI这两个东西。
事情是这样的
上个月,我们组接了个需求——给某三甲医院的药剂科做一个"智能用药审核助手"。需求文档写得挺漂亮:基于患者病历、检验指标、用药历史,自动进行药物相互作用审查,给出风险提示和建议。
产品经理开会的时候说得轻巧:"不就是调个大模型的API嘛,一周搞定。"
我当时就想翻白眼。兄弟,这是医疗场景啊,你让一个幻觉率感人的人工智能随便给用药建议?出了事谁负责?
但需求已经排进Sprint了,deadline就摆在那。我想了想,与其让大模型直接给结论,不如让它当一个"审核员",把规则引擎的结果做一层智能解读和补充。说白了,就是搞一个AI Agent的工作流。
为什么选智谱清言
技术选型这块,我纠结了好几天。OpenAI的GPT-4肯定好用,但有两个问题:一是数据合规,医疗数据出境这事儿太敏感了,公司合规部门直接一票否决;二是成本,按我们的调用量算下来,一个月光API费用就得小几万。
后来同事推荐我试试智谱清言(GLM系列模型)。说实话,一开始我是抱着"国产能行吗"的心态去试的,结果还真让我有点惊喜。
几个打动我的点:
| 对比维度 | GPT-4 | 智谱清言(GLM-4) |
|---|---|---|
| 数据合规 | 数据出境,合规风险高 | 国内部署,完全合规 ✅ |
| API价格 | 约$30/1M tokens | 约¥60/1M tokens,便宜一大截 ✅ |
| 中文医疗术语理解 | 还行,偶尔犯傻 | 对中文医学术语理解明显更好 ✅ |
| 响应速度 | 偶尔抽风 | 比较稳定 ✅ |
| Function Calling | 成熟 | 支持,基本够用 ✅ |
最让我满意的是它对中文医疗术语的理解。比如"丙氨酸氨基转移酶"和"ALT"它都知道是一回事,GPT-4有时候还会把"他汀类药物"跟"他唑"搞混,属实离谱。
# 智谱清言 API 调用示例
from zhipuai import ZhipuAI
client = ZhipuAI(api_key="your-api-key")
response = client.chat.completions.create(
model="glm-4",
messages=[
{
"role": "system",
"content": "你是一名专业的临床药师,负责审核处方中的药物相互作用风险。"
},
{
"role": "user",
"content": "患者正在服用华法林,新处方增加了阿司匹林,请评估风险。"
}
],
tools=[
{
"type": "function",
"function": {
"name": "check_drug_interaction",
"description": "查询两种药物之间的相互作用信息",
"parameters": {
"type": "object",
"properties": {
"drug_a": {"type": "string", "description": "药物A名称"},
"drug_b": {"type": "string", "description": "药物B名称"}
},
"required": ["drug_a", "drug_b"]
}
}
}
]
)
CrewAI登场:多Agent协作才是正道
光有大模型还不够。我们的场景其实挺复杂的——需要查药品数据库、需要看患者历史用药、需要比对检验指标、最后还要生成一份结构化的审核报告。一个Agent根本搞不定。
这时候CrewAI就派上用场了。
CrewAI是一个Python框架,专门用来编排多个AI Agent协同工作。你可以把它理解成给一群AI"分工派活"的工具。每个Agent有自己的角色、目标和工具,它们之间可以互相传递信息、协作完成任务。
我当时看到CrewAI的文档,第一反应是:这不就是给AI搞了个"项目管理"嘛。Agent是"员工",Task是"工单",Crew是"项目组"。嘿,这比喻还挺贴切的。
我设计的Agent工作流大概是这样的:
[数据采集Agent] → [规则审核Agent] → [智能解读Agent] → [报告生成Agent]
↓ ↓ ↓ ↓
拉取患者信息 比对药物相互作用 结合临床给出建议 输出结构化报告
检验指标等 查询禁忌症等 评估风险等级 格式化输出
来看看核心代码长什么样:
from crewai import Agent, Task, Crew, Process
from crewai_tools import tool
import requests
# 定义工具函数
@tool("查询药物相互作用数据库")
def check_interaction(drug_a: str, drug_b: str) -> str:
"""查询两种药物之间的相互作用信息"""
# 这里调用我们内部的药品知识库API
resp = requests.get(
f"http://internal-api/drug-interaction",
params={"drug_a": drug_a, "drug_b": drug_b}
)
return resp.json().get("result", "未查到相关信息")
@tool("获取患者用药历史")
def get_patient_med_history(patient_id: str) -> str:
"""获取患者近3个月的用药记录"""
resp = requests.get(f"http://internal-api/patient/{patient_id}/med-history")
return resp.json().get("history", "无记录")
# 定义Agent
data_agent = Agent(
role="医疗数据采集专员",
goal="准确获取患者的基本信息、当前用药和检验指标",
backstory="你是一个严谨的医疗数据管理员,对数据准确性有极高的要求。",
tools=[get_patient_med_history],
verbose=True,
allow_delegation=False
)
review_agent = Agent(
role="药物相互作用审核员",
goal="基于规则引擎和药品知识库,全面审查处方中的潜在风险",
backstory="你是一名资深临床药师,有20年的处方审核经验,对药物相互作用了如指掌。",
tools=[check_interaction],
verbose=True,
allow_delegation=True
)
interpret_agent = Agent(
role="临床建议分析师",
goal="结合患者具体情况,给出个性化的用药调整建议",
backstory="你是一名临床药学专家,擅长将复杂的药物相互作用信息转化为医生能理解的建议。",
verbose=True,
allow_delegation=True
)
report_agent = Agent(
role="审核报告生成专员",
goal="将审核结果整理成结构化的、符合医院规范的审核报告",
backstory="你是一名医疗文档专家,熟悉各类医疗报告的格式和规范要求。",
verbose=True,
allow_delegation=False
)
# 定义任务
task1 = Task(
description="获取患者{patient_id}的完整用药信息和近期检验指标",
expected_output="患者的用药历史列表和关键检验指标",
agent=data_agent
)
task2 = Task(
description="审查当前处方与患者用药历史之间的药物相互作用风险",
expected_output="详细的药物相互作用分析,包括风险等级和具体机制",
agent=review_agent,
context=[task1]
)
task3 = Task(
description="基于审核结果,结合患者肝肾功能等指标,给出个性化建议",
expected_output="包含风险评估和具体调整建议的分析报告",
agent=interpret_agent,
context=[task2]
)
task4 = Task(
description="生成最终的处方审核报告,包含风险等级、问题列表和建议",
expected_output="JSON格式的结构化审核报告",
agent=report_agent,
context=[task3]
)
# 组建团队并执行
crew = Crew(
agents=[data_agent, review_agent, interpret_agent, report_agent],
tasks=[task1, task2, task3, task4],
process=Process.sequential,
verbose=True
)
result = crew.kickoff(inputs={"patient_id": "P20250714001"})
踩过的坑,说多了都是泪
看着代码挺优雅的是吧?实际开发的时候,我踩的坑能填满一个化粪池。
坑一:Agent之间的"传话游戏"
CrewAI的Agent之间传递信息,本质上就是把上一个Agent的输出文本塞给下一个Agent当输入。问题是什么呢?信息在传递过程中会"失真"。
比如数据采集Agent拉了一大堆检验指标,传给审核Agent的时候,审核Agent可能只关注了其中几个,把其他数据"创造性地"忽略了。有一次它甚至把患者的肌酐值120看成了210,差点给出"建议停药"的离谱建议。
我的解决方案是在每个Task的description里把关键字段明确列出来,并且加了一句"你必须完整引用前序任务中的所有数据,不得遗漏或修改"。虽然有点笨,但确实管用。
坑二:Token消耗比想象中猛
四个Agent串行执行,每个Agent都有自己的system prompt和context,token消耗是指数级增长的。一开始我没注意,跑了几十次测试,智谱清言的账户余额直接见底。
后来我做了几个优化:
- 精简每个Agent的backstory,去掉那些"花里胡哨"的人设描述
- 对中间结果做截断,不需要的信息不往下传
- 加了一个缓存层,相同的药物相互作用查询直接走Redis
优化之后,单次审核的token消耗从大约8000降到了3000左右,成本直接砍了60%。
坑三:输出格式不稳定
报告生成Agent有时候会"自由发挥",输出的JSON格式不统一。今天给你多个字段,明天给你少个字段,下游系统解析直接报错。
最后我在prompt里加了few-shot examples,把期望的输出格式写死,同时在代码层加了JSON Schema校验,格式不对就让它重新生成。虽然多花了几秒钟,但至少不会炸了。
import jsonschema
REPORT_SCHEMA = {
"type": "object",
"properties": {
"risk_level": {"type": "string", "enum": ["低", "中", "高", "极高"]},
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"drug_pair": {"type": "array", "items": {"type": "string"}},
"severity": {"type": "string"},
"mechanism": {"type": "string"},
"suggestion": {"type": "string"}
},
"required": ["drug_pair", "severity", "suggestion"]
}
},
"overall_suggestion": {"type": "string"}
},
"required": ["risk_level", "issues", "overall_suggestion"]
}
def validate_report(report_json: dict) -> bool:
try:
jsonschema.validate(instance=report_json, schema=REPORT_SCHEMA)
return True
except jsonschema.ValidationError as e:
print(f"报告格式校验失败: {e.message}")
return False
最终效果
折腾了差不多三周(中间还加了两天班赶进度),这个智能用药审核助手总算上线了。跑了一个月的数据,效果还不错:
| 指标 | 数据 |
|---|---|
| 日均审核处方量 | 约1200份 |
| 药物相互作用检出率 | 94.7%(规则引擎单独跑是82.3%) |
| 假阳性率 | 从23%降到9% |
| 平均审核耗时 | 3.2秒/份 |
| 药剂科满意度 | 4.6/5.0 |
最让我开心的是,药剂科的主任跟我说,以前他们审一张处方要翻半天书,现在AI先把初筛做了,他们只需要复核高风险的就行了,效率提升了不止一倍。
听到这种反馈的时候,说实话,加班的那些怨气都散了。
一些心得体会
最后随便聊几点感受吧,不一定对,就当是碎碎念。
1. AI Agent不是万能的,但确实好用
别把Agent想得太神,它本质上还是大模型在驱动,该幻觉的时候照样幻觉。但如果你能把它放在合适的场景里,给它足够的约束和校验,它真的能解决很多实际问题。
2. 技术选型要务实
智谱清言不是最强的模型,但在我们的场景里,它是最合适的。合规、成本、中文能力,这三点缺一不可。不要盲目追新追强,适合业务的才是最好的。
3. CrewAI适合快速验证,生产环境还得打磨
CrewAI做原型验证是真的快,半天就能搭出一个多Agent工作流。但要上生产,你还需要自己加很多"防护网"——错误重试、超时控制、输出校验、日志追踪等等。框架不会帮你做这些脏活累活。
4. 远程办公需要自律
说回我自己,在家办公最大的挑战其实是自律。没有同事在旁边卷,很容易就摸鱼了。我给自己定了个规矩:每天至少写200行有效代码(不算注释和空行),完不成就不能开游戏。这个博客就是在这种"自律"下产出的,哈哈。
好了,凌晨两点了,该睡了。明天还得跟测试对Bug呢,听说他们又提了个"AI给出的建议跟医生经验不一致"的bug,这锅我可不背,AI又不是医生,建议仅供参考好吧。
下次有机会再聊聊Rust的学习心得吧,那玩意儿又是另一个坑了。
晚安,各位码农。🌙
如果这篇文章对你有帮助,欢迎点赞、收藏、转发。有问题可以在评论区交流,我远程摸鱼的时候可能会回你。


评论 0